diff --git a/Cargo.toml b/Cargo.toml index c1a7f0f..d1531f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,10 @@ zstd = ["dep:zstd"] name = "lzw" harness = false +[[bench]] +name = "encode" +harness = false + [[test]] name = "decode_libtiffpic" path = "tests/decode_libtiffpic.rs" diff --git a/benches/encode.rs b/benches/encode.rs new file mode 100644 index 0000000..23b2cf9 --- /dev/null +++ b/benches/encode.rs @@ -0,0 +1,44 @@ +extern crate criterion; +extern crate tiff; + +use criterion::{black_box, Criterion, Throughput}; +use std::io::Cursor; +use tiff::encoder::colortype::RGB32Float; +use tiff::encoder::{Compression, DeflateLevel, Predictor, TiffEncoder}; + +const WIDTH: u32 = 500; +const HEIGHT: u32 = 500; + +fn encode_image_f32_float_predictor_deflate(data: &[f32]) { + let mut buf = Cursor::new(Vec::with_capacity(WIDTH as usize * HEIGHT as usize * 4)); + TiffEncoder::new(&mut buf) + .unwrap() + .with_predictor(Predictor::FloatingPoint) + .with_compression(Compression::Deflate(DeflateLevel::Fast)) + .write_image::(WIDTH, HEIGHT, black_box(data)) + .unwrap(); +} + +fn main() { + // Generate synthetic f32 image data with a gradient pattern + let data: Vec = (0..WIDTH as usize * HEIGHT as usize * 3) + .map(|i| { + let pixel = i / 3; + let x = (pixel % WIDTH as usize) as f32 / WIDTH as f32; + let y = (pixel / WIDTH as usize) as f32 / HEIGHT as f32; + x * 0.5 + y * 0.5 + }) + .collect(); + + let mut c = Criterion::default().configure_from_args(); + let mut group = c.benchmark_group("tiff-encode"); + + group + .sample_size(20) + .throughput(Throughput::Bytes( + (WIDTH as u64) * (HEIGHT as u64) * 3 * std::mem::size_of::() as u64, + )) + .bench_function("f32-float-predictor-deflate-fast", |b| { + b.iter(|| encode_image_f32_float_predictor_deflate(&data)) + }); +} diff --git a/src/encoder/colortype.rs b/src/encoder/colortype.rs index ef4b9a8..189d80c 100644 --- a/src/encoder/colortype.rs +++ b/src/encoder/colortype.rs @@ -18,15 +18,15 @@ macro_rules! fp_predict { let byte_count = num_values * BYTE_SIZE; let start_len = result.len(); - // Reserve space for the output - result.reserve(byte_count); + // Allocate zeroed space so we can write to arbitrary positions + result.resize(start_len + byte_count, 0); + let output = &mut result[start_len..]; - // Step 1 & 2: Convert to big-endian and shuffle bytes - // All first bytes, then all second bytes, etc. - for byte_idx in 0..BYTE_SIZE { - for &value in row { - let bytes = value.to_be_bytes(); - result.push(bytes[byte_idx]); + // Step 1 & 2: Convert to big-endian and scatter bytes + for (i, &value) in row.iter().enumerate() { + let bytes = value.to_be_bytes(); + for b in 0..BYTE_SIZE { + output[b * num_values + i] = bytes[b]; } }