diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | examples/profile.rs | 20 | ||||
-rw-r--r-- | src/lib.rs | 117 |
3 files changed, 92 insertions, 47 deletions
@@ -1,2 +1,4 @@ /target /Cargo.lock +flamegraph.svg +perf.data* diff --git a/examples/profile.rs b/examples/profile.rs new file mode 100644 index 0000000..acc8154 --- /dev/null +++ b/examples/profile.rs @@ -0,0 +1,20 @@ +//! this file is just meant for profiling + +use std::hint::black_box; + +fn main() { + if cfg!(debug_assertions) { + eprintln!("this should only be run in release mode"); + } else { + println!("pid = {}", std::process::id()); + let large_image = black_box(std::fs::read("benches/large.png").unwrap()); + + for _ in 0..100 { + let mut png = &large_image[..]; + let header = tiny_png::read_png_header(&mut png).unwrap(); + let mut buf = vec![0; header.required_bytes()]; + let data = tiny_png::read_png(&mut png, Some(&header), &mut buf).unwrap(); + std::hint::black_box(data); + } + } +} @@ -895,8 +895,7 @@ fn read_idat<R: Read>( } } - #[cfg(feature = "adler")] - { + if cfg!(feature = "adler") { // Adler-32 checksum let padding = reader.bits_left % 8; if padding > 0 { @@ -944,55 +943,79 @@ fn apply_filters<I: IOError>(header: &ImageHeader, data: &mut [u8]) -> Result<() let scanline_bytes = header.bytes_per_row(); for scanline in 0..header.height() { let filter = data[s]; - s += 1; - - for i in 0..scanline_bytes { - let x = i32::from(data[s]); - let a = i32::from(if i < x_byte_offset { - 0 - } else { - data[d - x_byte_offset] - }); - let b = i32::from(if scanline == 0 { - 0 + const FILTER_NONE: u8 = 0; + const FILTER_SUB: u8 = 1; + const FILTER_UP: u8 = 2; + const FILTER_AVG: u8 = 3; + const FILTER_PAETH: u8 = 4; + + #[inline] + fn paeth(a: u8, b: u8, c: u8) -> u8 { + let p = i32::from(a) + i32::from(b) - i32::from(c); + let pa = (p - i32::from(a)).abs(); + let pb = (p - i32::from(b)).abs(); + let pc = (p - i32::from(c)).abs(); + if pa <= pb && pa <= pc { + a + } else if pb <= pc { + b } else { - data[d - scanline_bytes] - }); - let c = i32::from(if scanline == 0 || i < x_byte_offset { - 0 - } else { - data[d - x_byte_offset - scanline_bytes] - }); - - fn paeth(a: i32, b: i32, c: i32) -> i32 { - let p = a + b - c; - let pa = (p - a).abs(); - let pb = (p - b).abs(); - let pc = (p - c).abs(); - if pa <= pb && pa <= pc { - a - } else if pb <= pc { - b - } else { - c + c + } + } + + s += 1; + data.copy_within(s..s + scanline_bytes, d); + match (filter, scanline == 0) { + (FILTER_NONE, _) | (FILTER_UP, true) => {} + (FILTER_SUB, _) => { + for i in d + x_byte_offset..d + scanline_bytes { + data[i] = data[i].wrapping_add(data[i - x_byte_offset]); } } - data[d] = (match filter { - // none - 0 => x, - // sub - 1 => x + a, - // up - 2 => x + b, - // average - 3 => x + (a + b) / 2, - // paeth - 4 => x + paeth(a, b, c), - _ => return Err(Error::BadFilter), - }) as u8; - s += 1; - d += 1; + (FILTER_UP, false) => { + for i in d..d + scanline_bytes { + data[i] = data[i].wrapping_add(data[i - scanline_bytes]); + } + } + (FILTER_AVG, false) => { + for i in d..d + x_byte_offset { + data[i] = data[i].wrapping_add(data[i - scanline_bytes] / 2); + } + for i in d + x_byte_offset..d + scanline_bytes { + data[i] = data[i].wrapping_add( + ((u32::from(data[i - scanline_bytes]) + u32::from(data[i - x_byte_offset])) + / 2) as u8, + ); + } + } + (FILTER_AVG, true) => { + for i in d + x_byte_offset..d + scanline_bytes { + data[i] = data[i].wrapping_add(data[i - x_byte_offset] / 2); + } + } + (FILTER_PAETH, false) => { + for i in d..d + x_byte_offset { + data[i] = data[i].wrapping_add(paeth(0, data[i - scanline_bytes], 0)); + } + for i in d + x_byte_offset..d + scanline_bytes { + data[i] = data[i].wrapping_add(paeth( + data[i - x_byte_offset], + data[i - scanline_bytes], + data[i - scanline_bytes - x_byte_offset], + )); + } + } + (FILTER_PAETH, true) => { + for i in d + x_byte_offset..d + scanline_bytes { + data[i] = data[i].wrapping_add(paeth(data[i - x_byte_offset], 0, 0)); + } + } + (5.., _) => return Err(Error::BadFilter), } + + s += scanline_bytes; + d += scanline_bytes; } Ok(()) } |