1#[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#[derive(PartialEq, Eq, Clone, Copy, Debug)]
50pub enum Module {
51 Empty,
53
54 Masked(Color),
57
58 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 pub fn is_dark(self) -> bool {
74 Color::from(self) == Color::Dark
75 }
76
77 #[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#[derive(Clone)]
106pub struct Canvas {
107 width: i16,
109
110 version: Version,
112
113 ec_level: EcLevel,
115
116 modules: Vec<Module>,
119}
120
121impl Canvas {
122 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 #[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 pub fn get(&self, x: i16, y: i16) -> Module {
157 self.modules[self.coords_to_index(x, y)]
158 }
159
160 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 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
239impl Canvas {
244 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 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
341impl Canvas {
346 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 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
529static 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
588impl Canvas {
593 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 for x in x1..=x2 {
609 self.put(x, y1, if x % 2 == 0 { color_even } else { color_odd });
610 }
611 } else {
612 for y in y1..=y2 {
614 self.put(x1, y, if y % 2 == 0 { color_even } else { color_odd });
615 }
616 }
617 }
618
619 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
693impl Canvas {
698 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 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); }
725 }
726 }
727
728 fn draw_reserved_format_info_patterns(&mut self) {
730 self.draw_format_info_patterns_with_number(0);
731 }
732
733 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
1014impl Canvas {
1019 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
1034pub 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 || (x < 9 && y < 9) || (x < 9 && y >= width - 8) || (x >= width - 8 && y < 9); 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
1184struct 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] mod 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
1391impl 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 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#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1505#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1506pub enum MaskPattern {
1507 Checkerboard = 0b000,
1509
1510 HorizontalLines = 0b001,
1512
1513 VerticalLines = 0b010,
1515
1516 DiagonalLines = 0b011,
1518
1519 LargeCheckerboard = 0b100,
1521
1522 Fields = 0b101,
1524
1525 Diamonds = 0b110,
1527
1528 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 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 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
1762impl Canvas {
1767 #[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 #[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 #[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 #[cfg(test)]
1806 fn compute_balance_penalty_score(&self) -> u16 {
1807 compute_balance_penalty_score(&module_bytes(&self.modules))
1808 }
1809
1810 #[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 #[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 unsafe { count_dark_modules_neon(modules) }
1866 }
1867
1868 #[cfg(target_arch = "x86_64")]
1869 {
1870 if avx2_available() {
1871 return unsafe { count_dark_modules_avx2(modules) };
1875 }
1876
1877 if sse2_available() {
1878 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 unsafe { compute_block_penalty_score_neon(width, modules) }
2085 }
2086
2087 #[cfg(target_arch = "x86_64")]
2088 {
2089 if avx2_available() {
2090 return unsafe { compute_block_penalty_score_avx2(width, modules) };
2094 }
2095
2096 if sse2_available() {
2097 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 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 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 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 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 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 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
2629static 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 #[must_use]
2651 pub fn apply_best_mask(&self) -> Self {
2652 self.apply_best_mask_with_score().0
2653 }
2654
2655 #[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 #[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 pub fn into_colors(self) -> Vec<Color> {
2692 self.modules.into_iter().map(Color::from).collect()
2693 }
2694}
2695
2696