Skip to main content

epaper_dithering_core/
enums.rs

1use crate::algorithms::{
2    Kernel, ATKINSON, BURKES, FLOYD_STEINBERG, JARVIS_JUDICE_NINKE, SIERRA, SIERRA_LITE, STUCKI,
3};
4use crate::error::DitherError;
5use crate::palettes::Palette;
6use crate::tone_map;
7
8/// Dithering algorithm. Integer values match OpenDisplay firmware conventions.
9#[repr(u8)]
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
11pub enum DitherMode {
12    None           = 0,
13    #[default]
14    Burkes         = 1,
15    Ordered        = 2,
16    FloydSteinberg = 3,
17    Atkinson       = 4,
18    Stucki         = 5,
19    Sierra         = 6,
20    SierraLite     = 7,
21    JarvisJudiceNinke = 8,
22}
23
24/// Dynamic range compression applied before dithering.
25/// Only meaningful for measured palettes (ignored for `ColorScheme`).
26#[derive(Debug, Clone, Copy, PartialEq, Default)]
27pub enum ToneCompression {
28    /// Reinhard 2004 skewness-based auto strength. Best for photos.
29    #[default]
30    Auto,
31    /// Fixed blend strength in [0.0, 1.0]. 0.0 disables.
32    Fixed(f64),
33}
34
35impl ToneCompression {
36    pub fn apply(self, pixels: &mut [[f64; 3]], palette: &Palette) {
37        match self {
38            ToneCompression::Auto => tone_map::auto_compress_dynamic_range(pixels, palette),
39            ToneCompression::Fixed(s) => if s > 0.0 {
40                tone_map::compress_dynamic_range(pixels, palette, s)
41            }
42        }
43    }
44}
45
46/// Gamut compression applied before dithering.
47#[derive(Debug, Clone, Copy, PartialEq, Default)]
48pub enum GamutCompression {
49    /// No gamut compression (default).
50    #[default]
51    None,
52    /// Full gamut compression at strength 1.0.
53    Auto,
54    /// Fixed blend strength in [0.0, 1.0].
55    Fixed(f64),
56}
57
58impl GamutCompression {
59    pub fn apply(self, pixels: &mut [[f64; 3]], palette: &Palette) {
60        match self {
61            GamutCompression::None => {}
62            GamutCompression::Auto => tone_map::gamut_compress(pixels, palette, 1.0),
63            GamutCompression::Fixed(s) => if s > 0.0 {
64                tone_map::gamut_compress(pixels, palette, s)
65            }
66        }
67    }
68
69
70}
71
72impl TryFrom<u8> for DitherMode {
73    type Error = DitherError;
74
75    fn try_from(v: u8) -> Result<Self, Self::Error> {
76        match v {
77            0 => Ok(DitherMode::None),
78            1 => Ok(DitherMode::Burkes),
79            2 => Ok(DitherMode::Ordered),
80            3 => Ok(DitherMode::FloydSteinberg),
81            4 => Ok(DitherMode::Atkinson),
82            5 => Ok(DitherMode::Stucki),
83            6 => Ok(DitherMode::Sierra),
84            7 => Ok(DitherMode::SierraLite),
85            8 => Ok(DitherMode::JarvisJudiceNinke),
86            _ => Err(DitherError::UnknownDitherMode(v)),
87        }
88    }
89}
90
91impl DitherMode {
92    pub fn kernel(self) -> Option<&'static Kernel> {
93        match self {
94            DitherMode::None | DitherMode::Ordered => None,
95            DitherMode::FloydSteinberg => Some(&FLOYD_STEINBERG),
96            DitherMode::Burkes => Some(&BURKES),
97            DitherMode::Atkinson => Some(&ATKINSON),
98            DitherMode::Stucki => Some(&STUCKI),
99            DitherMode::Sierra => Some(&SIERRA),
100            DitherMode::SierraLite => Some(&SIERRA_LITE),
101            DitherMode::JarvisJudiceNinke => Some(&JARVIS_JUDICE_NINKE),
102        }
103    }
104}