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