1 //! Utility functions
2 use std::iter::{repeat, StepBy};
3 use std::ops::Range;
4 
5 #[inline(always)]
unpack_bits<F>(buf: &mut [u8], channels: usize, bit_depth: u8, func: F) where F: Fn(u8, &mut[u8])6 pub fn unpack_bits<F>(buf: &mut [u8], channels: usize, bit_depth: u8, func: F)
7 where F: Fn(u8, &mut[u8]) {
8     // Return early if empty. This enables to subtract `channels` later without overflow.
9     if buf.len() < channels {
10         return;
11     }
12 
13     let bits = buf.len()/channels*bit_depth as usize;
14     let extra_bits = bits % 8;
15     let entries = bits / 8 + match extra_bits {
16         0 => 0,
17         _ => 1
18     };
19     let skip = match extra_bits {
20         0 => 0,
21         n => (8-n) / bit_depth as usize
22     };
23     let mask = ((1u16 << bit_depth) - 1) as u8;
24     let i =
25         (0..entries)
26         .rev() // reverse iterator
27         .flat_map(|idx|
28             // this has to be reversed too
29             (0..8).step_by(bit_depth.into())
30             .zip(repeat(idx))
31         )
32         .skip(skip);
33     let j = (0..=buf.len() - channels).rev().step_by(channels);
34     for ((shift, i), j) in i.zip(j) {
35         let pixel = (buf[i] & (mask << shift)) >> shift;
36         func(pixel, &mut buf[j..(j + channels)])
37     }
38 }
39 
expand_trns_line(buf: &mut[u8], trns: &[u8], channels: usize)40 pub fn expand_trns_line(buf: &mut[u8], trns: &[u8], channels: usize) {
41     // Return early if empty. This enables to subtract `channels` later without overflow.
42     if buf.len() < (channels+1) {
43         return;
44     }
45 
46     let i = (0..=buf.len() / (channels+1) * channels - channels).rev().step_by(channels);
47     let j = (0..=buf.len() - (channels+1)).rev().step_by(channels+1);
48     for (i, j) in i.zip(j) {
49         let i_pixel = i;
50         let j_chunk = j;
51         if &buf[i_pixel..i_pixel+channels] == trns {
52             buf[j_chunk+channels] = 0
53         } else {
54             buf[j_chunk+channels] = 0xFF
55         }
56         for k in (0..channels).rev() {
57             buf[j_chunk+k] = buf[i_pixel+k];
58         }
59     }
60 }
61 
expand_trns_line16(buf: &mut[u8], trns: &[u8], channels: usize)62 pub fn expand_trns_line16(buf: &mut[u8], trns: &[u8], channels: usize) {
63     let c2 = 2 * channels;
64     // Return early if empty. This enables to subtract `channels` later without overflow.
65     if buf.len() < (c2+2) {
66         return;
67     }
68 
69     let i = (0..=buf.len() / (c2+2) * c2 - c2).rev().step_by(c2);
70     let j = (0..=buf.len() - (c2+2)).rev().step_by(c2+2);
71     for (i, j) in i.zip(j) {
72         let i_pixel = i;
73         let j_chunk = j;
74         if &buf[i_pixel..i_pixel+c2] == trns {
75             buf[j_chunk+c2] = 0;
76             buf[j_chunk+c2 + 1] = 0
77         } else {
78             buf[j_chunk+c2] = 0xFF;
79             buf[j_chunk+c2 + 1] = 0xFF
80         }
81         for k in (0..c2).rev() {
82             buf[j_chunk+k] = buf[i_pixel+k];
83         }
84     }
85 }
86 
87 
88 /// This iterator iterates over the different passes of an image Adam7 encoded
89 /// PNG image
90 /// The pattern is:
91 ///     16462646
92 ///     77777777
93 ///     56565656
94 ///     77777777
95 ///     36463646
96 ///     77777777
97 ///     56565656
98 ///     77777777
99 ///
100 #[derive(Clone)]
101 pub struct Adam7Iterator {
102     line: u32,
103     lines: u32,
104     line_width: u32,
105     current_pass: u8,
106     width: u32,
107     height: u32,
108 }
109 
110 impl Adam7Iterator {
new(width: u32, height: u32) -> Adam7Iterator111     pub fn new(width: u32, height: u32) -> Adam7Iterator {
112         let mut this = Adam7Iterator {
113             line: 0,
114             lines: 0,
115             line_width: 0,
116             current_pass: 1,
117             width,
118             height,
119         };
120         this.init_pass();
121         this
122     }
123 
124     /// Calculates the bounds of the current pass
init_pass(&mut self)125     fn init_pass(&mut self) {
126         let w = f64::from(self.width);
127         let h = f64::from(self.height);
128         let (line_width, lines) = match self.current_pass {
129             1 => (w/8.0, h/8.0),
130             2 => ((w-4.0)/8.0, h/8.0),
131             3 => (w/4.0, (h-4.0)/8.0),
132             4 => ((w-2.0)/4.0, h/4.0),
133             5 => (w/2.0, (h-2.0)/4.0),
134             6 => ((w-1.0)/2.0, h/2.0),
135             7 => (w, (h-1.0)/2.0),
136             _ => unreachable!()
137         };
138         self.line_width = line_width.ceil() as u32;
139         self.lines = lines.ceil() as u32;
140         self.line = 0;
141     }
142 
143     /// The current pass#.
current_pass(&self) -> u8144     pub fn current_pass(&self) -> u8 {
145         self.current_pass
146     }
147 }
148 
149 /// Iterates over the (passes, lines, widths)
150 impl Iterator for Adam7Iterator {
151     type Item = (u8, u32, u32);
next(&mut self) -> Option<(u8, u32, u32)>152     fn next(&mut self) -> Option<(u8, u32, u32)> {
153         if self.line < self.lines && self.line_width > 0 {
154             let this_line = self.line;
155             self.line += 1;
156             Some((self.current_pass, this_line, self.line_width))
157         } else if self.current_pass < 7 {
158             self.current_pass += 1;
159             self.init_pass();
160             self.next()
161         } else {
162             None
163         }
164     }
165 }
166 
subbyte_pixels<'a>(scanline: &'a [u8], bits_pp: usize) -> impl Iterator<Item=u8> + 'a167 fn subbyte_pixels<'a>(scanline: &'a [u8], bits_pp: usize) -> impl Iterator<Item=u8> + 'a {
168     (0..scanline.len() * 8).step_by(bits_pp).map(move |bit_idx| {
169         let byte_idx = bit_idx / 8;
170 
171         // sub-byte samples start in the high-order bits
172         let rem = 8 - bit_idx % 8 - bits_pp;
173 
174         match bits_pp {
175             // evenly divides bytes
176             1 => (scanline[byte_idx] >> rem) & 1,
177             2 => (scanline[byte_idx] >> rem) & 3,
178             4 => (scanline[byte_idx] >> rem) & 15,
179             _ => unreachable!(),
180         }
181     })
182 }
183 
184 /// Given pass, image width, and line number, produce an iterator of bit positions of pixels to copy
185 /// from the input scanline to the image buffer.
expand_adam7_bits(pass: u8, width: usize, line_no: usize, bits_pp: usize) -> StepBy<Range<usize>>186 fn expand_adam7_bits(pass: u8, width: usize, line_no: usize, bits_pp: usize) -> StepBy<Range<usize>> {
187     let (line_mul, line_off, samp_mul, samp_off) = match pass {
188         1 => (8, 0, 8, 0),
189         2 => (8, 0, 8, 4),
190         3 => (8, 4, 4, 0),
191         4 => (4, 0, 4, 2),
192         5 => (4, 2, 2, 0),
193         6 => (2, 0, 2, 1),
194         7 => (2, 1, 1, 0),
195         _ => panic!("Adam7 pass out of range: {}", pass)
196     };
197 
198     // the equivalent line number in progressive scan
199     let prog_line = line_mul * line_no + line_off;
200     // line width is rounded up to the next byte
201     let line_width = (width * bits_pp + 7) & !7;
202     let line_start = prog_line * line_width;
203     let start = line_start + (samp_off * bits_pp);
204     let stop = line_start + (width * bits_pp);
205 
206     (start .. stop).step_by(bits_pp * samp_mul)
207 }
208 
209 /// Expands an Adam 7 pass
expand_pass( img: &mut [u8], width: u32, scanline: &[u8], pass: u8, line_no: u32, bits_pp: u8)210 pub fn expand_pass(
211     img: &mut [u8], width: u32, scanline: &[u8],
212     pass: u8, line_no: u32, bits_pp: u8) {
213 
214     let width = width as usize;
215     let line_no = line_no as usize;
216     let bits_pp = bits_pp as usize;
217 
218 	// pass is out of range but don't blow up
219     if pass == 0 || pass > 7 { return; }
220 
221     let bit_indices = expand_adam7_bits(pass, width, line_no, bits_pp);
222 
223     if bits_pp < 8 {
224         for (pos, px) in bit_indices.zip(subbyte_pixels(scanline, bits_pp)) {
225             let rem = 8 - pos % 8 - bits_pp;
226             img[pos / 8] |= px << rem as u8;
227         }
228     } else {
229 		let bytes_pp = bits_pp / 8;
230 
231 		for (bitpos, px) in bit_indices.zip(scanline.chunks(bytes_pp)) {
232 			for (offset, val) in px.iter().enumerate() {
233 				img[bitpos / 8 + offset] = *val;
234 			}
235 		}
236 	}
237 }
238 
239 #[test]
test_adam7()240 fn test_adam7() {
241     /*
242         1646
243         7777
244         5656
245         7777
246     */
247     let it = Adam7Iterator::new(4, 4);
248     let passes: Vec<_> = it.collect();
249     assert_eq!(&*passes, &[(1, 0, 1), (4, 0, 1), (5, 0, 2), (6, 0, 2), (6, 1, 2), (7, 0, 4), (7, 1, 4)]);
250 }
251 
252 #[test]
test_subbyte_pixels()253 fn test_subbyte_pixels() {
254     let scanline = &[0b10101010, 0b10101010];
255 
256 
257     let pixels = subbyte_pixels(scanline, 1).collect::<Vec<_>>();
258     assert_eq!(pixels.len(), 16);
259     assert_eq!(pixels, [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]);
260 }
261 
262 #[test]
test_expand_adam7_bits()263 fn test_expand_adam7_bits() {
264     let width = 32;
265     let bits_pp = 1;
266 
267     let expected = |offset: usize, step: usize, count: usize| (0 .. count).map(move |i| step * i + offset).collect::<Vec<_>>();
268 
269     for line_no in 0..8 {
270         let start = 8 * line_no * width;
271 
272         assert_eq!(
273             expand_adam7_bits(1, width, line_no, bits_pp).collect::<Vec<_>>(),
274             expected(start, 8, 4)
275         );
276 
277         let start = start + 4;
278 
279         assert_eq!(
280             expand_adam7_bits(2, width, line_no, bits_pp).collect::<Vec<_>>(),
281             expected(start, 8, 4)
282         );
283 
284         let start = (8 * line_no + 4) as usize * width as usize;
285 
286         assert_eq!(
287             expand_adam7_bits(3, width, line_no, bits_pp).collect::<Vec<_>>(),
288             expected(start, 4, 8)
289         );
290     }
291 
292     for line_no in 0 .. 16 {
293         let start = 4 * line_no * width + 2;
294 
295         assert_eq!(
296             expand_adam7_bits(4, width, line_no, bits_pp).collect::<Vec<_>>(),
297             expected(start, 4, 8)
298         );
299 
300         let start = (4 * line_no + 2) * width;
301 
302         assert_eq!(
303             expand_adam7_bits(5, width, line_no, bits_pp).collect::<Vec<_>>(),
304             expected(start, 2, 16)
305         )
306     }
307 
308     for line_no in 0 .. 32 {
309         let start = 2 * line_no * width + 1;
310 
311         assert_eq!(
312             expand_adam7_bits(6, width, line_no, bits_pp).collect::<Vec<_>>(),
313             expected(start, 2, 16),
314             "line_no: {}", line_no
315         );
316 
317         let start = (2 * line_no + 1) * width;
318 
319         assert_eq!(
320             expand_adam7_bits(7, width, line_no, bits_pp).collect::<Vec<_>>(),
321             expected(start, 1, 32)
322         );
323     }
324 }
325 
326 #[test]
test_expand_pass_subbyte()327 fn test_expand_pass_subbyte() {
328     let mut img = [0u8; 8];
329     let width = 8;
330     let bits_pp = 1;
331 
332     expand_pass(&mut img, width, &[0b10000000], 1, 0, bits_pp);
333     assert_eq!(img, [0b10000000u8, 0, 0, 0, 0, 0, 0, 0]);
334 
335     expand_pass(&mut img, width, &[0b10000000], 2, 0, bits_pp);
336     assert_eq!(img, [0b10001000u8, 0, 0, 0, 0, 0, 0, 0]);
337 
338     expand_pass(&mut img, width, &[0b11000000], 3, 0, bits_pp);
339     assert_eq!(img, [0b10001000u8, 0, 0, 0, 0b10001000, 0, 0, 0]);
340 
341     expand_pass(&mut img, width, &[0b11000000], 4, 0, bits_pp);
342     assert_eq!(img, [0b10101010u8, 0, 0, 0, 0b10001000, 0, 0, 0]);
343 
344     expand_pass(&mut img, width, &[0b11000000], 4, 1, bits_pp);
345     assert_eq!(img, [0b10101010u8, 0, 0, 0, 0b10101010, 0, 0, 0]);
346 
347     expand_pass(&mut img, width, &[0b11110000], 5, 0, bits_pp);
348     assert_eq!(img, [0b10101010u8, 0, 0b10101010, 0, 0b10101010, 0, 0, 0]);
349 
350     expand_pass(&mut img, width, &[0b11110000], 5, 1, bits_pp);
351     assert_eq!(img, [0b10101010u8, 0, 0b10101010, 0, 0b10101010, 0, 0b10101010, 0]);
352 
353     expand_pass(&mut img, width, &[0b11110000], 6, 0, bits_pp);
354     assert_eq!(img, [0b11111111u8, 0, 0b10101010, 0, 0b10101010, 0, 0b10101010, 0]);
355 
356     expand_pass(&mut img, width, &[0b11110000], 6, 1, bits_pp);
357     assert_eq!(img, [0b11111111u8, 0, 0b11111111, 0, 0b10101010, 0, 0b10101010, 0]);
358 
359     expand_pass(&mut img, width, &[0b11110000], 6, 2, bits_pp);
360     assert_eq!(img, [0b11111111u8, 0, 0b11111111, 0, 0b11111111, 0, 0b10101010, 0]);
361 
362     expand_pass(&mut img, width, &[0b11110000], 6, 3, bits_pp);
363     assert_eq!([0b11111111u8, 0, 0b11111111, 0, 0b11111111, 0, 0b11111111, 0], img);
364 
365     expand_pass(&mut img, width, &[0b11111111], 7, 0, bits_pp);
366     assert_eq!([0b11111111u8, 0b11111111, 0b11111111, 0, 0b11111111, 0, 0b11111111, 0], img);
367 
368     expand_pass(&mut img, width, &[0b11111111], 7, 1, bits_pp);
369     assert_eq!([0b11111111u8, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0, 0b11111111, 0], img);
370 
371     expand_pass(&mut img, width, &[0b11111111], 7, 2, bits_pp);
372     assert_eq!([0b11111111u8, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0], img);
373 
374     expand_pass(&mut img, width, &[0b11111111], 7, 3, bits_pp);
375     assert_eq!([0b11111111u8, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111], img);
376 }
377