cadmus_core/framebuffer/
transform.rs

1use super::image::Pixmap;
2use crate::color::Color;
3use lazy_static::lazy_static;
4
5pub type ColorTransform = fn(u32, u32, Color) -> Color;
6
7const DITHER_PITCH: u32 = 128;
8
9lazy_static! {
10    // Tileable blue noise matrix.
11    pub static ref DITHER_G16_DRIFTS: Vec<i8> = {
12        let pixmap = Pixmap::from_png("resources/blue_noise-128.png").unwrap();
13        // The gap between two succesive colors in G16 is 17.
14        // Map {0 .. 255} to {-8 .. 8}.
15        pixmap.data().iter().map(|&v| {
16            match v {
17                  0..=119 => v as i8 / 15 - 8,
18                      120 => 0,
19                121..=255 => ((v - 121) / 15) as i8,
20            }
21        }).collect()
22    };
23
24    // Tileable blue noise matrix.
25    pub static ref DITHER_G2_DRIFTS: Vec<i8> = {
26        let pixmap = Pixmap::from_png("resources/blue_noise-128.png").unwrap();
27        // Map {0 .. 255} to {-128 .. 127}.
28        pixmap.data().iter().map(|&v| {
29            match v {
30                  0..=127 => -128 + (v as i8),
31                128..=255 => (v - 128) as i8,
32            }
33        }).collect()
34    };
35}
36
37// Ordered dithering.
38// The input color is in {0 .. 255}.
39// The output color is in G16.
40// G16 := {17 * i | i ∈ {0 .. 15}}.
41pub fn transform_dither_g16(x: u32, y: u32, color: Color) -> Color {
42    let gray = color.gray();
43    // Get the address of the drift value.
44    let addr = (x % DITHER_PITCH) + (y % DITHER_PITCH) * DITHER_PITCH;
45    // Apply the drift to the input color.
46    let c = (gray as i16 + DITHER_G16_DRIFTS[addr as usize] as i16).clamp(0, 255);
47    // Compute the distance to the previous color in G16.
48    let d = c % 17;
49    // Return the nearest color in G16.
50    Color::Gray(if d < 9 {
51        (c - d) as u8
52    } else {
53        (c + (17 - d)) as u8
54    })
55}
56
57// Ordered dithering.
58// The input color is in {0 .. 255}.
59// The output color is in {0, 255}.
60pub fn transform_dither_g2(x: u32, y: u32, color: Color) -> Color {
61    let gray = color.gray();
62    // Get the address of the drift value.
63    let addr = (x % DITHER_PITCH) + (y % DITHER_PITCH) * DITHER_PITCH;
64    // Apply the drift to the input color.
65    let c = (gray as i16 + DITHER_G2_DRIFTS[addr as usize] as i16).clamp(0, 255);
66    // Return the nearest color in G2.
67    Color::Gray(if c < 128 { 0 } else { 255 })
68}
69
70pub fn transform_identity(_x: u32, _y: u32, color: Color) -> Color {
71    color
72}