From f4c379a3e83889cd9e4e7f7fc4d414f882d50176 Mon Sep 17 00:00:00 2001 From: Victoria Fischer Date: Sat, 3 Jan 2026 14:47:38 +0100 Subject: [PATCH] graphics: ssd1306: reimplement dithering using an ordered dither map --- src/graphics/ssd1306.rs | 59 ++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/src/graphics/ssd1306.rs b/src/graphics/ssd1306.rs index 0ecb89e..6d1bbf7 100644 --- a/src/graphics/ssd1306.rs +++ b/src/graphics/ssd1306.rs @@ -1,12 +1,12 @@ #![cfg(feature="oled")] -use core::cmp::min; +use core::{cmp::min, ops::{BitAnd, Shr}}; use display_interface::DisplayError; use embedded_graphics::prelude::*; use esp_hal::{gpio::Output, i2c::master::I2c, Async}; use figments::{liber8tion::{interpolate::Fract8, noise}, mappings::embedded_graphics::Matrix2DSpace, prelude::*}; use embedded_graphics::pixelcolor::BinaryColor; -use figments::pixels::PixelSink; +use figments::pixels::AdditivePixelSink; use figments_render::output::OutputAsync; use ssd1306::{prelude::DisplayRotation, size::DisplaySize128x64, I2CDisplayInterface, Ssd1306Async}; use embassy_time::Delay; @@ -38,29 +38,38 @@ impl SsdPixel { } } -impl PixelSink for SsdPixel { - fn set(&mut self, pixel: &BinaryColor) { - self.set_pixel(*pixel); - } -} +const DITHER_MAP: [u32;15] = [ + 0b1000_0000_0000_0000, + 0b1000_0000_0010_0000, + 0b1010_0000_0010_0000, + 0b1010_0000_1010_0000, + 0b1010_0100_1010_0000, + 0b1010_0100_1010_0001, + 0b1010_0101_1010_0001, + 0b1010_0101_1010_0101, + 0b1110_0101_1010_0101, + 0b1110_0101_1011_0101, + 0b1111_0101_1011_0101, + 0b1111_0101_1111_0101, + 0b1111_1101_1111_0101, + 0b1111_1101_1111_0111, + 0b1111_1111_1111_0111, +]; -impl PixelBlend for SsdPixel { - fn blend_pixel(self, overlay: BinaryColor, opacity: Fract8) -> Self { - let scale = 48; - let x = self.coords.x * scale; - let y = self.coords.y * scale; - let stiple_idx = noise::inoise8(x as i16, y as i16); - if opacity >= stiple_idx { - self.set_pixel(overlay); +impl AdditivePixelSink for SsdPixel { + fn add(&mut self, pixel: BinaryColor, opacity: Fract8) { + match opacity { + 0 => (), + 255 => self.set_pixel(pixel), + _ => { + let dither_value = DITHER_MAP[opacity as usize / 16]; + let dither_x = self.coords.x % 4; + let dither_y = self.coords.x % 4; + if dither_value.shr(dither_x).shr(dither_y * 4).bitand(0x01) == 1 { + self.set_pixel(pixel); + } + } } - self - } - - fn multiply(self, overlay: BinaryColor) -> Self { - if overlay != self.get_pixel() { - self.set_pixel(overlay); - } - self } } @@ -195,9 +204,9 @@ impl DrawTarget for SsdOutput { fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> { if color.is_on() { - self.pixbuf.fill(255); + self.pixbuf.fill_with(|| { 255 }); } else { - self.pixbuf.fill(0); + self.pixbuf.fill_with(|| { 0 }); } Ok(()) }