Merge pull request #343 from Shnatsel/float-predictor-opt

Optimize floating-point predictor encoding
This commit is contained in:
Sergey "Shnatsel" Davidoff authored and GitHub committed 2026-02-27 01:14:36 +00:00
commit 9a9193345c
3 files changed
+55 -7

No files matched your search

+4
View File
@@ -50,6 +50,10 @@ zstd = ["dep:zstd"]
name = "lzw" name = "lzw"
harness = false harness = false
[[bench]]
name = "encode"
harness = false
[[test]] [[test]]
name = "decode_libtiffpic" name = "decode_libtiffpic"
path = "tests/decode_libtiffpic.rs" path = "tests/decode_libtiffpic.rs"
+44
View File
@@ -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::<RGB32Float>(WIDTH, HEIGHT, black_box(data))
.unwrap();
}
fn main() {
// Generate synthetic f32 image data with a gradient pattern
let data: Vec<f32> = (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::<f32>() as u64,
))
.bench_function("f32-float-predictor-deflate-fast", |b| {
b.iter(|| encode_image_f32_float_predictor_deflate(&data))
});
}
+7 -7
View File
@@ -18,15 +18,15 @@ macro_rules! fp_predict {
let byte_count = num_values * BYTE_SIZE; let byte_count = num_values * BYTE_SIZE;
let start_len = result.len(); let start_len = result.len();
// Reserve space for the output // Allocate zeroed space so we can write to arbitrary positions
result.reserve(byte_count); result.resize(start_len + byte_count, 0);
let output = &mut result[start_len..];
// Step 1 & 2: Convert to big-endian and shuffle bytes // Step 1 & 2: Convert to big-endian and scatter bytes
// All first bytes, then all second bytes, etc. for (i, &value) in row.iter().enumerate() {
for byte_idx in 0..BYTE_SIZE {
for &value in row {
let bytes = value.to_be_bytes(); let bytes = value.to_be_bytes();
result.push(bytes[byte_idx]); for b in 0..BYTE_SIZE {
output[b * num_values + i] = bytes[b];
} }
} }