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