add sleeping
This commit is contained in:
parent
a161fdb011
commit
dd148f07a2
|
@ -51,6 +51,12 @@ impl Wrapper {
|
||||||
}
|
}
|
||||||
pub fn display(&mut self, buf: &[u8]) -> Result<()>{
|
pub fn display(&mut self, buf: &[u8]) -> Result<()>{
|
||||||
self.panel.update_and_display_frame(&mut self.spi, buf, &mut self.delay)?;
|
self.panel.update_and_display_frame(&mut self.spi, buf, &mut self.delay)?;
|
||||||
|
self.panel.sleep(&mut self.spi, &mut self.delay)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn test(&mut self) -> Result<()> {
|
||||||
|
self.panel.show_7block(&mut self.spi, &mut self.delay)?;
|
||||||
|
self.panel.sleep(&mut self.spi, &mut self.delay)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use image::RgbImage;
|
use image::RgbImage;
|
||||||
|
|
||||||
use palette::{cast::FromComponents, color_difference::Ciede2000, IntoColor, Lab, Oklch, Srgb};
|
use palette::{cast::FromComponents, IntoColor, Lab, Oklch, Srgb};
|
||||||
|
use palette::color_difference::{Ciede2000, EuclideanDistance};
|
||||||
|
|
||||||
/// Palette used on the display; pixels can be one of these colors.
|
/// Palette used on the display; pixels can be one of these colors.
|
||||||
///
|
///
|
||||||
|
@ -90,6 +91,11 @@ impl EInkBuffer {
|
||||||
let v = vec![DisplayColor::Black; width * height];
|
let v = vec![DisplayColor::Black; width * height];
|
||||||
Self(v)
|
Self(v)
|
||||||
}
|
}
|
||||||
|
// pub fn make_image(&self) -> RgbImage {
|
||||||
|
// RgbImage::from_fn(800, 480, |x, y| {
|
||||||
|
// let srgb = Srgb::from(self.0[y * 800 + x]);
|
||||||
|
// })
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl EInkBuffer {
|
// impl EInkBuffer {
|
||||||
|
@ -128,16 +134,17 @@ pub trait Ditherer {
|
||||||
|
|
||||||
/// Find the closest approximate palette color to the given sRGB value.
|
/// Find the closest approximate palette color to the given sRGB value.
|
||||||
/// This uses euclidian distance in linear space.
|
/// This uses euclidian distance in linear space.
|
||||||
|
#[must_use]
|
||||||
pub fn nearest_neighbor(input_color: Lab) -> (DisplayColor, Lab) {
|
pub fn nearest_neighbor(input_color: Lab) -> (DisplayColor, Lab) {
|
||||||
let (nearest, _, color_diff) = DISPLAY_PALETTE
|
let (nearest, _, color_diff) = DISPLAY_PALETTE
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(idx, p_color)| {
|
.map(|(idx, p_color)| {
|
||||||
let c: Lab = (*p_color).into_color();
|
let c: Lab = (*p_color).into_color();
|
||||||
(idx, input_color.difference(c), input_color - c)
|
(idx, input_color.distance_squared(c), input_color - c)
|
||||||
})
|
})
|
||||||
.min_by(|(_, a, _), (_, b, _)| a.total_cmp(b))
|
.min_by(|(_, a, _), (_, b, _)| a.total_cmp(b))
|
||||||
.unwrap();
|
.expect("could not find a color");
|
||||||
(DisplayColor::from_u8(nearest as u8), color_diff)
|
(DisplayColor::from_u8(nearest as u8), color_diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,16 +165,85 @@ impl Ditherer for NNDither {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct FloydSteinbergDither();
|
pub struct FloydSteinbergDither();
|
||||||
|
|
||||||
|
/// Compute the vector index for a given image by using the size of rows. Assumes that images
|
||||||
|
/// are indexed in row-major order.
|
||||||
|
const fn coord_to_idx(x: u32, y: u32, xsize: u32) -> usize {
|
||||||
|
(y * xsize + x) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the error-adjusted new lab value based on the error value of the currently scanned
|
||||||
|
/// pixel, plus a scalar factor.
|
||||||
|
fn get_error_adjusted(orig: &Lab, err: &Lab, scalar: f32) -> Lab {
|
||||||
|
let (p_l, p_a, p_b) = orig.into_components();
|
||||||
|
let (err_l, err_a, err_b) = err.into_components();
|
||||||
|
Lab::from_components((
|
||||||
|
p_l + err_l * scalar,
|
||||||
|
p_a + err_a * scalar,
|
||||||
|
p_b + err_b * scalar,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DiffusionPoint {
|
||||||
|
xshift: i32,
|
||||||
|
yshift: i32,
|
||||||
|
scale: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiffusionPoint {
|
||||||
|
const fn new(xshift: i32, yshift: i32, scale: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
xshift,
|
||||||
|
yshift,
|
||||||
|
scale,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FLOYD_STEINBERG: [DiffusionPoint; 4] = [
|
||||||
|
DiffusionPoint::new(1, 0, 7.0 / 16.0),
|
||||||
|
DiffusionPoint::new(-1, 1, 3.0 / 16.0),
|
||||||
|
DiffusionPoint::new(0, 1, 5.0 / 16.0),
|
||||||
|
DiffusionPoint::new(1, 1, 1.0 / 16.0),
|
||||||
|
];
|
||||||
|
|
||||||
impl Ditherer for FloydSteinbergDither {
|
impl Ditherer for FloydSteinbergDither {
|
||||||
fn dither(&mut self, img: &RgbImage, output: &mut EInkBuffer) {
|
fn dither(&mut self, img: &RgbImage, output: &mut EInkBuffer) {
|
||||||
// create a copy of the image in Lab space, mutable.
|
// create a copy of the image in Lab space, mutable.
|
||||||
let srgb = <&[Srgb<u8>]>::from_components(&**img);
|
let srgb = <&[Srgb<u8>]>::from_components(&**img);
|
||||||
let mut temp_img: Vec<Lab> = Vec::new();
|
let (xsize, ysize) = img.dimensions();
|
||||||
|
let mut temp_img: Vec<Lab> = Vec::with_capacity((xsize * ysize) as usize);
|
||||||
for pix in srgb {
|
for pix in srgb {
|
||||||
temp_img.push(pix.into_format().into_color());
|
temp_img.push(pix.into_format().into_color());
|
||||||
}
|
}
|
||||||
|
// now we take our units.
|
||||||
|
|
||||||
|
for y in 0..ysize {
|
||||||
|
for x in 0..xsize {
|
||||||
|
let index = coord_to_idx(x, y, xsize);
|
||||||
|
let curr_pix = temp_img[index];
|
||||||
|
let (nearest, err) = nearest_neighbor(curr_pix);
|
||||||
|
// set the color in the output buffer.
|
||||||
|
output.0[index] = nearest;
|
||||||
|
// take the error, and propagate it.
|
||||||
|
for point in FLOYD_STEINBERG {
|
||||||
|
let Some(target_x) = x.checked_add_signed(point.xshift) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(target_y) = y.checked_add_signed(point.yshift) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let target = coord_to_idx(
|
||||||
|
target_x,
|
||||||
|
target_y,
|
||||||
|
xsize,
|
||||||
|
);
|
||||||
|
if let Some(pix) = temp_img.get(target) {
|
||||||
|
temp_img[target] = get_error_adjusted(pix, &err, point.scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
28
src/main.rs
28
src/main.rs
|
@ -3,7 +3,7 @@ pub mod display;
|
||||||
pub mod imageproc;
|
pub mod imageproc;
|
||||||
|
|
||||||
use crate::display::Wrapper;
|
use crate::display::Wrapper;
|
||||||
use crate::imageproc::{Ditherer, EInkBuffer, NNDither};
|
use crate::imageproc::{Ditherer, EInkBuffer, FloydSteinbergDither, NNDither};
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use image::RgbImage;
|
use image::RgbImage;
|
||||||
|
|
||||||
|
@ -19,25 +19,47 @@ struct Cli {
|
||||||
enum Command {
|
enum Command {
|
||||||
/// Load a single image
|
/// Load a single image
|
||||||
Show,
|
Show,
|
||||||
|
/// Display a test pattern
|
||||||
|
Test,
|
||||||
/// Start the HTTP sever
|
/// Start the HTTP sever
|
||||||
Serve,
|
Serve,
|
||||||
|
/// Convert an image and save it.
|
||||||
|
Convert,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
println!("CLI {cli:?}");
|
println!("CLI {cli:?}");
|
||||||
|
|
||||||
if let Command::Show = cli.command {
|
if matches!(cli.command, Command::Show) {
|
||||||
let img: RgbImage = image::io::Reader::open("myimage.png")?.decode()?.into();
|
let img: RgbImage = image::io::Reader::open("myimage.png")?.decode()?.into();
|
||||||
let mut display = Wrapper::new()?;
|
let mut display = Wrapper::new()?;
|
||||||
|
|
||||||
let mut eink_buf = EInkBuffer::new(800, 480);
|
let mut eink_buf = EInkBuffer::new(800, 480);
|
||||||
let dither = NNDither {};
|
let mut dither = NNDither{};
|
||||||
|
|
||||||
dither.dither(&img, &mut eink_buf);
|
dither.dither(&img, &mut eink_buf);
|
||||||
let raw_buf = eink_buf.into_display_buffer();
|
let raw_buf = eink_buf.into_display_buffer();
|
||||||
display.display(&raw_buf)?;
|
display.display(&raw_buf)?;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
if matches!(cli.command, Command::Test) {
|
||||||
|
let mut display = Wrapper::new()?;
|
||||||
|
|
||||||
|
display.test()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(cli.command, Command::Convert) {
|
||||||
|
let img: RgbImage = image::io::Reader::open("myimage.png")?.decode()?.into();
|
||||||
|
let mut eink_buf = EInkBuffer::new(800, 480);
|
||||||
|
let mut dither = FloydSteinbergDither{};
|
||||||
|
|
||||||
|
dither.dither(&img, &mut eink_buf);
|
||||||
|
let raw_buf = eink_buf.into_display_buffer();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue