png/
utils.rs

1//! Utility functions
2use std::iter::repeat;
3use num_iter::range_step;
4
5#[inline(always)]
6pub fn unpack_bits<F>(buf: &mut [u8], channels: usize, bit_depth: u8, func: F)
7where F: Fn(u8, &mut[u8]) {
8    let bits = buf.len()/channels*bit_depth as usize;
9    let extra_bits = bits % 8;
10    let entries = bits / 8 + match extra_bits {
11        0 => 0,
12        _ => 1
13    };
14    let skip = match extra_bits {
15        0 => 0,
16        n => (8-n) / bit_depth as usize
17    };
18    let mask = ((1u16 << bit_depth) - 1) as u8;
19    let i =
20        (0..entries)
21        .rev() // reverse iterator
22        .flat_map(|idx|
23            // this has to be reversed too
24            range_step(0, 8, bit_depth)
25            .zip(repeat(idx))
26        )
27        .skip(skip);
28    let channels = channels as isize;
29    let j = range_step(buf.len() as isize - channels, -channels, -channels);
30    //let j = range_step(0, buf.len(), channels).rev(); // ideal solution;
31    for ((shift, i), j) in i.zip(j) {
32        let pixel = (buf[i] & (mask << shift)) >> shift;
33        func(pixel, &mut buf[j as usize..(j + channels) as usize])
34    }
35}
36
37pub fn expand_trns_line(buf: &mut[u8], trns: &[u8], channels: usize) {
38    let channels = channels as isize;
39    let i = range_step(buf.len() as isize / (channels+1) * channels - channels, -channels, -channels);
40    let j = range_step(buf.len() as isize - (channels+1), -(channels+1), -(channels+1));
41    let channels = channels as usize;
42    for (i, j) in i.zip(j) {
43        let i_pixel = i as usize;
44        let j_chunk = j as usize;
45        if &buf[i_pixel..i_pixel+channels] == trns {
46            buf[j_chunk+channels] = 0
47        } else {
48            buf[j_chunk+channels] = 0xFF
49        }
50        for k in (0..channels).rev() {
51            buf[j_chunk+k] = buf[i_pixel+k];
52        }
53    }
54}
55
56pub fn expand_trns_line16(buf: &mut[u8], trns: &[u8], channels: usize) {
57    let channels = channels as isize;
58    let c2 = 2 * channels;
59    let i = range_step(buf.len() as isize / (c2+2) * c2 - c2, -c2, -c2);
60    let j = range_step(buf.len() as isize - (c2+2), -(c2+2), -(c2+2));
61    let c2 = c2 as usize;
62    for (i, j) in i.zip(j) {
63        let i_pixel = i as usize;
64        let j_chunk = j as usize;
65        if &buf[i_pixel..i_pixel+c2] == trns {
66            buf[j_chunk+c2] = 0;
67            buf[j_chunk+c2 + 1] = 0
68        } else {
69            buf[j_chunk+c2] = 0xFF;
70            buf[j_chunk+c2 + 1] = 0xFF
71        }
72        for k in (0..c2).rev() {
73            buf[j_chunk+k] = buf[i_pixel+k];
74        }
75    }
76}
77
78
79/// This iterator iterates over the different passes of an image Adam7 encoded
80/// PNG image
81/// The pattern is:
82///     16462646
83///     77777777
84///     56565656
85///     77777777
86///     36463646
87///     77777777
88///     56565656
89///     77777777
90///
91#[derive(Clone)]
92pub struct Adam7Iterator {
93    line: u32,
94    lines: u32,
95    line_width: u32,
96    current_pass: u8,
97    width: u32,
98    height: u32,
99}
100
101impl Adam7Iterator {
102    pub fn new(width: u32, height: u32) -> Adam7Iterator {
103        let mut this = Adam7Iterator {
104            line: 0,
105            lines: 0,
106            line_width: 0,
107            current_pass: 1,
108            width: width,
109            height: height
110        };
111        this.init_pass();
112        this
113    }
114
115    /// Calculates the bounds of the current pass
116    fn init_pass(&mut self) {
117        let w = self.width as f64;
118        let h = self.height as f64;
119        let (line_width, lines) = match self.current_pass {
120            1 => (w/8.0, h/8.0),
121            2 => ((w-4.0)/8.0, h/8.0),
122            3 => (w/4.0, (h-4.0)/8.0),
123            4 => ((w-2.0)/4.0, h/4.0),
124            5 => (w/2.0, (h-2.0)/4.0),
125            6 => ((w-1.0)/2.0, h/2.0),
126            7 => (w, (h-1.0)/2.0),
127            _ => unreachable!()
128        };
129        self.line_width = line_width.ceil() as u32;
130        self.lines = lines.ceil() as u32;
131        self.line = 0;
132    }
133    
134    /// The current pass#.
135    pub fn current_pass(&self) -> u8 {
136        self.current_pass
137    }
138}
139
140/// Iterates over the (passes, lines, widths)
141impl Iterator for Adam7Iterator {
142    type Item = (u8, u32, u32);
143    fn next(&mut self) -> Option<(u8, u32, u32)> {
144        if self.line < self.lines && self.line_width > 0 {
145            let this_line = self.line;
146            self.line += 1;
147            Some((self.current_pass, this_line, self.line_width))
148        } else if self.current_pass < 7 {
149            self.current_pass += 1;
150            self.init_pass();
151            self.next()
152        } else {
153            None
154        }
155    }
156}
157
158macro_rules! expand_pass(
159    ($img:expr, $scanline:expr, $j:ident, $pos:expr, $bytes_pp:expr) => {
160        for ($j, pixel) in $scanline.chunks($bytes_pp).enumerate() {
161            for (offset, val) in pixel.iter().enumerate() {
162                $img[$pos + offset] = *val
163            }
164        }
165    }
166);
167
168/// Expands an Adam 7 pass
169pub fn expand_pass(
170    img: &mut [u8], width: u32, scanline: &[u8],
171    pass: u8, line_no: u32, bytes_pp: u8) {
172    let line_no = line_no as usize;
173    let width = width as usize;
174    let bytes_pp = bytes_pp as usize;
175    match pass {
176        1 => expand_pass!(img, scanline, j,  8*line_no    * width + bytes_pp * j*8     , bytes_pp),
177        2 => expand_pass!(img, scanline, j,  8*line_no    * width + bytes_pp *(j*8 + 4), bytes_pp),
178        3 => expand_pass!(img, scanline, j, (8*line_no+4) * width + bytes_pp * j*4     , bytes_pp),
179        4 => expand_pass!(img, scanline, j,  4*line_no    * width + bytes_pp *(j*4 + 2), bytes_pp),
180        5 => expand_pass!(img, scanline, j, (4*line_no+2) * width + bytes_pp * j*2     , bytes_pp),
181        6 => expand_pass!(img, scanline, j,  2*line_no    * width + bytes_pp *(j*2+1)  , bytes_pp),
182        7 => expand_pass!(img, scanline, j, (2*line_no+1) * width + bytes_pp * j       , bytes_pp),
183        _ => {}
184    }
185}
186
187#[test]
188fn test_adam7() {
189    /*
190        1646
191        7777
192        5656
193        7777
194    */
195    let it = Adam7Iterator::new(4, 4);
196    let passes: Vec<_> = it.collect();
197    assert_eq!(&*passes, &[(1, 0, 1), (4, 0, 1), (5, 0, 2), (6, 0, 2), (6, 1, 2), (7, 0, 4), (7, 1, 4)]);
198}