Skip to main content

qrcode_core/
canvas.rs

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