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
697fn for_each_format_info_module(version: Version, format_info: u16, mut write: impl FnMut(i16, i16, Color)) {
698    fn emit_bits(format_info: u16, coords: &[(i16, i16)], write: &mut impl FnMut(i16, i16, Color)) {
699        let mut bit = 1_u16 << 14;
700        for &(x, y) in coords {
701            write(x, y, if format_info & bit == 0 { Color::Light } else { Color::Dark });
702            bit >>= 1;
703        }
704    }
705
706    match version {
707        Version::Micro(_) => emit_bits(format_info, &FORMAT_INFO_COORDS_MICRO_QR, &mut write),
708        Version::Normal(_) => {
709            emit_bits(format_info, &FORMAT_INFO_COORDS_QR_MAIN, &mut write);
710            emit_bits(format_info, &FORMAT_INFO_COORDS_QR_SIDE, &mut write);
711            write(8, -8, Color::Dark);
712        }
713    }
714}
715
716impl Canvas {
717    /// Draws a big-endian integer onto the canvas with the given coordinates.
718    ///
719    /// The 1 bits will be plotted with `on_color` and the 0 bits with
720    /// `off_color`. The coordinates will be extracted from the `coords`
721    /// iterator. It will start from the most significant bits first, so
722    /// *trailing* zeros will be ignored.
723    fn draw_number(&mut self, number: u32, bits: u32, on_color: Color, off_color: Color, coords: &[(i16, i16)]) {
724        let mut mask = 1 << (bits - 1);
725        for &(x, y) in coords {
726            let color = if (mask & number) == 0 { off_color } else { on_color };
727            self.put(x, y, color);
728            mask >>= 1;
729        }
730    }
731
732    /// Draws the format info patterns for an encoded number.
733    fn draw_format_info_patterns_with_number(&mut self, format_info: u16) {
734        for_each_format_info_module(self.version, format_info, |x, y, color| self.put(x, y, color));
735    }
736
737    /// Reserves area to put in the format information.
738    fn draw_reserved_format_info_patterns(&mut self) {
739        self.draw_format_info_patterns_with_number(0);
740    }
741
742    /// Draws the version information patterns.
743    fn draw_version_info_patterns(&mut self) {
744        match self.version {
745            Version::Micro(_) | Version::Normal(1..=6) => {}
746            Version::Normal(a) => {
747                let version_info = VERSION_INFOS[(a - 7).as_usize()];
748                self.draw_number(version_info, 18, Color::Dark, Color::Light, &VERSION_INFO_COORDS_BL);
749                self.draw_number(version_info, 18, Color::Dark, Color::Light, &VERSION_INFO_COORDS_TR);
750            }
751        }
752    }
753}
754
755#[cfg(test)]
756mod draw_version_info_tests {
757    use crate::canvas::Canvas;
758    use crate::types::{Color, EcLevel, Version};
759
760    #[test]
761    fn test_draw_number() {
762        let mut c = Canvas::new(Version::Micro(1), EcLevel::L);
763        c.draw_number(0b1010_1101, 8, Color::Dark, Color::Light, &[(0, 0), (0, -1), (-2, -2), (-2, 0)]);
764        assert_eq!(
765            &*c.to_debug_str(),
766            "\n\
767             #????????.?\n\
768             ???????????\n\
769             ???????????\n\
770             ???????????\n\
771             ???????????\n\
772             ???????????\n\
773             ???????????\n\
774             ???????????\n\
775             ???????????\n\
776             ?????????#?\n\
777             .??????????"
778        );
779    }
780
781    #[test]
782    fn test_draw_version_info_1() {
783        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
784        c.draw_version_info_patterns();
785        assert_eq!(
786            &*c.to_debug_str(),
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             ?????????????????????\n\
800             ?????????????????????\n\
801             ?????????????????????\n\
802             ?????????????????????\n\
803             ?????????????????????\n\
804             ?????????????????????\n\
805             ?????????????????????\n\
806             ?????????????????????\n\
807             ?????????????????????\n\
808             ?????????????????????"
809        );
810    }
811
812    #[test]
813    fn test_draw_version_info_7() {
814        let mut c = Canvas::new(Version::Normal(7), EcLevel::L);
815        c.draw_version_info_patterns();
816
817        assert_eq!(
818            &*c.to_debug_str(),
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             .####.???????????????????????????????????????\n\
856             #..##.???????????????????????????????????????\n\
857             ?????????????????????????????????????????????\n\
858             ?????????????????????????????????????????????\n\
859             ?????????????????????????????????????????????\n\
860             ?????????????????????????????????????????????\n\
861             ?????????????????????????????????????????????\n\
862             ?????????????????????????????????????????????\n\
863             ?????????????????????????????????????????????\n\
864             ?????????????????????????????????????????????"
865        );
866    }
867
868    #[test]
869    fn test_draw_reserved_format_info_patterns_qr() {
870        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
871        c.draw_reserved_format_info_patterns();
872        assert_eq!(
873            &*c.to_debug_str(),
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             ?????????????????????\n\
887             ?????????????????????\n\
888             ????????#????????????\n\
889             ????????.????????????\n\
890             ????????.????????????\n\
891             ????????.????????????\n\
892             ????????.????????????\n\
893             ????????.????????????\n\
894             ????????.????????????\n\
895             ????????.????????????"
896        );
897    }
898
899    #[test]
900    fn test_draw_reserved_format_info_patterns_micro_qr() {
901        let mut c = Canvas::new(Version::Micro(1), EcLevel::L);
902        c.draw_reserved_format_info_patterns();
903        assert_eq!(
904            &*c.to_debug_str(),
905            "\n\
906             ???????????\n\
907             ????????.??\n\
908             ????????.??\n\
909             ????????.??\n\
910             ????????.??\n\
911             ????????.??\n\
912             ????????.??\n\
913             ????????.??\n\
914             ?........??\n\
915             ???????????\n\
916             ???????????"
917        );
918    }
919}
920
921static VERSION_INFO_COORDS_BL: [(i16, i16); 18] = [
922    (5, -9),
923    (5, -10),
924    (5, -11),
925    (4, -9),
926    (4, -10),
927    (4, -11),
928    (3, -9),
929    (3, -10),
930    (3, -11),
931    (2, -9),
932    (2, -10),
933    (2, -11),
934    (1, -9),
935    (1, -10),
936    (1, -11),
937    (0, -9),
938    (0, -10),
939    (0, -11),
940];
941
942static VERSION_INFO_COORDS_TR: [(i16, i16); 18] = [
943    (-9, 5),
944    (-10, 5),
945    (-11, 5),
946    (-9, 4),
947    (-10, 4),
948    (-11, 4),
949    (-9, 3),
950    (-10, 3),
951    (-11, 3),
952    (-9, 2),
953    (-10, 2),
954    (-11, 2),
955    (-9, 1),
956    (-10, 1),
957    (-11, 1),
958    (-9, 0),
959    (-10, 0),
960    (-11, 0),
961];
962
963static FORMAT_INFO_COORDS_QR_MAIN: [(i16, i16); 15] = [
964    (0, 8),
965    (1, 8),
966    (2, 8),
967    (3, 8),
968    (4, 8),
969    (5, 8),
970    (7, 8),
971    (8, 8),
972    (8, 7),
973    (8, 5),
974    (8, 4),
975    (8, 3),
976    (8, 2),
977    (8, 1),
978    (8, 0),
979];
980
981static FORMAT_INFO_COORDS_QR_SIDE: [(i16, i16); 15] = [
982    (8, -1),
983    (8, -2),
984    (8, -3),
985    (8, -4),
986    (8, -5),
987    (8, -6),
988    (8, -7),
989    (-8, 8),
990    (-7, 8),
991    (-6, 8),
992    (-5, 8),
993    (-4, 8),
994    (-3, 8),
995    (-2, 8),
996    (-1, 8),
997];
998
999static FORMAT_INFO_COORDS_MICRO_QR: [(i16, i16); 15] = [
1000    (1, 8),
1001    (2, 8),
1002    (3, 8),
1003    (4, 8),
1004    (5, 8),
1005    (6, 8),
1006    (7, 8),
1007    (8, 8),
1008    (8, 7),
1009    (8, 6),
1010    (8, 5),
1011    (8, 4),
1012    (8, 3),
1013    (8, 2),
1014    (8, 1),
1015];
1016
1017static VERSION_INFOS: [u32; 34] = [
1018    0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, 0x0f928, 0x10b78, 0x1145d, 0x12a17,
1019    0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
1020    0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, 0x27541, 0x28c69,
1021];
1022
1023//}}}
1024//------------------------------------------------------------------------------
1025//{{{ All functional patterns before data placement
1026
1027impl Canvas {
1028    /// Draw all functional patterns, before data placement.
1029    ///
1030    /// All functional patterns (e.g. the finder pattern) *except* the format
1031    /// info pattern will be filled in. The format info pattern will be filled
1032    /// with light modules instead. Data bits can then put in the empty modules.
1033    /// with `.draw_data()`.
1034    pub fn draw_all_functional_patterns(&mut self) {
1035        self.draw_finder_patterns();
1036        self.draw_alignment_patterns();
1037        self.draw_reserved_format_info_patterns();
1038        self.draw_timing_patterns();
1039        self.draw_version_info_patterns();
1040    }
1041}
1042
1043/// Gets whether the module at the given coordinates represents a functional
1044/// module.
1045pub fn is_functional(version: Version, width: i16, x: i16, y: i16) -> bool {
1046    debug_assert!(width == version.width());
1047
1048    let x = if x < 0 { x + width } else { x };
1049    let y = if y < 0 { y + width } else { y };
1050
1051    match version {
1052        Version::Micro(_) => x == 0 || y == 0 || (x < 9 && y < 9),
1053        Version::Normal(a) => {
1054            let version_info_test = matches!(a, 7..=40)
1055                && width == version.width()
1056                && (((0..6).contains(&x) && (width - 11..width - 8).contains(&y))
1057                    || ((width - 11..width - 8).contains(&x) && (0..6).contains(&y)));
1058            let non_alignment_test = x == 6 || y == 6 || // Timing patterns
1059                (x < 9 && y < 9) ||                  // Top-left finder pattern
1060                (x < 9 && y >= width - 8) ||           // Bottom-left finder pattern
1061                (x >= width - 8 && y < 9) ||           // Top-right finder pattern
1062                version_info_test;
1063            match a {
1064                _ if non_alignment_test => true,
1065                1 => false,
1066                2..=40 => {
1067                    let positions = alignment_pattern_positions(a);
1068                    let last = positions.len() - 1;
1069                    for (i, align_x) in positions.iter().enumerate() {
1070                        for (j, align_y) in positions.iter().enumerate() {
1071                            if i == 0 && (j == 0 || j == last) || (i == last && j == 0) {
1072                                continue;
1073                            }
1074                            if (*align_x - x).abs() <= 2 && (*align_y - y).abs() <= 2 {
1075                                return true;
1076                            }
1077                        }
1078                    }
1079                    false
1080                }
1081                _ => false,
1082            }
1083        }
1084    }
1085}
1086
1087/// Counts modules classified by [`is_functional`] for canonical geometry.
1088///
1089/// Returns `None` for an unsupported version. This is an internal facade
1090/// helper; it follows the classifier's definition rather than inspecting
1091/// encoded module colors.
1092#[doc(hidden)]
1093pub fn functional_module_count(version: Version) -> Option<usize> {
1094    match version {
1095        Version::Micro(1..=4) => {
1096            let width = version.width().as_usize();
1097            // The 9x9 corner and the remaining portions of row/column zero.
1098            Some(81 + 2 * (width - 9))
1099        }
1100        Version::Normal(number @ 1..=40) => {
1101            let width = version.width().as_usize();
1102            // Three disjoint finder/format corners, plus timing-line cells
1103            // outside those corners.
1104            let base = 9 * 9 + 2 * 9 * 8 + 2 * (width - 17);
1105            let count = alignment_pattern_positions(number).len();
1106            let alignment = if count == 0 {
1107                0
1108            } else {
1109                // Alignment squares do not overlap. Exclude the three finder
1110                // corners, then remove five timing cells from each axis square.
1111                25 * (count * count - 3) - 10 * (count - 2)
1112            };
1113            let version_info = if number >= 7 { 2 * 18 } else { 0 };
1114            Some(base + alignment + version_info)
1115        }
1116        _ => None,
1117    }
1118}
1119
1120#[cfg(test)]
1121mod all_functional_patterns_tests {
1122    use crate::bits::Bits;
1123    use crate::canvas::{
1124        Canvas, Module, VERSION_INFO_COORDS_BL, VERSION_INFO_COORDS_TR, alignment_pattern_positions,
1125        functional_module_count, is_functional,
1126    };
1127    use crate::ec;
1128    use crate::types::{EcLevel, Version};
1129
1130    #[test]
1131    fn test_all_functional_patterns_qr() {
1132        let mut c = Canvas::new(Version::Normal(2), EcLevel::L);
1133        c.draw_all_functional_patterns();
1134        assert_eq!(
1135            &*c.to_debug_str(),
1136            "\n\
1137             #######..????????.#######\n\
1138             #.....#..????????.#.....#\n\
1139             #.###.#..????????.#.###.#\n\
1140             #.###.#..????????.#.###.#\n\
1141             #.###.#..????????.#.###.#\n\
1142             #.....#..????????.#.....#\n\
1143             #######.#.#.#.#.#.#######\n\
1144             .........????????........\n\
1145             ......#..????????........\n\
1146             ??????.??????????????????\n\
1147             ??????#??????????????????\n\
1148             ??????.??????????????????\n\
1149             ??????#??????????????????\n\
1150             ??????.??????????????????\n\
1151             ??????#??????????????????\n\
1152             ??????.??????????????????\n\
1153             ??????#?????????#####????\n\
1154             ........#???????#...#????\n\
1155             #######..???????#.#.#????\n\
1156             #.....#..???????#...#????\n\
1157             #.###.#..???????#####????\n\
1158             #.###.#..????????????????\n\
1159             #.###.#..????????????????\n\
1160             #.....#..????????????????\n\
1161             #######..????????????????"
1162        );
1163    }
1164
1165    #[test]
1166    fn test_all_functional_patterns_micro_qr() {
1167        let mut c = Canvas::new(Version::Micro(1), EcLevel::L);
1168        c.draw_all_functional_patterns();
1169        assert_eq!(
1170            &*c.to_debug_str(),
1171            "\n\
1172             #######.#.#\n\
1173             #.....#..??\n\
1174             #.###.#..??\n\
1175             #.###.#..??\n\
1176             #.###.#..??\n\
1177             #.....#..??\n\
1178             #######..??\n\
1179             .........??\n\
1180             #........??\n\
1181             .??????????\n\
1182             #??????????"
1183        );
1184    }
1185
1186    #[test]
1187    fn test_is_functional_qr_1() {
1188        let version = Version::Normal(1);
1189        assert!(is_functional(version, version.width(), 0, 0));
1190        assert!(is_functional(version, version.width(), 10, 6));
1191        assert!(!is_functional(version, version.width(), 10, 5));
1192        assert!(!is_functional(version, version.width(), 14, 14));
1193        assert!(is_functional(version, version.width(), 6, 11));
1194        assert!(!is_functional(version, version.width(), 4, 11));
1195        assert!(is_functional(version, version.width(), 4, 13));
1196        assert!(is_functional(version, version.width(), 17, 7));
1197        assert!(!is_functional(version, version.width(), 17, 17));
1198    }
1199
1200    #[test]
1201    fn test_is_functional_qr_3() {
1202        let version = Version::Normal(3);
1203        assert!(is_functional(version, version.width(), 0, 0));
1204        assert!(!is_functional(version, version.width(), 25, 24));
1205        assert!(is_functional(version, version.width(), 24, 24));
1206        assert!(!is_functional(version, version.width(), 9, 25));
1207        assert!(!is_functional(version, version.width(), 20, 0));
1208        assert!(is_functional(version, version.width(), 21, 0));
1209    }
1210
1211    #[test]
1212    fn test_is_functional_qr_7() {
1213        let version = Version::Normal(7);
1214        assert!(is_functional(version, version.width(), 21, 4));
1215        assert!(is_functional(version, version.width(), 7, 21));
1216        assert!(is_functional(version, version.width(), 22, 22));
1217        assert!(is_functional(version, version.width(), 8, 8));
1218        assert!(!is_functional(version, version.width(), 19, 5));
1219        assert!(is_functional(version, version.width(), 36, 3));
1220        assert!(is_functional(version, version.width(), 4, 36));
1221        assert!(is_functional(version, version.width(), 38, 38));
1222    }
1223
1224    #[test]
1225    fn test_is_functional_micro() {
1226        let version = Version::Micro(1);
1227        assert!(is_functional(version, version.width(), 8, 0));
1228        assert!(is_functional(version, version.width(), 10, 0));
1229        assert!(!is_functional(version, version.width(), 10, 1));
1230        assert!(is_functional(version, version.width(), 8, 8));
1231        assert!(is_functional(version, version.width(), 0, 9));
1232        assert!(!is_functional(version, version.width(), 1, 9));
1233    }
1234
1235    #[test]
1236    fn functional_module_count_matches_existing_classifier_for_every_version() {
1237        for version in (1..=40).map(Version::Normal).chain((1..=4).map(Version::Micro)) {
1238            let width = version.width();
1239            let expected =
1240                (0..width).map(|y| (0..width).filter(|&x| is_functional(version, width, x, y)).count()).sum::<usize>();
1241            assert_eq!(functional_module_count(version), Some(expected), "{version:?}");
1242        }
1243    }
1244
1245    #[test]
1246    fn functional_module_count_rejects_unsupported_versions_without_width_arithmetic() {
1247        for version in [
1248            Version::Normal(i16::MIN),
1249            Version::Normal(0),
1250            Version::Normal(41),
1251            Version::Normal(i16::MAX),
1252            Version::Micro(i16::MIN),
1253            Version::Micro(0),
1254            Version::Micro(5),
1255            Version::Micro(i16::MAX),
1256        ] {
1257            assert_eq!(functional_module_count(version), None);
1258        }
1259    }
1260
1261    // Retain the previous classifier to isolate the intended version-info
1262    // delta and protect its existing behavior outside the symbol coordinates.
1263    fn previous_classifier(version: Version, width: i16, x: i16, y: i16) -> bool {
1264        let x = if x < 0 { x + width } else { x };
1265        let y = if y < 0 { y + width } else { y };
1266        match version {
1267            Version::Micro(_) => x == 0 || y == 0 || (x < 9 && y < 9),
1268            Version::Normal(number) => {
1269                if x == 6 || y == 6 || (x < 9 && y < 9) || (x < 9 && y >= width - 8) || (x >= width - 8 && y < 9) {
1270                    return true;
1271                }
1272                if !(2..=40).contains(&number) {
1273                    return false;
1274                }
1275                let positions = alignment_pattern_positions(number);
1276                let last = positions.len() - 1;
1277                for (i, &center_x) in positions.iter().enumerate() {
1278                    for (j, &center_y) in positions.iter().enumerate() {
1279                        if i == 0 && (j == 0 || j == last) || i == last && j == 0 {
1280                            continue;
1281                        }
1282                        if (center_x - x).abs() <= 2 && (center_y - y).abs() <= 2 {
1283                            return true;
1284                        }
1285                    }
1286                }
1287                false
1288            }
1289        }
1290    }
1291
1292    #[test]
1293    fn functional_classification_matches_drawn_masked_modules_for_every_version() {
1294        for version in (1..=40).map(Version::Normal).chain((1..=4).map(Version::Micro)) {
1295            let width = version.width();
1296            let mut canvas = Canvas::new(version, EcLevel::L);
1297            canvas.draw_all_functional_patterns();
1298            let mut counted = 0;
1299            for y in 0..width {
1300                for x in 0..width {
1301                    let expected = matches!(canvas.get(x, y), Module::Masked(_));
1302                    let actual = is_functional(version, width, x, y);
1303                    assert_eq!(actual, expected, "{version:?} ({x}, {y})");
1304                    assert_eq!(is_functional(version, width, x - width, y), expected);
1305                    assert_eq!(is_functional(version, width, x, y - width), expected);
1306                    assert_eq!(is_functional(version, width, x - width, y - width), expected);
1307                    counted += usize::from(actual);
1308                }
1309            }
1310            assert_eq!(functional_module_count(version), Some(counted));
1311        }
1312    }
1313
1314    #[test]
1315    fn version_information_is_the_only_added_legal_functional_region() {
1316        for number in 1..=40 {
1317            let version = Version::Normal(number);
1318            let width = version.width();
1319            let mut version_canvas = Canvas::new(version, EcLevel::L);
1320            version_canvas.draw_version_info_patterns();
1321            let mut added = 0;
1322            for y in 0..width {
1323                for x in 0..width {
1324                    let previous = previous_classifier(version, width, x, y);
1325                    let current = is_functional(version, width, x, y);
1326                    let version_info = matches!(version_canvas.get(x, y), Module::Masked(_));
1327                    assert_eq!(current, previous || version_info, "v{number} ({x}, {y})");
1328                    added += usize::from(current && !previous);
1329                }
1330            }
1331            assert_eq!(added, if number >= 7 { 36 } else { 0 });
1332        }
1333    }
1334
1335    #[test]
1336    fn version_information_queries_preserve_negative_aliases_and_other_outside_behavior() {
1337        for number in 7..=40 {
1338            let version = Version::Normal(number);
1339            let width = version.width();
1340            for &(x, y) in VERSION_INFO_COORDS_BL.iter().chain(VERSION_INFO_COORDS_TR.iter()) {
1341                assert!(is_functional(version, width, x, y));
1342            }
1343            for (x, y) in [
1344                (-width - 1, 0),
1345                (0, -width - 1),
1346                (width, 0),
1347                (0, width),
1348                (width, width),
1349                (i16::MIN, i16::MAX),
1350                (i16::MAX, i16::MIN),
1351            ] {
1352                assert_eq!(is_functional(version, width, x, y), previous_classifier(version, width, x, y));
1353            }
1354        }
1355    }
1356
1357    #[test]
1358    fn classified_data_modules_match_placed_codewords_and_remaining_bits() {
1359        for version in (1..=40).map(Version::Normal).chain((1..=4).map(Version::Micro)) {
1360            for ec_level in [EcLevel::L, EcLevel::M, EcLevel::Q, EcLevel::H] {
1361                let mut bits = Bits::new(version);
1362                bits.push_numeric_data(b"1").unwrap();
1363                if bits.push_terminator(ec_level).is_err() {
1364                    continue;
1365                }
1366                let (data, correction) = ec::construct_codewords(&bits.into_bytes(), version, ec_level).unwrap();
1367                let mut canvas = Canvas::new(version, ec_level);
1368                canvas.draw_all_functional_patterns();
1369                let functional = canvas.modules.iter().filter(|module| matches!(module, Module::Masked(_))).count();
1370                let half_codeword = matches!(version, Version::Micro(1 | 3));
1371                let codeword_bits = (data.len() + correction.len()) * 8 - usize::from(half_codeword) * 4;
1372                canvas.draw_data(&data, &correction);
1373                let placed = canvas.modules.iter().filter(|module| matches!(module, Module::Unmasked(_))).count();
1374                let remaining = canvas.modules.iter().filter(|module| matches!(module, Module::Empty)).count();
1375
1376                assert_eq!(functional_module_count(version), Some(functional), "{version:?} {ec_level:?}");
1377                assert_eq!(placed, codeword_bits, "{version:?} {ec_level:?}");
1378                assert!(remaining < 8, "{version:?} {ec_level:?}, {remaining} remaining bits");
1379                assert_eq!(canvas.modules.len() - functional, placed + remaining);
1380            }
1381        }
1382    }
1383}
1384
1385//}}}
1386//------------------------------------------------------------------------------
1387//{{{ Data placement iterator
1388
1389struct DataModuleIter {
1390    x: i16,
1391    y: i16,
1392    width: i16,
1393    timing_pattern_column: i16,
1394}
1395
1396impl DataModuleIter {
1397    fn new(version: Version) -> Self {
1398        let width = version.width();
1399        Self {
1400            x: width - 1,
1401            y: width - 1,
1402            width,
1403            timing_pattern_column: match version {
1404                Version::Micro(_) => 0,
1405                Version::Normal(_) => 6,
1406            },
1407        }
1408    }
1409}
1410
1411impl Iterator for DataModuleIter {
1412    type Item = (i16, i16);
1413
1414    fn next(&mut self) -> Option<(i16, i16)> {
1415        let adjusted_ref_col = if self.x <= self.timing_pattern_column { self.x + 1 } else { self.x };
1416        if adjusted_ref_col <= 0 {
1417            return None;
1418        }
1419
1420        let res = (self.x, self.y);
1421        let column_type = (self.width - adjusted_ref_col) % 4;
1422
1423        match column_type {
1424            2 if self.y > 0 => {
1425                self.y -= 1;
1426                self.x += 1;
1427            }
1428            0 if self.y < self.width - 1 => {
1429                self.y += 1;
1430                self.x += 1;
1431            }
1432            0 | 2 if self.x == self.timing_pattern_column + 1 => {
1433                self.x -= 2;
1434            }
1435            _ => {
1436                self.x -= 1;
1437            }
1438        }
1439
1440        Some(res)
1441    }
1442}
1443
1444#[cfg(test)]
1445#[rustfmt::skip] // skip to prevent file becoming too long.
1446mod data_iter_tests {
1447    use crate::canvas::DataModuleIter;
1448    use crate::types::Version;
1449
1450    #[test]
1451    fn test_qr() {
1452        let res = DataModuleIter::new(Version::Normal(1)).collect::<Vec<(i16, i16)>>();
1453        assert_eq!(res, vec![
1454            (20, 20), (19, 20), (20, 19), (19, 19), (20, 18), (19, 18),
1455            (20, 17), (19, 17), (20, 16), (19, 16), (20, 15), (19, 15),
1456            (20, 14), (19, 14), (20, 13), (19, 13), (20, 12), (19, 12),
1457            (20, 11), (19, 11), (20, 10), (19, 10), (20, 9), (19, 9),
1458            (20, 8), (19, 8), (20, 7), (19, 7), (20, 6), (19, 6),
1459            (20, 5), (19, 5), (20, 4), (19, 4), (20, 3), (19, 3),
1460            (20, 2), (19, 2), (20, 1), (19, 1), (20, 0), (19, 0),
1461            (18, 0), (17, 0), (18, 1), (17, 1), (18, 2), (17, 2),
1462            (18, 3), (17, 3), (18, 4), (17, 4), (18, 5), (17, 5),
1463            (18, 6), (17, 6), (18, 7), (17, 7), (18, 8), (17, 8),
1464            (18, 9), (17, 9), (18, 10), (17, 10), (18, 11), (17, 11),
1465            (18, 12), (17, 12), (18, 13), (17, 13), (18, 14), (17, 14),
1466            (18, 15), (17, 15), (18, 16), (17, 16), (18, 17), (17, 17),
1467            (18, 18), (17, 18), (18, 19), (17, 19), (18, 20), (17, 20),
1468            (16, 20), (15, 20), (16, 19), (15, 19), (16, 18), (15, 18),
1469            (16, 17), (15, 17), (16, 16), (15, 16), (16, 15), (15, 15),
1470            (16, 14), (15, 14), (16, 13), (15, 13), (16, 12), (15, 12),
1471            (16, 11), (15, 11), (16, 10), (15, 10), (16, 9), (15, 9),
1472            (16, 8), (15, 8), (16, 7), (15, 7), (16, 6), (15, 6),
1473            (16, 5), (15, 5), (16, 4), (15, 4), (16, 3), (15, 3),
1474            (16, 2), (15, 2), (16, 1), (15, 1), (16, 0), (15, 0),
1475            (14, 0), (13, 0), (14, 1), (13, 1), (14, 2), (13, 2),
1476            (14, 3), (13, 3), (14, 4), (13, 4), (14, 5), (13, 5),
1477            (14, 6), (13, 6), (14, 7), (13, 7), (14, 8), (13, 8),
1478            (14, 9), (13, 9), (14, 10), (13, 10), (14, 11), (13, 11),
1479            (14, 12), (13, 12), (14, 13), (13, 13), (14, 14), (13, 14),
1480            (14, 15), (13, 15), (14, 16), (13, 16), (14, 17), (13, 17),
1481            (14, 18), (13, 18), (14, 19), (13, 19), (14, 20), (13, 20),
1482            (12, 20), (11, 20), (12, 19), (11, 19), (12, 18), (11, 18),
1483            (12, 17), (11, 17), (12, 16), (11, 16), (12, 15), (11, 15),
1484            (12, 14), (11, 14), (12, 13), (11, 13), (12, 12), (11, 12),
1485            (12, 11), (11, 11), (12, 10), (11, 10), (12, 9), (11, 9),
1486            (12, 8), (11, 8), (12, 7), (11, 7), (12, 6), (11, 6),
1487            (12, 5), (11, 5), (12, 4), (11, 4), (12, 3), (11, 3),
1488            (12, 2), (11, 2), (12, 1), (11, 1), (12, 0), (11, 0),
1489            (10, 0), (9, 0), (10, 1), (9, 1), (10, 2), (9, 2),
1490            (10, 3), (9, 3), (10, 4), (9, 4), (10, 5), (9, 5),
1491            (10, 6), (9, 6), (10, 7), (9, 7), (10, 8), (9, 8),
1492            (10, 9), (9, 9), (10, 10), (9, 10), (10, 11), (9, 11),
1493            (10, 12), (9, 12), (10, 13), (9, 13), (10, 14), (9, 14),
1494            (10, 15), (9, 15), (10, 16), (9, 16), (10, 17), (9, 17),
1495            (10, 18), (9, 18), (10, 19), (9, 19), (10, 20), (9, 20),
1496            (8, 20), (7, 20), (8, 19), (7, 19), (8, 18), (7, 18),
1497            (8, 17), (7, 17), (8, 16), (7, 16), (8, 15), (7, 15),
1498            (8, 14), (7, 14), (8, 13), (7, 13), (8, 12), (7, 12),
1499            (8, 11), (7, 11), (8, 10), (7, 10), (8, 9), (7, 9),
1500            (8, 8), (7, 8), (8, 7), (7, 7), (8, 6), (7, 6),
1501            (8, 5), (7, 5), (8, 4), (7, 4), (8, 3), (7, 3),
1502            (8, 2), (7, 2), (8, 1), (7, 1), (8, 0), (7, 0),
1503            (5, 0), (4, 0), (5, 1), (4, 1), (5, 2), (4, 2),
1504            (5, 3), (4, 3), (5, 4), (4, 4), (5, 5), (4, 5),
1505            (5, 6), (4, 6), (5, 7), (4, 7), (5, 8), (4, 8),
1506            (5, 9), (4, 9), (5, 10), (4, 10), (5, 11), (4, 11),
1507            (5, 12), (4, 12), (5, 13), (4, 13), (5, 14), (4, 14),
1508            (5, 15), (4, 15), (5, 16), (4, 16), (5, 17), (4, 17),
1509            (5, 18), (4, 18), (5, 19), (4, 19), (5, 20), (4, 20),
1510            (3, 20), (2, 20), (3, 19), (2, 19), (3, 18), (2, 18),
1511            (3, 17), (2, 17), (3, 16), (2, 16), (3, 15), (2, 15),
1512            (3, 14), (2, 14), (3, 13), (2, 13), (3, 12), (2, 12),
1513            (3, 11), (2, 11), (3, 10), (2, 10), (3, 9), (2, 9),
1514            (3, 8), (2, 8), (3, 7), (2, 7), (3, 6), (2, 6),
1515            (3, 5), (2, 5), (3, 4), (2, 4), (3, 3), (2, 3),
1516            (3, 2), (2, 2), (3, 1), (2, 1), (3, 0), (2, 0),
1517            (1, 0), (0, 0), (1, 1), (0, 1), (1, 2), (0, 2),
1518            (1, 3), (0, 3), (1, 4), (0, 4), (1, 5), (0, 5),
1519            (1, 6), (0, 6), (1, 7), (0, 7), (1, 8), (0, 8),
1520            (1, 9), (0, 9), (1, 10), (0, 10), (1, 11), (0, 11),
1521            (1, 12), (0, 12), (1, 13), (0, 13), (1, 14), (0, 14),
1522            (1, 15), (0, 15), (1, 16), (0, 16), (1, 17), (0, 17),
1523            (1, 18), (0, 18), (1, 19), (0, 19), (1, 20), (0, 20),
1524        ]);
1525    }
1526
1527    #[test]
1528    fn test_micro_qr() {
1529        let res = DataModuleIter::new(Version::Micro(1)).collect::<Vec<(i16, i16)>>();
1530        assert_eq!(res, vec![
1531            (10, 10), (9, 10), (10, 9), (9, 9), (10, 8), (9, 8),
1532            (10, 7), (9, 7), (10, 6), (9, 6), (10, 5), (9, 5),
1533            (10, 4), (9, 4), (10, 3), (9, 3), (10, 2), (9, 2),
1534            (10, 1), (9, 1), (10, 0), (9, 0),
1535            (8, 0), (7, 0), (8, 1), (7, 1), (8, 2), (7, 2),
1536            (8, 3), (7, 3), (8, 4), (7, 4), (8, 5), (7, 5),
1537            (8, 6), (7, 6), (8, 7), (7, 7), (8, 8), (7, 8),
1538            (8, 9), (7, 9), (8, 10), (7, 10),
1539            (6, 10), (5, 10), (6, 9), (5, 9), (6, 8), (5, 8),
1540            (6, 7), (5, 7), (6, 6), (5, 6), (6, 5), (5, 5),
1541            (6, 4), (5, 4), (6, 3), (5, 3), (6, 2), (5, 2),
1542            (6, 1), (5, 1), (6, 0), (5, 0),
1543            (4, 0), (3, 0), (4, 1), (3, 1), (4, 2), (3, 2),
1544            (4, 3), (3, 3), (4, 4), (3, 4), (4, 5), (3, 5),
1545            (4, 6), (3, 6), (4, 7), (3, 7), (4, 8), (3, 8),
1546            (4, 9), (3, 9), (4, 10), (3, 10),
1547            (2, 10), (1, 10), (2, 9), (1, 9), (2, 8), (1, 8),
1548            (2, 7), (1, 7), (2, 6), (1, 6), (2, 5), (1, 5),
1549            (2, 4), (1, 4), (2, 3), (1, 3), (2, 2), (1, 2),
1550            (2, 1), (1, 1), (2, 0), (1, 0),
1551        ]);
1552    }
1553
1554    #[test]
1555    fn test_micro_qr_2() {
1556        let res = DataModuleIter::new(Version::Micro(2)).collect::<Vec<(i16, i16)>>();
1557        assert_eq!(res, vec![
1558            (12, 12), (11, 12), (12, 11), (11, 11), (12, 10), (11, 10),
1559            (12, 9), (11, 9), (12, 8), (11, 8), (12, 7), (11, 7),
1560            (12, 6), (11, 6), (12, 5), (11, 5), (12, 4), (11, 4),
1561            (12, 3), (11, 3), (12, 2), (11, 2), (12, 1), (11, 1),
1562            (12, 0), (11, 0),
1563            (10, 0), (9, 0), (10, 1), (9, 1), (10, 2), (9, 2),
1564            (10, 3), (9, 3), (10, 4), (9, 4), (10, 5), (9, 5),
1565            (10, 6), (9, 6), (10, 7), (9, 7), (10, 8), (9, 8),
1566            (10, 9), (9, 9), (10, 10), (9, 10), (10, 11), (9, 11),
1567            (10, 12), (9, 12),
1568            (8, 12), (7, 12), (8, 11), (7, 11), (8, 10), (7, 10),
1569            (8, 9), (7, 9), (8, 8), (7, 8), (8, 7), (7, 7),
1570            (8, 6), (7, 6), (8, 5), (7, 5), (8, 4), (7, 4),
1571            (8, 3), (7, 3), (8, 2), (7, 2), (8, 1), (7, 1),
1572            (8, 0), (7, 0),
1573            (6, 0), (5, 0), (6, 1), (5, 1), (6, 2), (5, 2),
1574            (6, 3), (5, 3), (6, 4), (5, 4), (6, 5), (5, 5),
1575            (6, 6), (5, 6), (6, 7), (5, 7), (6, 8), (5, 8),
1576            (6, 9), (5, 9), (6, 10), (5, 10), (6, 11), (5, 11),
1577            (6, 12), (5, 12),
1578            (4, 12), (3, 12), (4, 11), (3, 11), (4, 10), (3, 10),
1579            (4, 9), (3, 9), (4, 8), (3, 8), (4, 7), (3, 7),
1580            (4, 6), (3, 6), (4, 5), (3, 5), (4, 4), (3, 4),
1581            (4, 3), (3, 3), (4, 2), (3, 2), (4, 1), (3, 1),
1582            (4, 0), (3, 0),
1583            (2, 0), (1, 0), (2, 1), (1, 1), (2, 2), (1, 2),
1584            (2, 3), (1, 3), (2, 4), (1, 4), (2, 5), (1, 5),
1585            (2, 6), (1, 6), (2, 7), (1, 7), (2, 8), (1, 8),
1586            (2, 9), (1, 9), (2, 10), (1, 10), (2, 11), (1, 11),
1587            (2, 12), (1, 12),
1588        ]);
1589    }
1590}
1591
1592//}}}
1593//------------------------------------------------------------------------------
1594//{{{ Data placement
1595
1596impl Canvas {
1597    fn draw_codewords<I>(&mut self, codewords: &[u8], is_half_codeword_at_end: bool, coords: &mut I)
1598    where
1599        I: Iterator<Item = (i16, i16)>,
1600    {
1601        let length = codewords.len();
1602        let last_word = if is_half_codeword_at_end { length - 1 } else { length };
1603        for (i, b) in codewords.iter().enumerate() {
1604            let bits_end = if i == last_word { 4 } else { 0 };
1605            'outside: for j in (bits_end..=7).rev() {
1606                let color = if (*b & (1 << j)) == 0 { Color::Light } else { Color::Dark };
1607                for (x, y) in coords.by_ref() {
1608                    let r = self.get_mut(x, y);
1609                    if *r == Module::Empty {
1610                        *r = Module::Unmasked(color);
1611                        continue 'outside;
1612                    }
1613                }
1614                return;
1615            }
1616        }
1617    }
1618
1619    /// Draws the encoded data and error correction codes to the empty modules.
1620    pub fn draw_data(&mut self, data: &[u8], ec: &[u8]) {
1621        let is_half_codeword_at_end = matches!(
1622            (self.version, self.ec_level),
1623            (Version::Micro(1 | 3), EcLevel::L) | (Version::Micro(3), EcLevel::M)
1624        );
1625
1626        let mut coords = DataModuleIter::new(self.version);
1627        self.draw_codewords(data, is_half_codeword_at_end, &mut coords);
1628        self.draw_codewords(ec, false, &mut coords);
1629    }
1630}
1631
1632#[cfg(test)]
1633mod draw_codewords_test {
1634    use crate::canvas::Canvas;
1635    use crate::types::{EcLevel, Version};
1636
1637    #[test]
1638    fn test_micro_qr_1() {
1639        let mut c = Canvas::new(Version::Micro(1), EcLevel::L);
1640        c.draw_all_functional_patterns();
1641        c.draw_data(b"\x6e\x5d\xe2", b"\x2b\x63");
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             #---*-*-**-"
1656        );
1657    }
1658
1659    #[test]
1660    fn test_qr_2() {
1661        let mut c = Canvas::new(Version::Normal(2), EcLevel::L);
1662        c.draw_all_functional_patterns();
1663        c.draw_data(
1664            b"\x92I$\x92I$\x92I$\x92I$\x92I$\x92I$\x92I$\x92I$\
1665              \x92I$\x92I$\x92I$\x92I$\x92I$\x92I$\x92I$",
1666            b"",
1667        );
1668        assert_eq!(
1669            &*c.to_debug_str(),
1670            "\n\
1671             #######..--*---*-.#######\n\
1672             #.....#..-*-*-*-*.#.....#\n\
1673             #.###.#..*---*---.#.###.#\n\
1674             #.###.#..--*---*-.#.###.#\n\
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             #######..*---*--*----*--*"
1696        );
1697    }
1698}
1699//}}}
1700//------------------------------------------------------------------------------
1701//{{{ Masking
1702
1703/// The mask patterns. Since QR code and Micro QR code do not use the same
1704/// pattern number, we name them according to their shape instead of the number.
1705#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1706#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1707pub enum MaskPattern {
1708    /// QR code pattern 000: `(x + y) % 2 == 0`.
1709    Checkerboard = 0b000,
1710
1711    /// QR code pattern 001: `y % 2 == 0`.
1712    HorizontalLines = 0b001,
1713
1714    /// QR code pattern 010: `x % 3 == 0`.
1715    VerticalLines = 0b010,
1716
1717    /// QR code pattern 011: `(x + y) % 3 == 0`.
1718    DiagonalLines = 0b011,
1719
1720    /// QR code pattern 100: `((x/3) + (y/2)) % 2 == 0`.
1721    LargeCheckerboard = 0b100,
1722
1723    /// QR code pattern 101: `(x*y)%2 + (x*y)%3 == 0`.
1724    Fields = 0b101,
1725
1726    /// QR code pattern 110: `((x*y)%2 + (x*y)%3) % 2 == 0`.
1727    Diamonds = 0b110,
1728
1729    /// QR code pattern 111: `((x+y)%2 + (x*y)%3) % 2 == 0`.
1730    Meadow = 0b111,
1731}
1732
1733mod mask_functions {
1734    pub fn checkerboard(x: i16, y: i16) -> bool {
1735        (x + y) % 2 == 0
1736    }
1737
1738    pub fn horizontal_lines(_: i16, y: i16) -> bool {
1739        y % 2 == 0
1740    }
1741
1742    pub fn vertical_lines(x: i16, _: i16) -> bool {
1743        x % 3 == 0
1744    }
1745
1746    pub fn diagonal_lines(x: i16, y: i16) -> bool {
1747        (x + y) % 3 == 0
1748    }
1749
1750    pub fn large_checkerboard(x: i16, y: i16) -> bool {
1751        ((y / 2) + (x / 3)) % 2 == 0
1752    }
1753
1754    pub fn fields(x: i16, y: i16) -> bool {
1755        (x * y) % 2 + (x * y) % 3 == 0
1756    }
1757
1758    pub fn diamonds(x: i16, y: i16) -> bool {
1759        ((x * y) % 2 + (x * y) % 3) % 2 == 0
1760    }
1761
1762    pub fn meadow(x: i16, y: i16) -> bool {
1763        ((x + y) % 2 + (x * y) % 3) % 2 == 0
1764    }
1765}
1766
1767fn get_mask_function(pattern: MaskPattern) -> fn(i16, i16) -> bool {
1768    match pattern {
1769        MaskPattern::Checkerboard => mask_functions::checkerboard,
1770        MaskPattern::HorizontalLines => mask_functions::horizontal_lines,
1771        MaskPattern::VerticalLines => mask_functions::vertical_lines,
1772        MaskPattern::DiagonalLines => mask_functions::diagonal_lines,
1773        MaskPattern::LargeCheckerboard => mask_functions::large_checkerboard,
1774        MaskPattern::Fields => mask_functions::fields,
1775        MaskPattern::Diamonds => mask_functions::diamonds,
1776        MaskPattern::Meadow => mask_functions::meadow,
1777    }
1778}
1779
1780impl Canvas {
1781    /// Applies a mask to the canvas. This method will also draw the format info
1782    /// patterns.
1783    pub fn apply_mask(&mut self, pattern: MaskPattern) {
1784        let mask_fn = get_mask_function(pattern);
1785        let width = self.width.as_usize();
1786        for y in 0..self.width {
1787            let row_start = y.as_usize() * width;
1788            for (x, module) in self.modules[row_start..row_start + width].iter_mut().enumerate() {
1789                *module = module.mask(mask_fn(x.as_i16(), y));
1790            }
1791        }
1792
1793        self.draw_format_info_patterns(pattern);
1794    }
1795
1796    fn write_masked_module_bytes(&self, pattern: MaskPattern, output: &mut [u8]) {
1797        debug_assert_eq!(output.len(), self.modules.len());
1798        // Function items keep each mask calculation statically dispatched in
1799        // the candidate loop while sharing the existing formula definitions.
1800        match pattern {
1801            MaskPattern::Checkerboard => self.write_masked_bytes_with(mask_functions::checkerboard, output),
1802            MaskPattern::HorizontalLines => self.write_masked_bytes_with(mask_functions::horizontal_lines, output),
1803            MaskPattern::VerticalLines => self.write_masked_bytes_with(mask_functions::vertical_lines, output),
1804            MaskPattern::DiagonalLines => self.write_masked_bytes_with(mask_functions::diagonal_lines, output),
1805            MaskPattern::LargeCheckerboard => self.write_masked_bytes_with(mask_functions::large_checkerboard, output),
1806            MaskPattern::Fields => self.write_masked_bytes_with(mask_functions::fields, output),
1807            MaskPattern::Diamonds => self.write_masked_bytes_with(mask_functions::diamonds, output),
1808            MaskPattern::Meadow => self.write_masked_bytes_with(mask_functions::meadow, output),
1809        }
1810        for_each_format_info_module(self.version, self.format_info_number(pattern), |x, y, color| {
1811            output[self.coords_to_index(x, y)] = u8::from(color == Color::Dark);
1812        });
1813    }
1814
1815    fn write_masked_bytes_with(&self, mask_fn: impl Fn(i16, i16) -> bool, output: &mut [u8]) {
1816        let width = self.width.as_usize();
1817        for (y, (row, output_row)) in self.modules.chunks_exact(width).zip(output.chunks_exact_mut(width)).enumerate() {
1818            for (x, (&module, byte)) in row.iter().zip(output_row).enumerate() {
1819                *byte = match module {
1820                    Module::Masked(color) => u8::from(color == Color::Dark),
1821                    Module::Empty => u8::from(mask_fn(x.as_i16(), y.as_i16())),
1822                    Module::Unmasked(color) => u8::from((color == Color::Dark) ^ mask_fn(x.as_i16(), y.as_i16())),
1823                };
1824            }
1825        }
1826    }
1827
1828    /// Draws the format information to encode the error correction level and
1829    /// mask pattern.
1830    ///
1831    /// If the error correction level or mask pattern is not supported in the
1832    /// current QR code version, this method will fail.
1833    fn draw_format_info_patterns(&mut self, pattern: MaskPattern) {
1834        self.draw_format_info_patterns_with_number(self.format_info_number(pattern));
1835    }
1836
1837    fn format_info_number(&self, pattern: MaskPattern) -> u16 {
1838        match self.version {
1839            Version::Normal(_) => {
1840                let simple_format_number = ((self.ec_level as usize) ^ 1) << 3 | (pattern as usize);
1841                FORMAT_INFOS_QR[simple_format_number]
1842            }
1843            Version::Micro(a) => {
1844                let micro_pattern_number = match pattern {
1845                    MaskPattern::HorizontalLines => 0b00,
1846                    MaskPattern::LargeCheckerboard => 0b01,
1847                    MaskPattern::Diamonds => 0b10,
1848                    MaskPattern::Meadow => 0b11,
1849                    _ => panic!("Unsupported mask pattern in Micro QR code"),
1850                };
1851                let symbol_number = match (a, self.ec_level) {
1852                    (1, EcLevel::L) => 0b000,
1853                    (2, EcLevel::L) => 0b001,
1854                    (2, EcLevel::M) => 0b010,
1855                    (3, EcLevel::L) => 0b011,
1856                    (3, EcLevel::M) => 0b100,
1857                    (4, EcLevel::L) => 0b101,
1858                    (4, EcLevel::M) => 0b110,
1859                    (4, EcLevel::Q) => 0b111,
1860                    _ => panic!("Unsupported version/ec_level combination in Micro QR code"),
1861                };
1862                let simple_format_number = symbol_number << 2 | micro_pattern_number;
1863                FORMAT_INFOS_MICRO_QR[simple_format_number]
1864            }
1865        }
1866    }
1867}
1868
1869#[cfg(test)]
1870mod mask_tests {
1871    use crate::canvas::{
1872        Canvas, FORMAT_INFO_COORDS_MICRO_QR, FORMAT_INFO_COORDS_QR_MAIN, FORMAT_INFO_COORDS_QR_SIDE,
1873        FORMAT_INFOS_MICRO_QR, FORMAT_INFOS_QR, MaskPattern,
1874    };
1875    use crate::types::{Color, EcLevel, Version};
1876
1877    #[test]
1878    fn test_apply_mask_qr() {
1879        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
1880        c.draw_all_functional_patterns();
1881        c.apply_mask(MaskPattern::Checkerboard);
1882
1883        assert_eq!(
1884            &*c.to_debug_str(),
1885            "\n\
1886             #######...#.#.#######\n\
1887             #.....#..#.#..#.....#\n\
1888             #.###.#.#.#.#.#.###.#\n\
1889             #.###.#..#.#..#.###.#\n\
1890             #.###.#...#.#.#.###.#\n\
1891             #.....#..#.#..#.....#\n\
1892             #######.#.#.#.#######\n\
1893             ........##.#.........\n\
1894             ###.#####.#.###...#..\n\
1895             .#.#.#.#.#.#.#.#.#.#.\n\
1896             #.#.#.#.#.#.#.#.#.#.#\n\
1897             .#.#.#.#.#.#.#.#.#.#.\n\
1898             #.#.#.#.#.#.#.#.#.#.#\n\
1899             ........##.#.#.#.#.#.\n\
1900             #######.#.#.#.#.#.#.#\n\
1901             #.....#.##.#.#.#.#.#.\n\
1902             #.###.#.#.#.#.#.#.#.#\n\
1903             #.###.#..#.#.#.#.#.#.\n\
1904             #.###.#.#.#.#.#.#.#.#\n\
1905             #.....#.##.#.#.#.#.#.\n\
1906             #######.#.#.#.#.#.#.#"
1907        );
1908    }
1909
1910    #[test]
1911    fn test_draw_format_info_patterns_qr() {
1912        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
1913        c.draw_format_info_patterns(MaskPattern::LargeCheckerboard);
1914        assert_eq!(
1915            &*c.to_debug_str(),
1916            "\n\
1917             ????????#????????????\n\
1918             ????????#????????????\n\
1919             ????????#????????????\n\
1920             ????????#????????????\n\
1921             ????????.????????????\n\
1922             ????????#????????????\n\
1923             ?????????????????????\n\
1924             ????????.????????????\n\
1925             ##..##?..????..#.####\n\
1926             ?????????????????????\n\
1927             ?????????????????????\n\
1928             ?????????????????????\n\
1929             ?????????????????????\n\
1930             ????????#????????????\n\
1931             ????????.????????????\n\
1932             ????????#????????????\n\
1933             ????????#????????????\n\
1934             ????????.????????????\n\
1935             ????????.????????????\n\
1936             ????????#????????????\n\
1937             ????????#????????????"
1938        );
1939    }
1940
1941    #[test]
1942    fn test_draw_format_info_patterns_micro_qr() {
1943        let mut c = Canvas::new(Version::Micro(2), EcLevel::L);
1944        c.draw_format_info_patterns(MaskPattern::LargeCheckerboard);
1945        assert_eq!(
1946            &*c.to_debug_str(),
1947            "\n\
1948             ?????????????\n\
1949             ????????#????\n\
1950             ????????.????\n\
1951             ????????.????\n\
1952             ????????#????\n\
1953             ????????#????\n\
1954             ????????.????\n\
1955             ????????.????\n\
1956             ?#.#....#????\n\
1957             ?????????????\n\
1958             ?????????????\n\
1959             ?????????????\n\
1960             ?????????????"
1961        );
1962    }
1963
1964    #[test]
1965    fn generated_format_info_tables_match_known_values() {
1966        assert_eq!(FORMAT_INFOS_QR[0], 0x5412);
1967        assert_eq!(FORMAT_INFOS_QR[31], 0x2bed);
1968        assert_eq!(FORMAT_INFOS_MICRO_QR[0], 0x4445);
1969        assert_eq!(FORMAT_INFOS_MICRO_QR[31], 0x3bba);
1970    }
1971
1972    #[test]
1973    fn format_module_callback_matches_original_number_writes() {
1974        for version in [Version::Normal(1), Version::Normal(40), Version::Micro(1), Version::Micro(4)] {
1975            let format_infos = match version {
1976                Version::Normal(_) => &FORMAT_INFOS_QR,
1977                Version::Micro(_) => &FORMAT_INFOS_MICRO_QR,
1978            };
1979            for &format_info in format_infos {
1980                let mut actual = Canvas::new(version, EcLevel::L);
1981                let mut expected = actual.clone();
1982                actual.draw_format_info_patterns_with_number(format_info);
1983                let number = u32::from(format_info);
1984                match version {
1985                    Version::Normal(_) => {
1986                        expected.draw_number(number, 15, Color::Dark, Color::Light, &FORMAT_INFO_COORDS_QR_MAIN);
1987                        expected.draw_number(number, 15, Color::Dark, Color::Light, &FORMAT_INFO_COORDS_QR_SIDE);
1988                        expected.put(8, -8, Color::Dark);
1989                    }
1990                    Version::Micro(_) => {
1991                        expected.draw_number(number, 15, Color::Dark, Color::Light, &FORMAT_INFO_COORDS_MICRO_QR);
1992                    }
1993                }
1994                assert_eq!(actual.modules, expected.modules, "version {version:?}, format {format_info:04x}");
1995            }
1996        }
1997    }
1998}
1999
2000const FORMAT_INFOS_QR: [u16; 32] = generate_format_infos(0x5412);
2001const FORMAT_INFOS_MICRO_QR: [u16; 32] = generate_format_infos(0x4445);
2002const FORMAT_INFO_GENERATOR: u16 = 0x537;
2003
2004const fn generate_format_infos(mask: u16) -> [u16; 32] {
2005    let mut table = [0; 32];
2006    let mut data = 0;
2007    while data < table.len() {
2008        table[data] = format_info(data as u16, mask);
2009        data += 1;
2010    }
2011    table
2012}
2013
2014const fn format_info(data: u16, mask: u16) -> u16 {
2015    let mut remainder = data << 10;
2016    let mut bit = 14;
2017    while bit >= 10 {
2018        if remainder & (1 << bit) != 0 {
2019            remainder ^= FORMAT_INFO_GENERATOR << (bit - 10);
2020        }
2021        if bit == 10 {
2022            break;
2023        }
2024        bit -= 1;
2025    }
2026    ((data << 10) | remainder) ^ mask
2027}
2028
2029//}}}
2030//------------------------------------------------------------------------------
2031//{{{ Penalty score
2032
2033impl Canvas {
2034    /// Compute the penalty score for having too many adjacent modules with the
2035    /// same color.
2036    ///
2037    /// Every 5+N adjacent modules in the same column/row having the same color
2038    /// will contribute 3+N points.
2039    #[cfg(test)]
2040    fn compute_adjacent_penalty_score(&self, is_horizontal: bool) -> u32 {
2041        compute_adjacent_penalty_score(self.width.as_usize(), &module_bytes(&self.modules), is_horizontal)
2042    }
2043
2044    /// Compute the penalty score for having too many rectangles with the same
2045    /// color.
2046    ///
2047    /// Every 2×2 blocks (with overlapping counted) having the same color will
2048    /// contribute 3 points.
2049    #[cfg(test)]
2050    fn compute_block_penalty_score(&self) -> u32 {
2051        compute_block_penalty_score(self.width.as_usize(), &module_bytes(&self.modules))
2052    }
2053
2054    /// Compute the penalty score for having a pattern similar to the finder
2055    /// pattern in the wrong place.
2056    ///
2057    /// Every pattern that looks like `#.###.#....` in any orientation will add
2058    /// 40 points.
2059    #[cfg(test)]
2060    fn compute_finder_penalty_score(&self, is_horizontal: bool) -> u32 {
2061        compute_finder_penalty_score(self.width.as_usize(), &module_bytes(&self.modules), is_horizontal)
2062    }
2063
2064    /// Compute the penalty score for having an unbalanced dark/light ratio.
2065    ///
2066    /// The score is given linearly by the deviation from a 50% ratio of dark
2067    /// modules. The highest possible score is 100.
2068    ///
2069    /// Note that this algorithm differs slightly from the standard we do not
2070    /// round the result every 5%, but the difference should be negligible and
2071    /// should not affect which mask is chosen.
2072    #[cfg(test)]
2073    fn compute_balance_penalty_score(&self) -> u32 {
2074        compute_balance_penalty_score(&module_bytes(&self.modules))
2075    }
2076
2077    /// Compute the penalty score for having too many light modules on the sides.
2078    ///
2079    /// This penalty score is exclusive to Micro QR code.
2080    ///
2081    /// Note that the standard gives the formula for *efficiency* score, which
2082    /// has the inverse meaning of this method, but it is very easy to convert
2083    /// between the two (this score is (16×width − standard-score)).
2084    #[cfg(test)]
2085    fn compute_light_side_penalty_score(&self) -> u32 {
2086        compute_light_side_penalty_score(self.width.as_usize(), &module_bytes(&self.modules))
2087    }
2088
2089    /// Compute the total penalty scores. A QR code having higher points is less
2090    /// desirable.
2091    #[cfg(test)]
2092    fn compute_total_penalty_scores(&self) -> u32 {
2093        compute_total_penalty_score_scalar(self.version, self.width, &self.modules)
2094    }
2095
2096    #[cfg(any(test, feature = "bench-internals"))]
2097    fn compute_total_penalty_scores_with_scratch(&self, scratch: &mut Vec<u8>) -> u32 {
2098        debug_assert_eq!((self.width * self.width).as_usize(), self.modules.len());
2099        write_module_bytes(&self.modules, scratch);
2100        compute_total_penalty_score_from_bytes(self.version, self.width.as_usize(), scratch)
2101    }
2102
2103    /// Scores the mask, clipping unusually high penalties to `u16::MAX`.
2104    #[cfg(feature = "bench-internals")]
2105    #[doc(hidden)]
2106    pub fn score_mask_for_bench(&self, scratch: &mut Vec<u8>) -> u16 {
2107        u16::try_from(self.compute_total_penalty_scores_with_scratch(scratch)).unwrap_or(u16::MAX)
2108    }
2109
2110    /// Scores the mask with the scalar path, clipping high penalties to `u16::MAX`.
2111    #[cfg(feature = "bench-internals")]
2112    #[doc(hidden)]
2113    pub fn score_mask_scalar_for_bench(&self, scratch: &mut Vec<u8>) -> u16 {
2114        debug_assert_eq!((self.width * self.width).as_usize(), self.modules.len());
2115        write_module_bytes(&self.modules, scratch);
2116        u16::try_from(compute_total_penalty_score_from_bytes_scalar(self.version, self.width.as_usize(), scratch))
2117            .unwrap_or(u16::MAX)
2118    }
2119}
2120
2121#[cfg(test)]
2122fn module_bytes(modules: &[Module]) -> Vec<u8> {
2123    modules.iter().map(|module| u8::from(module.is_dark())).collect()
2124}
2125
2126#[cfg(any(test, feature = "bench-internals"))]
2127fn write_module_bytes(modules: &[Module], output: &mut Vec<u8>) {
2128    output.clear();
2129    output.extend(modules.iter().map(|module| u8::from(module.is_dark())));
2130}
2131
2132fn count_dark_modules(modules: &[u8]) -> usize {
2133    #[cfg(target_arch = "aarch64")]
2134    {
2135        // SAFETY: AArch64 guarantees NEON support; the helper only performs
2136        // guarded unaligned loads within the input slice.
2137        unsafe { count_dark_modules_neon(modules) }
2138    }
2139
2140    #[cfg(target_arch = "x86_64")]
2141    {
2142        if avx2_available() {
2143            // SAFETY: runtime feature detection ensures AVX2 is available;
2144            // the helper only reads inside the slice bounds with unaligned
2145            // 32-byte loads.
2146            return unsafe { count_dark_modules_avx2(modules) };
2147        }
2148
2149        if sse2_available() {
2150            // SAFETY: runtime feature detection ensures SSE2 is available;
2151            // the helper only reads inside the slice bounds with unaligned
2152            // 16-byte loads.
2153            return unsafe { count_dark_modules_sse2(modules) };
2154        }
2155
2156        count_dark_modules_scalar(modules)
2157    }
2158
2159    #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
2160    {
2161        count_dark_modules_scalar(modules)
2162    }
2163}
2164
2165fn count_dark_modules_scalar(modules: &[u8]) -> usize {
2166    modules.iter().filter(|&&module| module != 0).count()
2167}
2168
2169fn compute_balance_penalty_score(modules: &[u8]) -> u32 {
2170    let dark_modules = count_dark_modules(modules);
2171    let total_modules = modules.len();
2172    let ratio = dark_modules * 200 / total_modules;
2173    ratio.abs_diff(100).as_u32()
2174}
2175
2176#[cfg(any(test, feature = "bench-internals"))]
2177fn compute_balance_penalty_score_scalar(modules: &[u8]) -> u32 {
2178    let dark_modules = count_dark_modules_scalar(modules);
2179    let total_modules = modules.len();
2180    let ratio = dark_modules * 200 / total_modules;
2181    ratio.abs_diff(100).as_u32()
2182}
2183
2184fn compute_light_side_penalty_score(width: usize, modules: &[u8]) -> u32 {
2185    let bottom_row = &modules[(width - 1) * width..][..width];
2186    let h = (1..width).filter(|&x| bottom_row[x] == 0).count();
2187    let v = (1..width).filter(|&y| modules[y * width + width - 1] == 0).count();
2188
2189    (h + v + 15 * max(h, v)).as_u32()
2190}
2191
2192fn compute_adjacent_penalty_score(width: usize, modules: &[u8], is_horizontal: bool) -> u32 {
2193    if is_horizontal {
2194        compute_horizontal_adjacent_penalty_score(width, modules)
2195    } else {
2196        compute_vertical_adjacent_penalty_score(width, modules)
2197    }
2198}
2199
2200fn compute_horizontal_adjacent_penalty_score(width: usize, modules: &[u8]) -> u32 {
2201    modules.chunks_exact(width).map(compute_line_adjacent_penalty_score).sum()
2202}
2203
2204fn compute_vertical_adjacent_penalty_score(width: usize, modules: &[u8]) -> u32 {
2205    let mut total_score = 0;
2206
2207    for x in 0..width {
2208        let mut last_color = 2;
2209        let mut consecutive_len = 1_u32;
2210
2211        for y in 0..width {
2212            let color = modules[y * width + x];
2213            if color == last_color {
2214                consecutive_len += 1;
2215            } else {
2216                last_color = color;
2217                if consecutive_len >= 5 {
2218                    total_score += consecutive_len - 2;
2219                }
2220                consecutive_len = 1;
2221            }
2222        }
2223
2224        total_score += adjacent_run_score(consecutive_len);
2225    }
2226
2227    total_score
2228}
2229
2230fn compute_line_adjacent_penalty_score(line: &[u8]) -> u32 {
2231    let mut total_score = 0;
2232    let mut last_color = 2;
2233    let mut consecutive_len = 1_u32;
2234
2235    for &color in line {
2236        if color == last_color {
2237            consecutive_len += 1;
2238        } else {
2239            last_color = color;
2240            if consecutive_len >= 5 {
2241                total_score += consecutive_len - 2;
2242            }
2243            consecutive_len = 1;
2244        }
2245    }
2246
2247    total_score + adjacent_run_score(consecutive_len)
2248}
2249
2250fn adjacent_run_score(consecutive_len: u32) -> u32 {
2251    if consecutive_len >= 5 { consecutive_len - 2 } else { 0 }
2252}
2253
2254const FINDER_LIKE_PATTERN_BITS: u8 = 0b1011101;
2255const FINDER_LIKE_PATTERN_WIDTH: usize = 7;
2256const FINDER_LIKE_PATTERN_MASK: u8 = (1 << FINDER_LIKE_PATTERN_WIDTH) - 1;
2257
2258fn compute_finder_penalty_score(width: usize, modules: &[u8], is_horizontal: bool) -> u32 {
2259    let total_score = if is_horizontal {
2260        compute_horizontal_finder_penalty_score(width, modules)
2261    } else {
2262        compute_vertical_finder_penalty_score(width, modules)
2263    };
2264
2265    total_score - 360
2266}
2267
2268fn compute_horizontal_finder_penalty_score(width: usize, modules: &[u8]) -> u32 {
2269    modules.chunks_exact(width).map(compute_line_finder_penalty_score).sum()
2270}
2271
2272fn compute_vertical_finder_penalty_score(width: usize, modules: &[u8]) -> u32 {
2273    let mut total_score = 0;
2274
2275    for x in 0..width {
2276        let mut window = initial_vertical_finder_window(width, modules, x);
2277
2278        for y in 0..width.saturating_sub(FINDER_LIKE_PATTERN_WIDTH - 1) {
2279            if y > 0 {
2280                window = roll_finder_window(window, modules[(y + FINDER_LIKE_PATTERN_WIDTH - 1) * width + x]);
2281            }
2282
2283            if window != FINDER_LIKE_PATTERN_BITS {
2284                continue;
2285            }
2286
2287            if !vertical_range_has_dark(width, modules, x, y.saturating_sub(4), y)
2288                || !vertical_range_has_dark(
2289                    width,
2290                    modules,
2291                    x,
2292                    y + FINDER_LIKE_PATTERN_WIDTH,
2293                    min(y + FINDER_LIKE_PATTERN_WIDTH + 4, width),
2294                )
2295            {
2296                total_score += 40;
2297            }
2298        }
2299    }
2300
2301    total_score
2302}
2303
2304fn compute_line_finder_penalty_score(line: &[u8]) -> u32 {
2305    let mut total_score = 0;
2306    let mut window = initial_finder_window(line);
2307
2308    for offset in 0..line.len().saturating_sub(FINDER_LIKE_PATTERN_WIDTH - 1) {
2309        if offset > 0 {
2310            window = roll_finder_window(window, line[offset + FINDER_LIKE_PATTERN_WIDTH - 1]);
2311        }
2312
2313        if window != FINDER_LIKE_PATTERN_BITS {
2314            continue;
2315        }
2316
2317        if !line_range_has_dark(line, offset.saturating_sub(4), offset)
2318            || !line_range_has_dark(
2319                line,
2320                offset + FINDER_LIKE_PATTERN_WIDTH,
2321                min(offset + FINDER_LIKE_PATTERN_WIDTH + 4, line.len()),
2322            )
2323        {
2324            total_score += 40;
2325        }
2326    }
2327
2328    total_score
2329}
2330
2331fn initial_finder_window(line: &[u8]) -> u8 {
2332    line.iter().take(FINDER_LIKE_PATTERN_WIDTH).fold(0, |window, &module| roll_finder_window(window, module))
2333}
2334
2335fn initial_vertical_finder_window(width: usize, modules: &[u8], x: usize) -> u8 {
2336    (0..min(FINDER_LIKE_PATTERN_WIDTH, width)).fold(0, |window, y| roll_finder_window(window, modules[y * width + x]))
2337}
2338
2339fn roll_finder_window(window: u8, module: u8) -> u8 {
2340    ((window << 1) | (module & 1)) & FINDER_LIKE_PATTERN_MASK
2341}
2342
2343fn vertical_range_has_dark(width: usize, modules: &[u8], x: usize, start: usize, end: usize) -> bool {
2344    (start..end).any(|y| modules[y * width + x] != 0)
2345}
2346
2347fn line_range_has_dark(line: &[u8], start: usize, end: usize) -> bool {
2348    line[start..end].iter().any(|&module| module != 0)
2349}
2350
2351fn compute_block_penalty_score(width: usize, modules: &[u8]) -> u32 {
2352    #[cfg(target_arch = "aarch64")]
2353    {
2354        // SAFETY: AArch64 guarantees NEON support; the helper only performs
2355        // guarded unaligned loads within each row pair.
2356        unsafe { compute_block_penalty_score_neon(width, modules) }
2357    }
2358
2359    #[cfg(target_arch = "x86_64")]
2360    {
2361        if avx2_available() {
2362            // SAFETY: runtime feature detection ensures AVX2 is available;
2363            // the helper only performs guarded unaligned vector loads within
2364            // each row pair.
2365            return unsafe { compute_block_penalty_score_avx2(width, modules) };
2366        }
2367
2368        if sse2_available() {
2369            // SAFETY: runtime feature detection ensures SSE2 is available;
2370            // the helper only performs guarded unaligned vector loads within
2371            // each row pair.
2372            return unsafe { compute_block_penalty_score_sse2(width, modules) };
2373        }
2374
2375        compute_block_penalty_score_scalar(width, modules)
2376    }
2377
2378    #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
2379    {
2380        compute_block_penalty_score_scalar(width, modules)
2381    }
2382}
2383
2384#[cfg(any(
2385    test,
2386    feature = "bench-internals",
2387    target_arch = "x86_64",
2388    not(any(target_arch = "aarch64", target_arch = "x86_64"))
2389))]
2390fn compute_block_penalty_score_scalar(width: usize, modules: &[u8]) -> u32 {
2391    let mut total_score = 0;
2392
2393    for y in 0..width.saturating_sub(1) {
2394        let row = &modules[y * width..][..width];
2395        let next_row = &modules[(y + 1) * width..][..width];
2396        for x in 0..width.saturating_sub(1) {
2397            let this = row[x];
2398            if this == row[x + 1] && this == next_row[x] && this == next_row[x + 1] {
2399                total_score += 3;
2400            }
2401        }
2402    }
2403
2404    total_score
2405}
2406
2407#[cfg(all(target_arch = "x86_64", feature = "std"))]
2408fn sse2_available() -> bool {
2409    std::is_x86_feature_detected!("sse2")
2410}
2411
2412#[cfg(all(target_arch = "x86_64", not(feature = "std")))]
2413fn sse2_available() -> bool {
2414    true
2415}
2416
2417#[cfg(all(target_arch = "x86_64", feature = "std"))]
2418fn avx2_available() -> bool {
2419    std::is_x86_feature_detected!("avx2")
2420}
2421
2422#[cfg(all(target_arch = "x86_64", not(feature = "std")))]
2423fn avx2_available() -> bool {
2424    false
2425}
2426
2427#[cfg(target_arch = "aarch64")]
2428#[target_feature(enable = "neon")]
2429unsafe fn count_dark_modules_neon(modules: &[u8]) -> usize {
2430    use core::arch::aarch64::{vaddvq_u8, vceqq_u8, vcntq_u8, vdupq_n_u8, vld1q_u8, vmvnq_u8};
2431
2432    let mut count = 0;
2433    let mut offset = 0;
2434    let zero = vdupq_n_u8(0);
2435
2436    while offset + 16 <= modules.len() {
2437        // SAFETY: the loop guard ensures the full 16-byte vector is in bounds
2438        // for this slice.
2439        let chunk = unsafe { vld1q_u8(modules.as_ptr().wrapping_add(offset)) };
2440        let nonzero_lanes = vmvnq_u8(vceqq_u8(chunk, zero));
2441        count += (vaddvq_u8(vcntq_u8(nonzero_lanes)) / 8) as usize;
2442        offset += 16;
2443    }
2444
2445    count + count_dark_modules_scalar(&modules[offset..])
2446}
2447
2448#[cfg(target_arch = "x86_64")]
2449#[target_feature(enable = "avx2")]
2450unsafe fn count_dark_modules_avx2(modules: &[u8]) -> usize {
2451    use core::arch::x86_64::{
2452        __m256i, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8, _mm256_setzero_si256,
2453    };
2454
2455    let mut count = 0;
2456    let mut offset = 0;
2457    let zero = _mm256_setzero_si256();
2458
2459    while offset + 32 <= modules.len() {
2460        let ptr = modules.as_ptr().wrapping_add(offset).cast::<__m256i>();
2461        // SAFETY: `_mm256_loadu_si256` accepts unaligned input; the loop guard
2462        // ensures the full 32-byte vector is in bounds for this slice.
2463        let chunk = unsafe { _mm256_loadu_si256(ptr) };
2464        let zero_lanes = _mm256_movemask_epi8(_mm256_cmpeq_epi8(chunk, zero)) as u32;
2465        count += 32 - zero_lanes.count_ones() as usize;
2466        offset += 32;
2467    }
2468
2469    count + count_dark_modules_sse2_remainder(&modules[offset..])
2470}
2471
2472#[cfg(target_arch = "x86_64")]
2473#[target_feature(enable = "sse2")]
2474unsafe fn count_dark_modules_sse2(modules: &[u8]) -> usize {
2475    count_dark_modules_sse2_remainder(modules)
2476}
2477
2478#[cfg(target_arch = "x86_64")]
2479#[target_feature(enable = "sse2")]
2480fn count_dark_modules_sse2_remainder(modules: &[u8]) -> usize {
2481    use core::arch::x86_64::{__m128i, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm_setzero_si128};
2482
2483    let mut count = 0;
2484    let mut offset = 0;
2485    let zero = _mm_setzero_si128();
2486
2487    while offset + 16 <= modules.len() {
2488        let ptr = modules.as_ptr().wrapping_add(offset).cast::<__m128i>();
2489        // SAFETY: `_mm_loadu_si128` accepts unaligned input; the loop guard
2490        // ensures the full 16-byte vector is in bounds for this slice.
2491        let chunk = unsafe { _mm_loadu_si128(ptr) };
2492        let zero_lanes = _mm_movemask_epi8(_mm_cmpeq_epi8(chunk, zero)) as u32;
2493        count += 16 - zero_lanes.count_ones() as usize;
2494        offset += 16;
2495    }
2496
2497    count + count_dark_modules_scalar(&modules[offset..])
2498}
2499
2500#[cfg(target_arch = "aarch64")]
2501#[target_feature(enable = "neon")]
2502unsafe fn compute_block_penalty_score_neon(width: usize, modules: &[u8]) -> u32 {
2503    use core::arch::aarch64::{vaddvq_u8, vandq_u8, vceqq_u8, vcntq_u8, vld1q_u8};
2504
2505    let mut total_score = 0;
2506    for y in 0..width.saturating_sub(1) {
2507        let row = &modules[y * width..][..width];
2508        let next_row = &modules[(y + 1) * width..][..width];
2509        let mut x = 0;
2510
2511        while x + 16 < width {
2512            // SAFETY: the loop guard ensures all four 16-byte windows stay
2513            // inside their row slices.
2514            let (row_chunk, row_right_chunk, next_chunk, next_right_chunk) = unsafe {
2515                (
2516                    vld1q_u8(row.as_ptr().wrapping_add(x)),
2517                    vld1q_u8(row.as_ptr().wrapping_add(x + 1)),
2518                    vld1q_u8(next_row.as_ptr().wrapping_add(x)),
2519                    vld1q_u8(next_row.as_ptr().wrapping_add(x + 1)),
2520                )
2521            };
2522
2523            let horizontal = vceqq_u8(row_chunk, row_right_chunk);
2524            let vertical = vceqq_u8(row_chunk, next_chunk);
2525            let diagonal = vceqq_u8(row_chunk, next_right_chunk);
2526            let blocks = vandq_u8(vandq_u8(horizontal, vertical), diagonal);
2527            total_score += u32::from(vaddvq_u8(vcntq_u8(blocks)) / 8) * 3;
2528            x += 16;
2529        }
2530
2531        total_score += compute_block_penalty_score_scalar_tail(row, next_row, x);
2532    }
2533
2534    total_score
2535}
2536
2537#[cfg(target_arch = "x86_64")]
2538#[target_feature(enable = "avx2")]
2539unsafe fn compute_block_penalty_score_avx2(width: usize, modules: &[u8]) -> u32 {
2540    use core::arch::x86_64::{__m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8};
2541
2542    let mut total_score = 0;
2543    for y in 0..width.saturating_sub(1) {
2544        let row = &modules[y * width..][..width];
2545        let next_row = &modules[(y + 1) * width..][..width];
2546        let mut x = 0;
2547
2548        while x + 32 < width {
2549            let row_ptr = row.as_ptr().wrapping_add(x).cast::<__m256i>();
2550            let row_right_ptr = row.as_ptr().wrapping_add(x + 1).cast::<__m256i>();
2551            let next_ptr = next_row.as_ptr().wrapping_add(x).cast::<__m256i>();
2552            let next_right_ptr = next_row.as_ptr().wrapping_add(x + 1).cast::<__m256i>();
2553
2554            // SAFETY: `_mm256_loadu_si256` accepts unaligned input; the loop
2555            // guard ensures all four 32-byte windows stay inside their row
2556            // slices.
2557            let (row_chunk, row_right_chunk, next_chunk, next_right_chunk) = unsafe {
2558                (
2559                    _mm256_loadu_si256(row_ptr),
2560                    _mm256_loadu_si256(row_right_ptr),
2561                    _mm256_loadu_si256(next_ptr),
2562                    _mm256_loadu_si256(next_right_ptr),
2563                )
2564            };
2565
2566            let horizontal = _mm256_cmpeq_epi8(row_chunk, row_right_chunk);
2567            let vertical = _mm256_cmpeq_epi8(row_chunk, next_chunk);
2568            let diagonal = _mm256_cmpeq_epi8(row_chunk, next_right_chunk);
2569            let blocks = _mm256_and_si256(_mm256_and_si256(horizontal, vertical), diagonal);
2570            total_score += (_mm256_movemask_epi8(blocks) as u32).count_ones() * 3;
2571            x += 32;
2572        }
2573
2574        total_score += compute_block_penalty_score_scalar_tail(row, next_row, x);
2575    }
2576
2577    total_score
2578}
2579
2580#[cfg(target_arch = "x86_64")]
2581#[target_feature(enable = "sse2")]
2582unsafe fn compute_block_penalty_score_sse2(width: usize, modules: &[u8]) -> u32 {
2583    use core::arch::x86_64::{__m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8};
2584
2585    let mut total_score = 0;
2586    for y in 0..width.saturating_sub(1) {
2587        let row = &modules[y * width..][..width];
2588        let next_row = &modules[(y + 1) * width..][..width];
2589        let mut x = 0;
2590
2591        while x + 16 < width {
2592            let row_ptr = row.as_ptr().wrapping_add(x).cast::<__m128i>();
2593            let row_right_ptr = row.as_ptr().wrapping_add(x + 1).cast::<__m128i>();
2594            let next_ptr = next_row.as_ptr().wrapping_add(x).cast::<__m128i>();
2595            let next_right_ptr = next_row.as_ptr().wrapping_add(x + 1).cast::<__m128i>();
2596
2597            // SAFETY: `_mm_loadu_si128` accepts unaligned input; the loop guard
2598            // ensures all four 16-byte windows stay inside their row slices.
2599            let (row_chunk, row_right_chunk, next_chunk, next_right_chunk) = unsafe {
2600                (
2601                    _mm_loadu_si128(row_ptr),
2602                    _mm_loadu_si128(row_right_ptr),
2603                    _mm_loadu_si128(next_ptr),
2604                    _mm_loadu_si128(next_right_ptr),
2605                )
2606            };
2607
2608            let horizontal = _mm_cmpeq_epi8(row_chunk, row_right_chunk);
2609            let vertical = _mm_cmpeq_epi8(row_chunk, next_chunk);
2610            let diagonal = _mm_cmpeq_epi8(row_chunk, next_right_chunk);
2611            let blocks = _mm_and_si128(_mm_and_si128(horizontal, vertical), diagonal);
2612            total_score += (_mm_movemask_epi8(blocks) as u32).count_ones() * 3;
2613            x += 16;
2614        }
2615
2616        total_score += compute_block_penalty_score_scalar_tail(row, next_row, x);
2617    }
2618
2619    total_score
2620}
2621
2622#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
2623fn compute_block_penalty_score_scalar_tail(row: &[u8], next_row: &[u8], start: usize) -> u32 {
2624    let mut total_score = 0;
2625    let mut x = start;
2626
2627    while x + 1 < row.len() {
2628        let this = row[x];
2629        if this == row[x + 1] && this == next_row[x] && this == next_row[x + 1] {
2630            total_score += 3;
2631        }
2632        x += 1;
2633    }
2634
2635    total_score
2636}
2637
2638fn compute_total_penalty_score_from_bytes(version: Version, width: usize, modules: &[u8]) -> u32 {
2639    match version {
2640        Version::Normal(_) => {
2641            let s1_a = compute_adjacent_penalty_score(width, modules, true);
2642            let s1_b = compute_adjacent_penalty_score(width, modules, false);
2643            let s2 = compute_block_penalty_score(width, modules);
2644            let s3_a = compute_finder_penalty_score(width, modules, true);
2645            let s3_b = compute_finder_penalty_score(width, modules, false);
2646            let s4 = compute_balance_penalty_score(modules);
2647            s1_a + s1_b + s2 + s3_a + s3_b + s4
2648        }
2649        Version::Micro(_) => compute_light_side_penalty_score(width, modules),
2650    }
2651}
2652
2653#[cfg(any(test, feature = "bench-internals"))]
2654fn compute_total_penalty_score_from_bytes_scalar(version: Version, width: usize, modules: &[u8]) -> u32 {
2655    match version {
2656        Version::Normal(_) => {
2657            let s1_a = compute_adjacent_penalty_score(width, modules, true);
2658            let s1_b = compute_adjacent_penalty_score(width, modules, false);
2659            let s2 = compute_block_penalty_score_scalar(width, modules);
2660            let s3_a = compute_finder_penalty_score(width, modules, true);
2661            let s3_b = compute_finder_penalty_score(width, modules, false);
2662            let s4 = compute_balance_penalty_score_scalar(modules);
2663            s1_a + s1_b + s2 + s3_a + s3_b + s4
2664        }
2665        Version::Micro(_) => compute_light_side_penalty_score(width, modules),
2666    }
2667}
2668
2669#[cfg(test)]
2670fn compute_total_penalty_score_scalar(version: Version, width: i16, modules: &[Module]) -> u32 {
2671    debug_assert_eq!((width * width).as_usize(), modules.len());
2672    let modules = modules.iter().map(|module| u8::from(module.is_dark())).collect::<Vec<_>>();
2673    compute_total_penalty_score_from_bytes_scalar(version, width.as_usize(), &modules)
2674}
2675
2676#[cfg(test)]
2677mod penalty_tests {
2678    use crate::canvas::{
2679        Canvas, MaskPattern, compute_adjacent_penalty_score, compute_balance_penalty_score,
2680        compute_block_penalty_score, compute_block_penalty_score_scalar, compute_finder_penalty_score,
2681        compute_light_side_penalty_score, compute_total_penalty_score_from_bytes,
2682        compute_total_penalty_score_from_bytes_scalar, compute_total_penalty_score_scalar, count_dark_modules,
2683        count_dark_modules_scalar,
2684    };
2685    use crate::cast::As;
2686    use crate::types::{Color, EcLevel, Version};
2687
2688    fn create_test_canvas() -> Canvas {
2689        let mut c = Canvas::new(Version::Normal(1), EcLevel::Q);
2690        c.draw_all_functional_patterns();
2691        c.draw_data(
2692            b"\x20\x5b\x0b\x78\xd1\x72\xdc\x4d\x43\x40\xec\x11\x00",
2693            b"\xa8\x48\x16\x52\xd9\x36\x9c\x00\x2e\x0f\xb4\x7a\x10",
2694        );
2695        c.apply_mask(MaskPattern::Checkerboard);
2696        c
2697    }
2698
2699    #[test]
2700    fn check_penalty_canvas() {
2701        let c = create_test_canvas();
2702        assert_eq!(
2703            &*c.to_debug_str(),
2704            "\n\
2705             #######.##....#######\n\
2706             #.....#.#..#..#.....#\n\
2707             #.###.#.#..##.#.###.#\n\
2708             #.###.#.#.....#.###.#\n\
2709             #.###.#.#.#...#.###.#\n\
2710             #.....#...#...#.....#\n\
2711             #######.#.#.#.#######\n\
2712             ........#............\n\
2713             .##.#.##....#.#.#####\n\
2714             .#......####....#...#\n\
2715             ..##.###.##...#.##...\n\
2716             .##.##.#..##.#.#.###.\n\
2717             #...#.#.#.###.###.#.#\n\
2718             ........##.#..#...#.#\n\
2719             #######.#.#....#.##..\n\
2720             #.....#..#.##.##.#...\n\
2721             #.###.#.#.#...#######\n\
2722             #.###.#..#.#.#.#...#.\n\
2723             #.###.#.#...####.#..#\n\
2724             #.....#.#.##.#...#.##\n\
2725             #######.....####....#"
2726        );
2727    }
2728
2729    #[test]
2730    fn test_penalty_score_adjacent() {
2731        let c = create_test_canvas();
2732        assert_eq!(c.compute_adjacent_penalty_score(true), 88);
2733        assert_eq!(c.compute_adjacent_penalty_score(false), 92);
2734    }
2735
2736    #[test]
2737    fn adjacent_penalty_score_matches_canvas_wrapper() {
2738        let c = create_test_canvas();
2739        let modules = c.modules.iter().map(|module| u8::from(module.is_dark())).collect::<Vec<_>>();
2740        assert_eq!(
2741            compute_adjacent_penalty_score(c.width.as_usize(), &modules, true),
2742            c.compute_adjacent_penalty_score(true)
2743        );
2744        assert_eq!(
2745            compute_adjacent_penalty_score(c.width.as_usize(), &modules, false),
2746            c.compute_adjacent_penalty_score(false)
2747        );
2748    }
2749
2750    #[test]
2751    fn test_penalty_score_block() {
2752        let c = create_test_canvas();
2753        assert_eq!(c.compute_block_penalty_score(), 90);
2754    }
2755
2756    #[test]
2757    fn block_penalty_score_matches_scalar_for_varied_widths() {
2758        for width in [1_usize, 2, 7, 21, 45] {
2759            let modules = (0..width * width).map(|i| u8::from((i + i / width) % 5 < 2)).collect::<Vec<_>>();
2760            assert_eq!(
2761                compute_block_penalty_score(width, &modules),
2762                compute_block_penalty_score_scalar(width, &modules)
2763            );
2764        }
2765    }
2766
2767    #[test]
2768    fn test_penalty_score_finder() {
2769        let c = create_test_canvas();
2770        assert_eq!(c.compute_finder_penalty_score(true), 0);
2771        assert_eq!(c.compute_finder_penalty_score(false), 40);
2772    }
2773
2774    #[test]
2775    fn finder_penalty_score_matches_canvas_wrapper() {
2776        let c = create_test_canvas();
2777        let modules = c.modules.iter().map(|module| u8::from(module.is_dark())).collect::<Vec<_>>();
2778        assert_eq!(
2779            compute_finder_penalty_score(c.width.as_usize(), &modules, true),
2780            c.compute_finder_penalty_score(true)
2781        );
2782        assert_eq!(
2783            compute_finder_penalty_score(c.width.as_usize(), &modules, false),
2784            c.compute_finder_penalty_score(false)
2785        );
2786    }
2787
2788    #[test]
2789    fn test_penalty_score_balance() {
2790        let c = create_test_canvas();
2791        assert_eq!(c.compute_balance_penalty_score(), 2);
2792    }
2793
2794    #[test]
2795    fn balance_penalty_score_matches_canvas_wrapper() {
2796        let c = create_test_canvas();
2797        let modules = c.modules.iter().map(|module| u8::from(module.is_dark())).collect::<Vec<_>>();
2798        assert_eq!(compute_balance_penalty_score(&modules), c.compute_balance_penalty_score());
2799    }
2800
2801    #[test]
2802    fn dark_module_count_matches_scalar_for_varied_lengths() {
2803        for len in 0..65 {
2804            let modules = (0..len).map(|i| u8::from(i % 3 == 0 || i % 7 == 0)).collect::<Vec<_>>();
2805            assert_eq!(count_dark_modules(&modules), count_dark_modules_scalar(&modules));
2806        }
2807    }
2808
2809    #[test]
2810    fn scalar_penalty_score_matches_canvas_wrapper() {
2811        let c = create_test_canvas();
2812        assert_eq!(
2813            compute_total_penalty_score_scalar(c.version, c.width, &c.modules),
2814            c.compute_total_penalty_scores()
2815        );
2816    }
2817
2818    #[test]
2819    fn accelerated_penalty_score_matches_scalar_byte_grid() {
2820        let c = create_test_canvas();
2821        let modules = c.modules.iter().map(|module| u8::from(module.is_dark())).collect::<Vec<_>>();
2822
2823        assert_eq!(
2824            compute_total_penalty_score_from_bytes(c.version, c.width.as_usize(), &modules),
2825            compute_total_penalty_score_from_bytes_scalar(c.version, c.width.as_usize(), &modules)
2826        );
2827    }
2828
2829    #[test]
2830    fn scratch_penalty_score_matches_canvas_wrapper() {
2831        let c = create_test_canvas();
2832        let mut scratch = Vec::new();
2833
2834        assert_eq!(c.compute_total_penalty_scores_with_scratch(&mut scratch), c.compute_total_penalty_scores());
2835        assert_eq!(scratch.len(), c.modules.len());
2836    }
2837
2838    #[test]
2839    fn test_penalty_score_light_sides() {
2840        static HORIZONTAL_SIDE: [Color; 17] = [
2841            Color::Dark,
2842            Color::Light,
2843            Color::Light,
2844            Color::Dark,
2845            Color::Dark,
2846            Color::Dark,
2847            Color::Light,
2848            Color::Light,
2849            Color::Dark,
2850            Color::Light,
2851            Color::Dark,
2852            Color::Light,
2853            Color::Light,
2854            Color::Dark,
2855            Color::Light,
2856            Color::Light,
2857            Color::Light,
2858        ];
2859        static VERTICAL_SIDE: [Color; 17] = [
2860            Color::Dark,
2861            Color::Dark,
2862            Color::Dark,
2863            Color::Light,
2864            Color::Light,
2865            Color::Dark,
2866            Color::Dark,
2867            Color::Light,
2868            Color::Dark,
2869            Color::Light,
2870            Color::Dark,
2871            Color::Light,
2872            Color::Dark,
2873            Color::Light,
2874            Color::Light,
2875            Color::Dark,
2876            Color::Light,
2877        ];
2878
2879        let mut c = Canvas::new(Version::Micro(4), EcLevel::Q);
2880        for i in 0_i16..17 {
2881            c.put(i, -1, HORIZONTAL_SIDE[i.as_usize()]);
2882            c.put(-1, i, VERTICAL_SIDE[i.as_usize()]);
2883        }
2884
2885        assert_eq!(c.compute_light_side_penalty_score(), 168);
2886    }
2887
2888    #[test]
2889    fn light_side_penalty_score_matches_canvas_wrapper() {
2890        let mut c = Canvas::new(Version::Micro(4), EcLevel::Q);
2891        c.draw_all_functional_patterns();
2892        let modules = c.modules.iter().map(|module| u8::from(module.is_dark())).collect::<Vec<_>>();
2893
2894        assert_eq!(
2895            compute_light_side_penalty_score(c.width.as_usize(), &modules),
2896            c.compute_light_side_penalty_score()
2897        );
2898    }
2899}
2900
2901//}}}
2902//------------------------------------------------------------------------------
2903//{{{ Select mask with lowest penalty score
2904
2905static ALL_PATTERNS_QR: [MaskPattern; 8] = [
2906    MaskPattern::Checkerboard,
2907    MaskPattern::HorizontalLines,
2908    MaskPattern::VerticalLines,
2909    MaskPattern::DiagonalLines,
2910    MaskPattern::LargeCheckerboard,
2911    MaskPattern::Fields,
2912    MaskPattern::Diamonds,
2913    MaskPattern::Meadow,
2914];
2915
2916static ALL_PATTERNS_MICRO_QR: [MaskPattern; 4] =
2917    [MaskPattern::HorizontalLines, MaskPattern::LargeCheckerboard, MaskPattern::Diamonds, MaskPattern::Meadow];
2918
2919impl Canvas {
2920    /// Construct a new canvas and apply the best masking that gives the lowest
2921    /// penalty score.
2922    #[must_use]
2923    pub fn apply_best_mask(&self) -> Self {
2924        self.apply_best_mask_with_score().0
2925    }
2926
2927    /// Construct a new canvas with the best mask and return the selected mask
2928    /// pattern alongside it.
2929    #[must_use]
2930    pub fn apply_best_mask_with_pattern(&self) -> (Self, MaskPattern) {
2931        let (canvas, pattern, _) = self.apply_best_mask_with_score();
2932        (canvas, pattern)
2933    }
2934
2935    /// Construct a new canvas with the best mask and return the selected mask
2936    /// pattern and its penalty score.
2937    ///
2938    /// Masks are compared using full `u32` scores. Unusually high scores exceeding
2939    /// `u16::MAX` are clipped to `u16::MAX` in the returned score.
2940    #[must_use]
2941    pub fn apply_best_mask_with_score(&self) -> (Self, MaskPattern, u16) {
2942        let patterns: &[MaskPattern] = match self.version {
2943            Version::Normal(_) => &ALL_PATTERNS_QR,
2944            Version::Micro(_) => &ALL_PATTERNS_MICRO_QR,
2945        };
2946        let mut scratch = vec![0; self.modules.len()];
2947        let mut best_pattern = patterns[0];
2948        self.write_masked_module_bytes(best_pattern, &mut scratch);
2949        let mut best_score = compute_total_penalty_score_from_bytes(self.version, self.width.as_usize(), &scratch);
2950
2951        for &pattern in &patterns[1..] {
2952            self.write_masked_module_bytes(pattern, &mut scratch);
2953            let score = compute_total_penalty_score_from_bytes(self.version, self.width.as_usize(), &scratch);
2954            if score < best_score {
2955                best_score = score;
2956                best_pattern = pattern;
2957            }
2958        }
2959
2960        let mut best_canvas = self.clone();
2961        best_canvas.apply_mask(best_pattern);
2962        (best_canvas, best_pattern, u16::try_from(best_score).unwrap_or(u16::MAX))
2963    }
2964
2965    /// Convert the modules into a vector of colors.
2966    pub fn into_colors(self) -> Vec<Color> {
2967        self.modules.into_iter().map(Color::from).collect()
2968    }
2969}
2970
2971#[cfg(test)]
2972mod mask_selection_tests {
2973    use crate::canvas::{
2974        ALL_PATTERNS_MICRO_QR, ALL_PATTERNS_QR, Canvas, MaskPattern, Module, compute_total_penalty_score_from_bytes,
2975        get_mask_function, module_bytes,
2976    };
2977    use crate::cast::As;
2978    use crate::types::{Color, EcLevel, Version};
2979
2980    const VERSIONS: [Version; 9] = [
2981        Version::Normal(1),
2982        Version::Normal(10),
2983        Version::Normal(20),
2984        Version::Normal(30),
2985        Version::Normal(40),
2986        Version::Micro(1),
2987        Version::Micro(2),
2988        Version::Micro(3),
2989        Version::Micro(4),
2990    ];
2991
2992    fn supported_ec_levels(version: Version) -> &'static [EcLevel] {
2993        match version {
2994            Version::Normal(_) => &[EcLevel::L, EcLevel::M, EcLevel::Q, EcLevel::H],
2995            Version::Micro(1) => &[EcLevel::L],
2996            Version::Micro(2 | 3) => &[EcLevel::L, EcLevel::M],
2997            Version::Micro(4) => &[EcLevel::L, EcLevel::M, EcLevel::Q],
2998            Version::Micro(_) => unreachable!(),
2999        }
3000    }
3001
3002    fn patterns_for_version(version: Version) -> &'static [MaskPattern] {
3003        match version {
3004            Version::Normal(_) => &ALL_PATTERNS_QR,
3005            Version::Micro(_) => &ALL_PATTERNS_MICRO_QR,
3006        }
3007    }
3008
3009    fn create_canvas(version: Version, ec_level: EcLevel, data_pattern: usize) -> Canvas {
3010        let mut canvas = Canvas::new(version, ec_level);
3011        canvas.draw_all_functional_patterns();
3012        for (index, module) in canvas.modules.iter_mut().enumerate() {
3013            if *module != Module::Empty {
3014                continue;
3015            }
3016            *module = match data_pattern {
3017                0 => Module::Empty,
3018                1 => Module::Unmasked(Color::Light),
3019                2 => Module::Unmasked(Color::Dark),
3020                3 => Module::Unmasked(if index % 2 == 0 { Color::Light } else { Color::Dark }),
3021                4 => match (index * 7 + index / 3) % 5 {
3022                    0 => Module::Empty,
3023                    1 => Module::Masked(Color::Light),
3024                    2 => Module::Masked(Color::Dark),
3025                    3 => Module::Unmasked(Color::Light),
3026                    _ => Module::Unmasked(Color::Dark),
3027                },
3028                _ => unreachable!(),
3029            };
3030        }
3031        canvas
3032    }
3033
3034    fn apply_mask_column_major(canvas: &mut Canvas, pattern: MaskPattern) {
3035        let mask_fn = get_mask_function(pattern);
3036        for x in 0..canvas.width {
3037            for y in 0..canvas.width {
3038                let module = canvas.get_mut(x, y);
3039                *module = module.mask(mask_fn(x, y));
3040            }
3041        }
3042        canvas.draw_format_info_patterns(pattern);
3043    }
3044
3045    fn select_mask_by_cloning(canvas: &Canvas) -> (Canvas, MaskPattern, u32) {
3046        let patterns = patterns_for_version(canvas.version);
3047        let mut best_canvas = None;
3048        let mut best_pattern = patterns[0];
3049        let mut best_score = u32::MAX;
3050        for &pattern in patterns {
3051            let mut candidate = canvas.clone();
3052            apply_mask_column_major(&mut candidate, pattern);
3053            let score = candidate.compute_total_penalty_scores();
3054            if score < best_score {
3055                best_canvas = Some(candidate);
3056                best_pattern = pattern;
3057                best_score = score;
3058            }
3059        }
3060        (best_canvas.expect("at least one candidate"), best_pattern, best_score)
3061    }
3062
3063    fn assert_candidate_bytes_and_scores_match_cloned_canvas(canvas: &Canvas) {
3064        let original = canvas.modules.clone();
3065        let mut output = vec![0; canvas.modules.len()];
3066        for &pattern in patterns_for_version(canvas.version) {
3067            canvas.write_masked_module_bytes(pattern, &mut output);
3068            let mut expected = canvas.clone();
3069            apply_mask_column_major(&mut expected, pattern);
3070            assert_eq!(output, module_bytes(&expected.modules), "version {:?}, pattern {pattern:?}", canvas.version);
3071            assert_eq!(
3072                compute_total_penalty_score_from_bytes(canvas.version, canvas.width.as_usize(), &output),
3073                expected.compute_total_penalty_scores(),
3074                "version {:?}, pattern {pattern:?}",
3075                canvas.version
3076            );
3077        }
3078        assert_eq!(canvas.modules, original);
3079    }
3080
3081    #[test]
3082    fn row_major_mask_matches_column_major_for_every_pattern() {
3083        for version in VERSIONS {
3084            for &pattern in patterns_for_version(version) {
3085                let mut actual = create_canvas(version, EcLevel::L, 4);
3086                let mut expected = actual.clone();
3087                actual.apply_mask(pattern);
3088                apply_mask_column_major(&mut expected, pattern);
3089                assert_eq!(actual.modules, expected.modules, "version {version:?}, pattern {pattern:?}");
3090            }
3091        }
3092    }
3093
3094    #[test]
3095    fn byte_candidates_match_cloned_reference_for_all_versions_and_data_patterns() {
3096        for version in VERSIONS {
3097            for &ec_level in supported_ec_levels(version) {
3098                for data_pattern in 0..5 {
3099                    let canvas = create_canvas(version, ec_level, data_pattern);
3100                    let original = canvas.modules.clone();
3101                    assert_candidate_bytes_and_scores_match_cloned_canvas(&canvas);
3102                    let (actual, actual_pattern, actual_score) = canvas.apply_best_mask_with_score();
3103                    let (expected, expected_pattern, expected_score) = select_mask_by_cloning(&canvas);
3104                    assert_eq!(
3105                        (actual_pattern, actual_score, actual.modules),
3106                        (expected_pattern, u16::try_from(expected_score).unwrap_or(u16::MAX), expected.modules),
3107                        "version {version:?}, ec level {ec_level:?}, data pattern {data_pattern}"
3108                    );
3109                    assert_eq!(canvas.modules, original);
3110                }
3111            }
3112        }
3113    }
3114
3115    #[test]
3116    fn direct_bytes_match_cloned_reference_for_random_module_states() {
3117        let mut seed = 2_712_u64;
3118        for case in 0..64 {
3119            let version = VERSIONS[case % VERSIONS.len()];
3120            let ec_levels = supported_ec_levels(version);
3121            let ec_level = ec_levels[case / VERSIONS.len() % ec_levels.len()];
3122            let mut canvas = create_canvas(version, ec_level, 0);
3123            for module in &mut canvas.modules {
3124                if *module != Module::Empty {
3125                    continue;
3126                }
3127                seed = seed.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
3128                *module = match (seed >> 32) % 5 {
3129                    0 => Module::Empty,
3130                    1 => Module::Masked(Color::Light),
3131                    2 => Module::Masked(Color::Dark),
3132                    3 => Module::Unmasked(Color::Light),
3133                    _ => Module::Unmasked(Color::Dark),
3134                };
3135            }
3136            let original = canvas.modules.clone();
3137            assert_candidate_bytes_and_scores_match_cloned_canvas(&canvas);
3138            let (actual, actual_pattern, actual_score) = canvas.apply_best_mask_with_score();
3139            let (expected, expected_pattern, expected_score) = select_mask_by_cloning(&canvas);
3140            assert_eq!(
3141                (actual_pattern, actual_score, actual.modules),
3142                (expected_pattern, u16::try_from(expected_score).unwrap_or(u16::MAX), expected.modules),
3143                "version {version:?}, ec level {ec_level:?}, case {case}"
3144            );
3145            assert_eq!(canvas.modules, original);
3146        }
3147    }
3148
3149    #[test]
3150    fn high_penalty_scores_remain_exact_and_match_scalar_scoring() {
3151        for (version, expected_total, expected_block) in
3152            [(Version::Normal(30), 87_333, 52_005), (Version::Normal(40), 148_032, 88_395)]
3153        {
3154            let mut canvas = create_canvas(version, EcLevel::L, 3);
3155            canvas.apply_mask(MaskPattern::Checkerboard);
3156            let accelerated = canvas.compute_total_penalty_scores_with_scratch(&mut Vec::new());
3157            let scalar = canvas.compute_total_penalty_scores();
3158            assert_eq!(
3159                (accelerated, scalar, canvas.compute_block_penalty_score()),
3160                (expected_total, expected_total, expected_block),
3161                "version {version:?}"
3162            );
3163        }
3164    }
3165
3166    #[test]
3167    fn mask_selection_compares_full_scores_before_clipping_the_result() {
3168        for (version, expected_best_score) in [(Version::Normal(30), 87_296), (Version::Normal(40), 147_995)] {
3169            let mut canvas = create_canvas(version, EcLevel::L, 2);
3170            for module in &mut canvas.modules {
3171                if let Module::Unmasked(color) = *module {
3172                    *module = Module::Masked(color);
3173                }
3174            }
3175            for &pattern in &ALL_PATTERNS_QR {
3176                let mut candidate = canvas.clone();
3177                apply_mask_column_major(&mut candidate, pattern);
3178                assert!(candidate.compute_total_penalty_scores() > u32::from(u16::MAX));
3179            }
3180            let (expected, expected_pattern, expected_score) = select_mask_by_cloning(&canvas);
3181            assert_eq!((expected_pattern, expected_score), (MaskPattern::Meadow, expected_best_score));
3182            let (actual, actual_pattern, actual_score) = canvas.apply_best_mask_with_score();
3183            assert_eq!(
3184                (actual_pattern, actual_score, actual.modules),
3185                (MaskPattern::Meadow, u16::MAX, expected.modules),
3186                "version {version:?}"
3187            );
3188        }
3189    }
3190
3191    #[cfg(feature = "bench-internals")]
3192    #[test]
3193    fn benchmark_score_hooks_clip_high_penalties_consistently() {
3194        for version in [Version::Normal(30), Version::Normal(40)] {
3195            let mut canvas = create_canvas(version, EcLevel::L, 3);
3196            canvas.apply_mask(MaskPattern::Checkerboard);
3197            assert_eq!(
3198                (canvas.score_mask_for_bench(&mut Vec::new()), canvas.score_mask_scalar_for_bench(&mut Vec::new())),
3199                (u16::MAX, u16::MAX),
3200                "version {version:?}"
3201            );
3202        }
3203    }
3204
3205    #[test]
3206    fn equal_micro_mask_scores_keep_the_first_pattern() {
3207        for version in [Version::Micro(1), Version::Micro(2), Version::Micro(3), Version::Micro(4)] {
3208            let mut canvas = Canvas::new(version, EcLevel::L);
3209            canvas.modules.fill(Module::Masked(Color::Dark));
3210            let (actual, actual_pattern, actual_score) = canvas.apply_best_mask_with_score();
3211            for &pattern in &ALL_PATTERNS_MICRO_QR {
3212                let mut candidate = canvas.clone();
3213                apply_mask_column_major(&mut candidate, pattern);
3214                assert_eq!(candidate.compute_total_penalty_scores(), u32::from(actual_score));
3215            }
3216            let mut expected = canvas.clone();
3217            apply_mask_column_major(&mut expected, MaskPattern::HorizontalLines);
3218            assert_eq!(
3219                (actual_pattern, actual.modules),
3220                (MaskPattern::HorizontalLines, expected.modules),
3221                "version {version:?}"
3222            );
3223        }
3224    }
3225}
3226
3227//}}}
3228//------------------------------------------------------------------------------