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<()>{
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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.
|
||||
///
|
||||
|
@ -90,6 +91,11 @@ impl EInkBuffer {
|
|||
let v = vec![DisplayColor::Black; width * height];
|
||||
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 {
|
||||
|
@ -128,16 +134,17 @@ pub trait Ditherer {
|
|||
|
||||
/// Find the closest approximate palette color to the given sRGB value.
|
||||
/// This uses euclidian distance in linear space.
|
||||
#[must_use]
|
||||
pub fn nearest_neighbor(input_color: Lab) -> (DisplayColor, Lab) {
|
||||
let (nearest, _, color_diff) = DISPLAY_PALETTE
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, p_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))
|
||||
.unwrap();
|
||||
.expect("could not find a color");
|
||||
(DisplayColor::from_u8(nearest as u8), color_diff)
|
||||
}
|
||||
|
||||
|
@ -158,16 +165,85 @@ impl Ditherer for NNDither {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
fn dither(&mut self, img: &RgbImage, output: &mut EInkBuffer) {
|
||||
// create a copy of the image in Lab space, mutable.
|
||||
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 {
|
||||
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;
|
||||
|
||||
use crate::display::Wrapper;
|
||||
use crate::imageproc::{Ditherer, EInkBuffer, NNDither};
|
||||
use crate::imageproc::{Ditherer, EInkBuffer, FloydSteinbergDither, NNDither};
|
||||
use clap::{Parser, Subcommand};
|
||||
use image::RgbImage;
|
||||
|
||||
|
@ -19,25 +19,47 @@ struct Cli {
|
|||
enum Command {
|
||||
/// Load a single image
|
||||
Show,
|
||||
/// Display a test pattern
|
||||
Test,
|
||||
/// Start the HTTP sever
|
||||
Serve,
|
||||
/// Convert an image and save it.
|
||||
Convert,
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let cli = Cli::parse();
|
||||
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 mut display = Wrapper::new()?;
|
||||
|
||||
let mut eink_buf = EInkBuffer::new(800, 480);
|
||||
let dither = NNDither {};
|
||||
let mut dither = NNDither{};
|
||||
|
||||
dither.dither(&img, &mut eink_buf);
|
||||
let raw_buf = eink_buf.into_display_buffer();
|
||||
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(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue