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 Color::Dark,
60 Color::Light,
61 Color::Light,
62 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}