qrcode_rs/render/
image.rs

1#![cfg(feature = "image")]
2use crate::render::{Canvas, Pixel};
3use crate::types::Color;
4
5use image::{ImageBuffer, Luma, LumaA, Primitive, Rgb, Rgba};
6
7macro_rules! impl_pixel_for_image_pixel {
8    ($p:ident<$s:ident>: $c:pat => $d:expr) => {
9        impl<$s> Pixel for $p<$s>
10        where
11            $s: Primitive + 'static,
12            $p<$s>: image::Pixel<Subpixel = $s>,
13        {
14            type Image = ImageBuffer<Self, Vec<$s>>;
15            type Canvas = (Self, Self::Image);
16
17            fn default_color(color: Color) -> Self {
18                match color.select($s::zero(), $s::max_value()) {
19                    $c => $p($d),
20                }
21            }
22        }
23    };
24}
25
26impl_pixel_for_image_pixel! { Luma<S>: p => [p] }
27impl_pixel_for_image_pixel! { LumaA<S>: p => [p, S::max_value()] }
28impl_pixel_for_image_pixel! { Rgb<S>: p => [p, p, p] }
29impl_pixel_for_image_pixel! { Rgba<S>: p => [p, p, p, S::max_value()] }
30
31impl<P: image::Pixel + 'static> Canvas for (P, ImageBuffer<P, Vec<P::Subpixel>>) {
32    type Pixel = P;
33    type Image = ImageBuffer<P, Vec<P::Subpixel>>;
34
35    fn new(width: u32, height: u32, dark_pixel: P, light_pixel: P) -> Self {
36        (dark_pixel, ImageBuffer::from_pixel(width, height, light_pixel))
37    }
38
39    fn draw_dark_pixel(&mut self, x: u32, y: u32) {
40        self.1.put_pixel(x, y, self.0);
41    }
42
43    fn into_image(self) -> ImageBuffer<P, Vec<P::Subpixel>> {
44        self.1
45    }
46}
47
48#[cfg(test)]
49mod render_tests {
50    use crate::render::Renderer;
51    use crate::types::Color;
52    use image::{Luma, Rgba};
53
54    #[test]
55    fn test_render_luma8_unsized() {
56        let image = Renderer::<Luma<u8>>::new(
57            &[
58                Color::Light,
59                Color::Dark,
60                Color::Dark,
61                //
62                Color::Dark,
63                Color::Light,
64                Color::Light,
65                //
66                Color::Light,
67                Color::Dark,
68                Color::Light,
69            ],
70            3,
71            1,
72        )
73        .module_dimensions(1, 1)
74        .build();
75
76        #[rustfmt::skip]
77            let expected = [
78            255, 255, 255, 255, 255,
79            255, 255,   0,   0, 255,
80            255,   0, 255, 255, 255,
81            255, 255,   0, 255, 255,
82            255, 255, 255, 255, 255,
83        ];
84        assert_eq!(image.into_raw(), expected);
85    }
86
87    #[test]
88    fn test_render_rgba_unsized() {
89        let image = Renderer::<Rgba<u8>>::new(&[Color::Light, Color::Dark, Color::Dark, Color::Dark], 2, 1)
90            .module_dimensions(1, 1)
91            .build();
92
93        #[rustfmt::skip]
94            let expected: &[u8] = &[
95            255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
96            255,255,255,255, 255,255,255,255,   0,  0,  0,255, 255,255,255,255,
97            255,255,255,255,   0,  0,  0,255,   0,  0,  0,255, 255,255,255,255,
98            255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
99        ];
100
101        assert_eq!(image.into_raw(), expected);
102    }
103
104    #[test]
105    fn test_render_resized_min() {
106        let image = Renderer::<Luma<u8>>::new(&[Color::Dark, Color::Light, Color::Light, Color::Dark], 2, 1)
107            .min_dimensions(10, 10)
108            .build();
109
110        #[rustfmt::skip]
111            let expected: &[u8] = &[
112            255,255,255, 255,255,255, 255,255,255, 255,255,255,
113            255,255,255, 255,255,255, 255,255,255, 255,255,255,
114            255,255,255, 255,255,255, 255,255,255, 255,255,255,
115
116            255,255,255,   0,  0,  0, 255,255,255, 255,255,255,
117            255,255,255,   0,  0,  0, 255,255,255, 255,255,255,
118            255,255,255,   0,  0,  0, 255,255,255, 255,255,255,
119
120            255,255,255, 255,255,255,   0,  0,  0, 255,255,255,
121            255,255,255, 255,255,255,   0,  0,  0, 255,255,255,
122            255,255,255, 255,255,255,   0,  0,  0, 255,255,255,
123
124            255,255,255, 255,255,255, 255,255,255, 255,255,255,
125            255,255,255, 255,255,255, 255,255,255, 255,255,255,
126            255,255,255, 255,255,255, 255,255,255, 255,255,255,
127        ];
128
129        assert_eq!(image.dimensions(), (12, 12));
130        assert_eq!(image.into_raw(), expected);
131    }
132
133    #[test]
134    fn test_render_resized_max() {
135        let image = Renderer::<Luma<u8>>::new(&[Color::Dark, Color::Light, Color::Light, Color::Dark], 2, 1)
136            .max_dimensions(10, 5)
137            .build();
138
139        #[rustfmt::skip]
140            let expected: &[u8] = &[
141            255,255, 255,255, 255,255, 255,255,
142
143            255,255,   0,  0, 255,255, 255,255,
144
145            255,255, 255,255,   0,  0, 255,255,
146
147            255,255, 255,255, 255,255, 255,255,
148        ];
149
150        assert_eq!(image.dimensions(), (8, 4));
151        assert_eq!(image.into_raw(), expected);
152    }
153}