use crate::png::PngImage;
#[derive(Debug, Clone)]
pub struct ScanLines<'a> {
iter: ScanLineRanges,
raw_data: &'a [u8],
has_filter: bool,
}
impl<'a> ScanLines<'a> {
#[must_use]
pub fn new(png: &'a PngImage, has_filter: bool) -> Self {
Self {
iter: ScanLineRanges::new(png, has_filter),
raw_data: &png.data,
has_filter,
}
}
}
impl<'a> Iterator for ScanLines<'a> {
type Item = ScanLine<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let (len, pass, num_pixels) = self.iter.next()?;
debug_assert!(self.raw_data.len() >= len);
debug_assert!(!self.has_filter || len > 1);
if self.raw_data.len() < len {
return None;
}
let (data, rest) = self.raw_data.split_at(len);
self.raw_data = rest;
let (&filter, data) = if self.has_filter {
data.split_first().unwrap()
} else {
(&0, data)
};
Some(ScanLine {
filter,
data,
pass,
num_pixels,
})
}
}
#[derive(Debug, Clone)]
struct ScanLineRanges {
pass: Option<(u8, u32)>,
bits_per_pixel: usize,
width: u32,
height: u32,
left: usize,
has_filter: bool,
}
impl ScanLineRanges {
pub fn new(png: &PngImage, has_filter: bool) -> Self {
Self {
bits_per_pixel: png.ihdr.bpp(),
width: png.ihdr.width,
height: png.ihdr.height,
left: png.data.len(),
pass: if png.ihdr.interlaced {
Some((1, 0))
} else {
None
},
has_filter,
}
}
}
impl Iterator for ScanLineRanges {
type Item = (usize, Option<u8>, usize);
fn next(&mut self) -> Option<Self::Item> {
if self.left == 0 {
return None;
}
let (pixels_per_line, current_pass) = if let Some(ref mut pass) = self.pass {
if self.width < 5 && pass.0 == 2 {
pass.0 = 3;
pass.1 = 4;
}
if self.height < 5 && pass.0 == 3 {
pass.0 = 4;
pass.1 = 0;
}
if self.width < 3 && pass.0 == 4 {
pass.0 = 5;
pass.1 = 2;
}
if self.height < 3 && pass.0 == 5 {
pass.0 = 6;
pass.1 = 0;
}
if self.width == 1 && pass.0 == 6 {
pass.0 = 7;
pass.1 = 1;
}
let (pixels_factor, y_steps) = match pass.0 {
1 | 2 => (8, 8),
3 => (4, 8),
4 => (4, 4),
5 => (2, 4),
6 => (2, 2),
7 => (1, 2),
_ => unreachable!(),
};
let mut pixels_per_line = self.width / pixels_factor;
let gap = self.width % pixels_factor;
match pass.0 {
1 | 3 | 5 if gap > 0 => {
pixels_per_line += 1;
}
2 if gap >= 5 => {
pixels_per_line += 1;
}
4 if gap >= 3 => {
pixels_per_line += 1;
}
6 if gap >= 2 => {
pixels_per_line += 1;
}
_ => (),
}
let current_pass = Some(pass.0);
if pass.1 + y_steps >= self.height {
pass.0 += 1;
pass.1 = match pass.0 {
3 => 4,
5 => 2,
7 => 1,
_ => 0,
};
} else {
pass.1 += y_steps;
}
(pixels_per_line, current_pass)
} else {
(self.width, None)
};
let bits_per_line = pixels_per_line as usize * self.bits_per_pixel;
let mut len = bits_per_line.div_ceil(8);
if self.has_filter {
len += 1;
}
self.left = self.left.checked_sub(len)?;
Some((len, current_pass, pixels_per_line as usize))
}
}
#[derive(Debug, Clone)]
pub struct ScanLine<'a> {
pub filter: u8,
pub data: &'a [u8],
pub pass: Option<u8>,
pub num_pixels: usize,
}