summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--examples/profile.rs20
-rw-r--r--src/lib.rs117
3 files changed, 92 insertions, 47 deletions
diff --git a/.gitignore b/.gitignore
index 4fffb2f..f40f6b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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);
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index fa3bab9..096aebb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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(())
}