Skip to main content

qrcode_core/
canvas.rs

1//! QR code canvas construction and masking.
2//!
3//! The canvas is responsible for placing encoded data and functional patterns
4//! (finder patterns, alignment patterns, timing patterns, format/version info)
5//! onto the QR code grid, then selecting the optimal mask pattern to ensure
6//! reliable scanning.
7//!
8//! The typical flow is:
9//!
10//! 1. Create a [`Canvas`] for a given version and error correction level
11//! 2. Draw all functional patterns with [`draw_all_functional_patterns`]
12//! 3. Place encoded data with [`draw_data`]
13//! 4. Apply the best mask with [`apply_best_mask`] (evaluates all 8 patterns)
14//!
15//! ```
16//! use qrcode_core::types::{Version, EcLevel};
17//! use qrcode_core::canvas::Canvas;
18//!
19//! let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
20//! c.draw_all_functional_patterns();
21//! c.draw_data(b"data_here", b"ec_code_here");
22//! let c = c.apply_best_mask();
23//! let colors = c.into_colors();
24//! ```
25//!
26//! [`draw_all_functional_patterns`]: Canvas::draw_all_functional_patterns
27//! [`draw_data`]: Canvas::draw_data
28//! [`apply_best_mask`]: Canvas::apply_best_mask
29
30#[cfg(not(feature = "std"))]
31#[allow(unused_imports)]
32use alloc::{
33    borrow::ToOwned,
34    format,
35    string::{String, ToString},
36    vec,
37    vec::Vec,
38};
39
40use core::cmp::{max, min};
41
42use crate::cast::As;
43use crate::types::{Color, EcLevel, Version};
44
45//------------------------------------------------------------------------------
46//{{{ Modules
47
48/// The color of a module (pixel) in the QR code.
49#[derive(PartialEq, Eq, Clone, Copy, Debug)]
50pub enum Module {
51    /// The module is empty.
52    Empty,
53
54    /// The module is of functional patterns which cannot be masked, or pixels
55    /// which have been masked.
56    Masked(Color),
57
58    /// The module is of data and error correction bits before masking.
59    Unmasked(Color),
60}
61
62impl From<Module> for Color {
63    fn from(module: Module) -> Self {
64        match module {
65            Module::Empty => Color::Light,
66            Module::Masked(c) | Module::Unmasked(c) => c,
67        }
68    }
69}
70
71impl Module {
72    /// Checks whether a module is dark.
73    pub fn is_dark(self) -> bool {
74        Color::from(self) == Color::Dark
75    }
76
77    /// Apply a mask to the unmasked modules.
78    ///
79    ///     use qrcode_core::canvas::Module;
80    ///     use qrcode_core::types::Color;
81    ///
82    ///     assert_eq!(Module::Unmasked(Color::Light).mask(true), Module::Masked(Color::Dark));
83    ///     assert_eq!(Module::Unmasked(Color::Dark).mask(true), Module::Masked(Color::Light));
84    ///     assert_eq!(Module::Unmasked(Color::Light).mask(false), Module::Masked(Color::Light));
85    ///     assert_eq!(Module::Masked(Color::Dark).mask(true), Module::Masked(Color::Dark));
86    ///     assert_eq!(Module::Masked(Color::Dark).mask(false), Module::Masked(Color::Dark));
87    ///
88    #[must_use]
89    pub fn mask(self, should_invert: bool) -> Self {
90        match (self, should_invert) {
91            (Module::Empty, true) => Module::Masked(Color::Dark),
92            (Module::Empty, false) => Module::Masked(Color::Light),
93            (Module::Unmasked(c), true) => Module::Masked(!c),
94            (Module::Unmasked(c), false) | (Module::Masked(c), _) => Module::Masked(c),
95        }
96    }
97}
98
99//}}}
100//------------------------------------------------------------------------------
101//{{{ Canvas
102
103/// `Canvas` is an intermediate helper structure to render error-corrected data
104/// into a QR code.
105#[derive(Clone)]
106pub struct Canvas {
107    /// The width and height of the canvas (cached as it is needed frequently).
108    width: i16,
109
110    /// The version of the QR code.
111    version: Version,
112
113    /// The error correction level of the QR code.
114    ec_level: EcLevel,
115
116    /// The modules of the QR code. Modules are arranged in left-to-right, then
117    /// top-to-bottom order.
118    modules: Vec<Module>,
119}
120
121impl Canvas {
122    /// Constructs a new canvas big enough for a QR code of the given version.
123    pub fn new(version: Version, ec_level: EcLevel) -> Self {
124        let width = version.width();
125        Self { width, version, ec_level, modules: vec![Module::Empty; (width * width).as_usize()] }
126    }
127
128    /// Converts the canvas into a human-readable string.
129    #[cfg(test)]
130    fn to_debug_str(&self) -> String {
131        let width = self.width;
132        let mut res = String::with_capacity((width * (width + 1)).as_usize());
133        for y in 0..width {
134            res.push('\n');
135            for x in 0..width {
136                res.push(match self.get(x, y) {
137                    Module::Empty => '?',
138                    Module::Masked(Color::Light) => '.',
139                    Module::Masked(Color::Dark) => '#',
140                    Module::Unmasked(Color::Light) => '-',
141                    Module::Unmasked(Color::Dark) => '*',
142                });
143            }
144        }
145        res
146    }
147
148    fn coords_to_index(&self, x: i16, y: i16) -> usize {
149        let x = if x < 0 { x + self.width } else { x }.as_usize();
150        let y = if y < 0 { y + self.width } else { y }.as_usize();
151        y * self.width.as_usize() + x
152    }
153
154    /// Obtains a module at the given coordinates. For convenience, negative
155    /// coordinates will wrap around.
156    pub fn get(&self, x: i16, y: i16) -> Module {
157        self.modules[self.coords_to_index(x, y)]
158    }
159
160    /// Obtains a mutable module at the given coordinates. For convenience,
161    /// negative coordinates will wrap around.
162    pub fn get_mut(&mut self, x: i16, y: i16) -> &mut Module {
163        let index = self.coords_to_index(x, y);
164        &mut self.modules[index]
165    }
166
167    /// Sets the color of a functional module at the given coordinates. For
168    /// convenience, negative coordinates will wrap around.
169    pub fn put(&mut self, x: i16, y: i16, color: Color) {
170        *self.get_mut(x, y) = Module::Masked(color);
171    }
172}
173
174#[cfg(test)]
175mod basic_canvas_tests {
176    use crate::canvas::{Canvas, Module};
177    use crate::types::{Color, EcLevel, Version};
178
179    #[test]
180    fn test_index() {
181        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
182
183        assert_eq!(c.get(0, 4), Module::Empty);
184        assert_eq!(c.get(-1, -7), Module::Empty);
185        assert_eq!(c.get(21 - 1, 21 - 7), Module::Empty);
186
187        c.put(0, 0, Color::Dark);
188        c.put(-1, -7, Color::Light);
189        assert_eq!(c.get(0, 0), Module::Masked(Color::Dark));
190        assert_eq!(c.get(21 - 1, -7), Module::Masked(Color::Light));
191        assert_eq!(c.get(-1, 21 - 7), Module::Masked(Color::Light));
192    }
193
194    #[test]
195    fn test_debug_str() {
196        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
197
198        for i in 3_i16..20 {
199            for j in 3_i16..20 {
200                *c.get_mut(i, j) = match ((i * 3) ^ j) % 5 {
201                    0 => Module::Empty,
202                    1 => Module::Masked(Color::Light),
203                    2 => Module::Masked(Color::Dark),
204                    3 => Module::Unmasked(Color::Light),
205                    4 => Module::Unmasked(Color::Dark),
206                    _ => unreachable!(),
207                };
208            }
209        }
210
211        assert_eq!(
212            &*c.to_debug_str(),
213            "\n\
214             ?????????????????????\n\
215             ?????????????????????\n\
216             ?????????????????????\n\
217             ?????####****....---?\n\
218             ???--.##-..##?..#??.?\n\
219             ???#*?-.*?#.-*#?-*.??\n\
220             ?????*?*?****-*-*---?\n\
221             ???*.-.-.-?-?#?#?#*#?\n\
222             ???.*#.*.*#.*#*#.*#*?\n\
223             ?????.#-#--??.?.#---?\n\
224             ???-.?*.-#?-.?#*-#?.?\n\
225             ???##*??*..##*--*..??\n\
226             ?????-???--??---?---?\n\
227             ???*.#.*.#**.#*#.#*#?\n\
228             ???##.-##..##..?#..??\n\
229             ???.-?*.-?#.-?#*-?#*?\n\
230             ????-.#?-.**#?-.#?-.?\n\
231             ???**?-**??--**?-**??\n\
232             ???#?*?#?*#.*-.-*-.-?\n\
233             ???..-...--??###?###?\n\
234             ?????????????????????"
235        );
236    }
237}
238
239//}}}
240//------------------------------------------------------------------------------
241//{{{ Finder patterns
242
243impl Canvas {
244    /// Draws a single finder pattern with the center at (x, y).
245    fn draw_finder_pattern_at(&mut self, x: i16, y: i16) {
246        let (dx_left, dx_right) = if x >= 0 { (-3, 4) } else { (-4, 3) };
247        let (dy_top, dy_bottom) = if y >= 0 { (-3, 4) } else { (-4, 3) };
248        for j in dy_top..=dy_bottom {
249            for i in dx_left..=dx_right {
250                self.put(
251                    x + i,
252                    y + j,
253                    #[allow(clippy::match_same_arms)]
254                    match (i, j) {
255                        (4 | -4, _) | (_, 4 | -4) => Color::Light,
256                        (3 | -3, _) | (_, 3 | -3) => Color::Dark,
257                        (2 | -2, _) | (_, 2 | -2) => Color::Light,
258                        _ => Color::Dark,
259                    },
260                );
261            }
262        }
263    }
264
265    /// Draws the finder patterns.
266    ///
267    /// The finder patterns is are 7×7 square patterns appearing at the three
268    /// corners of a QR code. They allows scanner to locate the QR code and
269    /// determine the orientation.
270    fn draw_finder_patterns(&mut self) {
271        self.draw_finder_pattern_at(3, 3);
272
273        match self.version {
274            Version::Micro(_) => {}
275            Version::Normal(_) => {
276                self.draw_finder_pattern_at(-4, 3);
277                self.draw_finder_pattern_at(3, -4);
278            }
279        }
280    }
281}
282
283#[cfg(test)]
284mod finder_pattern_tests {
285    use crate::canvas::Canvas;
286    use crate::types::{EcLevel, Version};
287
288    #[test]
289    fn test_qr() {
290        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
291        c.draw_finder_patterns();
292        assert_eq!(
293            &*c.to_debug_str(),
294            "\n\
295             #######.?????.#######\n\
296             #.....#.?????.#.....#\n\
297             #.###.#.?????.#.###.#\n\
298             #.###.#.?????.#.###.#\n\
299             #.###.#.?????.#.###.#\n\
300             #.....#.?????.#.....#\n\
301             #######.?????.#######\n\
302             ........?????........\n\
303             ?????????????????????\n\
304             ?????????????????????\n\
305             ?????????????????????\n\
306             ?????????????????????\n\
307             ?????????????????????\n\
308             ........?????????????\n\
309             #######.?????????????\n\
310             #.....#.?????????????\n\
311             #.###.#.?????????????\n\
312             #.###.#.?????????????\n\
313             #.###.#.?????????????\n\
314             #.....#.?????????????\n\
315             #######.?????????????"
316        );
317    }
318
319    #[test]
320    fn test_micro_qr() {
321        let mut c = Canvas::new(Version::Micro(1), EcLevel::L);
322        c.draw_finder_patterns();
323        assert_eq!(
324            &*c.to_debug_str(),
325            "\n\
326             #######.???\n\
327             #.....#.???\n\
328             #.###.#.???\n\
329             #.###.#.???\n\
330             #.###.#.???\n\
331             #.....#.???\n\
332             #######.???\n\
333             ........???\n\
334             ???????????\n\
335             ???????????\n\
336             ???????????"
337        );
338    }
339}
340
341//}}}
342//------------------------------------------------------------------------------
343//{{{ Alignment patterns
344
345impl Canvas {
346    /// Draws a alignment pattern with the center at (x, y).
347    fn draw_alignment_pattern_at(&mut self, x: i16, y: i16) {
348        if self.get(x, y) != Module::Empty {
349            return;
350        }
351        for j in -2..=2 {
352            for i in -2..=2 {
353                self.put(
354                    x + i,
355                    y + j,
356                    match (i, j) {
357                        (2 | -2, _) | (_, 2 | -2) | (0, 0) => Color::Dark,
358                        _ => Color::Light,
359                    },
360                );
361            }
362        }
363    }
364
365    /// Draws the alignment patterns.
366    ///
367    /// The alignment patterns are 5×5 square patterns inside the QR code symbol
368    /// to help the scanner create the square grid.
369    fn draw_alignment_patterns(&mut self) {
370        match self.version {
371            Version::Micro(_) | Version::Normal(1) => {}
372            Version::Normal(a @ 2..=40) => {
373                let positions = alignment_pattern_positions(a);
374                for x in positions {
375                    for y in positions {
376                        self.draw_alignment_pattern_at(*x, *y);
377                    }
378                }
379            }
380            Version::Normal(_) => {}
381        }
382    }
383}
384
385#[cfg(test)]
386mod alignment_pattern_tests {
387    use crate::canvas::{Canvas, alignment_pattern_positions};
388    use crate::types::{EcLevel, Version};
389
390    #[test]
391    fn test_draw_alignment_patterns_1() {
392        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
393        c.draw_finder_patterns();
394        c.draw_alignment_patterns();
395        assert_eq!(
396            &*c.to_debug_str(),
397            "\n\
398             #######.?????.#######\n\
399             #.....#.?????.#.....#\n\
400             #.###.#.?????.#.###.#\n\
401             #.###.#.?????.#.###.#\n\
402             #.###.#.?????.#.###.#\n\
403             #.....#.?????.#.....#\n\
404             #######.?????.#######\n\
405             ........?????........\n\
406             ?????????????????????\n\
407             ?????????????????????\n\
408             ?????????????????????\n\
409             ?????????????????????\n\
410             ?????????????????????\n\
411             ........?????????????\n\
412             #######.?????????????\n\
413             #.....#.?????????????\n\
414             #.###.#.?????????????\n\
415             #.###.#.?????????????\n\
416             #.###.#.?????????????\n\
417             #.....#.?????????????\n\
418             #######.?????????????"
419        );
420    }
421
422    #[test]
423    fn test_draw_alignment_patterns_3() {
424        let mut c = Canvas::new(Version::Normal(3), EcLevel::L);
425        c.draw_finder_patterns();
426        c.draw_alignment_patterns();
427        assert_eq!(
428            &*c.to_debug_str(),
429            "\n\
430             #######.?????????????.#######\n\
431             #.....#.?????????????.#.....#\n\
432             #.###.#.?????????????.#.###.#\n\
433             #.###.#.?????????????.#.###.#\n\
434             #.###.#.?????????????.#.###.#\n\
435             #.....#.?????????????.#.....#\n\
436             #######.?????????????.#######\n\
437             ........?????????????........\n\
438             ?????????????????????????????\n\
439             ?????????????????????????????\n\
440             ?????????????????????????????\n\
441             ?????????????????????????????\n\
442             ?????????????????????????????\n\
443             ?????????????????????????????\n\
444             ?????????????????????????????\n\
445             ?????????????????????????????\n\
446             ?????????????????????????????\n\
447             ?????????????????????????????\n\
448             ?????????????????????????????\n\
449             ?????????????????????????????\n\
450             ????????????????????#####????\n\
451             ........????????????#...#????\n\
452             #######.????????????#.#.#????\n\
453             #.....#.????????????#...#????\n\
454             #.###.#.????????????#####????\n\
455             #.###.#.?????????????????????\n\
456             #.###.#.?????????????????????\n\
457             #.....#.?????????????????????\n\
458             #######.?????????????????????"
459        );
460    }
461
462    #[test]
463    fn test_draw_alignment_patterns_7() {
464        let mut c = Canvas::new(Version::Normal(7), EcLevel::L);
465        c.draw_finder_patterns();
466        c.draw_alignment_patterns();
467        assert_eq!(
468            &*c.to_debug_str(),
469            "\n\
470             #######.?????????????????????????????.#######\n\
471             #.....#.?????????????????????????????.#.....#\n\
472             #.###.#.?????????????????????????????.#.###.#\n\
473             #.###.#.?????????????????????????????.#.###.#\n\
474             #.###.#.????????????#####????????????.#.###.#\n\
475             #.....#.????????????#...#????????????.#.....#\n\
476             #######.????????????#.#.#????????????.#######\n\
477             ........????????????#...#????????????........\n\
478             ????????????????????#####????????????????????\n\
479             ?????????????????????????????????????????????\n\
480             ?????????????????????????????????????????????\n\
481             ?????????????????????????????????????????????\n\
482             ?????????????????????????????????????????????\n\
483             ?????????????????????????????????????????????\n\
484             ?????????????????????????????????????????????\n\
485             ?????????????????????????????????????????????\n\
486             ?????????????????????????????????????????????\n\
487             ?????????????????????????????????????????????\n\
488             ?????????????????????????????????????????????\n\
489             ?????????????????????????????????????????????\n\
490             ????#####???????????#####???????????#####????\n\
491             ????#...#???????????#...#???????????#...#????\n\
492             ????#.#.#???????????#.#.#???????????#.#.#????\n\
493             ????#...#???????????#...#???????????#...#????\n\
494             ????#####???????????#####???????????#####????\n\
495             ?????????????????????????????????????????????\n\
496             ?????????????????????????????????????????????\n\
497             ?????????????????????????????????????????????\n\
498             ?????????????????????????????????????????????\n\
499             ?????????????????????????????????????????????\n\
500             ?????????????????????????????????????????????\n\
501             ?????????????????????????????????????????????\n\
502             ?????????????????????????????????????????????\n\
503             ?????????????????????????????????????????????\n\
504             ?????????????????????????????????????????????\n\
505             ?????????????????????????????????????????????\n\
506             ????????????????????#####???????????#####????\n\
507             ........????????????#...#???????????#...#????\n\
508             #######.????????????#.#.#???????????#.#.#????\n\
509             #.....#.????????????#...#???????????#...#????\n\
510             #.###.#.????????????#####???????????#####????\n\
511             #.###.#.?????????????????????????????????????\n\
512             #.###.#.?????????????????????????????????????\n\
513             #.....#.?????????????????????????????????????\n\
514            #######.?????????????????????????????????????"
515        );
516    }
517
518    #[test]
519    fn generated_alignment_pattern_positions_match_known_versions() {
520        assert_eq!(alignment_pattern_positions(1), &[]);
521        assert_eq!(alignment_pattern_positions(2), &[6, 18]);
522        assert_eq!(alignment_pattern_positions(7), &[6, 22, 38]);
523        assert_eq!(alignment_pattern_positions(14), &[6, 26, 46, 66]);
524        assert_eq!(alignment_pattern_positions(32), &[6, 34, 60, 86, 112, 138]);
525        assert_eq!(alignment_pattern_positions(40), &[6, 30, 58, 86, 114, 142, 170]);
526    }
527}
528
529/// `ALIGNMENT_PATTERN_POSITIONS` describes the x- and y-coordinates of the
530/// center of the alignment patterns. Since the QR code is symmetric, only one
531/// coordinate is needed.
532static ALIGNMENT_PATTERN_POSITIONS: [AlignmentPatternPositions; 40] = generate_alignment_pattern_positions();
533const MAX_ALIGNMENT_PATTERNS: usize = 7;
534
535#[derive(Clone, Copy)]
536struct AlignmentPatternPositions {
537    positions: [i16; MAX_ALIGNMENT_PATTERNS],
538    len: usize,
539}
540
541impl AlignmentPatternPositions {
542    fn as_slice(&self) -> &[i16] {
543        &self.positions[..self.len]
544    }
545}
546
547fn alignment_pattern_positions(version: i16) -> &'static [i16] {
548    match version {
549        1..=40 => ALIGNMENT_PATTERN_POSITIONS[(version - 1) as usize].as_slice(),
550        _ => &[],
551    }
552}
553
554const fn generate_alignment_pattern_positions() -> [AlignmentPatternPositions; 40] {
555    let mut table = [AlignmentPatternPositions { positions: [0; MAX_ALIGNMENT_PATTERNS], len: 0 }; 40];
556    let mut version = 1;
557    while version <= 40 {
558        table[(version - 1) as usize] = compute_alignment_pattern_positions(version);
559        version += 1;
560    }
561    table
562}
563
564const fn compute_alignment_pattern_positions(version: i16) -> AlignmentPatternPositions {
565    let count = if version == 1 { 0 } else { version / 7 + 2 };
566    let mut result = AlignmentPatternPositions { positions: [0; MAX_ALIGNMENT_PATTERNS], len: count as usize };
567    if count == 0 {
568        return result;
569    }
570
571    let width = version * 4 + 17;
572    let step = alignment_pattern_step(version, count);
573    let mut index = count - 1;
574    let mut position = width - 7;
575    while index > 0 {
576        result.positions[index as usize] = position;
577        position -= step;
578        index -= 1;
579    }
580    result.positions[0] = 6;
581    result
582}
583
584const fn alignment_pattern_step(version: i16, count: i16) -> i16 {
585    if version == 32 { 26 } else { ((version * 4 + count * 2 + 1) / (count * 2 - 2)) * 2 }
586}
587
588//}}}
589//------------------------------------------------------------------------------
590//{{{ Timing patterns
591
592impl Canvas {
593    /// Draws a line from (x1, y1) to (x2, y2), inclusively.
594    ///
595    /// The line must be either horizontal or vertical, i.e.
596    /// `x1 == x2 || y1 == y2`. Additionally, the first coordinates must be less
597    /// then the second ones.
598    ///
599    /// On even coordinates, `color_even` will be plotted; on odd coordinates,
600    /// `color_odd` will be plotted instead. Thus the timing pattern can be
601    /// drawn using this method.
602    ///
603    fn draw_line(&mut self, x1: i16, y1: i16, x2: i16, y2: i16, color_even: Color, color_odd: Color) {
604        debug_assert!(x1 == x2 || y1 == y2);
605
606        if y1 == y2 {
607            // Horizontal line.
608            for x in x1..=x2 {
609                self.put(x, y1, if x % 2 == 0 { color_even } else { color_odd });
610            }
611        } else {
612            // Vertical line.
613            for y in y1..=y2 {
614                self.put(x1, y, if y % 2 == 0 { color_even } else { color_odd });
615            }
616        }
617    }
618
619    /// Draws the timing patterns.
620    ///
621    /// The timing patterns are checkboard-colored lines near the edge of the QR
622    /// code symbol, to establish the fine-grained module coordinates when
623    /// scanning.
624    fn draw_timing_patterns(&mut self) {
625        let width = self.width;
626        let (y, x1, x2) = match self.version {
627            Version::Micro(_) => (0, 8, width - 1),
628            Version::Normal(_) => (6, 8, width - 9),
629        };
630        self.draw_line(x1, y, x2, y, Color::Dark, Color::Light);
631        self.draw_line(y, x1, y, x2, Color::Dark, Color::Light);
632    }
633}
634
635#[cfg(test)]
636mod timing_pattern_tests {
637    use crate::canvas::Canvas;
638    use crate::types::{EcLevel, Version};
639
640    #[test]
641    fn test_draw_timing_patterns_qr() {
642        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
643        c.draw_timing_patterns();
644        assert_eq!(
645            &*c.to_debug_str(),
646            "\n\
647             ?????????????????????\n\
648             ?????????????????????\n\
649             ?????????????????????\n\
650             ?????????????????????\n\
651             ?????????????????????\n\
652             ?????????????????????\n\
653             ????????#.#.#????????\n\
654             ?????????????????????\n\
655             ??????#??????????????\n\
656             ??????.??????????????\n\
657             ??????#??????????????\n\
658             ??????.??????????????\n\
659             ??????#??????????????\n\
660             ?????????????????????\n\
661             ?????????????????????\n\
662             ?????????????????????\n\
663             ?????????????????????\n\
664             ?????????????????????\n\
665             ?????????????????????\n\
666             ?????????????????????\n\
667             ?????????????????????"
668        );
669    }
670
671    #[test]
672    fn test_draw_timing_patterns_micro_qr() {
673        let mut c = Canvas::new(Version::Micro(1), EcLevel::L);
674        c.draw_timing_patterns();
675        assert_eq!(
676            &*c.to_debug_str(),
677            "\n\
678             ????????#.#\n\
679             ???????????\n\
680             ???????????\n\
681             ???????????\n\
682             ???????????\n\
683             ???????????\n\
684             ???????????\n\
685             ???????????\n\
686             #??????????\n\
687             .??????????\n\
688             #??????????"
689        );
690    }
691}
692
693//}}}
694//------------------------------------------------------------------------------
695//{{{ Format info & Version info
696
697impl Canvas {
698    /// Draws a big-endian integer onto the canvas with the given coordinates.
699    ///
700    /// The 1 bits will be plotted with `on_color` and the 0 bits with
701    /// `off_color`. The coordinates will be extracted from the `coords`
702    /// iterator. It will start from the most significant bits first, so
703    /// *trailing* zeros will be ignored.
704    fn draw_number(&mut self, number: u32, bits: u32, on_color: Color, off_color: Color, coords: &[(i16, i16)]) {
705        let mut mask = 1 << (bits - 1);
706        for &(x, y) in coords {
707            let color = if (mask & number) == 0 { off_color } else { on_color };
708            self.put(x, y, color);
709            mask >>= 1;
710        }
711    }
712
713    /// Draws the format info patterns for an encoded number.
714    fn draw_format_info_patterns_with_number(&mut self, format_info: u16) {
715        let format_info = u32::from(format_info);
716        match self.version {
717            Version::Micro(_) => {
718                self.draw_number(format_info, 15, Color::Dark, Color::Light, &FORMAT_INFO_COORDS_MICRO_QR);
719            }
720            Version::Normal(_) => {
721                self.draw_number(format_info, 15, Color::Dark, Color::Light, &FORMAT_INFO_COORDS_QR_MAIN);
722                self.draw_number(format_info, 15, Color::Dark, Color::Light, &FORMAT_INFO_COORDS_QR_SIDE);
723                self.put(8, -8, Color::Dark); // Dark module.
724            }
725        }
726    }
727
728    /// Reserves area to put in the format information.
729    fn draw_reserved_format_info_patterns(&mut self) {
730        self.draw_format_info_patterns_with_number(0);
731    }
732
733    /// Draws the version information patterns.
734    fn draw_version_info_patterns(&mut self) {
735        match self.version {
736            Version::Micro(_) | Version::Normal(1..=6) => {}
737            Version::Normal(a) => {
738                let version_info = VERSION_INFOS[(a - 7).as_usize()];
739                self.draw_number(version_info, 18, Color::Dark, Color::Light, &VERSION_INFO_COORDS_BL);
740                self.draw_number(version_info, 18, Color::Dark, Color::Light, &VERSION_INFO_COORDS_TR);
741            }
742        }
743    }
744}
745
746#[cfg(test)]
747mod draw_version_info_tests {
748    use crate::canvas::Canvas;
749    use crate::types::{Color, EcLevel, Version};
750
751    #[test]
752    fn test_draw_number() {
753        let mut c = Canvas::new(Version::Micro(1), EcLevel::L);
754        c.draw_number(0b1010_1101, 8, Color::Dark, Color::Light, &[(0, 0), (0, -1), (-2, -2), (-2, 0)]);
755        assert_eq!(
756            &*c.to_debug_str(),
757            "\n\
758             #????????.?\n\
759             ???????????\n\
760             ???????????\n\
761             ???????????\n\
762             ???????????\n\
763             ???????????\n\
764             ???????????\n\
765             ???????????\n\
766             ???????????\n\
767             ?????????#?\n\
768             .??????????"
769        );
770    }
771
772    #[test]
773    fn test_draw_version_info_1() {
774        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
775        c.draw_version_info_patterns();
776        assert_eq!(
777            &*c.to_debug_str(),
778            "\n\
779             ?????????????????????\n\
780             ?????????????????????\n\
781             ?????????????????????\n\
782             ?????????????????????\n\
783             ?????????????????????\n\
784             ?????????????????????\n\
785             ?????????????????????\n\
786             ?????????????????????\n\
787             ?????????????????????\n\
788             ?????????????????????\n\
789             ?????????????????????\n\
790             ?????????????????????\n\
791             ?????????????????????\n\
792             ?????????????????????\n\
793             ?????????????????????\n\
794             ?????????????????????\n\
795             ?????????????????????\n\
796             ?????????????????????\n\
797             ?????????????????????\n\
798             ?????????????????????\n\
799             ?????????????????????"
800        );
801    }
802
803    #[test]
804    fn test_draw_version_info_7() {
805        let mut c = Canvas::new(Version::Normal(7), EcLevel::L);
806        c.draw_version_info_patterns();
807
808        assert_eq!(
809            &*c.to_debug_str(),
810            "\n\
811             ??????????????????????????????????..#????????\n\
812             ??????????????????????????????????.#.????????\n\
813             ??????????????????????????????????.#.????????\n\
814             ??????????????????????????????????.##????????\n\
815             ??????????????????????????????????###????????\n\
816             ??????????????????????????????????...????????\n\
817             ?????????????????????????????????????????????\n\
818             ?????????????????????????????????????????????\n\
819             ?????????????????????????????????????????????\n\
820             ?????????????????????????????????????????????\n\
821             ?????????????????????????????????????????????\n\
822             ?????????????????????????????????????????????\n\
823             ?????????????????????????????????????????????\n\
824             ?????????????????????????????????????????????\n\
825             ?????????????????????????????????????????????\n\
826             ?????????????????????????????????????????????\n\
827             ?????????????????????????????????????????????\n\
828             ?????????????????????????????????????????????\n\
829             ?????????????????????????????????????????????\n\
830             ?????????????????????????????????????????????\n\
831             ?????????????????????????????????????????????\n\
832             ?????????????????????????????????????????????\n\
833             ?????????????????????????????????????????????\n\
834             ?????????????????????????????????????????????\n\
835             ?????????????????????????????????????????????\n\
836             ?????????????????????????????????????????????\n\
837             ?????????????????????????????????????????????\n\
838             ?????????????????????????????????????????????\n\
839             ?????????????????????????????????????????????\n\
840             ?????????????????????????????????????????????\n\
841             ?????????????????????????????????????????????\n\
842             ?????????????????????????????????????????????\n\
843             ?????????????????????????????????????????????\n\
844             ?????????????????????????????????????????????\n\
845             ....#.???????????????????????????????????????\n\
846             .####.???????????????????????????????????????\n\
847             #..##.???????????????????????????????????????\n\
848             ?????????????????????????????????????????????\n\
849             ?????????????????????????????????????????????\n\
850             ?????????????????????????????????????????????\n\
851             ?????????????????????????????????????????????\n\
852             ?????????????????????????????????????????????\n\
853             ?????????????????????????????????????????????\n\
854             ?????????????????????????????????????????????\n\
855             ?????????????????????????????????????????????"
856        );
857    }
858
859    #[test]
860    fn test_draw_reserved_format_info_patterns_qr() {
861        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
862        c.draw_reserved_format_info_patterns();
863        assert_eq!(
864            &*c.to_debug_str(),
865            "\n\
866             ????????.????????????\n\
867             ????????.????????????\n\
868             ????????.????????????\n\
869             ????????.????????????\n\
870             ????????.????????????\n\
871             ????????.????????????\n\
872             ?????????????????????\n\
873             ????????.????????????\n\
874             ......?..????........\n\
875             ?????????????????????\n\
876             ?????????????????????\n\
877             ?????????????????????\n\
878             ?????????????????????\n\
879             ????????#????????????\n\
880             ????????.????????????\n\
881             ????????.????????????\n\
882             ????????.????????????\n\
883             ????????.????????????\n\
884             ????????.????????????\n\
885             ????????.????????????\n\
886             ????????.????????????"
887        );
888    }
889
890    #[test]
891    fn test_draw_reserved_format_info_patterns_micro_qr() {
892        let mut c = Canvas::new(Version::Micro(1), EcLevel::L);
893        c.draw_reserved_format_info_patterns();
894        assert_eq!(
895            &*c.to_debug_str(),
896            "\n\
897             ???????????\n\
898             ????????.??\n\
899             ????????.??\n\
900             ????????.??\n\
901             ????????.??\n\
902             ????????.??\n\
903             ????????.??\n\
904             ????????.??\n\
905             ?........??\n\
906             ???????????\n\
907             ???????????"
908        );
909    }
910}
911
912static VERSION_INFO_COORDS_BL: [(i16, i16); 18] = [
913    (5, -9),
914    (5, -10),
915    (5, -11),
916    (4, -9),
917    (4, -10),
918    (4, -11),
919    (3, -9),
920    (3, -10),
921    (3, -11),
922    (2, -9),
923    (2, -10),
924    (2, -11),
925    (1, -9),
926    (1, -10),
927    (1, -11),
928    (0, -9),
929    (0, -10),
930    (0, -11),
931];
932
933static VERSION_INFO_COORDS_TR: [(i16, i16); 18] = [
934    (-9, 5),
935    (-10, 5),
936    (-11, 5),
937    (-9, 4),
938    (-10, 4),
939    (-11, 4),
940    (-9, 3),
941    (-10, 3),
942    (-11, 3),
943    (-9, 2),
944    (-10, 2),
945    (-11, 2),
946    (-9, 1),
947    (-10, 1),
948    (-11, 1),
949    (-9, 0),
950    (-10, 0),
951    (-11, 0),
952];
953
954static FORMAT_INFO_COORDS_QR_MAIN: [(i16, i16); 15] = [
955    (0, 8),
956    (1, 8),
957    (2, 8),
958    (3, 8),
959    (4, 8),
960    (5, 8),
961    (7, 8),
962    (8, 8),
963    (8, 7),
964    (8, 5),
965    (8, 4),
966    (8, 3),
967    (8, 2),
968    (8, 1),
969    (8, 0),
970];
971
972static FORMAT_INFO_COORDS_QR_SIDE: [(i16, i16); 15] = [
973    (8, -1),
974    (8, -2),
975    (8, -3),
976    (8, -4),
977    (8, -5),
978    (8, -6),
979    (8, -7),
980    (-8, 8),
981    (-7, 8),
982    (-6, 8),
983    (-5, 8),
984    (-4, 8),
985    (-3, 8),
986    (-2, 8),
987    (-1, 8),
988];
989
990static FORMAT_INFO_COORDS_MICRO_QR: [(i16, i16); 15] = [
991    (1, 8),
992    (2, 8),
993    (3, 8),
994    (4, 8),
995    (5, 8),
996    (6, 8),
997    (7, 8),
998    (8, 8),
999    (8, 7),
1000    (8, 6),
1001    (8, 5),
1002    (8, 4),
1003    (8, 3),
1004    (8, 2),
1005    (8, 1),
1006];
1007
1008static VERSION_INFOS: [u32; 34] = [
1009    0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, 0x0f928, 0x10b78, 0x1145d, 0x12a17,
1010    0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
1011    0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, 0x27541, 0x28c69,
1012];
1013
1014//}}}
1015//------------------------------------------------------------------------------
1016//{{{ All functional patterns before data placement
1017
1018impl Canvas {
1019    /// Draw all functional patterns, before data placement.
1020    ///
1021    /// All functional patterns (e.g. the finder pattern) *except* the format
1022    /// info pattern will be filled in. The format info pattern will be filled
1023    /// with light modules instead. Data bits can then put in the empty modules.
1024    /// with `.draw_data()`.
1025    pub fn draw_all_functional_patterns(&mut self) {
1026        self.draw_finder_patterns();
1027        self.draw_alignment_patterns();
1028        self.draw_reserved_format_info_patterns();
1029        self.draw_timing_patterns();
1030        self.draw_version_info_patterns();
1031    }
1032}
1033
1034/// Gets whether the module at the given coordinates represents a functional
1035/// module.
1036pub fn is_functional(version: Version, width: i16, x: i16, y: i16) -> bool {
1037    debug_assert!(width == version.width());
1038
1039    let x = if x < 0 { x + width } else { x };
1040    let y = if y < 0 { y + width } else { y };
1041
1042    match version {
1043        Version::Micro(_) => x == 0 || y == 0 || (x < 9 && y < 9),
1044        Version::Normal(a) => {
1045            let non_alignment_test = x == 6 || y == 6 || // Timing patterns
1046                (x < 9 && y < 9) ||                  // Top-left finder pattern
1047                (x < 9 && y >= width - 8) ||           // Bottom-left finder pattern
1048                (x >= width - 8 && y < 9); // Top-right finder pattern
1049            match a {
1050                _ if non_alignment_test => true,
1051                1 => false,
1052                2..=40 => {
1053                    let positions = alignment_pattern_positions(a);
1054                    let last = positions.len() - 1;
1055                    for (i, align_x) in positions.iter().enumerate() {
1056                        for (j, align_y) in positions.iter().enumerate() {
1057                            if i == 0 && (j == 0 || j == last) || (i == last && j == 0) {
1058                                continue;
1059                            }
1060                            if (*align_x - x).abs() <= 2 && (*align_y - y).abs() <= 2 {
1061                                return true;
1062                            }
1063                        }
1064                    }
1065                    false
1066                }
1067                _ => false,
1068            }
1069        }
1070    }
1071}
1072
1073#[cfg(test)]
1074mod all_functional_patterns_tests {
1075    use crate::canvas::{Canvas, is_functional};
1076    use crate::types::{EcLevel, Version};
1077
1078    #[test]
1079    fn test_all_functional_patterns_qr() {
1080        let mut c = Canvas::new(Version::Normal(2), EcLevel::L);
1081        c.draw_all_functional_patterns();
1082        assert_eq!(
1083            &*c.to_debug_str(),
1084            "\n\
1085             #######..????????.#######\n\
1086             #.....#..????????.#.....#\n\
1087             #.###.#..????????.#.###.#\n\
1088             #.###.#..????????.#.###.#\n\
1089             #.###.#..????????.#.###.#\n\
1090             #.....#..????????.#.....#\n\
1091             #######.#.#.#.#.#.#######\n\
1092             .........????????........\n\
1093             ......#..????????........\n\
1094             ??????.??????????????????\n\
1095             ??????#??????????????????\n\
1096             ??????.??????????????????\n\
1097             ??????#??????????????????\n\
1098             ??????.??????????????????\n\
1099             ??????#??????????????????\n\
1100             ??????.??????????????????\n\
1101             ??????#?????????#####????\n\
1102             ........#???????#...#????\n\
1103             #######..???????#.#.#????\n\
1104             #.....#..???????#...#????\n\
1105             #.###.#..???????#####????\n\
1106             #.###.#..????????????????\n\
1107             #.###.#..????????????????\n\
1108             #.....#..????????????????\n\
1109             #######..????????????????"
1110        );
1111    }
1112
1113    #[test]
1114    fn test_all_functional_patterns_micro_qr() {
1115        let mut c = Canvas::new(Version::Micro(1), EcLevel::L);
1116        c.draw_all_functional_patterns();
1117        assert_eq!(
1118            &*c.to_debug_str(),
1119            "\n\
1120             #######.#.#\n\
1121             #.....#..??\n\
1122             #.###.#..??\n\
1123             #.###.#..??\n\
1124             #.###.#..??\n\
1125             #.....#..??\n\
1126             #######..??\n\
1127             .........??\n\
1128             #........??\n\
1129             .??????????\n\
1130             #??????????"
1131        );
1132    }
1133
1134    #[test]
1135    fn test_is_functional_qr_1() {
1136        let version = Version::Normal(1);
1137        assert!(is_functional(version, version.width(), 0, 0));
1138        assert!(is_functional(version, version.width(), 10, 6));
1139        assert!(!is_functional(version, version.width(), 10, 5));
1140        assert!(!is_functional(version, version.width(), 14, 14));
1141        assert!(is_functional(version, version.width(), 6, 11));
1142        assert!(!is_functional(version, version.width(), 4, 11));
1143        assert!(is_functional(version, version.width(), 4, 13));
1144        assert!(is_functional(version, version.width(), 17, 7));
1145        assert!(!is_functional(version, version.width(), 17, 17));
1146    }
1147
1148    #[test]
1149    fn test_is_functional_qr_3() {
1150        let version = Version::Normal(3);
1151        assert!(is_functional(version, version.width(), 0, 0));
1152        assert!(!is_functional(version, version.width(), 25, 24));
1153        assert!(is_functional(version, version.width(), 24, 24));
1154        assert!(!is_functional(version, version.width(), 9, 25));
1155        assert!(!is_functional(version, version.width(), 20, 0));
1156        assert!(is_functional(version, version.width(), 21, 0));
1157    }
1158
1159    #[test]
1160    fn test_is_functional_qr_7() {
1161        let version = Version::Normal(7);
1162        assert!(is_functional(version, version.width(), 21, 4));
1163        assert!(is_functional(version, version.width(), 7, 21));
1164        assert!(is_functional(version, version.width(), 22, 22));
1165        assert!(is_functional(version, version.width(), 8, 8));
1166        assert!(!is_functional(version, version.width(), 19, 5));
1167        assert!(!is_functional(version, version.width(), 36, 3));
1168        assert!(!is_functional(version, version.width(), 4, 36));
1169        assert!(is_functional(version, version.width(), 38, 38));
1170    }
1171
1172    #[test]
1173    fn test_is_functional_micro() {
1174        let version = Version::Micro(1);
1175        assert!(is_functional(version, version.width(), 8, 0));
1176        assert!(is_functional(version, version.width(), 10, 0));
1177        assert!(!is_functional(version, version.width(), 10, 1));
1178        assert!(is_functional(version, version.width(), 8, 8));
1179        assert!(is_functional(version, version.width(), 0, 9));
1180        assert!(!is_functional(version, version.width(), 1, 9));
1181    }
1182}
1183
1184//}}}
1185//------------------------------------------------------------------------------
1186//{{{ Data placement iterator
1187
1188struct DataModuleIter {
1189    x: i16,
1190    y: i16,
1191    width: i16,
1192    timing_pattern_column: i16,
1193}
1194
1195impl DataModuleIter {
1196    fn new(version: Version) -> Self {
1197        let width = version.width();
1198        Self {
1199            x: width - 1,
1200            y: width - 1,
1201            width,
1202            timing_pattern_column: match version {
1203                Version::Micro(_) => 0,
1204                Version::Normal(_) => 6,
1205            },
1206        }
1207    }
1208}
1209
1210impl Iterator for DataModuleIter {
1211    type Item = (i16, i16);
1212
1213    fn next(&mut self) -> Option<(i16, i16)> {
1214        let adjusted_ref_col = if self.x <= self.timing_pattern_column { self.x + 1 } else { self.x };
1215        if adjusted_ref_col <= 0 {
1216            return None;
1217        }
1218
1219        let res = (self.x, self.y);
1220        let column_type = (self.width - adjusted_ref_col) % 4;
1221
1222        match column_type {
1223            2 if self.y > 0 => {
1224                self.y -= 1;
1225                self.x += 1;
1226            }
1227            0 if self.y < self.width - 1 => {
1228                self.y += 1;
1229                self.x += 1;
1230            }
1231            0 | 2 if self.x == self.timing_pattern_column + 1 => {
1232                self.x -= 2;
1233            }
1234            _ => {
1235                self.x -= 1;
1236            }
1237        }
1238
1239        Some(res)
1240    }
1241}
1242
1243#[cfg(test)]
1244#[rustfmt::skip] // skip to prevent file becoming too long.
1245mod data_iter_tests {
1246    use crate::canvas::DataModuleIter;
1247    use crate::types::Version;
1248
1249    #[test]
1250    fn test_qr() {
1251        let res = DataModuleIter::new(Version::Normal(1)).collect::<Vec<(i16, i16)>>();
1252        assert_eq!(res, vec![
1253            (20, 20), (19, 20), (20, 19), (19, 19), (20, 18), (19, 18),
1254            (20, 17), (19, 17), (20, 16), (19, 16), (20, 15), (19, 15),
1255            (20, 14), (19, 14), (20, 13), (19, 13), (20, 12), (19, 12),
1256            (20, 11), (19, 11), (20, 10), (19, 10), (20, 9), (19, 9),
1257            (20, 8), (19, 8), (20, 7), (19, 7), (20, 6), (19, 6),
1258            (20, 5), (19, 5), (20, 4), (19, 4), (20, 3), (19, 3),
1259            (20, 2), (19, 2), (20, 1), (19, 1), (20, 0), (19, 0),
1260            (18, 0), (17, 0), (18, 1), (17, 1), (18, 2), (17, 2),
1261            (18, 3), (17, 3), (18, 4), (17, 4), (18, 5), (17, 5),
1262            (18, 6), (17, 6), (18, 7), (17, 7), (18, 8), (17, 8),
1263            (18, 9), (17, 9), (18, 10), (17, 10), (18, 11), (17, 11),
1264            (18, 12), (17, 12), (18, 13), (17, 13), (18, 14), (17, 14),
1265            (18, 15), (17, 15), (18, 16), (17, 16), (18, 17), (17, 17),
1266            (18, 18), (17, 18), (18, 19), (17, 19), (18, 20), (17, 20),
1267            (16, 20), (15, 20), (16, 19), (15, 19), (16, 18), (15, 18),
1268            (16, 17), (15, 17), (16, 16), (15, 16), (16, 15), (15, 15),
1269            (16, 14), (15, 14), (16, 13), (15, 13), (16, 12), (15, 12),
1270            (16, 11), (15, 11), (16, 10), (15, 10), (16, 9), (15, 9),
1271            (16, 8), (15, 8), (16, 7), (15, 7), (16, 6), (15, 6),
1272            (16, 5), (15, 5), (16, 4), (15, 4), (16, 3), (15, 3),
1273            (16, 2), (15, 2), (16, 1), (15, 1), (16, 0), (15, 0),
1274            (14, 0), (13, 0), (14, 1), (13, 1), (14, 2), (13, 2),
1275            (14, 3), (13, 3), (14, 4), (13, 4), (14, 5), (13, 5),
1276            (14, 6), (13, 6), (14, 7), (13, 7), (14, 8), (13, 8),
1277            (14, 9), (13, 9), (14, 10), (13, 10), (14, 11), (13, 11),
1278            (14, 12), (13, 12), (14, 13), (13, 13), (14, 14), (13, 14),
1279            (14, 15), (13, 15), (14, 16), (13, 16), (14, 17), (13, 17),
1280            (14, 18), (13, 18), (14, 19), (13, 19), (14, 20), (13, 20),
1281            (12, 20), (11, 20), (12, 19), (11, 19), (12, 18), (11, 18),
1282            (12, 17), (11, 17), (12, 16), (11, 16), (12, 15), (11, 15),
1283            (12, 14), (11, 14), (12, 13), (11, 13), (12, 12), (11, 12),
1284            (12, 11), (11, 11), (12, 10), (11, 10), (12, 9), (11, 9),
1285            (12, 8), (11, 8), (12, 7), (11, 7), (12, 6), (11, 6),
1286            (12, 5), (11, 5), (12, 4), (11, 4), (12, 3), (11, 3),
1287            (12, 2), (11, 2), (12, 1), (11, 1), (12, 0), (11, 0),
1288            (10, 0), (9, 0), (10, 1), (9, 1), (10, 2), (9, 2),
1289            (10, 3), (9, 3), (10, 4), (9, 4), (10, 5), (9, 5),
1290            (10, 6), (9, 6), (10, 7), (9, 7), (10, 8), (9, 8),
1291            (10, 9), (9, 9), (10, 10), (9, 10), (10, 11), (9, 11),
1292            (10, 12), (9, 12), (10, 13), (9, 13), (10, 14), (9, 14),
1293            (10, 15), (9, 15), (10, 16), (9, 16), (10, 17), (9, 17),
1294            (10, 18), (9, 18), (10, 19), (9, 19), (10, 20), (9, 20),
1295            (8, 20), (7, 20), (8, 19), (7, 19), (8, 18), (7, 18),
1296            (8, 17), (7, 17), (8, 16), (7, 16), (8, 15), (7, 15),
1297            (8, 14), (7, 14), (8, 13), (7, 13), (8, 12), (7, 12),
1298            (8, 11), (7, 11), (8, 10), (7, 10), (8, 9), (7, 9),
1299            (8, 8), (7, 8), (8, 7), (7, 7), (8, 6), (7, 6),
1300            (8, 5), (7, 5), (8, 4), (7, 4), (8, 3), (7, 3),
1301            (8, 2), (7, 2), (8, 1), (7, 1), (8, 0), (7, 0),
1302            (5, 0), (4, 0), (5, 1), (4, 1), (5, 2), (4, 2),
1303            (5, 3), (4, 3), (5, 4), (4, 4), (5, 5), (4, 5),
1304            (5, 6), (4, 6), (5, 7), (4, 7), (5, 8), (4, 8),
1305            (5, 9), (4, 9), (5, 10), (4, 10), (5, 11), (4, 11),
1306            (5, 12), (4, 12), (5, 13), (4, 13), (5, 14), (4, 14),
1307            (5, 15), (4, 15), (5, 16), (4, 16), (5, 17), (4, 17),
1308            (5, 18), (4, 18), (5, 19), (4, 19), (5, 20), (4, 20),
1309            (3, 20), (2, 20), (3, 19), (2, 19), (3, 18), (2, 18),
1310            (3, 17), (2, 17), (3, 16), (2, 16), (3, 15), (2, 15),
1311            (3, 14), (2, 14), (3, 13), (2, 13), (3, 12), (2, 12),
1312            (3, 11), (2, 11), (3, 10), (2, 10), (3, 9), (2, 9),
1313            (3, 8), (2, 8), (3, 7), (2, 7), (3, 6), (2, 6),
1314            (3, 5), (2, 5), (3, 4), (2, 4), (3, 3), (2, 3),
1315            (3, 2), (2, 2), (3, 1), (2, 1), (3, 0), (2, 0),
1316            (1, 0), (0, 0), (1, 1), (0, 1), (1, 2), (0, 2),
1317            (1, 3), (0, 3), (1, 4), (0, 4), (1, 5), (0, 5),
1318            (1, 6), (0, 6), (1, 7), (0, 7), (1, 8), (0, 8),
1319            (1, 9), (0, 9), (1, 10), (0, 10), (1, 11), (0, 11),
1320            (1, 12), (0, 12), (1, 13), (0, 13), (1, 14), (0, 14),
1321            (1, 15), (0, 15), (1, 16), (0, 16), (1, 17), (0, 17),
1322            (1, 18), (0, 18), (1, 19), (0, 19), (1, 20), (0, 20),
1323        ]);
1324    }
1325
1326    #[test]
1327    fn test_micro_qr() {
1328        let res = DataModuleIter::new(Version::Micro(1)).collect::<Vec<(i16, i16)>>();
1329        assert_eq!(res, vec![
1330            (10, 10), (9, 10), (10, 9), (9, 9), (10, 8), (9, 8),
1331            (10, 7), (9, 7), (10, 6), (9, 6), (10, 5), (9, 5),
1332            (10, 4), (9, 4), (10, 3), (9, 3), (10, 2), (9, 2),
1333            (10, 1), (9, 1), (10, 0), (9, 0),
1334            (8, 0), (7, 0), (8, 1), (7, 1), (8, 2), (7, 2),
1335            (8, 3), (7, 3), (8, 4), (7, 4), (8, 5), (7, 5),
1336            (8, 6), (7, 6), (8, 7), (7, 7), (8, 8), (7, 8),
1337            (8, 9), (7, 9), (8, 10), (7, 10),
1338            (6, 10), (5, 10), (6, 9), (5, 9), (6, 8), (5, 8),
1339            (6, 7), (5, 7), (6, 6), (5, 6), (6, 5), (5, 5),
1340            (6, 4), (5, 4), (6, 3), (5, 3), (6, 2), (5, 2),
1341            (6, 1), (5, 1), (6, 0), (5, 0),
1342            (4, 0), (3, 0), (4, 1), (3, 1), (4, 2), (3, 2),
1343            (4, 3), (3, 3), (4, 4), (3, 4), (4, 5), (3, 5),
1344            (4, 6), (3, 6), (4, 7), (3, 7), (4, 8), (3, 8),
1345            (4, 9), (3, 9), (4, 10), (3, 10),
1346            (2, 10), (1, 10), (2, 9), (1, 9), (2, 8), (1, 8),
1347            (2, 7), (1, 7), (2, 6), (1, 6), (2, 5), (1, 5),
1348            (2, 4), (1, 4), (2, 3), (1, 3), (2, 2), (1, 2),
1349            (2, 1), (1, 1), (2, 0), (1, 0),
1350        ]);
1351    }
1352
1353    #[test]
1354    fn test_micro_qr_2() {
1355        let res = DataModuleIter::new(Version::Micro(2)).collect::<Vec<(i16, i16)>>();
1356        assert_eq!(res, vec![
1357            (12, 12), (11, 12), (12, 11), (11, 11), (12, 10), (11, 10),
1358            (12, 9), (11, 9), (12, 8), (11, 8), (12, 7), (11, 7),
1359            (12, 6), (11, 6), (12, 5), (11, 5), (12, 4), (11, 4),
1360            (12, 3), (11, 3), (12, 2), (11, 2), (12, 1), (11, 1),
1361            (12, 0), (11, 0),
1362            (10, 0), (9, 0), (10, 1), (9, 1), (10, 2), (9, 2),
1363            (10, 3), (9, 3), (10, 4), (9, 4), (10, 5), (9, 5),
1364            (10, 6), (9, 6), (10, 7), (9, 7), (10, 8), (9, 8),
1365            (10, 9), (9, 9), (10, 10), (9, 10), (10, 11), (9, 11),
1366            (10, 12), (9, 12),
1367            (8, 12), (7, 12), (8, 11), (7, 11), (8, 10), (7, 10),
1368            (8, 9), (7, 9), (8, 8), (7, 8), (8, 7), (7, 7),
1369            (8, 6), (7, 6), (8, 5), (7, 5), (8, 4), (7, 4),
1370            (8, 3), (7, 3), (8, 2), (7, 2), (8, 1), (7, 1),
1371            (8, 0), (7, 0),
1372            (6, 0), (5, 0), (6, 1), (5, 1), (6, 2), (5, 2),
1373            (6, 3), (5, 3), (6, 4), (5, 4), (6, 5), (5, 5),
1374            (6, 6), (5, 6), (6, 7), (5, 7), (6, 8), (5, 8),
1375            (6, 9), (5, 9), (6, 10), (5, 10), (6, 11), (5, 11),
1376            (6, 12), (5, 12),
1377            (4, 12), (3, 12), (4, 11), (3, 11), (4, 10), (3, 10),
1378            (4, 9), (3, 9), (4, 8), (3, 8), (4, 7), (3, 7),
1379            (4, 6), (3, 6), (4, 5), (3, 5), (4, 4), (3, 4),
1380            (4, 3), (3, 3), (4, 2), (3, 2), (4, 1), (3, 1),
1381            (4, 0), (3, 0),
1382            (2, 0), (1, 0), (2, 1), (1, 1), (2, 2), (1, 2),
1383            (2, 3), (1, 3), (2, 4), (1, 4), (2, 5), (1, 5),
1384            (2, 6), (1, 6), (2, 7), (1, 7), (2, 8), (1, 8),
1385            (2, 9), (1, 9), (2, 10), (1, 10), (2, 11), (1, 11),
1386            (2, 12), (1, 12),
1387        ]);
1388    }
1389}
1390
1391//}}}
1392//------------------------------------------------------------------------------
1393//{{{ Data placement
1394
1395impl Canvas {
1396    fn draw_codewords<I>(&mut self, codewords: &[u8], is_half_codeword_at_end: bool, coords: &mut I)
1397    where
1398        I: Iterator<Item = (i16, i16)>,
1399    {
1400        let length = codewords.len();
1401        let last_word = if is_half_codeword_at_end { length - 1 } else { length };
1402        for (i, b) in codewords.iter().enumerate() {
1403            let bits_end = if i == last_word { 4 } else { 0 };
1404            'outside: for j in (bits_end..=7).rev() {
1405                let color = if (*b & (1 << j)) == 0 { Color::Light } else { Color::Dark };
1406                for (x, y) in coords.by_ref() {
1407                    let r = self.get_mut(x, y);
1408                    if *r == Module::Empty {
1409                        *r = Module::Unmasked(color);
1410                        continue 'outside;
1411                    }
1412                }
1413                return;
1414            }
1415        }
1416    }
1417
1418    /// Draws the encoded data and error correction codes to the empty modules.
1419    pub fn draw_data(&mut self, data: &[u8], ec: &[u8]) {
1420        let is_half_codeword_at_end = matches!(
1421            (self.version, self.ec_level),
1422            (Version::Micro(1 | 3), EcLevel::L) | (Version::Micro(3), EcLevel::M)
1423        );
1424
1425        let mut coords = DataModuleIter::new(self.version);
1426        self.draw_codewords(data, is_half_codeword_at_end, &mut coords);
1427        self.draw_codewords(ec, false, &mut coords);
1428    }
1429}
1430
1431#[cfg(test)]
1432mod draw_codewords_test {
1433    use crate::canvas::Canvas;
1434    use crate::types::{EcLevel, Version};
1435
1436    #[test]
1437    fn test_micro_qr_1() {
1438        let mut c = Canvas::new(Version::Micro(1), EcLevel::L);
1439        c.draw_all_functional_patterns();
1440        c.draw_data(b"\x6e\x5d\xe2", b"\x2b\x63");
1441        assert_eq!(
1442            &*c.to_debug_str(),
1443            "\n\
1444             #######.#.#\n\
1445             #.....#..-*\n\
1446             #.###.#..**\n\
1447             #.###.#..*-\n\
1448             #.###.#..**\n\
1449             #.....#..*-\n\
1450             #######..*-\n\
1451             .........-*\n\
1452             #........**\n\
1453             .***-**---*\n\
1454             #---*-*-**-"
1455        );
1456    }
1457
1458    #[test]
1459    fn test_qr_2() {
1460        let mut c = Canvas::new(Version::Normal(2), EcLevel::L);
1461        c.draw_all_functional_patterns();
1462        c.draw_data(
1463            b"\x92I$\x92I$\x92I$\x92I$\x92I$\x92I$\x92I$\x92I$\
1464              \x92I$\x92I$\x92I$\x92I$\x92I$\x92I$\x92I$",
1465            b"",
1466        );
1467        assert_eq!(
1468            &*c.to_debug_str(),
1469            "\n\
1470             #######..--*---*-.#######\n\
1471             #.....#..-*-*-*-*.#.....#\n\
1472             #.###.#..*---*---.#.###.#\n\
1473             #.###.#..--*---*-.#.###.#\n\
1474             #.###.#..-*-*-*-*.#.###.#\n\
1475             #.....#..*---*---.#.....#\n\
1476             #######.#.#.#.#.#.#######\n\
1477             .........--*---*-........\n\
1478             ......#..-*-*-*-*........\n\
1479             --*-*-.-**---*---*--**--*\n\
1480             -*-*--#----*---*---------\n\
1481             *----*.*--*-*-*-*-**--**-\n\
1482             --*-*-#-**---*---*--**--*\n\
1483             -*-*--.----*---*---------\n\
1484             *----*#*--*-*-*-*-**--**-\n\
1485             --*-*-.-**---*---*--**--*\n\
1486             -*-*--#----*---*#####----\n\
1487             ........#-*-*-*-#...#-**-\n\
1488             #######..*---*--#.#.#*--*\n\
1489             #.....#..--*---*#...#----\n\
1490             #.###.#..-*-*-*-#####-**-\n\
1491             #.###.#..*---*--*----*--*\n\
1492             #.###.#..--*------**-----\n\
1493             #.....#..-*-*-**-*--*-**-\n\
1494             #######..*---*--*----*--*"
1495        );
1496    }
1497}
1498//}}}
1499//------------------------------------------------------------------------------
1500//{{{ Masking
1501
1502/// The mask patterns. Since QR code and Micro QR code do not use the same
1503/// pattern number, we name them according to their shape instead of the number.
1504#[derive(Debug, Copy, Clone)]
1505pub enum MaskPattern {
1506    /// QR code pattern 000: `(x + y) % 2 == 0`.
1507    Checkerboard = 0b000,
1508
1509    /// QR code pattern 001: `y % 2 == 0`.
1510    HorizontalLines = 0b001,
1511
1512    /// QR code pattern 010: `x % 3 == 0`.
1513    VerticalLines = 0b010,
1514
1515    /// QR code pattern 011: `(x + y) % 3 == 0`.
1516    DiagonalLines = 0b011,
1517
1518    /// QR code pattern 100: `((x/3) + (y/2)) % 2 == 0`.
1519    LargeCheckerboard = 0b100,
1520
1521    /// QR code pattern 101: `(x*y)%2 + (x*y)%3 == 0`.
1522    Fields = 0b101,
1523
1524    /// QR code pattern 110: `((x*y)%2 + (x*y)%3) % 2 == 0`.
1525    Diamonds = 0b110,
1526
1527    /// QR code pattern 111: `((x+y)%2 + (x*y)%3) % 2 == 0`.
1528    Meadow = 0b111,
1529}
1530
1531mod mask_functions {
1532    pub fn checkerboard(x: i16, y: i16) -> bool {
1533        (x + y) % 2 == 0
1534    }
1535
1536    pub fn horizontal_lines(_: i16, y: i16) -> bool {
1537        y % 2 == 0
1538    }
1539
1540    pub fn vertical_lines(x: i16, _: i16) -> bool {
1541        x % 3 == 0
1542    }
1543
1544    pub fn diagonal_lines(x: i16, y: i16) -> bool {
1545        (x + y) % 3 == 0
1546    }
1547
1548    pub fn large_checkerboard(x: i16, y: i16) -> bool {
1549        ((y / 2) + (x / 3)) % 2 == 0
1550    }
1551
1552    pub fn fields(x: i16, y: i16) -> bool {
1553        (x * y) % 2 + (x * y) % 3 == 0
1554    }
1555
1556    pub fn diamonds(x: i16, y: i16) -> bool {
1557        ((x * y) % 2 + (x * y) % 3) % 2 == 0
1558    }
1559
1560    pub fn meadow(x: i16, y: i16) -> bool {
1561        ((x + y) % 2 + (x * y) % 3) % 2 == 0
1562    }
1563}
1564
1565fn get_mask_function(pattern: MaskPattern) -> fn(i16, i16) -> bool {
1566    match pattern {
1567        MaskPattern::Checkerboard => mask_functions::checkerboard,
1568        MaskPattern::HorizontalLines => mask_functions::horizontal_lines,
1569        MaskPattern::VerticalLines => mask_functions::vertical_lines,
1570        MaskPattern::DiagonalLines => mask_functions::diagonal_lines,
1571        MaskPattern::LargeCheckerboard => mask_functions::large_checkerboard,
1572        MaskPattern::Fields => mask_functions::fields,
1573        MaskPattern::Diamonds => mask_functions::diamonds,
1574        MaskPattern::Meadow => mask_functions::meadow,
1575    }
1576}
1577
1578impl Canvas {
1579    /// Applies a mask to the canvas. This method will also draw the format info
1580    /// patterns.
1581    pub fn apply_mask(&mut self, pattern: MaskPattern) {
1582        let mask_fn = get_mask_function(pattern);
1583        for x in 0..self.width {
1584            for y in 0..self.width {
1585                let module = self.get_mut(x, y);
1586                *module = module.mask(mask_fn(x, y));
1587            }
1588        }
1589
1590        self.draw_format_info_patterns(pattern);
1591    }
1592
1593    /// Draws the format information to encode the error correction level and
1594    /// mask pattern.
1595    ///
1596    /// If the error correction level or mask pattern is not supported in the
1597    /// current QR code version, this method will fail.
1598    fn draw_format_info_patterns(&mut self, pattern: MaskPattern) {
1599        let format_number = match self.version {
1600            Version::Normal(_) => {
1601                let simple_format_number = ((self.ec_level as usize) ^ 1) << 3 | (pattern as usize);
1602                FORMAT_INFOS_QR[simple_format_number]
1603            }
1604            Version::Micro(a) => {
1605                let micro_pattern_number = match pattern {
1606                    MaskPattern::HorizontalLines => 0b00,
1607                    MaskPattern::LargeCheckerboard => 0b01,
1608                    MaskPattern::Diamonds => 0b10,
1609                    MaskPattern::Meadow => 0b11,
1610                    _ => panic!("Unsupported mask pattern in Micro QR code"),
1611                };
1612                let symbol_number = match (a, self.ec_level) {
1613                    (1, EcLevel::L) => 0b000,
1614                    (2, EcLevel::L) => 0b001,
1615                    (2, EcLevel::M) => 0b010,
1616                    (3, EcLevel::L) => 0b011,
1617                    (3, EcLevel::M) => 0b100,
1618                    (4, EcLevel::L) => 0b101,
1619                    (4, EcLevel::M) => 0b110,
1620                    (4, EcLevel::Q) => 0b111,
1621                    _ => panic!("Unsupported version/ec_level combination in Micro QR code"),
1622                };
1623                let simple_format_number = symbol_number << 2 | micro_pattern_number;
1624                FORMAT_INFOS_MICRO_QR[simple_format_number]
1625            }
1626        };
1627        self.draw_format_info_patterns_with_number(format_number);
1628    }
1629}
1630
1631#[cfg(test)]
1632mod mask_tests {
1633    use crate::canvas::{Canvas, FORMAT_INFOS_MICRO_QR, FORMAT_INFOS_QR, MaskPattern};
1634    use crate::types::{EcLevel, Version};
1635
1636    #[test]
1637    fn test_apply_mask_qr() {
1638        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
1639        c.draw_all_functional_patterns();
1640        c.apply_mask(MaskPattern::Checkerboard);
1641
1642        assert_eq!(
1643            &*c.to_debug_str(),
1644            "\n\
1645             #######...#.#.#######\n\
1646             #.....#..#.#..#.....#\n\
1647             #.###.#.#.#.#.#.###.#\n\
1648             #.###.#..#.#..#.###.#\n\
1649             #.###.#...#.#.#.###.#\n\
1650             #.....#..#.#..#.....#\n\
1651             #######.#.#.#.#######\n\
1652             ........##.#.........\n\
1653             ###.#####.#.###...#..\n\
1654             .#.#.#.#.#.#.#.#.#.#.\n\
1655             #.#.#.#.#.#.#.#.#.#.#\n\
1656             .#.#.#.#.#.#.#.#.#.#.\n\
1657             #.#.#.#.#.#.#.#.#.#.#\n\
1658             ........##.#.#.#.#.#.\n\
1659             #######.#.#.#.#.#.#.#\n\
1660             #.....#.##.#.#.#.#.#.\n\
1661             #.###.#.#.#.#.#.#.#.#\n\
1662             #.###.#..#.#.#.#.#.#.\n\
1663             #.###.#.#.#.#.#.#.#.#\n\
1664             #.....#.##.#.#.#.#.#.\n\
1665             #######.#.#.#.#.#.#.#"
1666        );
1667    }
1668
1669    #[test]
1670    fn test_draw_format_info_patterns_qr() {
1671        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
1672        c.draw_format_info_patterns(MaskPattern::LargeCheckerboard);
1673        assert_eq!(
1674            &*c.to_debug_str(),
1675            "\n\
1676             ????????#????????????\n\
1677             ????????#????????????\n\
1678             ????????#????????????\n\
1679             ????????#????????????\n\
1680             ????????.????????????\n\
1681             ????????#????????????\n\
1682             ?????????????????????\n\
1683             ????????.????????????\n\
1684             ##..##?..????..#.####\n\
1685             ?????????????????????\n\
1686             ?????????????????????\n\
1687             ?????????????????????\n\
1688             ?????????????????????\n\
1689             ????????#????????????\n\
1690             ????????.????????????\n\
1691             ????????#????????????\n\
1692             ????????#????????????\n\
1693             ????????.????????????\n\
1694             ????????.????????????\n\
1695             ????????#????????????\n\
1696             ????????#????????????"
1697        );
1698    }
1699
1700    #[test]
1701    fn test_draw_format_info_patterns_micro_qr() {
1702        let mut c = Canvas::new(Version::Micro(2), EcLevel::L);
1703        c.draw_format_info_patterns(MaskPattern::LargeCheckerboard);
1704        assert_eq!(
1705            &*c.to_debug_str(),
1706            "\n\
1707             ?????????????\n\
1708             ????????#????\n\
1709             ????????.????\n\
1710             ????????.????\n\
1711             ????????#????\n\
1712             ????????#????\n\
1713             ????????.????\n\
1714             ????????.????\n\
1715             ?#.#....#????\n\
1716             ?????????????\n\
1717             ?????????????\n\
1718             ?????????????\n\
1719             ?????????????"
1720        );
1721    }
1722
1723    #[test]
1724    fn generated_format_info_tables_match_known_values() {
1725        assert_eq!(FORMAT_INFOS_QR[0], 0x5412);
1726        assert_eq!(FORMAT_INFOS_QR[31], 0x2bed);
1727        assert_eq!(FORMAT_INFOS_MICRO_QR[0], 0x4445);
1728        assert_eq!(FORMAT_INFOS_MICRO_QR[31], 0x3bba);
1729    }
1730}
1731
1732const FORMAT_INFOS_QR: [u16; 32] = generate_format_infos(0x5412);
1733const FORMAT_INFOS_MICRO_QR: [u16; 32] = generate_format_infos(0x4445);
1734const FORMAT_INFO_GENERATOR: u16 = 0x537;
1735
1736const fn generate_format_infos(mask: u16) -> [u16; 32] {
1737    let mut table = [0; 32];
1738    let mut data = 0;
1739    while data < table.len() {
1740        table[data] = format_info(data as u16, mask);
1741        data += 1;
1742    }
1743    table
1744}
1745
1746const fn format_info(data: u16, mask: u16) -> u16 {
1747    let mut remainder = data << 10;
1748    let mut bit = 14;
1749    while bit >= 10 {
1750        if remainder & (1 << bit) != 0 {
1751            remainder ^= FORMAT_INFO_GENERATOR << (bit - 10);
1752        }
1753        if bit == 10 {
1754            break;
1755        }
1756        bit -= 1;
1757    }
1758    ((data << 10) | remainder) ^ mask
1759}
1760
1761//}}}
1762//------------------------------------------------------------------------------
1763//{{{ Penalty score
1764
1765impl Canvas {
1766    /// Compute the penalty score for having too many adjacent modules with the
1767    /// same color.
1768    ///
1769    /// Every 5+N adjacent modules in the same column/row having the same color
1770    /// will contribute 3+N points.
1771    #[cfg(test)]
1772    fn compute_adjacent_penalty_score(&self, is_horizontal: bool) -> u16 {
1773        compute_adjacent_penalty_score(self.width.as_usize(), &module_bytes(&self.modules), is_horizontal)
1774    }
1775
1776    /// Compute the penalty score for having too many rectangles with the same
1777    /// color.
1778    ///
1779    /// Every 2×2 blocks (with overlapping counted) having the same color will
1780    /// contribute 3 points.
1781    #[cfg(test)]
1782    fn compute_block_penalty_score(&self) -> u16 {
1783        compute_block_penalty_score(self.width.as_usize(), &module_bytes(&self.modules))
1784    }
1785
1786    /// Compute the penalty score for having a pattern similar to the finder
1787    /// pattern in the wrong place.
1788    ///
1789    /// Every pattern that looks like `#.###.#....` in any orientation will add
1790    /// 40 points.
1791    #[cfg(test)]
1792    fn compute_finder_penalty_score(&self, is_horizontal: bool) -> u16 {
1793        compute_finder_penalty_score(self.width.as_usize(), &module_bytes(&self.modules), is_horizontal)
1794    }
1795
1796    /// Compute the penalty score for having an unbalanced dark/light ratio.
1797    ///
1798    /// The score is given linearly by the deviation from a 50% ratio of dark
1799    /// modules. The highest possible score is 100.
1800    ///
1801    /// Note that this algorithm differs slightly from the standard we do not
1802    /// round the result every 5%, but the difference should be negligible and
1803    /// should not affect which mask is chosen.
1804    #[cfg(test)]
1805    fn compute_balance_penalty_score(&self) -> u16 {
1806        compute_balance_penalty_score(&module_bytes(&self.modules))
1807    }
1808
1809    /// Compute the penalty score for having too many light modules on the sides.
1810    ///
1811    /// This penalty score is exclusive to Micro QR code.
1812    ///
1813    /// Note that the standard gives the formula for *efficiency* score, which
1814    /// has the inverse meaning of this method, but it is very easy to convert
1815    /// between the two (this score is (16×width − standard-score)).
1816    #[cfg(test)]
1817    fn compute_light_side_penalty_score(&self) -> u16 {
1818        compute_light_side_penalty_score(self.width.as_usize(), &module_bytes(&self.modules))
1819    }
1820
1821    /// Compute the total penalty scores. A QR code having higher points is less
1822    /// desirable.
1823    #[cfg(test)]
1824    fn compute_total_penalty_scores(&self) -> u16 {
1825        compute_total_penalty_score_scalar(self.version, self.width, &self.modules)
1826    }
1827
1828    fn compute_total_penalty_scores_with_scratch(&self, scratch: &mut Vec<u8>) -> u16 {
1829        debug_assert_eq!((self.width * self.width).as_usize(), self.modules.len());
1830        write_module_bytes(&self.modules, scratch);
1831        compute_total_penalty_score_from_bytes(self.version, self.width.as_usize(), scratch)
1832    }
1833
1834    #[cfg(feature = "bench-internals")]
1835    #[doc(hidden)]
1836    pub fn score_mask_for_bench(&self, scratch: &mut Vec<u8>) -> u16 {
1837        self.compute_total_penalty_scores_with_scratch(scratch)
1838    }
1839
1840    #[cfg(feature = "bench-internals")]
1841    #[doc(hidden)]
1842    pub fn score_mask_scalar_for_bench(&self, scratch: &mut Vec<u8>) -> u16 {
1843        debug_assert_eq!((self.width * self.width).as_usize(), self.modules.len());
1844        write_module_bytes(&self.modules, scratch);
1845        compute_total_penalty_score_from_bytes_scalar(self.version, self.width.as_usize(), scratch)
1846    }
1847}
1848
1849#[cfg(test)]
1850fn module_bytes(modules: &[Module]) -> Vec<u8> {
1851    modules.iter().map(|module| u8::from(module.is_dark())).collect()
1852}
1853
1854fn write_module_bytes(modules: &[Module], output: &mut Vec<u8>) {
1855    output.clear();
1856    output.extend(modules.iter().map(|module| u8::from(module.is_dark())));
1857}
1858
1859fn count_dark_modules(modules: &[u8]) -> usize {
1860    #[cfg(target_arch = "aarch64")]
1861    {
1862        // AArch64 guarantees NEON support; the helper only performs guarded
1863        // unaligned loads within the input slice.
1864        unsafe { count_dark_modules_neon(modules) }
1865    }
1866
1867    #[cfg(target_arch = "x86_64")]
1868    {
1869        if avx2_available() {
1870            // The helper only reads inside the slice bounds with unaligned
1871            // 32-byte loads.
1872            return unsafe { count_dark_modules_avx2(modules) };
1873        }
1874
1875        if sse2_available() {
1876            // The helper only reads inside the slice bounds with unaligned
1877            // 16-byte loads.
1878            return unsafe { count_dark_modules_sse2(modules) };
1879        }
1880
1881        count_dark_modules_scalar(modules)
1882    }
1883
1884    #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
1885    {
1886        count_dark_modules_scalar(modules)
1887    }
1888}
1889
1890fn count_dark_modules_scalar(modules: &[u8]) -> usize {
1891    modules.iter().filter(|&&module| module != 0).count()
1892}
1893
1894fn compute_balance_penalty_score(modules: &[u8]) -> u16 {
1895    let dark_modules = count_dark_modules(modules);
1896    let total_modules = modules.len();
1897    let ratio = dark_modules * 200 / total_modules;
1898    ratio.abs_diff(100).as_u16()
1899}
1900
1901#[cfg(any(test, feature = "bench-internals"))]
1902fn compute_balance_penalty_score_scalar(modules: &[u8]) -> u16 {
1903    let dark_modules = count_dark_modules_scalar(modules);
1904    let total_modules = modules.len();
1905    let ratio = dark_modules * 200 / total_modules;
1906    ratio.abs_diff(100).as_u16()
1907}
1908
1909fn compute_light_side_penalty_score(width: usize, modules: &[u8]) -> u16 {
1910    let bottom_row = &modules[(width - 1) * width..][..width];
1911    let h = (1..width).filter(|&x| bottom_row[x] == 0).count();
1912    let v = (1..width).filter(|&y| modules[y * width + width - 1] == 0).count();
1913
1914    (h + v + 15 * max(h, v)).as_u16()
1915}
1916
1917fn compute_adjacent_penalty_score(width: usize, modules: &[u8], is_horizontal: bool) -> u16 {
1918    if is_horizontal {
1919        compute_horizontal_adjacent_penalty_score(width, modules)
1920    } else {
1921        compute_vertical_adjacent_penalty_score(width, modules)
1922    }
1923}
1924
1925fn compute_horizontal_adjacent_penalty_score(width: usize, modules: &[u8]) -> u16 {
1926    modules.chunks_exact(width).map(compute_line_adjacent_penalty_score).sum()
1927}
1928
1929fn compute_vertical_adjacent_penalty_score(width: usize, modules: &[u8]) -> u16 {
1930    let mut total_score = 0;
1931
1932    for x in 0..width {
1933        let mut last_color = 2;
1934        let mut consecutive_len = 1_u16;
1935
1936        for y in 0..width {
1937            let color = modules[y * width + x];
1938            if color == last_color {
1939                consecutive_len += 1;
1940            } else {
1941                last_color = color;
1942                if consecutive_len >= 5 {
1943                    total_score += consecutive_len - 2;
1944                }
1945                consecutive_len = 1;
1946            }
1947        }
1948
1949        total_score += adjacent_run_score(consecutive_len);
1950    }
1951
1952    total_score
1953}
1954
1955fn compute_line_adjacent_penalty_score(line: &[u8]) -> u16 {
1956    let mut total_score = 0;
1957    let mut last_color = 2;
1958    let mut consecutive_len = 1_u16;
1959
1960    for &color in line {
1961        if color == last_color {
1962            consecutive_len += 1;
1963        } else {
1964            last_color = color;
1965            if consecutive_len >= 5 {
1966                total_score += consecutive_len - 2;
1967            }
1968            consecutive_len = 1;
1969        }
1970    }
1971
1972    total_score + adjacent_run_score(consecutive_len)
1973}
1974
1975fn adjacent_run_score(consecutive_len: u16) -> u16 {
1976    if consecutive_len >= 5 { consecutive_len - 2 } else { 0 }
1977}
1978
1979const FINDER_LIKE_PATTERN_BITS: u8 = 0b1011101;
1980const FINDER_LIKE_PATTERN_WIDTH: usize = 7;
1981const FINDER_LIKE_PATTERN_MASK: u8 = (1 << FINDER_LIKE_PATTERN_WIDTH) - 1;
1982
1983fn compute_finder_penalty_score(width: usize, modules: &[u8], is_horizontal: bool) -> u16 {
1984    let total_score = if is_horizontal {
1985        compute_horizontal_finder_penalty_score(width, modules)
1986    } else {
1987        compute_vertical_finder_penalty_score(width, modules)
1988    };
1989
1990    total_score - 360
1991}
1992
1993fn compute_horizontal_finder_penalty_score(width: usize, modules: &[u8]) -> u16 {
1994    modules.chunks_exact(width).map(compute_line_finder_penalty_score).sum()
1995}
1996
1997fn compute_vertical_finder_penalty_score(width: usize, modules: &[u8]) -> u16 {
1998    let mut total_score = 0;
1999
2000    for x in 0..width {
2001        let mut window = initial_vertical_finder_window(width, modules, x);
2002
2003        for y in 0..width.saturating_sub(FINDER_LIKE_PATTERN_WIDTH - 1) {
2004            if y > 0 {
2005                window = roll_finder_window(window, modules[(y + FINDER_LIKE_PATTERN_WIDTH - 1) * width + x]);
2006            }
2007
2008            if window != FINDER_LIKE_PATTERN_BITS {
2009                continue;
2010            }
2011
2012            if !vertical_range_has_dark(width, modules, x, y.saturating_sub(4), y)
2013                || !vertical_range_has_dark(
2014                    width,
2015                    modules,
2016                    x,
2017                    y + FINDER_LIKE_PATTERN_WIDTH,
2018                    min(y + FINDER_LIKE_PATTERN_WIDTH + 4, width),
2019                )
2020            {
2021                total_score += 40;
2022            }
2023        }
2024    }
2025
2026    total_score
2027}
2028
2029fn compute_line_finder_penalty_score(line: &[u8]) -> u16 {
2030    let mut total_score = 0;
2031    let mut window = initial_finder_window(line);
2032
2033    for offset in 0..line.len().saturating_sub(FINDER_LIKE_PATTERN_WIDTH - 1) {
2034        if offset > 0 {
2035            window = roll_finder_window(window, line[offset + FINDER_LIKE_PATTERN_WIDTH - 1]);
2036        }
2037
2038        if window != FINDER_LIKE_PATTERN_BITS {
2039            continue;
2040        }
2041
2042        if !line_range_has_dark(line, offset.saturating_sub(4), offset)
2043            || !line_range_has_dark(
2044                line,
2045                offset + FINDER_LIKE_PATTERN_WIDTH,
2046                min(offset + FINDER_LIKE_PATTERN_WIDTH + 4, line.len()),
2047            )
2048        {
2049            total_score += 40;
2050        }
2051    }
2052
2053    total_score
2054}
2055
2056fn initial_finder_window(line: &[u8]) -> u8 {
2057    line.iter().take(FINDER_LIKE_PATTERN_WIDTH).fold(0, |window, &module| roll_finder_window(window, module))
2058}
2059
2060fn initial_vertical_finder_window(width: usize, modules: &[u8], x: usize) -> u8 {
2061    (0..min(FINDER_LIKE_PATTERN_WIDTH, width)).fold(0, |window, y| roll_finder_window(window, modules[y * width + x]))
2062}
2063
2064fn roll_finder_window(window: u8, module: u8) -> u8 {
2065    ((window << 1) | (module & 1)) & FINDER_LIKE_PATTERN_MASK
2066}
2067
2068fn vertical_range_has_dark(width: usize, modules: &[u8], x: usize, start: usize, end: usize) -> bool {
2069    (start..end).any(|y| modules[y * width + x] != 0)
2070}
2071
2072fn line_range_has_dark(line: &[u8], start: usize, end: usize) -> bool {
2073    line[start..end].iter().any(|&module| module != 0)
2074}
2075
2076fn compute_block_penalty_score(width: usize, modules: &[u8]) -> u16 {
2077    #[cfg(target_arch = "aarch64")]
2078    {
2079        // AArch64 guarantees NEON support; the helper only performs guarded
2080        // unaligned loads within each row pair.
2081        unsafe { compute_block_penalty_score_neon(width, modules) }
2082    }
2083
2084    #[cfg(target_arch = "x86_64")]
2085    {
2086        if avx2_available() {
2087            // The helper only performs guarded unaligned vector loads within
2088            // each row pair.
2089            return unsafe { compute_block_penalty_score_avx2(width, modules) };
2090        }
2091
2092        if sse2_available() {
2093            // The helper only performs guarded unaligned vector loads within
2094            // each row pair.
2095            return unsafe { compute_block_penalty_score_sse2(width, modules) };
2096        }
2097
2098        compute_block_penalty_score_scalar(width, modules)
2099    }
2100
2101    #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
2102    {
2103        compute_block_penalty_score_scalar(width, modules)
2104    }
2105}
2106
2107#[cfg(any(
2108    test,
2109    feature = "bench-internals",
2110    target_arch = "x86_64",
2111    not(any(target_arch = "aarch64", target_arch = "x86_64"))
2112))]
2113fn compute_block_penalty_score_scalar(width: usize, modules: &[u8]) -> u16 {
2114    let mut total_score = 0;
2115
2116    for y in 0..width.saturating_sub(1) {
2117        let row = &modules[y * width..][..width];
2118        let next_row = &modules[(y + 1) * width..][..width];
2119        for x in 0..width.saturating_sub(1) {
2120            let this = row[x];
2121            if this == row[x + 1] && this == next_row[x] && this == next_row[x + 1] {
2122                total_score += 3;
2123            }
2124        }
2125    }
2126
2127    total_score
2128}
2129
2130#[cfg(all(target_arch = "x86_64", feature = "std"))]
2131fn sse2_available() -> bool {
2132    std::is_x86_feature_detected!("sse2")
2133}
2134
2135#[cfg(all(target_arch = "x86_64", not(feature = "std")))]
2136fn sse2_available() -> bool {
2137    true
2138}
2139
2140#[cfg(all(target_arch = "x86_64", feature = "std"))]
2141fn avx2_available() -> bool {
2142    std::is_x86_feature_detected!("avx2")
2143}
2144
2145#[cfg(all(target_arch = "x86_64", not(feature = "std")))]
2146fn avx2_available() -> bool {
2147    false
2148}
2149
2150#[cfg(target_arch = "aarch64")]
2151#[target_feature(enable = "neon")]
2152unsafe fn count_dark_modules_neon(modules: &[u8]) -> usize {
2153    use core::arch::aarch64::{vaddvq_u8, vceqq_u8, vcntq_u8, vdupq_n_u8, vld1q_u8, vmvnq_u8};
2154
2155    let mut count = 0;
2156    let mut offset = 0;
2157    let zero = vdupq_n_u8(0);
2158
2159    while offset + 16 <= modules.len() {
2160        // The loop guard ensures the full 16-byte vector is in bounds for this
2161        // slice.
2162        let chunk = unsafe { vld1q_u8(modules.as_ptr().wrapping_add(offset)) };
2163        let nonzero_lanes = vmvnq_u8(vceqq_u8(chunk, zero));
2164        count += (vaddvq_u8(vcntq_u8(nonzero_lanes)) / 8) as usize;
2165        offset += 16;
2166    }
2167
2168    count + count_dark_modules_scalar(&modules[offset..])
2169}
2170
2171#[cfg(target_arch = "x86_64")]
2172#[target_feature(enable = "avx2")]
2173unsafe fn count_dark_modules_avx2(modules: &[u8]) -> usize {
2174    use core::arch::x86_64::{
2175        __m256i, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8, _mm256_setzero_si256,
2176    };
2177
2178    let mut count = 0;
2179    let mut offset = 0;
2180    let zero = _mm256_setzero_si256();
2181
2182    while offset + 32 <= modules.len() {
2183        let ptr = modules.as_ptr().wrapping_add(offset).cast::<__m256i>();
2184        // `_mm256_loadu_si256` accepts unaligned input; the loop guard ensures
2185        // the full 32-byte vector is in bounds for this slice.
2186        let chunk = unsafe { _mm256_loadu_si256(ptr) };
2187        let zero_lanes = _mm256_movemask_epi8(_mm256_cmpeq_epi8(chunk, zero)) as u32;
2188        count += 32 - zero_lanes.count_ones() as usize;
2189        offset += 32;
2190    }
2191
2192    count + count_dark_modules_sse2_remainder(&modules[offset..])
2193}
2194
2195#[cfg(target_arch = "x86_64")]
2196#[target_feature(enable = "sse2")]
2197unsafe fn count_dark_modules_sse2(modules: &[u8]) -> usize {
2198    count_dark_modules_sse2_remainder(modules)
2199}
2200
2201#[cfg(target_arch = "x86_64")]
2202#[target_feature(enable = "sse2")]
2203fn count_dark_modules_sse2_remainder(modules: &[u8]) -> usize {
2204    use core::arch::x86_64::{__m128i, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm_setzero_si128};
2205
2206    let mut count = 0;
2207    let mut offset = 0;
2208    let zero = _mm_setzero_si128();
2209
2210    while offset + 16 <= modules.len() {
2211        let ptr = modules.as_ptr().wrapping_add(offset).cast::<__m128i>();
2212        // `_mm_loadu_si128` accepts unaligned input; the loop guard ensures the
2213        // full 16-byte vector is in bounds for this slice.
2214        let chunk = unsafe { _mm_loadu_si128(ptr) };
2215        let zero_lanes = _mm_movemask_epi8(_mm_cmpeq_epi8(chunk, zero)) as u32;
2216        count += 16 - zero_lanes.count_ones() as usize;
2217        offset += 16;
2218    }
2219
2220    count + count_dark_modules_scalar(&modules[offset..])
2221}
2222
2223#[cfg(target_arch = "aarch64")]
2224#[target_feature(enable = "neon")]
2225unsafe fn compute_block_penalty_score_neon(width: usize, modules: &[u8]) -> u16 {
2226    use core::arch::aarch64::{vaddvq_u8, vandq_u8, vceqq_u8, vcntq_u8, vld1q_u8};
2227
2228    let mut total_score = 0;
2229    for y in 0..width.saturating_sub(1) {
2230        let row = &modules[y * width..][..width];
2231        let next_row = &modules[(y + 1) * width..][..width];
2232        let mut x = 0;
2233
2234        while x + 16 < width {
2235            // The loop guard ensures all four 16-byte windows stay inside
2236            // their row slices.
2237            let row_chunk = unsafe { vld1q_u8(row.as_ptr().wrapping_add(x)) };
2238            let row_right_chunk = unsafe { vld1q_u8(row.as_ptr().wrapping_add(x + 1)) };
2239            let next_chunk = unsafe { vld1q_u8(next_row.as_ptr().wrapping_add(x)) };
2240            let next_right_chunk = unsafe { vld1q_u8(next_row.as_ptr().wrapping_add(x + 1)) };
2241
2242            let horizontal = vceqq_u8(row_chunk, row_right_chunk);
2243            let vertical = vceqq_u8(row_chunk, next_chunk);
2244            let diagonal = vceqq_u8(row_chunk, next_right_chunk);
2245            let blocks = vandq_u8(vandq_u8(horizontal, vertical), diagonal);
2246            total_score += (vaddvq_u8(vcntq_u8(blocks)) / 8) as u16 * 3;
2247            x += 16;
2248        }
2249
2250        total_score += compute_block_penalty_score_scalar_tail(row, next_row, x);
2251    }
2252
2253    total_score
2254}
2255
2256#[cfg(target_arch = "x86_64")]
2257#[target_feature(enable = "avx2")]
2258unsafe fn compute_block_penalty_score_avx2(width: usize, modules: &[u8]) -> u16 {
2259    use core::arch::x86_64::{__m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8};
2260
2261    let mut total_score = 0;
2262    for y in 0..width.saturating_sub(1) {
2263        let row = &modules[y * width..][..width];
2264        let next_row = &modules[(y + 1) * width..][..width];
2265        let mut x = 0;
2266
2267        while x + 32 < width {
2268            let row_ptr = row.as_ptr().wrapping_add(x).cast::<__m256i>();
2269            let row_right_ptr = row.as_ptr().wrapping_add(x + 1).cast::<__m256i>();
2270            let next_ptr = next_row.as_ptr().wrapping_add(x).cast::<__m256i>();
2271            let next_right_ptr = next_row.as_ptr().wrapping_add(x + 1).cast::<__m256i>();
2272
2273            // `_mm256_loadu_si256` accepts unaligned input; the loop guard
2274            // ensures all four 32-byte windows stay inside their row slices.
2275            let row_chunk = unsafe { _mm256_loadu_si256(row_ptr) };
2276            let row_right_chunk = unsafe { _mm256_loadu_si256(row_right_ptr) };
2277            let next_chunk = unsafe { _mm256_loadu_si256(next_ptr) };
2278            let next_right_chunk = unsafe { _mm256_loadu_si256(next_right_ptr) };
2279
2280            let horizontal = _mm256_cmpeq_epi8(row_chunk, row_right_chunk);
2281            let vertical = _mm256_cmpeq_epi8(row_chunk, next_chunk);
2282            let diagonal = _mm256_cmpeq_epi8(row_chunk, next_right_chunk);
2283            let blocks = _mm256_and_si256(_mm256_and_si256(horizontal, vertical), diagonal);
2284            total_score += (_mm256_movemask_epi8(blocks) as u32).count_ones() as u16 * 3;
2285            x += 32;
2286        }
2287
2288        total_score += compute_block_penalty_score_scalar_tail(row, next_row, x);
2289    }
2290
2291    total_score
2292}
2293
2294#[cfg(target_arch = "x86_64")]
2295#[target_feature(enable = "sse2")]
2296unsafe fn compute_block_penalty_score_sse2(width: usize, modules: &[u8]) -> u16 {
2297    use core::arch::x86_64::{__m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8};
2298
2299    let mut total_score = 0;
2300    for y in 0..width.saturating_sub(1) {
2301        let row = &modules[y * width..][..width];
2302        let next_row = &modules[(y + 1) * width..][..width];
2303        let mut x = 0;
2304
2305        while x + 16 < width {
2306            let row_ptr = row.as_ptr().wrapping_add(x).cast::<__m128i>();
2307            let row_right_ptr = row.as_ptr().wrapping_add(x + 1).cast::<__m128i>();
2308            let next_ptr = next_row.as_ptr().wrapping_add(x).cast::<__m128i>();
2309            let next_right_ptr = next_row.as_ptr().wrapping_add(x + 1).cast::<__m128i>();
2310
2311            // `_mm_loadu_si128` accepts unaligned input; the loop guard ensures
2312            // all four 16-byte windows stay inside their row slices.
2313            let row_chunk = unsafe { _mm_loadu_si128(row_ptr) };
2314            let row_right_chunk = unsafe { _mm_loadu_si128(row_right_ptr) };
2315            let next_chunk = unsafe { _mm_loadu_si128(next_ptr) };
2316            let next_right_chunk = unsafe { _mm_loadu_si128(next_right_ptr) };
2317
2318            let horizontal = _mm_cmpeq_epi8(row_chunk, row_right_chunk);
2319            let vertical = _mm_cmpeq_epi8(row_chunk, next_chunk);
2320            let diagonal = _mm_cmpeq_epi8(row_chunk, next_right_chunk);
2321            let blocks = _mm_and_si128(_mm_and_si128(horizontal, vertical), diagonal);
2322            total_score += (_mm_movemask_epi8(blocks) as u32).count_ones() as u16 * 3;
2323            x += 16;
2324        }
2325
2326        total_score += compute_block_penalty_score_scalar_tail(row, next_row, x);
2327    }
2328
2329    total_score
2330}
2331
2332#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
2333fn compute_block_penalty_score_scalar_tail(row: &[u8], next_row: &[u8], start: usize) -> u16 {
2334    let mut total_score = 0;
2335    let mut x = start;
2336
2337    while x + 1 < row.len() {
2338        let this = row[x];
2339        if this == row[x + 1] && this == next_row[x] && this == next_row[x + 1] {
2340            total_score += 3;
2341        }
2342        x += 1;
2343    }
2344
2345    total_score
2346}
2347
2348fn compute_total_penalty_score_from_bytes(version: Version, width: usize, modules: &[u8]) -> u16 {
2349    match version {
2350        Version::Normal(_) => {
2351            let s1_a = compute_adjacent_penalty_score(width, modules, true);
2352            let s1_b = compute_adjacent_penalty_score(width, modules, false);
2353            let s2 = compute_block_penalty_score(width, modules);
2354            let s3_a = compute_finder_penalty_score(width, modules, true);
2355            let s3_b = compute_finder_penalty_score(width, modules, false);
2356            let s4 = compute_balance_penalty_score(modules);
2357            s1_a + s1_b + s2 + s3_a + s3_b + s4
2358        }
2359        Version::Micro(_) => compute_light_side_penalty_score(width, modules),
2360    }
2361}
2362
2363#[cfg(any(test, feature = "bench-internals"))]
2364fn compute_total_penalty_score_from_bytes_scalar(version: Version, width: usize, modules: &[u8]) -> u16 {
2365    match version {
2366        Version::Normal(_) => {
2367            let s1_a = compute_adjacent_penalty_score(width, modules, true);
2368            let s1_b = compute_adjacent_penalty_score(width, modules, false);
2369            let s2 = compute_block_penalty_score_scalar(width, modules);
2370            let s3_a = compute_finder_penalty_score(width, modules, true);
2371            let s3_b = compute_finder_penalty_score(width, modules, false);
2372            let s4 = compute_balance_penalty_score_scalar(modules);
2373            s1_a + s1_b + s2 + s3_a + s3_b + s4
2374        }
2375        Version::Micro(_) => compute_light_side_penalty_score(width, modules),
2376    }
2377}
2378
2379#[cfg(test)]
2380fn compute_total_penalty_score_scalar(version: Version, width: i16, modules: &[Module]) -> u16 {
2381    debug_assert_eq!((width * width).as_usize(), modules.len());
2382    let modules = modules.iter().map(|module| u8::from(module.is_dark())).collect::<Vec<_>>();
2383    compute_total_penalty_score_from_bytes_scalar(version, width.as_usize(), &modules)
2384}
2385
2386#[cfg(test)]
2387mod penalty_tests {
2388    use crate::canvas::{
2389        Canvas, MaskPattern, compute_adjacent_penalty_score, compute_balance_penalty_score,
2390        compute_block_penalty_score, compute_block_penalty_score_scalar, compute_finder_penalty_score,
2391        compute_light_side_penalty_score, compute_total_penalty_score_from_bytes,
2392        compute_total_penalty_score_from_bytes_scalar, compute_total_penalty_score_scalar, count_dark_modules,
2393        count_dark_modules_scalar,
2394    };
2395    use crate::cast::As;
2396    use crate::types::{Color, EcLevel, Version};
2397
2398    fn create_test_canvas() -> Canvas {
2399        let mut c = Canvas::new(Version::Normal(1), EcLevel::Q);
2400        c.draw_all_functional_patterns();
2401        c.draw_data(
2402            b"\x20\x5b\x0b\x78\xd1\x72\xdc\x4d\x43\x40\xec\x11\x00",
2403            b"\xa8\x48\x16\x52\xd9\x36\x9c\x00\x2e\x0f\xb4\x7a\x10",
2404        );
2405        c.apply_mask(MaskPattern::Checkerboard);
2406        c
2407    }
2408
2409    #[test]
2410    fn check_penalty_canvas() {
2411        let c = create_test_canvas();
2412        assert_eq!(
2413            &*c.to_debug_str(),
2414            "\n\
2415             #######.##....#######\n\
2416             #.....#.#..#..#.....#\n\
2417             #.###.#.#..##.#.###.#\n\
2418             #.###.#.#.....#.###.#\n\
2419             #.###.#.#.#...#.###.#\n\
2420             #.....#...#...#.....#\n\
2421             #######.#.#.#.#######\n\
2422             ........#............\n\
2423             .##.#.##....#.#.#####\n\
2424             .#......####....#...#\n\
2425             ..##.###.##...#.##...\n\
2426             .##.##.#..##.#.#.###.\n\
2427             #...#.#.#.###.###.#.#\n\
2428             ........##.#..#...#.#\n\
2429             #######.#.#....#.##..\n\
2430             #.....#..#.##.##.#...\n\
2431             #.###.#.#.#...#######\n\
2432             #.###.#..#.#.#.#...#.\n\
2433             #.###.#.#...####.#..#\n\
2434             #.....#.#.##.#...#.##\n\
2435             #######.....####....#"
2436        );
2437    }
2438
2439    #[test]
2440    fn test_penalty_score_adjacent() {
2441        let c = create_test_canvas();
2442        assert_eq!(c.compute_adjacent_penalty_score(true), 88);
2443        assert_eq!(c.compute_adjacent_penalty_score(false), 92);
2444    }
2445
2446    #[test]
2447    fn adjacent_penalty_score_matches_canvas_wrapper() {
2448        let c = create_test_canvas();
2449        let modules = c.modules.iter().map(|module| u8::from(module.is_dark())).collect::<Vec<_>>();
2450        assert_eq!(
2451            compute_adjacent_penalty_score(c.width.as_usize(), &modules, true),
2452            c.compute_adjacent_penalty_score(true)
2453        );
2454        assert_eq!(
2455            compute_adjacent_penalty_score(c.width.as_usize(), &modules, false),
2456            c.compute_adjacent_penalty_score(false)
2457        );
2458    }
2459
2460    #[test]
2461    fn test_penalty_score_block() {
2462        let c = create_test_canvas();
2463        assert_eq!(c.compute_block_penalty_score(), 90);
2464    }
2465
2466    #[test]
2467    fn block_penalty_score_matches_scalar_for_varied_widths() {
2468        for width in [1_usize, 2, 7, 21, 45] {
2469            let modules = (0..width * width).map(|i| u8::from((i + i / width) % 5 < 2)).collect::<Vec<_>>();
2470            assert_eq!(
2471                compute_block_penalty_score(width, &modules),
2472                compute_block_penalty_score_scalar(width, &modules)
2473            );
2474        }
2475    }
2476
2477    #[test]
2478    fn test_penalty_score_finder() {
2479        let c = create_test_canvas();
2480        assert_eq!(c.compute_finder_penalty_score(true), 0);
2481        assert_eq!(c.compute_finder_penalty_score(false), 40);
2482    }
2483
2484    #[test]
2485    fn finder_penalty_score_matches_canvas_wrapper() {
2486        let c = create_test_canvas();
2487        let modules = c.modules.iter().map(|module| u8::from(module.is_dark())).collect::<Vec<_>>();
2488        assert_eq!(
2489            compute_finder_penalty_score(c.width.as_usize(), &modules, true),
2490            c.compute_finder_penalty_score(true)
2491        );
2492        assert_eq!(
2493            compute_finder_penalty_score(c.width.as_usize(), &modules, false),
2494            c.compute_finder_penalty_score(false)
2495        );
2496    }
2497
2498    #[test]
2499    fn test_penalty_score_balance() {
2500        let c = create_test_canvas();
2501        assert_eq!(c.compute_balance_penalty_score(), 2);
2502    }
2503
2504    #[test]
2505    fn balance_penalty_score_matches_canvas_wrapper() {
2506        let c = create_test_canvas();
2507        let modules = c.modules.iter().map(|module| u8::from(module.is_dark())).collect::<Vec<_>>();
2508        assert_eq!(compute_balance_penalty_score(&modules), c.compute_balance_penalty_score());
2509    }
2510
2511    #[test]
2512    fn dark_module_count_matches_scalar_for_varied_lengths() {
2513        for len in 0..65 {
2514            let modules = (0..len).map(|i| u8::from(i % 3 == 0 || i % 7 == 0)).collect::<Vec<_>>();
2515            assert_eq!(count_dark_modules(&modules), count_dark_modules_scalar(&modules));
2516        }
2517    }
2518
2519    #[test]
2520    fn scalar_penalty_score_matches_canvas_wrapper() {
2521        let c = create_test_canvas();
2522        assert_eq!(
2523            compute_total_penalty_score_scalar(c.version, c.width, &c.modules),
2524            c.compute_total_penalty_scores()
2525        );
2526    }
2527
2528    #[test]
2529    fn accelerated_penalty_score_matches_scalar_byte_grid() {
2530        let c = create_test_canvas();
2531        let modules = c.modules.iter().map(|module| u8::from(module.is_dark())).collect::<Vec<_>>();
2532
2533        assert_eq!(
2534            compute_total_penalty_score_from_bytes(c.version, c.width.as_usize(), &modules),
2535            compute_total_penalty_score_from_bytes_scalar(c.version, c.width.as_usize(), &modules)
2536        );
2537    }
2538
2539    #[test]
2540    fn scratch_penalty_score_matches_canvas_wrapper() {
2541        let c = create_test_canvas();
2542        let mut scratch = Vec::new();
2543
2544        assert_eq!(c.compute_total_penalty_scores_with_scratch(&mut scratch), c.compute_total_penalty_scores());
2545        assert_eq!(scratch.len(), c.modules.len());
2546    }
2547
2548    #[test]
2549    fn test_penalty_score_light_sides() {
2550        static HORIZONTAL_SIDE: [Color; 17] = [
2551            Color::Dark,
2552            Color::Light,
2553            Color::Light,
2554            Color::Dark,
2555            Color::Dark,
2556            Color::Dark,
2557            Color::Light,
2558            Color::Light,
2559            Color::Dark,
2560            Color::Light,
2561            Color::Dark,
2562            Color::Light,
2563            Color::Light,
2564            Color::Dark,
2565            Color::Light,
2566            Color::Light,
2567            Color::Light,
2568        ];
2569        static VERTICAL_SIDE: [Color; 17] = [
2570            Color::Dark,
2571            Color::Dark,
2572            Color::Dark,
2573            Color::Light,
2574            Color::Light,
2575            Color::Dark,
2576            Color::Dark,
2577            Color::Light,
2578            Color::Dark,
2579            Color::Light,
2580            Color::Dark,
2581            Color::Light,
2582            Color::Dark,
2583            Color::Light,
2584            Color::Light,
2585            Color::Dark,
2586            Color::Light,
2587        ];
2588
2589        let mut c = Canvas::new(Version::Micro(4), EcLevel::Q);
2590        for i in 0_i16..17 {
2591            c.put(i, -1, HORIZONTAL_SIDE[i.as_usize()]);
2592            c.put(-1, i, VERTICAL_SIDE[i.as_usize()]);
2593        }
2594
2595        assert_eq!(c.compute_light_side_penalty_score(), 168);
2596    }
2597
2598    #[test]
2599    fn light_side_penalty_score_matches_canvas_wrapper() {
2600        let mut c = Canvas::new(Version::Micro(4), EcLevel::Q);
2601        c.draw_all_functional_patterns();
2602        let modules = c.modules.iter().map(|module| u8::from(module.is_dark())).collect::<Vec<_>>();
2603
2604        assert_eq!(
2605            compute_light_side_penalty_score(c.width.as_usize(), &modules),
2606            c.compute_light_side_penalty_score()
2607        );
2608    }
2609}
2610
2611//}}}
2612//------------------------------------------------------------------------------
2613//{{{ Select mask with lowest penalty score
2614
2615static ALL_PATTERNS_QR: [MaskPattern; 8] = [
2616    MaskPattern::Checkerboard,
2617    MaskPattern::HorizontalLines,
2618    MaskPattern::VerticalLines,
2619    MaskPattern::DiagonalLines,
2620    MaskPattern::LargeCheckerboard,
2621    MaskPattern::Fields,
2622    MaskPattern::Diamonds,
2623    MaskPattern::Meadow,
2624];
2625
2626static ALL_PATTERNS_MICRO_QR: [MaskPattern; 4] =
2627    [MaskPattern::HorizontalLines, MaskPattern::LargeCheckerboard, MaskPattern::Diamonds, MaskPattern::Meadow];
2628
2629impl Canvas {
2630    /// Construct a new canvas and apply the best masking that gives the lowest
2631    /// penalty score.
2632    #[must_use]
2633    pub fn apply_best_mask(&self) -> Self {
2634        let patterns: &[MaskPattern] = match self.version {
2635            Version::Normal(_) => &ALL_PATTERNS_QR,
2636            Version::Micro(_) => &ALL_PATTERNS_MICRO_QR,
2637        };
2638        let mut scratch = Vec::with_capacity(self.modules.len());
2639        let mut best_canvas = None;
2640        let mut best_score = u16::MAX;
2641
2642        for &pattern in patterns {
2643            let mut c = self.clone();
2644            c.apply_mask(pattern);
2645            let score = c.compute_total_penalty_scores_with_scratch(&mut scratch);
2646            if score < best_score {
2647                best_score = score;
2648                best_canvas = Some(c);
2649            }
2650        }
2651
2652        best_canvas.expect("at least one pattern")
2653    }
2654
2655    /// Convert the modules into a vector of colors.
2656    pub fn into_colors(self) -> Vec<Color> {
2657        self.modules.into_iter().map(Color::from).collect()
2658    }
2659}
2660
2661//}}}
2662//------------------------------------------------------------------------------