blob: cbad3afafc21e20ca2d45d1b04e08cdba6300d15 [file] [log] [blame]
//! Utility functions
use std::iter::repeat;
use num_iter::range_step;
#[inline(always)]
pub fn unpack_bits<F>(buf: &mut [u8], channels: usize, bit_depth: u8, func: F)
where F: Fn(u8, &mut[u8]) {
let bits = buf.len()/channels*bit_depth as usize;
let extra_bits = bits % 8;
let entries = bits / 8 + match extra_bits {
0 => 0,
_ => 1
};
let skip = match extra_bits {
0 => 0,
n => (8-n) / bit_depth as usize
};
let mask = ((1u16 << bit_depth) - 1) as u8;
let i =
(0..entries)
.rev() // reverse iterator
.flat_map(|idx|
// this has to be reversed too
range_step(0, 8, bit_depth)
.zip(repeat(idx))
)
.skip(skip);
let channels = channels as isize;
let j = range_step(buf.len() as isize - channels, -channels, -channels);
//let j = range_step(0, buf.len(), channels).rev(); // ideal solution;
for ((shift, i), j) in i.zip(j) {
let pixel = (buf[i] & (mask << shift)) >> shift;
func(pixel, &mut buf[j as usize..(j + channels) as usize])
}
}
pub fn expand_trns_line(buf: &mut[u8], trns: &[u8], channels: usize) {
let channels = channels as isize;
let i = range_step(buf.len() as isize / (channels+1) * channels - channels, -channels, -channels);
let j = range_step(buf.len() as isize - (channels+1), -(channels+1), -(channels+1));
let channels = channels as usize;
for (i, j) in i.zip(j) {
let i_pixel = i as usize;
let j_chunk = j as usize;
if &buf[i_pixel..i_pixel+channels] == trns {
buf[j_chunk+channels] = 0
} else {
buf[j_chunk+channels] = 0xFF
}
for k in (0..channels).rev() {
buf[j_chunk+k] = buf[i_pixel+k];
}
}
}
pub fn expand_trns_line16(buf: &mut[u8], trns: &[u8], channels: usize) {
let channels = channels as isize;
let c2 = 2 * channels;
let i = range_step(buf.len() as isize / (c2+2) * c2 - c2, -c2, -c2);
let j = range_step(buf.len() as isize - (c2+2), -(c2+2), -(c2+2));
let c2 = c2 as usize;
for (i, j) in i.zip(j) {
let i_pixel = i as usize;
let j_chunk = j as usize;
if &buf[i_pixel..i_pixel+c2] == trns {
buf[j_chunk+c2] = 0;
buf[j_chunk+c2 + 1] = 0
} else {
buf[j_chunk+c2] = 0xFF;
buf[j_chunk+c2 + 1] = 0xFF
}
for k in (0..c2).rev() {
buf[j_chunk+k] = buf[i_pixel+k];
}
}
}
/// This iterator iterates over the different passes of an image Adam7 encoded
/// PNG image
/// The pattern is:
/// 16462646
/// 77777777
/// 56565656
/// 77777777
/// 36463646
/// 77777777
/// 56565656
/// 77777777
///
#[derive(Clone)]
pub struct Adam7Iterator {
line: u32,
lines: u32,
line_width: u32,
current_pass: u8,
width: u32,
height: u32,
}
impl Adam7Iterator {
pub fn new(width: u32, height: u32) -> Adam7Iterator {
let mut this = Adam7Iterator {
line: 0,
lines: 0,
line_width: 0,
current_pass: 1,
width: width,
height: height
};
this.init_pass();
this
}
/// Calculates the bounds of the current pass
fn init_pass(&mut self) {
let w = self.width as f64;
let h = self.height as f64;
let (line_width, lines) = match self.current_pass {
1 => (w/8.0, h/8.0),
2 => ((w-4.0)/8.0, h/8.0),
3 => (w/4.0, (h-4.0)/8.0),
4 => ((w-2.0)/4.0, h/4.0),
5 => (w/2.0, (h-2.0)/4.0),
6 => ((w-1.0)/2.0, h/2.0),
7 => (w, (h-1.0)/2.0),
_ => unreachable!()
};
self.line_width = line_width.ceil() as u32;
self.lines = lines.ceil() as u32;
self.line = 0;
}
/// The current pass#.
pub fn current_pass(&self) -> u8 {
self.current_pass
}
}
/// Iterates over the (passes, lines, widths)
impl Iterator for Adam7Iterator {
type Item = (u8, u32, u32);
fn next(&mut self) -> Option<(u8, u32, u32)> {
if self.line < self.lines && self.line_width > 0 {
let this_line = self.line;
self.line += 1;
Some((self.current_pass, this_line, self.line_width))
} else if self.current_pass < 7 {
self.current_pass += 1;
self.init_pass();
self.next()
} else {
None
}
}
}
macro_rules! expand_pass(
($img:expr, $scanline:expr, $j:ident, $pos:expr, $bytes_pp:expr) => {
for ($j, pixel) in $scanline.chunks($bytes_pp).enumerate() {
for (offset, val) in pixel.iter().enumerate() {
$img[$pos + offset] = *val
}
}
}
);
/// Expands an Adam 7 pass
pub fn expand_pass(
img: &mut [u8], width: u32, scanline: &[u8],
pass: u8, line_no: u32, bytes_pp: u8) {
let line_no = line_no as usize;
let width = width as usize;
let bytes_pp = bytes_pp as usize;
match pass {
1 => expand_pass!(img, scanline, j, 8*line_no * width + bytes_pp * j*8 , bytes_pp),
2 => expand_pass!(img, scanline, j, 8*line_no * width + bytes_pp *(j*8 + 4), bytes_pp),
3 => expand_pass!(img, scanline, j, (8*line_no+4) * width + bytes_pp * j*4 , bytes_pp),
4 => expand_pass!(img, scanline, j, 4*line_no * width + bytes_pp *(j*4 + 2), bytes_pp),
5 => expand_pass!(img, scanline, j, (4*line_no+2) * width + bytes_pp * j*2 , bytes_pp),
6 => expand_pass!(img, scanline, j, 2*line_no * width + bytes_pp *(j*2+1) , bytes_pp),
7 => expand_pass!(img, scanline, j, (2*line_no+1) * width + bytes_pp * j , bytes_pp),
_ => {}
}
}
#[test]
fn test_adam7() {
/*
1646
7777
5656
7777
*/
let it = Adam7Iterator::new(4, 4);
let passes: Vec<_> = it.collect();
assert_eq!(&*passes, &[(1, 0, 1), (4, 0, 1), (5, 0, 2), (6, 0, 2), (6, 1, 2), (7, 0, 4), (7, 1, 4)]);
}