1use crate::scan_converter::ScanConverter;
9
10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
15pub enum GrayscaleLevel {
16 Level2x2 = 2,
18 Level4x4 = 4,
20 Level8x8 = 8,
22}
23
24impl GrayscaleLevel {
25 pub const fn factor(self) -> usize {
27 self as usize
28 }
29
30 pub const fn samples_per_pixel(self) -> usize {
32 let f = self.factor();
33 f * f
34 }
35
36 pub const fn max_alpha(self) -> u8 {
38 self.samples_per_pixel() as u8
39 }
40}
41
42pub fn render_grayscale(
60 sc: &mut ScanConverter,
61 width: usize,
62 height: usize,
63 level: GrayscaleLevel,
64) -> Vec<u8> {
65 let factor = level.factor();
66 let _samples_per_pixel = level.samples_per_pixel();
67
68 let mono_width = width * factor;
72 let mono_height = height * factor;
73
74 let mut mono_bitmap = vec![0u8; mono_width * mono_height];
75 sc.render_mono(&mut mono_bitmap);
76
77 downsample_to_grayscale(&mono_bitmap, mono_width, mono_height, width, height, level)
79}
80
81#[cfg(target_feature = "simd128")]
87fn downsample_to_grayscale_simd(
88 mono: &[u8],
89 mono_width: usize,
90 _mono_height: usize,
91 out_width: usize,
92 out_height: usize,
93 level: GrayscaleLevel,
94) -> Vec<u8> {
95 let factor = level.factor();
96 let max_coverage = level.samples_per_pixel() as u32;
97 let normalization_factor = 255.0 / max_coverage as f32;
98
99 let mut output = vec![0u8; out_width * out_height];
100
101 for out_y in 0..out_height {
102 let src_y_base = out_y * factor;
103 let out_row_start = out_y * out_width;
104
105 for out_x in 0..out_width {
106 let src_x_base = out_x * factor;
107 let mut coverage = 0u32;
108
109 for dy in 0..factor {
112 let src_row_start = (src_y_base + dy) * mono_width;
113 let row_start = src_row_start + src_x_base;
114
115 if row_start + factor <= mono.len() {
116 for i in 0..factor {
118 coverage += mono[row_start + i] as u32;
119 }
120 } else {
121 for i in 0..factor {
123 let x = src_x_base + i;
124 if x < mono_width {
125 coverage += mono[src_row_start + x] as u32;
126 }
127 }
128 }
129 }
130
131 let alpha = (coverage as f32 * normalization_factor).round() as u8;
133 output[out_row_start + out_x] = alpha;
134 }
135 }
136 output
137}
138
139fn downsample_to_grayscale_scalar(
144 mono: &[u8],
145 mono_width: usize,
146 mono_height: usize,
147 out_width: usize,
148 out_height: usize,
149 level: GrayscaleLevel,
150) -> Vec<u8> {
151 let factor = level.factor();
152 let max_coverage = level.samples_per_pixel();
153
154 let mut output = vec![0u8; out_width * out_height];
155
156 for out_y in 0..out_height {
157 for out_x in 0..out_width {
158 let mut coverage = 0u32;
160
161 let src_x = out_x * factor;
162 let src_y = out_y * factor;
163
164 for dy in 0..factor {
165 for dx in 0..factor {
166 let x = src_x + dx;
167 let y = src_y + dy;
168
169 if x < mono_width && y < mono_height && mono[y * mono_width + x] != 0 {
170 coverage += 1;
171 }
172 }
173 }
174
175 let alpha = ((coverage * 255) / max_coverage as u32) as u8;
177 output[out_y * out_width + out_x] = alpha;
178 }
179 }
180
181 output
182}
183
184#[inline]
189fn downsample_to_grayscale(
190 mono: &[u8],
191 mono_width: usize,
192 mono_height: usize,
193 out_width: usize,
194 out_height: usize,
195 level: GrayscaleLevel,
196) -> Vec<u8> {
197 #[cfg(target_feature = "simd128")]
198 {
199 downsample_to_grayscale_simd(mono, mono_width, mono_height, out_width, out_height, level)
200 }
201 #[cfg(not(target_feature = "simd128"))]
202 {
203 downsample_to_grayscale_scalar(mono, mono_width, mono_height, out_width, out_height, level)
204 }
205}
206
207pub fn render_grayscale_direct(
213 width: usize,
214 height: usize,
215 level: GrayscaleLevel,
216 build_outline: impl FnOnce(&mut ScanConverter),
217) -> Vec<u8> {
218 let factor = level.factor();
219 let _samples_per_pixel = level.samples_per_pixel();
220
221 let oversample_width = width * factor;
223 let oversample_height = height * factor;
224 let mut sc = ScanConverter::new(oversample_width, oversample_height);
225
226 build_outline(&mut sc);
228
229 let mut mono_bitmap = vec![0u8; oversample_width * oversample_height];
231 sc.render_mono(&mut mono_bitmap);
232
233 downsample_to_grayscale(
235 &mono_bitmap,
236 oversample_width,
237 oversample_height,
238 width,
239 height,
240 level,
241 )
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247 use crate::fixed::F26Dot6;
248
249 #[test]
250 fn test_grayscale_level_factor() {
251 assert_eq!(GrayscaleLevel::Level2x2.factor(), 2);
252 assert_eq!(GrayscaleLevel::Level4x4.factor(), 4);
253 assert_eq!(GrayscaleLevel::Level8x8.factor(), 8);
254 }
255
256 #[test]
257 fn test_grayscale_level_samples() {
258 assert_eq!(GrayscaleLevel::Level2x2.samples_per_pixel(), 4);
259 assert_eq!(GrayscaleLevel::Level4x4.samples_per_pixel(), 16);
260 assert_eq!(GrayscaleLevel::Level8x8.samples_per_pixel(), 64);
261 }
262
263 #[test]
264 fn test_downsample_all_black() {
265 let mono = vec![1u8; 4 * 4]; let gray = downsample_to_grayscale(&mono, 4, 4, 2, 2, GrayscaleLevel::Level2x2);
268
269 assert_eq!(gray.len(), 4);
271 for &alpha in &gray {
272 assert_eq!(alpha, 255);
273 }
274 }
275
276 #[test]
277 fn test_downsample_all_white() {
278 let mono = vec![0u8; 4 * 4];
280 let gray = downsample_to_grayscale(&mono, 4, 4, 2, 2, GrayscaleLevel::Level2x2);
281
282 for &alpha in &gray {
284 assert_eq!(alpha, 0);
285 }
286 }
287
288 #[test]
289 fn test_downsample_half_coverage() {
290 let mono = vec![1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1];
292 let gray = downsample_to_grayscale(&mono, 4, 4, 2, 2, GrayscaleLevel::Level2x2);
293
294 for &alpha in &gray {
296 assert!((120..=135).contains(&alpha), "Alpha = {}", alpha);
297 }
298 }
299
300 #[test]
301 fn test_render_grayscale_direct_rectangle() {
302 let gray = render_grayscale_direct(10, 10, GrayscaleLevel::Level2x2, |sc| {
303 sc.move_to(F26Dot6::from_int(4), F26Dot6::from_int(4));
305 sc.line_to(F26Dot6::from_int(16), F26Dot6::from_int(4));
306 sc.line_to(F26Dot6::from_int(16), F26Dot6::from_int(16));
307 sc.line_to(F26Dot6::from_int(4), F26Dot6::from_int(16));
308 sc.close();
309 });
310
311 assert_eq!(gray.len(), 100);
312
313 assert!(
315 gray[5 * 10 + 5] > 200,
316 "Center alpha = {}",
317 gray[5 * 10 + 5]
318 );
319
320 assert!(gray[0] < 50, "Corner alpha = {}", gray[0]);
322 }
323
324 #[test]
325 fn test_render_grayscale_levels() {
326 for level in [
328 GrayscaleLevel::Level2x2,
329 GrayscaleLevel::Level4x4,
330 GrayscaleLevel::Level8x8,
331 ] {
332 let factor = level.factor() as i32;
333 let gray = render_grayscale_direct(8, 8, level, |sc| {
334 let x1 = 2 * factor;
336 let y1 = 2 * factor;
337 let x2 = 6 * factor;
338 let y2 = 6 * factor;
339
340 sc.move_to(F26Dot6::from_int(x1), F26Dot6::from_int(y1));
341 sc.line_to(F26Dot6::from_int(x2), F26Dot6::from_int(y1));
342 sc.line_to(F26Dot6::from_int(x2), F26Dot6::from_int(y2));
343 sc.line_to(F26Dot6::from_int(x1), F26Dot6::from_int(y2));
344 sc.close();
345 });
346
347 assert_eq!(gray.len(), 64);
348 let filled = gray.iter().filter(|&&a| a > 100).count();
350 assert!(filled > 0, "Level {:?} has no filled pixels", level);
351 }
352 }
353}