add sleeping

This commit is contained in:
saji 2024-07-17 00:24:11 -05:00
parent a161fdb011
commit dd148f07a2
3 changed files with 112 additions and 8 deletions

View file

@ -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(())
}
}

View file

@ -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);
}
}
}
}
}
}

View file

@ -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(())
}