qrcode/render/
image.rs

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