Simplified iterator
This commit is contained in:
parent
e05c2f6268
commit
ba8c0b4d2f
2 changed files with 76 additions and 103 deletions
|
|
@ -224,12 +224,7 @@ impl PngData {
|
||||||
/// Return an iterator over the scanlines of the image
|
/// Return an iterator over the scanlines of the image
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn scan_lines(&self) -> ScanLines {
|
pub fn scan_lines(&self) -> ScanLines {
|
||||||
ScanLines {
|
ScanLines::new(self)
|
||||||
png: self,
|
|
||||||
start: 0,
|
|
||||||
end: 0,
|
|
||||||
pass: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reverse all filters applied on the image, returning an unfiltered IDAT bytestream
|
/// Reverse all filters applied on the image, returning an unfiltered IDAT bytestream
|
||||||
|
|
|
||||||
|
|
@ -4,125 +4,103 @@ use super::PngData;
|
||||||
/// An iterator over the scan lines of a PNG image
|
/// An iterator over the scan lines of a PNG image
|
||||||
pub struct ScanLines<'a> {
|
pub struct ScanLines<'a> {
|
||||||
/// A reference to the PNG image being iterated upon
|
/// A reference to the PNG image being iterated upon
|
||||||
pub png: &'a PngData,
|
start: usize,
|
||||||
pub start: usize,
|
|
||||||
pub end: usize,
|
|
||||||
/// Current pass number, and 0-indexed row within the pass
|
/// Current pass number, and 0-indexed row within the pass
|
||||||
pub pass: Option<(u8, u32)>,
|
pass: Option<(u8, u32)>,
|
||||||
|
bits_per_pixel: u8,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
raw_data: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ScanLines<'a> {
|
||||||
|
pub fn new(png: &'a PngData) -> Self {
|
||||||
|
Self {
|
||||||
|
bits_per_pixel: png.ihdr_data.bit_depth.as_u8() * png.channels_per_pixel(),
|
||||||
|
width: png.ihdr_data.width,
|
||||||
|
height: png.ihdr_data.height,
|
||||||
|
raw_data: &png.raw_data,
|
||||||
|
start: 0,
|
||||||
|
pass: if png.ihdr_data.interlaced == 1 {Some((1, 0))} else {None},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for ScanLines<'a> {
|
impl<'a> Iterator for ScanLines<'a> {
|
||||||
type Item = ScanLine<'a>;
|
type Item = ScanLine<'a>;
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.end == self.png.raw_data.len() {
|
if self.start >= self.raw_data.len() {
|
||||||
None
|
None
|
||||||
} else if self.png.ihdr_data.interlaced == 1 {
|
} else if let Some(ref mut pass) = self.pass {
|
||||||
// Scanlines for interlaced PNG files
|
// Scanlines for interlaced PNG files
|
||||||
if self.pass.is_none() {
|
|
||||||
self.pass = Some((1, 0));
|
|
||||||
}
|
|
||||||
// Handle edge cases for images smaller than 5 pixels in either direction
|
// Handle edge cases for images smaller than 5 pixels in either direction
|
||||||
if self.png.ihdr_data.width < 5 && self.pass.unwrap().0 == 2 {
|
if self.width < 5 && pass.0 == 2 {
|
||||||
if let Some(pass) = self.pass.as_mut() {
|
pass.0 = 3;
|
||||||
pass.0 = 3;
|
pass.1 = 4;
|
||||||
pass.1 = 4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Intentionally keep these separate so that they can be applied one after another
|
// Intentionally keep these separate so that they can be applied one after another
|
||||||
if self.png.ihdr_data.height < 5 && self.pass.unwrap().0 == 3 {
|
if self.height < 5 && pass.0 == 3 {
|
||||||
if let Some(pass) = self.pass.as_mut() {
|
pass.0 = 4;
|
||||||
pass.0 = 4;
|
pass.1 = 0;
|
||||||
pass.1 = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let bits_per_pixel = u32::from(self.png.ihdr_data.bit_depth.as_u8())
|
let (pixels_factor, y_steps) = match pass {
|
||||||
* u32::from(self.png.channels_per_pixel());
|
(1, _) | (2, _) => (8, 8),
|
||||||
let y_steps;
|
(3, _) => (4, 8),
|
||||||
let pixels_factor;
|
(4, _) => (4, 4),
|
||||||
match self.pass {
|
(5, _) => (2, 4),
|
||||||
Some((1, _)) | Some((2, _)) => {
|
(6, _) => (2, 2),
|
||||||
pixels_factor = 8;
|
(7, _) => (1, 2),
|
||||||
y_steps = 8;
|
|
||||||
}
|
|
||||||
Some((3, _)) => {
|
|
||||||
pixels_factor = 4;
|
|
||||||
y_steps = 8;
|
|
||||||
}
|
|
||||||
Some((4, _)) => {
|
|
||||||
pixels_factor = 4;
|
|
||||||
y_steps = 4;
|
|
||||||
}
|
|
||||||
Some((5, _)) => {
|
|
||||||
pixels_factor = 2;
|
|
||||||
y_steps = 4;
|
|
||||||
}
|
|
||||||
Some((6, _)) => {
|
|
||||||
pixels_factor = 2;
|
|
||||||
y_steps = 2;
|
|
||||||
}
|
|
||||||
Some((7, _)) => {
|
|
||||||
pixels_factor = 1;
|
|
||||||
y_steps = 2;
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
|
||||||
let mut pixels_per_line = self.png.ihdr_data.width / pixels_factor as u32;
|
|
||||||
// Determine whether to add pixels if there is a final, incomplete 8x8 block
|
|
||||||
let gap = self.png.ihdr_data.width % pixels_factor;
|
|
||||||
if gap > 0 {
|
|
||||||
match self.pass.unwrap().0 {
|
|
||||||
1 | 3 | 5 => {
|
|
||||||
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 = if let Some(pass) = self.pass {
|
|
||||||
Some(pass.0)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
};
|
||||||
let bytes_per_line = ((pixels_per_line * bits_per_pixel + 7) / 8) as usize;
|
let mut pixels_per_line = self.width / pixels_factor as u32;
|
||||||
self.start = self.end;
|
// Determine whether to add pixels if there is a final, incomplete 8x8 block
|
||||||
self.end = self.start + bytes_per_line + 1;
|
let gap = self.width % pixels_factor;
|
||||||
if let Some(pass) = self.pass.as_mut() {
|
match pass.0 {
|
||||||
if pass.1 + y_steps >= self.png.ihdr_data.height {
|
1 | 3 | 5 if gap > 0 => {
|
||||||
pass.0 += 1;
|
pixels_per_line += 1;
|
||||||
pass.1 = match pass.0 {
|
|
||||||
3 => 4,
|
|
||||||
5 => 2,
|
|
||||||
7 => 1,
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
pass.1 += y_steps;
|
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
let bytes_per_line = ((pixels_per_line * self.bits_per_pixel as u32 + 7) / 8) as usize;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
let start = self.start;
|
||||||
|
let len = bytes_per_line + 1;
|
||||||
|
self.start += len;
|
||||||
Some(ScanLine {
|
Some(ScanLine {
|
||||||
filter: self.png.raw_data[self.start],
|
filter: self.raw_data[start],
|
||||||
data: &self.png.raw_data[(self.start + 1)..self.end],
|
data: &self.raw_data[(start + 1)..(start + len)],
|
||||||
pass: current_pass,
|
pass: current_pass,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Standard, non-interlaced PNG scanlines
|
// Standard, non-interlaced PNG scanlines
|
||||||
let bits_per_line = self.png.ihdr_data.width as usize
|
let bits_per_line = self.width * self.bits_per_pixel as u32;
|
||||||
* self.png.ihdr_data.bit_depth.as_u8() as usize
|
let bytes_per_line = ((bits_per_line + 7) / 8) as usize;
|
||||||
* self.png.channels_per_pixel() as usize;
|
let start = self.start;
|
||||||
let bytes_per_line = (bits_per_line + 7) / 8 as usize;
|
let len = bytes_per_line + 1;
|
||||||
self.start = self.end;
|
self.start += len;
|
||||||
self.end = self.start + bytes_per_line + 1;
|
|
||||||
Some(ScanLine {
|
Some(ScanLine {
|
||||||
filter: self.png.raw_data[self.start],
|
filter: self.raw_data[start],
|
||||||
data: &self.png.raw_data[(self.start + 1)..self.end],
|
data: &self.raw_data[(start + 1)..(start + len)],
|
||||||
pass: None,
|
pass: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue