graphics: ssd1306: reimplement dithering using an ordered dither map

This commit is contained in:
2026-01-03 14:47:38 +01:00
parent b6029778e0
commit f4c379a3e8

View File

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