Compare commits

...
Author SHA1 Message Date
w.pomp c462915a61 - repo link fix
Rust CI / features (zstd) (push) Successful in 38s
Fuzz / Fuzz (decode_comprehensive) (push) Failing after 40s
Fuzz / Fuzz (decode_image) (push) Failing after 38s
Fuzz / Fuzz regression (push) Successful in 31s
Rust CI / build (1.88.0) (push) Failing after 51s
Rust CI / build (beta) (push) Canceled after 0s
Rust CI / build (nightly) (push) Canceled after 0s
Rust CI / build (stable) (push) Canceled after 0s
Rust CI / test-platforms (ubuntu-latest) (push) Successful in 34s
Rust CI / features () (push) Successful in 31s
Rust CI / features (default) (push) Successful in 39s
Rust CI / features (default,zstd) (push) Successful in 37s
Rust CI / features (deflate) (push) Successful in 39s
Rust CI / features (jpeg) (push) Successful in 37s
Rust CI / features (lzw) (push) Successful in 39s
Rust CI / other_archs (i686-unknown-linux-gnu) (push) Failing after 9s
Rust CI / other_archs (powerpc-unknown-linux-gnu) (push) Failing after 8s
Rust CI / msrv (push) Successful in 54s
Rust CI / rustfmt (push) Successful in 25s
Rust CI / clippy (push) Successful in 36s
Rust CI / test-platforms (macos-26-intel) (push) Canceled after 0s
Rust CI / test-platforms (macos-latest) (push) Canceled after 0s
Rust CI / test-platforms (ubuntu-24.04-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-11-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-latest) (push) Canceled after 0s
2026-10-27 19:13:30 +01:00
w.pomp caf8908fbf - repo link fix 2026-10-27 19:12:59 +01:00
w.pomp 08492b5e16 - repo link fix 2026-10-27 19:10:14 +01:00
w.pomp 563b2b11d7 - bump dependencies
Fuzz / Fuzz (decode_comprehensive) (push) Failing after 40s
Fuzz / Fuzz (decode_image) (push) Failing after 38s
Fuzz / Fuzz regression (push) Successful in 31s
Rust CI / build (1.88.0) (push) Failing after 51s
Rust CI / build (beta) (push) Canceled after 0s
Rust CI / build (nightly) (push) Canceled after 0s
Rust CI / build (stable) (push) Canceled after 0s
Rust CI / test-platforms (ubuntu-latest) (push) Successful in 41s
Rust CI / features () (push) Successful in 30s
Rust CI / features (default) (push) Successful in 38s
Rust CI / features (default,zstd) (push) Successful in 37s
Rust CI / features (deflate) (push) Successful in 38s
Rust CI / features (jpeg) (push) Successful in 38s
Rust CI / features (zstd) (push) Successful in 38s
Rust CI / other_archs (i686-unknown-linux-gnu) (push) Failing after 16s
Rust CI / features (lzw) (push) Successful in 37s
Rust CI / other_archs (powerpc-unknown-linux-gnu) (push) Failing after 8s
Rust CI / msrv (push) Successful in 59s
Rust CI / rustfmt (push) Successful in 26s
Rust CI / clippy (push) Successful in 34s
Rust CI / test-platforms (macos-26-intel) (push) Canceled after 0s
Rust CI / test-platforms (macos-latest) (push) Canceled after 0s
Rust CI / test-platforms (ubuntu-24.04-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-11-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-latest) (push) Canceled after 0s
2026-10-27 14:43:16 +01:00
Wim Pomp a6e8ed772d doc typo
Fuzz / Fuzz (decode_comprehensive) (push) Failing after 1m14s
Fuzz / Fuzz (decode_image) (push) Failing after 39s
Fuzz / Fuzz regression (push) Successful in 40s
Rust CI / build (1.88.0) (push) Successful in 58s
Rust CI / build (beta) (push) Successful in 1m8s
Rust CI / build (nightly) (push) Successful in 56s
Rust CI / build (stable) (push) Successful in 58s
Rust CI / features (default,zstd) (push) Successful in 41s
Rust CI / features (jpeg) (push) Successful in 41s
Rust CI / features (lzw) (push) Successful in 41s
Rust CI / features (zstd) (push) Successful in 40s
Rust CI / other_archs (i686-unknown-linux-gnu) (push) Failing after 17s
Rust CI / clippy (push) Successful in 36s
Rust CI / test-platforms (ubuntu-latest) (push) Successful in 45s
Rust CI / features () (push) Successful in 37s
Rust CI / features (default) (push) Successful in 41s
Rust CI / features (deflate) (push) Successful in 45s
Rust CI / other_archs (powerpc-unknown-linux-gnu) (push) Failing after 11s
Rust CI / msrv (push) Successful in 1m0s
Rust CI / rustfmt (push) Successful in 29s
Rust CI / test-platforms (macos-26-intel) (push) Canceled after 0s
Rust CI / test-platforms (macos-latest) (push) Canceled after 0s
Rust CI / test-platforms (ubuntu-24.04-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-11-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-latest) (push) Canceled after 0s
2026-10-10 12:31:33 +02:00
Wim Pomp 9b452e93b7 - fix tiled image reading
Fuzz / Fuzz (decode_comprehensive) (push) Failing after 37s
Fuzz / Fuzz (decode_image) (push) Failing after 35s
Fuzz / Fuzz regression (push) Successful in 28s
Rust CI / build (1.88.0) (push) Successful in 46s
Rust CI / build (beta) (push) Successful in 38s
Rust CI / build (nightly) (push) Successful in 36s
Rust CI / build (stable) (push) Successful in 39s
Rust CI / test-platforms (ubuntu-latest) (push) Successful in 33s
Rust CI / features () (push) Successful in 29s
Rust CI / features (default) (push) Successful in 37s
Rust CI / features (default,zstd) (push) Successful in 36s
Rust CI / features (deflate) (push) Successful in 35s
Rust CI / features (jpeg) (push) Successful in 36s
Rust CI / features (lzw) (push) Successful in 35s
Rust CI / features (zstd) (push) Successful in 35s
Rust CI / other_archs (i686-unknown-linux-gnu) (push) Failing after 19s
Rust CI / other_archs (powerpc-unknown-linux-gnu) (push) Failing after 18s
Rust CI / rustfmt (push) Successful in 25s
Rust CI / msrv (push) Successful in 57s
Rust CI / clippy (push) Successful in 33s
Rust CI / test-platforms (macos-26-intel) (push) Canceled after 0s
Rust CI / test-platforms (macos-latest) (push) Canceled after 0s
Rust CI / test-platforms (ubuntu-24.04-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-11-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-latest) (push) Canceled after 0s
2026-10-03 23:07:34 +02:00
Wim Pomp 96d37bb087 - use a fork of the tiff crate to open RGBPalette tiffs
Fuzz / Fuzz (decode_comprehensive) (push) Failing after 27s
Fuzz / Fuzz (decode_image) (push) Failing after 36s
Fuzz / Fuzz regression (push) Successful in 29s
Rust CI / build (beta) (push) Successful in 40s
Rust CI / build (nightly) (push) Successful in 39s
Rust CI / features (default,zstd) (push) Successful in 35s
Rust CI / features (jpeg) (push) Successful in 35s
Rust CI / features (lzw) (push) Successful in 36s
Rust CI / features (zstd) (push) Successful in 36s
Rust CI / other_archs (i686-unknown-linux-gnu) (push) Failing after 19s
Rust CI / msrv (push) Successful in 56s
Rust CI / build (1.88.0) (push) Successful in 47s
Rust CI / build (stable) (push) Successful in 39s
Rust CI / test-platforms (ubuntu-latest) (push) Successful in 32s
Rust CI / features () (push) Successful in 31s
Rust CI / features (default) (push) Successful in 35s
Rust CI / features (deflate) (push) Successful in 37s
Rust CI / other_archs (powerpc-unknown-linux-gnu) (push) Failing after 17s
Rust CI / rustfmt (push) Successful in 25s
Rust CI / clippy (push) Successful in 33s
Rust CI / test-platforms (macos-26-intel) (push) Canceled after 0s
Rust CI / test-platforms (macos-latest) (push) Canceled after 0s
Rust CI / test-platforms (ubuntu-24.04-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-11-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-latest) (push) Canceled after 0s
2026-10-03 19:35:01 +02:00
Wim Pomp 12f16dbfdd - ignore missing colormap
Fuzz / Fuzz (decode_comprehensive) (push) Successful in 1m39s
Fuzz / Fuzz regression (push) Successful in 27s
Fuzz / Fuzz (decode_image) (push) Failing after 37s
Rust CI / build (1.88.0) (push) Successful in 47s
Rust CI / build (beta) (push) Successful in 37s
Rust CI / build (nightly) (push) Successful in 39s
Rust CI / build (stable) (push) Successful in 37s
Rust CI / test-platforms (ubuntu-latest) (push) Successful in 31s
Rust CI / features () (push) Successful in 29s
Rust CI / features (default) (push) Failing after 35s
Rust CI / features (default,zstd) (push) Canceled after 0s
Rust CI / features (deflate) (push) Canceled after 0s
Rust CI / features (jpeg) (push) Canceled after 0s
Rust CI / features (lzw) (push) Canceled after 0s
Rust CI / features (zstd) (push) Canceled after 0s
Rust CI / other_archs (i686-unknown-linux-gnu) (push) Failing after 10s
Rust CI / other_archs (powerpc-unknown-linux-gnu) (push) Failing after 10s
Rust CI / msrv (push) Successful in 47s
Rust CI / rustfmt (push) Successful in 24s
Rust CI / clippy (push) Failing after 33s
Rust CI / test-platforms (macos-26-intel) (push) Canceled after 0s
Rust CI / test-platforms (macos-latest) (push) Canceled after 0s
Rust CI / test-platforms (ubuntu-24.04-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-11-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-latest) (push) Canceled after 0s
2026-10-03 16:50:04 +02:00
Wim Pomp a8abdd2484 update README
Fuzz / Fuzz (decode_comprehensive) (push) Successful in 2m31s
Fuzz / Fuzz (decode_image) (push) Failing after 37s
Fuzz / Fuzz regression (push) Successful in 35s
Rust CI / build (1.88.0) (push) Successful in 52s
Rust CI / build (beta) (push) Successful in 47s
Rust CI / build (nightly) (push) Successful in 48s
Rust CI / test-platforms (ubuntu-latest) (push) Successful in 41s
Rust CI / features (default) (push) Failing after 35s
Rust CI / features (default,zstd) (push) Canceled after 0s
Rust CI / features (deflate) (push) Canceled after 0s
Rust CI / features (jpeg) (push) Canceled after 0s
Rust CI / features (lzw) (push) Canceled after 0s
Rust CI / features (zstd) (push) Canceled after 0s
Rust CI / build (stable) (push) Successful in 47s
Rust CI / features () (push) Successful in 29s
Rust CI / other_archs (i686-unknown-linux-gnu) (push) Failing after 16s
Rust CI / other_archs (powerpc-unknown-linux-gnu) (push) Failing after 9s
Rust CI / msrv (push) Successful in 55s
Rust CI / rustfmt (push) Successful in 24s
Rust CI / clippy (push) Failing after 32s
Rust CI / test-platforms (macos-26-intel) (push) Canceled after 0s
Rust CI / test-platforms (macos-latest) (push) Canceled after 0s
Rust CI / test-platforms (ubuntu-24.04-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-11-arm) (push) Canceled after 0s
Rust CI / test-platforms (windows-latest) (push) Canceled after 0s
2026-10-03 15:00:48 +02:00
Wim Pomp 934fde185a Default missing PhotometricInterpretation to BlackIsZero 2026-10-03 14:52:07 +02:00
Aurelia Molzer 3302936b08 Merge pull request #415 from paolobarbolini/zstd-0.14
Upgrade zstd to 0.14
2026-09-06 18:22:06 +00:00
Paolo Barbolini a5b9a57e09 Upgrade zstd to 0.14
Bumps the optional `zstd` dependency from `0.13` to `0.14` (pulls in
`zstd-safe` 8.0.0 / `zstd-sys` 2.1.0).

We only use `zstd::Decoder::new` for streaming decode, which is unchanged
in 0.14.0, so this is a pure version bump with no code changes.

Also worth noting: `zstd-safe` 8.0.0 fixes a dangling dictionary pointer
reachable from safe code, and `zstd-sys` 2.1.0 no longer needs `libclang`
by default. The crate stack moved from MIT to BSD-3-Clause (still
permissive, compatible with our MIT).
2026-09-06 16:59:40 +02:00
11 changed files with 147 additions and 74 deletions

No files matched your search

+2 -1
View File
@@ -1,5 +1,6 @@
/target/ /target/
**/*.rs.bk **/*.rs.bk
Cargo.lock Cargo.lock
tests/benches/corpus/ tests/benches/corpus/
.idea
.agentbridge
+16 -14
View File
@@ -1,8 +1,7 @@
[package] [package]
name = "tiff" name = "ndbioimage-tiff"
version = "0.11.3" version = "0.11.3"
edition = "2021" edition = "2021"
resolver = "2"
# note: when changed, also update test runner in `.github/workflows/rust.yml` # note: when changed, also update test runner in `.github/workflows/rust.yml`
rust-version = "1.88.0" rust-version = "1.88.0"
@@ -11,30 +10,33 @@ license = "MIT"
description = "TIFF decoding and encoding library in pure Rust" description = "TIFF decoding and encoding library in pure Rust"
authors = ["The image-rs Developers"] authors = ["The image-rs Developers"]
repository = "https://github.com/image-rs/image-tiff" repository = "https://git.pomppervova.nl/wim/image-tiff"
categories = ["multimedia::images", "multimedia::encoding"] categories = ["multimedia::images", "multimedia::encoding"]
exclude = ["tests/images/*", "tests/fuzz_images/*", "tests/**/*.tif"] exclude = ["tests/images/*", "tests/fuzz_images/*", "tests/**/*.tif"]
[lib]
name = "tiff"
[dependencies] [dependencies]
half = { version = "2.4.1" } half = { version = "2.4.1" }
quick-error = "2.0.1" quick-error = "2.0.1"
# Rename to avoid confusion with the feature name. # Rename to avoid confusion with the feature name.
fax34 = { package = "fax", version = "0.2.6", optional = true } fax34 = { package = "fax", version = "0.3", optional = true }
flate2 = { version = "1.0.20", optional = true } flate2 = { version = "1", optional = true }
weezl = { version = "0.2.1", optional = true } weezl = { version = "0.2", optional = true }
zstd = { version = "0.13", optional = true } zstd = { version = "0.14", optional = true }
zrip-decode = { version = "0.8.2", features = ["paranoid"], optional = true } zrip-decode = { version = "0.8", features = ["paranoid"], optional = true }
zune-jpeg = { version = "0.5.1", optional = true } zune-jpeg = { version = "0.5", optional = true }
image-webp = {version = "0.2.4", optional = true } image-webp = {version = "0.2", optional = true }
[dev-dependencies] [dev-dependencies]
criterion = "0.3.1" criterion = "0.3"
# We may already depend on this via flate2 # We may already depend on this via flate2
crc32fast = "1.5" crc32fast = "1"
libtest-mimic = "0.8.1" libtest-mimic = "0.8"
walkdir = "2.5" walkdir = "2"
[features] [features]
default = ["deflate", "fax", "jpeg", "lzw"] default = ["deflate", "fax", "jpeg", "lzw"]
+2
View File
@@ -5,6 +5,8 @@
TIFF decoding and encoding library in pure Rust TIFF decoding and encoding library in pure Rust
Forked from https://github.com/image-rs/image-tiff. Changes: read images containing RGBPalette frames, ignoring colormaps.
## Supported ## Supported
### Features ### Features
+42 -22
View File
@@ -599,8 +599,10 @@ impl Entry {
self.decode_values(self.count, self.type_, reader, |bytes| { self.decode_values(self.count, self.type_, reader, |bytes| {
v.extend( v.extend(
bytes bytes
.chunks_exact(2) .as_chunks::<2>()
.map(|ch| Short(u16::from_ne_bytes(ch.try_into().unwrap()))), .0
.iter()
.map(|&ch| Short(u16::from_ne_bytes(ch))),
) )
}) })
} }
@@ -609,8 +611,10 @@ impl Entry {
self.decode_values(self.count, self.type_, reader, |bytes| { self.decode_values(self.count, self.type_, reader, |bytes| {
v.extend( v.extend(
bytes bytes
.chunks_exact(2) .as_chunks::<2>()
.map(|ch| SignedShort(i16::from_ne_bytes(ch.try_into().unwrap()))), .0
.iter()
.map(|&ch| SignedShort(i16::from_ne_bytes(ch))),
) )
}) })
} }
@@ -619,8 +623,10 @@ impl Entry {
self.decode_values(self.count, self.type_, reader, |bytes| { self.decode_values(self.count, self.type_, reader, |bytes| {
v.extend( v.extend(
bytes bytes
.chunks_exact(4) .as_chunks::<4>()
.map(|ch| Unsigned(u32::from_ne_bytes(ch.try_into().unwrap()))), .0
.iter()
.map(|&ch| Unsigned(u32::from_ne_bytes(ch))),
) )
}) })
} }
@@ -629,8 +635,10 @@ impl Entry {
self.decode_values(self.count, self.type_, reader, |bytes| { self.decode_values(self.count, self.type_, reader, |bytes| {
v.extend( v.extend(
bytes bytes
.chunks_exact(4) .as_chunks::<4>()
.map(|ch| Signed(i32::from_ne_bytes(ch.try_into().unwrap()))), .0
.iter()
.map(|&ch| Signed(i32::from_ne_bytes(ch))),
) )
}) })
} }
@@ -639,8 +647,10 @@ impl Entry {
self.decode_values(self.count, self.type_, reader, |bytes| { self.decode_values(self.count, self.type_, reader, |bytes| {
v.extend( v.extend(
bytes bytes
.chunks_exact(4) .as_chunks::<4>()
.map(|ch| Float(f32::from_ne_bytes(ch.try_into().unwrap()))), .0
.iter()
.map(|&ch| Float(f32::from_ne_bytes(ch))),
) )
}) })
} }
@@ -649,15 +659,17 @@ impl Entry {
self.decode_values(self.count, self.type_, reader, |bytes| { self.decode_values(self.count, self.type_, reader, |bytes| {
v.extend( v.extend(
bytes bytes
.chunks_exact(8) .as_chunks::<8>()
.map(|ch| Double(f64::from_ne_bytes(ch.try_into().unwrap()))), .0
.iter()
.map(|&ch| Double(f64::from_ne_bytes(ch))),
) )
}) })
} }
Type::RATIONAL => { Type::RATIONAL => {
v = Self::vec_with_capacity(self.count, self.type_, limits)?; v = Self::vec_with_capacity(self.count, self.type_, limits)?;
self.decode_values(self.count, self.type_, reader, |bytes| { self.decode_values(self.count, self.type_, reader, |bytes| {
v.extend(bytes.chunks_exact(8).map(|ch| { v.extend(bytes.as_chunks::<8>().0.iter().map(|ch| {
Rational( Rational(
u32::from_ne_bytes(ch[..4].try_into().unwrap()), u32::from_ne_bytes(ch[..4].try_into().unwrap()),
u32::from_ne_bytes(ch[4..].try_into().unwrap()), u32::from_ne_bytes(ch[4..].try_into().unwrap()),
@@ -668,7 +680,7 @@ impl Entry {
Type::SRATIONAL => { Type::SRATIONAL => {
v = Self::vec_with_capacity(self.count, self.type_, limits)?; v = Self::vec_with_capacity(self.count, self.type_, limits)?;
self.decode_values(self.count, self.type_, reader, |bytes| { self.decode_values(self.count, self.type_, reader, |bytes| {
v.extend(bytes.chunks_exact(8).map(|ch| { v.extend(bytes.as_chunks::<8>().0.iter().map(|ch| {
SRational( SRational(
i32::from_ne_bytes(ch[..4].try_into().unwrap()), i32::from_ne_bytes(ch[..4].try_into().unwrap()),
i32::from_ne_bytes(ch[4..].try_into().unwrap()), i32::from_ne_bytes(ch[4..].try_into().unwrap()),
@@ -681,8 +693,10 @@ impl Entry {
self.decode_values(self.count, self.type_, reader, |bytes| { self.decode_values(self.count, self.type_, reader, |bytes| {
v.extend( v.extend(
bytes bytes
.chunks_exact(8) .as_chunks::<8>()
.map(|ch| UnsignedBig(u64::from_ne_bytes(ch.try_into().unwrap()))), .0
.iter()
.map(|&ch| UnsignedBig(u64::from_ne_bytes(ch))),
) )
}) })
} }
@@ -691,8 +705,10 @@ impl Entry {
self.decode_values(self.count, self.type_, reader, |bytes| { self.decode_values(self.count, self.type_, reader, |bytes| {
v.extend( v.extend(
bytes bytes
.chunks_exact(8) .as_chunks::<8>()
.map(|ch| SignedBig(i64::from_ne_bytes(ch.try_into().unwrap()))), .0
.iter()
.map(|&ch| SignedBig(i64::from_ne_bytes(ch))),
) )
}) })
} }
@@ -701,8 +717,10 @@ impl Entry {
self.decode_values(self.count, self.type_, reader, |bytes| { self.decode_values(self.count, self.type_, reader, |bytes| {
v.extend( v.extend(
bytes bytes
.chunks_exact(4) .as_chunks::<4>()
.map(|ch| Ifd(u32::from_ne_bytes(ch.try_into().unwrap()))), .0
.iter()
.map(|&ch| Ifd(u32::from_ne_bytes(ch))),
) )
}) })
} }
@@ -711,8 +729,10 @@ impl Entry {
self.decode_values(self.count, self.type_, reader, |bytes| { self.decode_values(self.count, self.type_, reader, |bytes| {
v.extend( v.extend(
bytes bytes
.chunks_exact(8) .as_chunks::<8>()
.map(|ch| IfdBig(u64::from_ne_bytes(ch.try_into().unwrap()))), .0
.iter()
.map(|&ch| IfdBig(u64::from_ne_bytes(ch))),
) )
}) })
} }
+18 -7
View File
@@ -36,6 +36,9 @@ impl TileAttributes {
pub fn tiles_down(&self) -> usize { pub fn tiles_down(&self) -> usize {
self.image_height.div_ceil(self.tile_length) self.image_height.div_ceil(self.tile_length)
} }
pub fn tiles_per_plane(&self) -> usize {
self.tiles_across() * self.tiles_down()
}
fn padding_right(&self) -> usize { fn padding_right(&self) -> usize {
(self.tile_width - self.image_width % self.tile_width) % self.tile_width (self.tile_width - self.image_width % self.tile_width) % self.tile_width
} }
@@ -176,7 +179,7 @@ impl Image {
.map(Value::into_u16) .map(Value::into_u16)
.transpose()? .transpose()?
.and_then(PhotometricInterpretation::from_u16) .and_then(PhotometricInterpretation::from_u16)
.ok_or(TiffUnsupportedError::UnknownInterpretation)?; .unwrap_or(PhotometricInterpretation::BlackIsZero);
// Try to parse both the compression method and the number, format, and bits of the included samples. // Try to parse both the compression method and the number, format, and bits of the included samples.
// If they are not explicitly specified, those tags are reset to their default values and not carried from previous images. // If they are not explicitly specified, those tags are reset to their default values and not carried from previous images.
@@ -467,11 +470,11 @@ impl Image {
} }
Some(map) Some(map)
} }
None => { // A missing palette is not an error. The pixel values of an indexed image are just
return Err(TiffError::FormatError( // indices into the palette, and the palette itself is only display information, so
TiffFormatError::RequiredTagNotFound(Tag::ColorMap), // the image still decodes. Callers observe the absence as `None` from
)); // `Decoder::color_map`.
} None => None,
} }
} else { } else {
None None
@@ -1016,7 +1019,15 @@ impl Image {
} }
ChunkType::Tile => { ChunkType::Tile => {
let tile_attrs = self.tile_attributes.as_ref().unwrap(); let tile_attrs = self.tile_attributes.as_ref().unwrap();
let (padding_right, padding_down) = tile_attrs.get_padding(chunk_index as usize); // The chunk index counts the tiles of all planes, but the tile grid repeats per
// plane, so only the position within the plane decides the padding.
let tiles_per_plane = tile_attrs.tiles_per_plane();
let tile = if tiles_per_plane == 0 {
0
} else {
chunk_index as usize % tiles_per_plane
};
let (padding_right, padding_down) = tile_attrs.get_padding(tile);
let tile_width = tile_attrs.tile_width - padding_right; let tile_width = tile_attrs.tile_width - padding_right;
let tile_length = tile_attrs.tile_length - padding_down; let tile_length = tile_attrs.tile_length - padding_down;
+4 -4
View File
@@ -294,8 +294,8 @@ fn one_byte_predict<const N: usize>(buf: &mut [u8]) {
acc.copy_from_slice(&buf[..N]); acc.copy_from_slice(&buf[..N]);
// Process N bytes per iteration. Each channel's prefix sum is independent. // Process N bytes per iteration. Each channel's prefix sum is independent.
let mut chunks = buf[N..].chunks_exact_mut(N); let chunks = buf[N..].as_chunks_mut::<N>();
for chunk in &mut chunks { for chunk in &mut chunks.0.iter_mut() {
for c in 0..N { for c in 0..N {
acc[c] = chunk[c].wrapping_add(acc[c]); acc[c] = chunk[c].wrapping_add(acc[c]);
chunk[c] = acc[c]; chunk[c] = acc[c];
@@ -303,7 +303,7 @@ fn one_byte_predict<const N: usize>(buf: &mut [u8]) {
} }
// Handle trailing bytes (when buf.len() - N is not a multiple of N). // Handle trailing bytes (when buf.len() - N is not a multiple of N).
for (c, byte) in chunks.into_remainder().iter_mut().enumerate() { for (c, byte) in chunks.1.iter_mut().enumerate() {
*byte = byte.wrapping_add(acc[c]); *byte = byte.wrapping_add(acc[c]);
} }
} }
@@ -1288,7 +1288,7 @@ impl<R: Read + Seek> Decoder<R> {
/// Utility to create a reader like [`Self::read_entry`], from the current IFD. /// Utility to create a reader like [`Self::read_entry`], from the current IFD.
/// ///
/// Returns an `Ok(None)` if the tag is not found in the current directory. See /// Returns an `Ok(None)` if the tag is not found in the current directory. See
/// [`Self::read_entry´] of which this is a simple wrapper. /// [`Self::read_entry`] of which this is a simple wrapper.
pub fn read_tag(&mut self, tag: Tag) -> TiffResult<Option<EntryBytesReader<'_, R>>> { pub fn read_tag(&mut self, tag: Tag) -> TiffResult<Option<EntryBytesReader<'_, R>>> {
match self.current_ifd().find_entry(tag) { match self.current_ifd().find_entry(tag) {
None => Ok(None), None => Ok(None),
+12 -12
View File
@@ -294,7 +294,7 @@ pub struct Group4Reader<R: Read> {
decoder: fax34::decoder::Group4Decoder<FaxBytes<R>>, decoder: fax34::decoder::Group4Decoder<FaxBytes<R>>,
line_buf: io::Cursor<Vec<u8>>, line_buf: io::Cursor<Vec<u8>>,
height: u32, height: u32,
width: u16, width: u32,
y: u32, y: u32,
} }
@@ -306,7 +306,7 @@ impl<R: Read> Group4Reader<R> {
compressed_length: u64, compressed_length: u64,
fill_order: u16, fill_order: u16,
) -> crate::TiffResult<Self> { ) -> crate::TiffResult<Self> {
let width = u16::try_from(dimensions.0)?; let width = dimensions.0;
let height = dimensions.1; let height = dimensions.1;
Ok(Self { Ok(Self {
@@ -314,7 +314,7 @@ impl<R: Read> Group4Reader<R> {
fax_byte_iter(reader, compressed_length, fill_order), fax_byte_iter(reader, compressed_length, fill_order),
width, width,
)?, )?,
line_buf: io::Cursor::new(Vec::with_capacity(width.into())), line_buf: io::Cursor::new(Vec::with_capacity(width as usize)),
width, width,
height, height,
y: 0, y: 0,
@@ -344,7 +344,7 @@ impl<R: Read> Read for Group4Reader<R> {
let transitions = fax34::decoder::pels(self.decoder.transition(), self.width); let transitions = fax34::decoder::pels(self.decoder.transition(), self.width);
let buffer = self.line_buf.get_mut(); let buffer = self.line_buf.get_mut();
buffer.resize(usize::from(self.width).div_ceil(8), 0u8); buffer.resize((self.width as usize).div_ceil(8), 0u8);
let target = &mut buffer[..]; let target = &mut buffer[..];
@@ -409,7 +409,7 @@ pub struct Group3Reader {
reader: OwnedBitReader, reader: OwnedBitReader,
line_buf: io::Cursor<Vec<u8>>, line_buf: io::Cursor<Vec<u8>>,
height: u32, height: u32,
width: u16, width: u32,
y: u32, y: u32,
} }
@@ -421,7 +421,7 @@ impl Group3Reader {
compressed_length: u64, compressed_length: u64,
fill_order: u16, fill_order: u16,
) -> crate::TiffResult<Self> { ) -> crate::TiffResult<Self> {
let width = u16::try_from(dimensions.0)?; let width = dimensions.0;
let height = dimensions.1; let height = dimensions.1;
// Buffer all compressed data and apply FillOrder bit reversal // Buffer all compressed data and apply FillOrder bit reversal
@@ -442,7 +442,7 @@ impl Group3Reader {
Ok(Self { Ok(Self {
reader: bit_reader, reader: bit_reader,
line_buf: io::Cursor::new(Vec::with_capacity(width.into())), line_buf: io::Cursor::new(Vec::with_capacity(width as usize)),
width, width,
height, height,
y: 0, y: 0,
@@ -463,10 +463,10 @@ impl Group3Reader {
fn with_markup<R: fax34::BitReader>( fn with_markup<R: fax34::BitReader>(
decoder: fn(&mut R) -> Option<u16>, decoder: fn(&mut R) -> Option<u16>,
reader: &mut R, reader: &mut R,
) -> Option<u16> { ) -> Option<u32> {
let mut sum = 0u16; let mut sum = 0u32;
while let Some(n) = decoder(reader) { while let Some(n) = decoder(reader) {
sum = sum.checked_add(n)?; sum = sum.checked_add(n as u32)?;
if n < 64 { if n < 64 {
return Some(sum); return Some(sum);
} }
@@ -483,7 +483,7 @@ impl Read for Group3Reader {
{ {
// Decode one line of alternating white/black run-lengths // Decode one line of alternating white/black run-lengths
let mut transitions = Vec::new(); let mut transitions = Vec::new();
let mut a0 = 0u16; let mut a0 = 0u32;
let mut is_white = true; let mut is_white = true;
loop { loop {
let run = if is_white { let run = if is_white {
@@ -507,7 +507,7 @@ impl Read for Group3Reader {
self.y += 1; self.y += 1;
// Convert transitions to packed bilevel output // Convert transitions to packed bilevel output
let bytes_per_line = usize::from(self.width).div_ceil(8); let bytes_per_line = (self.width as usize).div_ceil(8);
let buffer = self.line_buf.get_mut(); let buffer = self.line_buf.get_mut();
buffer.clear(); buffer.clear();
buffer.resize(bytes_per_line, 0u8); buffer.resize(bytes_per_line, 0u8);
+1 -1
View File
@@ -171,7 +171,7 @@ mod tests {
dir.get(Tag::Unknown(1)) dir.get(Tag::Unknown(1))
.expect("tag 1 should be present after this chain") .expect("tag 1 should be present after this chain")
.count(), .count(),
u16::MAX.into() u64::from(u16::MAX)
); );
} }
+3 -7
View File
@@ -617,26 +617,22 @@ impl ByteOrder {
return; return;
} }
// FIXME: at MSRV 1.89 or higher use `slice::as_chunks_mut`.
match cls { match cls {
EndianBytes::One => { EndianBytes::One => {
// No change needed // No change needed
} }
EndianBytes::Two => { EndianBytes::Two => {
for chunk in buffer.chunks_exact_mut(2) { for chunk in buffer.as_chunks_mut::<2>().0 {
let chunk: &mut [u8; 2] = chunk.try_into().unwrap();
*chunk = u16::from_be_bytes(*chunk).to_le_bytes(); *chunk = u16::from_be_bytes(*chunk).to_le_bytes();
} }
} }
EndianBytes::Four => { EndianBytes::Four => {
for chunk in buffer.chunks_exact_mut(4) { for chunk in buffer.as_chunks_mut::<4>().0 {
let chunk: &mut [u8; 4] = chunk.try_into().unwrap();
*chunk = u32::from_be_bytes(*chunk).to_le_bytes(); *chunk = u32::from_be_bytes(*chunk).to_le_bytes();
} }
} }
EndianBytes::Eight => { EndianBytes::Eight => {
for chunk in buffer.chunks_exact_mut(8) { for chunk in buffer.as_chunks_mut::<8>().0 {
let chunk: &mut [u8; 8] = chunk.try_into().unwrap();
*chunk = u64::from_be_bytes(*chunk).to_le_bytes(); *chunk = u64::from_be_bytes(*chunk).to_le_bytes();
} }
} }
+47 -6
View File
@@ -506,6 +506,47 @@ fn test_tiled_extra_samples_border_u8() {
assert_eq!(data, expected); assert_eq!(data, expected);
} }
#[test]
fn test_tiled_planar_border_u16() {
// A planar (separate) tiled image whose dimensions are not multiples of the tile size.
// The tile grid repeats for every plane, but the chunk (tile) index counts all planes, so
// the border padding of the second plane used to be looked up with the wrong tile index.
// The bottom-right tile of plane 1 then claimed a full tile height, and the decoder ran
// past the end of the output buffer.
//
// Note that `read_image` only decodes the first plane, so this has to go through
// `read_image_to_buffer` to reach the tiles that were mis-sized.
let path = PathBuf::from(TEST_IMAGE_DIR).join("tiled-planar-2c-16b.tif");
let img_file = File::open(path).expect("Cannot find test image!");
let mut decoder = Decoder::open(img_file).expect("Cannot create decoder");
decoder.next_image().expect("Cannot read image IFD");
assert_eq!(decoder.dimensions().unwrap(), (30, 20));
assert_eq!(
decoder.colortype().unwrap(),
ColorType::Multiband {
bit_depth: 16,
num_samples: 2
}
);
let mut buffer = DecodingSampleBuffer::U16(Vec::new());
decoder.read_image_to_buffer(&mut buffer).unwrap();
let DecodingSampleBuffer::U16(data) = buffer else {
panic!("Wrong bit depth")
};
assert_eq!(data.len(), 2 * 20 * 30);
let mut expected = Vec::with_capacity(2 * 20 * 30);
for plane in 0..2u16 {
for y in 0..20u16 {
for x in 0..30u16 {
expected.push(1000 * plane + 10 * y + x);
}
}
}
assert_eq!(data, expected);
}
#[test] #[test]
fn test_inner_access() { fn test_inner_access() {
let path = PathBuf::from(TEST_IMAGE_DIR).join("tiled-rect-rgb-u8.tif"); let path = PathBuf::from(TEST_IMAGE_DIR).join("tiled-rect-rgb-u8.tif");
@@ -898,22 +939,22 @@ fn test_image_bytes(
} }
fn byte_order_u16(bytes: &mut [u8]) { fn byte_order_u16(bytes: &mut [u8]) {
for chunk in bytes.chunks_exact_mut(2) { for chunk in bytes.as_chunks_mut::<2>().0 {
let n = u16::from_ne_bytes(chunk.try_into().unwrap()); let n = u16::from_ne_bytes(*chunk);
chunk.copy_from_slice(&n.to_le_bytes()); chunk.copy_from_slice(&n.to_le_bytes());
} }
} }
fn byte_order_u32(bytes: &mut [u8]) { fn byte_order_u32(bytes: &mut [u8]) {
for chunk in bytes.chunks_exact_mut(4) { for chunk in bytes.as_chunks_mut::<4>().0 {
let n = u32::from_ne_bytes(chunk.try_into().unwrap()); let n = u32::from_ne_bytes(*chunk);
chunk.copy_from_slice(&n.to_le_bytes()); chunk.copy_from_slice(&n.to_le_bytes());
} }
} }
fn byte_order_u64(bytes: &mut [u8]) { fn byte_order_u64(bytes: &mut [u8]) {
for chunk in bytes.chunks_exact_mut(8) { for chunk in bytes.as_chunks_mut::<8>().0 {
let n = u64::from_ne_bytes(chunk.try_into().unwrap()); let n = u64::from_ne_bytes(*chunk);
chunk.copy_from_slice(&n.to_le_bytes()); chunk.copy_from_slice(&n.to_le_bytes());
} }
} }
Binary file not shown.