1use core::ops::RangeTo;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub struct Adam7Info {
12 pub(crate) pass: u8,
14 pub(crate) line: u32,
16 pub(crate) width: u32,
18 pub(crate) samples: u32,
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26struct BitPostion {
27 byte: usize,
28 bit: u8,
30}
31
32impl Adam7Info {
33 pub fn new(pass: u8, line: u32, width: u32) -> Self {
49 assert!(1 <= pass && pass <= 7);
50 assert!(width > 0);
51
52 let info = PassConstants::PASSES[pass as usize - 1];
53 let samples = info.count_samples(width);
54
55 Self {
56 pass,
57 line,
58 width,
59 samples,
60 }
61 }
62
63 fn pass_constants(&self) -> PassConstants {
64 PassConstants::PASSES[self.pass as usize - 1]
65 }
66
67 fn splat_pixel_repeat(self, idx: usize) -> u8 {
69 let pass = self.pass_constants();
70 let x_pixel = idx as u32 * u32::from(pass.x_sampling) + u32::from(pass.x_offset);
71 (self.width - x_pixel).min(pass.splat_x_repeat().into()) as u8
72 }
73
74 fn splat_line_repeat(self, height: u32) -> u8 {
75 let pass = self.pass_constants();
76 let y_line = self.line * u32::from(pass.y_sampling) + u32::from(pass.y_offset);
77 (height - y_line).min(pass.splat_y_repeat().into()) as u8
78 }
79}
80
81#[derive(Clone, Copy)]
82struct PassConstants {
83 x_sampling: u8,
84 x_offset: u8,
85 y_sampling: u8,
86 y_offset: u8,
87}
88
89impl PassConstants {
90 const fn splat_x_repeat(self) -> u8 {
91 self.x_sampling - self.x_offset
92 }
93
94 const fn splat_y_repeat(self) -> u8 {
95 self.y_sampling - self.y_offset
96 }
97
98 fn count_samples(self, width: u32) -> u32 {
99 width
100 .saturating_sub(u32::from(self.x_offset))
101 .div_ceil(u32::from(self.x_sampling))
102 }
103
104 fn count_lines(self, height: u32) -> u32 {
105 height
106 .saturating_sub(u32::from(self.y_offset))
107 .div_ceil(u32::from(self.y_sampling))
108 }
109
110 pub const PASSES: [Self; 7] = {
113 const fn new(x_sampling: u8, x_offset: u8, y_sampling: u8, y_offset: u8) -> PassConstants {
115 PassConstants {
116 x_sampling,
117 x_offset,
118 y_sampling,
119 y_offset,
120 }
121 }
122
123 [
124 new(8, 0, 8, 0),
125 new(8, 4, 8, 0),
126 new(4, 0, 8, 4),
127 new(4, 2, 4, 0),
128 new(2, 0, 4, 2),
129 new(2, 1, 2, 0),
130 new(1, 0, 2, 1),
131 ]
132 };
133}
134
135#[derive(Clone)]
148pub(crate) struct Adam7Iterator {
149 line: u32,
150 lines: u32,
151 line_width: u32,
152 current_pass: u8,
153 width: u32,
154 height: u32,
155}
156
157impl Adam7Iterator {
158 pub fn new(width: u32, height: u32) -> Adam7Iterator {
159 let mut this = Adam7Iterator {
160 line: 0,
161 lines: 0,
162 line_width: 0,
163 current_pass: 1,
164 width,
165 height,
166 };
167 this.init_pass();
168 this
169 }
170
171 fn init_pass(&mut self) {
173 let info = PassConstants::PASSES[self.current_pass as usize - 1];
174 self.line_width = info.count_samples(self.width);
175 self.lines = info.count_lines(self.height);
176 self.line = 0;
177 }
178}
179
180impl Iterator for Adam7Iterator {
182 type Item = Adam7Info;
183 fn next(&mut self) -> Option<Self::Item> {
184 if self.line < self.lines && self.line_width > 0 {
185 let this_line = self.line;
186 self.line += 1;
187 Some(Adam7Info {
188 pass: self.current_pass,
189 line: this_line,
190 width: self.width,
191 samples: self.line_width,
192 })
193 } else if self.current_pass < 7 {
194 self.current_pass += 1;
195 self.init_pass();
196 self.next()
197 } else {
198 None
199 }
200 }
201}
202
203#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
217#[non_exhaustive]
218pub enum Adam7Variant {
219 #[default]
226 Sparse,
227 Splat,
234}
235
236fn subbyte_values<const N: usize>(
237 scanline: &[u8],
238 bit_pos: [u8; N],
239 mask: u8,
240) -> impl Iterator<Item = u8> + '_ {
241 (scanline.iter().copied()).flat_map(move |value| bit_pos.map(|n| (value >> n) & mask))
242}
243
244fn expand_adam7_bits(
250 row_stride_in_bytes: usize,
251 info: &Adam7Info,
252 bits_pp: u8,
253) -> impl Iterator<Item = BitPostion> {
254 debug_assert!(bits_pp == 1 || bits_pp == 2 || bits_pp == 4);
255 let (line_mul, line_off, samp_mul, samp_off) = {
256 let constants = info.pass_constants();
257 (
258 usize::from(constants.y_sampling),
260 usize::from(constants.y_offset),
261 u64::from(constants.x_sampling),
262 u64::from(constants.x_offset),
263 )
264 };
265
266 let prog_line = line_mul * info.line as usize + line_off;
268 let byte_start = prog_line * row_stride_in_bytes;
269
270 (0..u64::from(info.samples))
272 .map(move |i| (i * samp_mul + samp_off) * u64::from(bits_pp))
274 .map(move |i| BitPostion {
275 byte: byte_start + (i / 8) as usize,
277 bit: i as u8 % 8,
278 })
279}
280
281fn expand_adam7_bytes(
282 row_stride_in_bytes: usize,
283 info: &Adam7Info,
284 bytes_pp: u8,
285) -> impl Iterator<Item = usize> {
286 let (line_mul, line_off, samp_mul, samp_off) = {
287 let constants = info.pass_constants();
288 (
289 usize::from(constants.y_sampling),
291 usize::from(constants.y_offset),
292 u64::from(constants.x_sampling),
293 u64::from(constants.x_offset),
294 )
295 };
296
297 let prog_line = line_mul * info.line as usize + line_off;
299 let byte_start = prog_line * row_stride_in_bytes;
300
301 (0..u64::from(info.samples))
302 .map(move |i| (i * samp_mul + samp_off) * u64::from(bytes_pp))
303 .map(move |i| i as usize + byte_start)
305}
306
307pub fn expand_pass(
342 img: &mut [u8],
343 img_row_stride: usize,
344 interlaced_row: &[u8],
345 interlace_info: &Adam7Info,
346 bits_per_pixel: u8,
347) {
348 match bits_per_pixel {
349 1 => {
353 const BIT_POS_1: [u8; 8] = [7, 6, 5, 4, 3, 2, 1, 0];
354 let bit_indices = expand_adam7_bits(img_row_stride, interlace_info, 1);
355 for (pos, px) in bit_indices.zip(subbyte_values(interlaced_row, BIT_POS_1, 0b1)) {
356 let shift = 8 - bits_per_pixel - pos.bit;
357 img[pos.byte] |= px << shift;
358 }
359 }
360 2 => {
361 const BIT_POS_2: [u8; 4] = [6, 4, 2, 0];
362 let bit_indices = expand_adam7_bits(img_row_stride, interlace_info, 2);
363
364 for (pos, px) in bit_indices.zip(subbyte_values(interlaced_row, BIT_POS_2, 0b11)) {
365 let shift = 8 - bits_per_pixel - pos.bit;
366 img[pos.byte] |= px << shift;
367 }
368 }
369 4 => {
370 const BIT_POS_4: [u8; 2] = [4, 0];
371 let bit_indices = expand_adam7_bits(img_row_stride, interlace_info, 4);
372
373 for (pos, px) in bit_indices.zip(subbyte_values(interlaced_row, BIT_POS_4, 0b1111)) {
374 let shift = 8 - bits_per_pixel - pos.bit;
375 img[pos.byte] |= px << shift;
376 }
377 }
378 8 => {
382 let byte_indices = expand_adam7_bytes(img_row_stride, interlace_info, 1);
383
384 for (bytepos, &px) in byte_indices.zip(interlaced_row) {
385 img[bytepos] = px;
386 }
387 }
388 16 => {
389 let byte_indices = expand_adam7_bytes(img_row_stride, interlace_info, 2);
390
391 for (bytepos, px) in byte_indices.zip(interlaced_row.chunks(2)) {
392 img[bytepos..][..2].copy_from_slice(px);
393 }
394 }
395 _ => {
396 debug_assert!(bits_per_pixel % 8 == 0);
397 let bytes_pp = bits_per_pixel / 8;
398 let byte_indices = expand_adam7_bytes(img_row_stride, interlace_info, bytes_pp);
399
400 for (bytepos, px) in byte_indices.zip(interlaced_row.chunks(bytes_pp.into())) {
401 img[bytepos..][..px.len()].copy_from_slice(px);
402 }
403 }
404 }
405}
406
407pub fn expand_pass_splat(
440 img: &mut [u8],
441 img_row_stride: usize,
442 interlaced_row: &[u8],
443 interlace_info: &Adam7Info,
444 bits_per_pixel: u8,
445) {
446 fn expand_bits_to_img(
447 img: &mut [u8],
448 px: u8,
449 mut pos: BitPostion,
450 repeat: RangeTo<u8>,
451 bpp: u8,
452 ) {
453 let (mut into, mut tail) = img[pos.byte..].split_first_mut().unwrap();
454 let mask = (1u8 << bpp) - 1;
455
456 for _ in 0..repeat.end {
457 if pos.bit >= 8 {
458 pos.byte += 1;
459 pos.bit -= 8;
460
461 (into, tail) = tail.split_first_mut().unwrap();
462 }
463
464 let shift = 8 - bpp - pos.bit;
465 let pre = (*into >> shift) & mask;
467 *into ^= (pre ^ px) << shift;
468
469 pos.bit += bpp;
470 }
471 }
472
473 let height = (img.len() / img_row_stride) as u32;
474 let y_repeat = interlace_info.splat_line_repeat(height);
475 debug_assert!(y_repeat > 0);
476
477 match bits_per_pixel {
478 1 => {
482 const BIT_POS_1: [u8; 8] = [7, 6, 5, 4, 3, 2, 1, 0];
483
484 for offset in 0..y_repeat {
485 let bit_indices = expand_adam7_bits(img_row_stride, interlace_info, 1);
486 let line_offset = usize::from(offset) * img_row_stride;
487
488 for (idx, (mut pos, px)) in bit_indices
489 .zip(subbyte_values(interlaced_row, BIT_POS_1, 0b1))
490 .enumerate()
491 {
492 let x_repeat = interlace_info.splat_pixel_repeat(idx);
493 debug_assert!(x_repeat > 0);
494 pos.byte += line_offset;
495 expand_bits_to_img(img, px, pos, ..x_repeat, bits_per_pixel);
496 }
497 }
498 }
499 2 => {
500 const BIT_POS_2: [u8; 4] = [6, 4, 2, 0];
501
502 for offset in 0..y_repeat {
503 let bit_indices = expand_adam7_bits(img_row_stride, interlace_info, 2);
504 let line_offset = usize::from(offset) * img_row_stride;
505
506 for (idx, (mut pos, px)) in bit_indices
507 .zip(subbyte_values(interlaced_row, BIT_POS_2, 0b11))
508 .enumerate()
509 {
510 let x_repeat = interlace_info.splat_pixel_repeat(idx);
511 pos.byte += line_offset;
512 expand_bits_to_img(img, px, pos, ..x_repeat, bits_per_pixel);
513 }
514 }
515 }
516 4 => {
517 const BIT_POS_4: [u8; 2] = [4, 0];
518
519 for offset in 0..y_repeat {
520 let bit_indices = expand_adam7_bits(img_row_stride, interlace_info, 4);
521 let line_offset = usize::from(offset) * img_row_stride;
522
523 for (idx, (mut pos, px)) in bit_indices
524 .zip(subbyte_values(interlaced_row, BIT_POS_4, 0b1111))
525 .enumerate()
526 {
527 let x_repeat = interlace_info.splat_pixel_repeat(idx);
528 pos.byte += line_offset;
529 expand_bits_to_img(img, px, pos, ..x_repeat, bits_per_pixel);
530 }
531 }
532 }
533 8 => {
537 for offset in 0..y_repeat {
538 let byte_indices = expand_adam7_bytes(img_row_stride, interlace_info, 1);
539 let line_offset = usize::from(offset) * img_row_stride;
540
541 for (idx, (bytepos, px)) in byte_indices.zip(interlaced_row).enumerate() {
542 let x_repeat = usize::from(interlace_info.splat_pixel_repeat(idx));
543 debug_assert!(x_repeat > 0);
544 img[line_offset + bytepos..][..x_repeat].fill(*px);
545 }
546 }
547 }
548 _ => {
549 debug_assert!(bits_per_pixel % 8 == 0);
550 let bytes = bits_per_pixel / 8;
551 let chunk = usize::from(bytes);
552
553 for offset in 0..y_repeat {
554 let byte_indices = expand_adam7_bytes(img_row_stride, interlace_info, bytes);
555 let line_offset = usize::from(offset) * img_row_stride;
556
557 for (idx, (bytepos, px)) in byte_indices
558 .zip(interlaced_row.chunks_exact(chunk))
559 .enumerate()
560 {
561 let x_repeat = usize::from(interlace_info.splat_pixel_repeat(idx));
562 let target = &mut img[line_offset + bytepos..][..chunk * x_repeat];
563
564 for target in target.chunks_exact_mut(chunk) {
565 target.copy_from_slice(px);
566 }
567 }
568 }
569 }
570 }
571}
572
573#[cfg(test)]
574mod tests {
575 use super::*;
576
577 #[test]
578 fn test_adam7() {
579 let it = Adam7Iterator::new(4, 4);
586 let passes: Vec<_> = it.collect();
587 assert_eq!(
588 &*passes,
589 &[
590 Adam7Info {
591 pass: 1,
592 line: 0,
593 samples: 1,
594 width: 4,
595 },
596 Adam7Info {
597 pass: 4,
598 line: 0,
599 samples: 1,
600 width: 4,
601 },
602 Adam7Info {
603 pass: 5,
604 line: 0,
605 samples: 2,
606 width: 4,
607 },
608 Adam7Info {
609 pass: 6,
610 line: 0,
611 samples: 2,
612 width: 4,
613 },
614 Adam7Info {
615 pass: 6,
616 line: 1,
617 samples: 2,
618 width: 4,
619 },
620 Adam7Info {
621 pass: 7,
622 line: 0,
623 samples: 4,
624 width: 4,
625 },
626 Adam7Info {
627 pass: 7,
628 line: 1,
629 samples: 4,
630 width: 4,
631 }
632 ]
633 );
634 }
635
636 #[test]
637 fn test_subbyte_pixels() {
638 const BIT_POS_1: [u8; 8] = [7, 6, 5, 4, 3, 2, 1, 0];
639
640 let scanline = &[0b10101010, 0b10101010];
641 let pixels = subbyte_values(scanline, BIT_POS_1, 1).collect::<Vec<_>>();
642
643 assert_eq!(pixels.len(), 16);
644 assert_eq!(pixels, [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]);
645 }
646
647 #[test]
648 fn test_expand_adam7_bits() {
649 let width = 32;
650 let bits_pp = 1;
651 let stride = width / 8;
652 let info =
653 |pass, line, img_width| create_adam7_info_for_tests(pass, line as u32, img_width);
654
655 let expected = |offset: usize, step: usize, count: usize| {
656 (0..count)
657 .map(move |i| step * i + offset)
658 .map(|i| BitPostion {
659 byte: i / 8,
660 bit: (i % 8) as u8,
661 })
662 .collect::<Vec<_>>()
663 };
664
665 for line_no in 0..8 {
666 let start = 8 * line_no * width;
667
668 assert_eq!(
669 expand_adam7_bits(stride, &info(1, line_no, width), bits_pp).collect::<Vec<_>>(),
670 expected(start, 8, 4)
671 );
672
673 let start = start + 4;
674
675 assert_eq!(
676 expand_adam7_bits(stride, &info(2, line_no, width), bits_pp).collect::<Vec<_>>(),
677 expected(start, 8, 4)
678 );
679
680 let start = (8 * line_no + 4) * width;
681
682 assert_eq!(
683 expand_adam7_bits(stride, &info(3, line_no, width), bits_pp).collect::<Vec<_>>(),
684 expected(start, 4, 8)
685 );
686 }
687
688 for line_no in 0..16 {
689 let start = 4 * line_no * width + 2;
690
691 assert_eq!(
692 expand_adam7_bits(stride, &info(4, line_no, width), bits_pp).collect::<Vec<_>>(),
693 expected(start, 4, 8)
694 );
695
696 let start = (4 * line_no + 2) * width;
697
698 assert_eq!(
699 expand_adam7_bits(stride, &info(5, line_no, width), bits_pp).collect::<Vec<_>>(),
700 expected(start, 2, 16)
701 )
702 }
703
704 for line_no in 0..32 {
705 let start = 2 * line_no * width + 1;
706
707 assert_eq!(
708 expand_adam7_bits(stride, &info(6, line_no, width), bits_pp).collect::<Vec<_>>(),
709 expected(start, 2, 16),
710 "line_no: {}",
711 line_no
712 );
713
714 let start = (2 * line_no + 1) * width;
715
716 assert_eq!(
717 expand_adam7_bits(stride, &info(7, line_no, width), bits_pp).collect::<Vec<_>>(),
718 expected(start, 1, 32)
719 );
720 }
721 }
722
723 #[test]
724 fn test_expand_adam7_bits_independent_row_stride() {
725 let pass = 1;
726 let line_no = 1;
727 let width = 32;
728 let info = create_adam7_info_for_tests;
729
730 {
731 let stride = width;
732 assert_eq!(
733 expand_adam7_bytes(stride, &info(pass, line_no, width), 1).collect::<Vec<_>>(),
734 [2048, 2112, 2176, 2240].map(|n| n / 8),
735 );
736 }
737
738 {
739 let stride = 10000;
740 assert_eq!(
741 expand_adam7_bytes(stride, &info(pass, line_no, width), 1).collect::<Vec<_>>(),
742 [640000, 640064, 640128, 640192].map(|n| n / 8),
743 );
744 }
745 }
746
747 #[test]
748 fn test_expand_pass_subbyte() {
749 let mut img = [0u8; 8];
750 let width = 8;
751 let stride = width / 8;
752 let bits_pp = 1;
753 let info = create_adam7_info_for_tests;
754
755 expand_pass(&mut img, stride, &[0b10000000], &info(1, 0, width), bits_pp);
756 assert_eq!(img, [0b10000000u8, 0, 0, 0, 0, 0, 0, 0]);
757
758 expand_pass(&mut img, stride, &[0b10000000], &info(2, 0, width), bits_pp);
759 assert_eq!(img, [0b10001000u8, 0, 0, 0, 0, 0, 0, 0]);
760
761 expand_pass(&mut img, stride, &[0b11000000], &info(3, 0, width), bits_pp);
762 assert_eq!(img, [0b10001000u8, 0, 0, 0, 0b10001000, 0, 0, 0]);
763
764 expand_pass(&mut img, stride, &[0b11000000], &info(4, 0, width), bits_pp);
765 assert_eq!(img, [0b10101010u8, 0, 0, 0, 0b10001000, 0, 0, 0]);
766
767 expand_pass(&mut img, stride, &[0b11000000], &info(4, 1, width), bits_pp);
768 assert_eq!(img, [0b10101010u8, 0, 0, 0, 0b10101010, 0, 0, 0]);
769
770 expand_pass(&mut img, stride, &[0b11110000], &info(5, 0, width), bits_pp);
771 assert_eq!(img, [0b10101010u8, 0, 0b10101010, 0, 0b10101010, 0, 0, 0]);
772
773 expand_pass(&mut img, stride, &[0b11110000], &info(5, 1, width), bits_pp);
774 assert_eq!(
775 img,
776 [0b10101010u8, 0, 0b10101010, 0, 0b10101010, 0, 0b10101010, 0]
777 );
778
779 expand_pass(&mut img, stride, &[0b11110000], &info(6, 0, width), bits_pp);
780 assert_eq!(
781 img,
782 [0b11111111u8, 0, 0b10101010, 0, 0b10101010, 0, 0b10101010, 0]
783 );
784
785 expand_pass(&mut img, stride, &[0b11110000], &info(6, 1, width), bits_pp);
786 assert_eq!(
787 img,
788 [0b11111111u8, 0, 0b11111111, 0, 0b10101010, 0, 0b10101010, 0]
789 );
790
791 expand_pass(&mut img, stride, &[0b11110000], &info(6, 2, width), bits_pp);
792 assert_eq!(
793 img,
794 [0b11111111u8, 0, 0b11111111, 0, 0b11111111, 0, 0b10101010, 0]
795 );
796
797 expand_pass(&mut img, stride, &[0b11110000], &info(6, 3, width), bits_pp);
798 assert_eq!(
799 [0b11111111u8, 0, 0b11111111, 0, 0b11111111, 0, 0b11111111, 0],
800 img
801 );
802
803 expand_pass(&mut img, stride, &[0b11111111], &info(7, 0, width), bits_pp);
804 assert_eq!(
805 [
806 0b11111111u8,
807 0b11111111,
808 0b11111111,
809 0,
810 0b11111111,
811 0,
812 0b11111111,
813 0
814 ],
815 img
816 );
817
818 expand_pass(&mut img, stride, &[0b11111111], &info(7, 1, width), bits_pp);
819 assert_eq!(
820 [
821 0b11111111u8,
822 0b11111111,
823 0b11111111,
824 0b11111111,
825 0b11111111,
826 0,
827 0b11111111,
828 0
829 ],
830 img
831 );
832
833 expand_pass(&mut img, stride, &[0b11111111], &info(7, 2, width), bits_pp);
834 assert_eq!(
835 [
836 0b11111111u8,
837 0b11111111,
838 0b11111111,
839 0b11111111,
840 0b11111111,
841 0b11111111,
842 0b11111111,
843 0
844 ],
845 img
846 );
847
848 expand_pass(&mut img, stride, &[0b11111111], &info(7, 3, width), bits_pp);
849 assert_eq!(
850 [
851 0b11111111u8,
852 0b11111111,
853 0b11111111,
854 0b11111111,
855 0b11111111,
856 0b11111111,
857 0b11111111,
858 0b11111111
859 ],
860 img
861 );
862 }
863
864 #[test]
867 fn test_expand_pass_splat_4bpp() {
868 let width = 8;
869 let bits_pp = 4;
870
871 let mut img = [0u8; 8];
872 let stride = width / 2;
873
874 let passes: &[(&'static [u8], &'static [u8])] = &[
875 (&[0x10], &[0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11]), (&[0x20], &[0x11, 0x11, 0x22, 0x22, 0x11, 0x11, 0x22, 0x22]), (&[0x4a], &[0x11, 0x44, 0x22, 0xaa, 0x11, 0x44, 0x22, 0xaa]), (
881 &[0x6b, 0x6b],
882 &[0x16, 0x4b, 0x26, 0xab, 0x16, 0x4b, 0x26, 0xab],
883 ), (
885 &[0x7c, 0xc7, 0x7c, 0x7c],
886 &[0x16, 0x4b, 0x26, 0xab, 0x7c, 0xc7, 0x7c, 0x7c],
887 ), ];
889
890 let adam7 = Adam7Iterator::new(8, 2);
891 for ((data, expected), adam7_info) in passes.iter().zip(adam7) {
892 expand_pass_splat(&mut img, stride, data, &adam7_info, bits_pp);
893 assert_eq!(img, *expected, "{img:x?} {expected:x?} {adam7_info:?}");
894 }
895 }
896
897 #[test]
900 fn adam7_equivalence() {
901 const WIDTH: u32 = 8;
903 const HEIGHT: u32 = 8;
904
905 let interace_pool: Vec<_> = (0x42u8..).take(32).collect();
906
907 for &bpp in &[1u8, 2, 4, 8, 16, 24, 32][2..] {
908 let bytes_of = |pix: u32| (u32::from(bpp) * pix).next_multiple_of(8) as usize / 8;
909
910 let rowbytes = bytes_of(WIDTH);
911
912 let mut buf_sparse = vec![0x00; rowbytes * HEIGHT as usize];
914 let mut buf_splat = vec![0xaa; rowbytes * HEIGHT as usize];
916
917 for adam7_info in Adam7Iterator::new(WIDTH, HEIGHT) {
919 let adam7_bytes = bytes_of(adam7_info.samples);
920 let interlace_line = &interace_pool[..adam7_bytes];
921
922 expand_pass(&mut buf_sparse, rowbytes, interlace_line, &adam7_info, bpp);
923 expand_pass_splat(&mut buf_splat, rowbytes, interlace_line, &adam7_info, bpp);
924 }
925
926 assert_eq!(
927 buf_sparse, buf_splat,
928 "{buf_sparse:x?} {buf_splat:x?} bpp={bpp}"
929 );
930 }
931 }
932
933 #[test]
934 fn test_expand_pass_splat_1bpp() {
935 let width = 8;
936 let bits_pp = 1;
937
938 let mut img = [0u8; 8];
939 let stride = 1;
940
941 let passes: &[(&'static [u8], &'static [u8])] = &[
945 (&[0x80], &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), (&[0x7f], &[0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0]), (&[0xc0], &[0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff]), (&[0x3f], &[0xc0, 0xc0, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff]), (&[0x3f], &[0xc0, 0xc0, 0xc0, 0xc0, 0xcc, 0xcc, 0xcc, 0xcc]), (&[0xf0], &[0xc0, 0xc0, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xcc]), (&[0xf0], &[0xc0, 0xc0, 0xff, 0xff, 0xcc, 0xcc, 0xff, 0xff]), (&[0x0f], &[0x80, 0x80, 0xff, 0xff, 0xcc, 0xcc, 0xff, 0xff]), (&[0x0f], &[0x80, 0x80, 0xaa, 0xaa, 0xcc, 0xcc, 0xff, 0xff]), (&[0x0f], &[0x80, 0x80, 0xaa, 0xaa, 0x88, 0x88, 0xff, 0xff]), (&[0x0f], &[0x80, 0x80, 0xaa, 0xaa, 0x88, 0x88, 0xaa, 0xaa]), (&[0xff], &[0x80, 0xff, 0xaa, 0xaa, 0x88, 0x88, 0xaa, 0xaa]), (&[0xff], &[0x80, 0xff, 0xaa, 0xff, 0x88, 0x88, 0xaa, 0xaa]), (&[0xff], &[0x80, 0xff, 0xaa, 0xff, 0x88, 0xff, 0xaa, 0xaa]), (&[0xff], &[0x80, 0xff, 0xaa, 0xff, 0x88, 0xff, 0xaa, 0xff]), ];
961
962 let adam7 = Adam7Iterator::new(width, 8);
963 for ((data, expected), adam7_info) in passes.iter().zip(adam7) {
964 expand_pass_splat(&mut img, stride, data, &adam7_info, bits_pp);
965 assert_eq!(img, *expected, "{img:x?} {expected:x?} {adam7_info:?}");
966 }
967 }
968
969 #[cfg(target_pointer_width = "32")]
973 #[test]
974 fn regression_overflow_adam7_bitfill() {
975 fn multibyte_expand_pass_test_helper(width: usize, height: usize, bits_pp: u8) -> Vec<u8> {
976 let bytes_pp = bits_pp / 8;
977 let size = width * height * bytes_pp as usize;
978 let mut img = vec![0u8; size];
979 let img_row_stride = width * bytes_pp as usize;
980
981 for it in Adam7Iterator::new(width as u32, height as u32).into_iter() {
982 if it.pass != 7 {
983 continue;
984 }
985
986 if it.line != (width / 2) as u32 - 1 {
987 continue;
988 }
989
990 let interlace_size = it.width * (bytes_pp as u32);
991 let interlaced_row: Vec<_> = (0..interlace_size).map(|_| 0xff).collect();
994
995 expand_pass(&mut img, img_row_stride, &interlaced_row, &it, bits_pp);
996 }
997
998 img
999 }
1000
1001 let expanded = multibyte_expand_pass_test_helper(1 << 14, 1 << 14, 32);
1002 assert_eq!(*expanded.last().unwrap(), 0xff);
1003 }
1004
1005 #[cfg(test)]
1006 fn create_adam7_info_for_tests(pass: u8, line: u32, img_width: usize) -> Adam7Info {
1007 let width = {
1008 let img_height = 8;
1009 Adam7Iterator::new(img_width as u32, img_height)
1010 .filter(|info| info.pass == pass)
1011 .map(|info| info.samples)
1012 .next()
1013 .unwrap()
1014 };
1015
1016 Adam7Info {
1017 pass,
1018 line,
1019 samples: width,
1020 width,
1021 }
1022 }
1023}