Commit Graph
883 Commits
Author SHA1 Message Date
Sergey "Shnatsel" Davidoff f3f9ff1244 Merge pull request #398 from Shnatsel/safe-rust-zstd-2
Add safe Rust zstd decoding backend
2026-07-20 00:33:05 +01:00
Aurelia Molzer a7dda6f4b1 Merge pull request #399 from lilith/fix/clamp-extended-depth
decoder: reconstruct extended-depth samples modulo declared bit depth (supersedes #396)
2026-07-19 22:18:33 +00:00
Lilith River 35c14f1e3c decoder: reconstruct extended-depth samples modulo declared bit depth
Promoted extended-depth grayscale (9-15 bits -> u16, 17-31 bits -> u32)
can carry bits above the declared depth after the horizontal predictor,
which reconstructs prev+delta in the promoted storage width rather than
modulo 2^BitsPerSample. This underflowed `max - v` in invert_colors (the
image-rs/image-tiff#396 panic in debug, wrong pixel in release) and left
junk in the high bits of the non-inverted output (#396 review, @197g:
"clamp the channel values in the non-inverted color case").

An n-bit predictor delta is stored in exactly n bits, i.e. modulo 2^n,
so reconstruction is defined modulo 2^n as well. Since 2^n divides the
u16/u32 storage modulus, masking each promoted sample with (1<<n)-1 once
after the predictor yields exactly the mod-2^n value -- including
legitimate wrap-arounds -- and also bounds any out-of-range sample from
malformed input. invert_colors' `max - v` is then always in range,
superseding the targeted saturating_sub from #396.

mask_samples_to_bit_depth runs in every integer decode branch of
expand_chunk; outside the extended-depth unpacking branch it is a
statically provable no-op (promoted layouts, declared < storage, occur
only there -- sub-byte depths keep byte-granular storage and reject the
horizontal predictor, standard depths have declared == storage, and the
float predictor requires IEEEFP samples that are never promoted), so the
branches cannot diverge.

Adds exact-pixel regression tests on hand-built TIFFs: 12/20-bit
wrap-around (plain and WhiteIsZero) and a 9-31-bit encode/decode
identity sweep, alongside the two fuzzer-minimized panic seeds. A
saturating clamp corrupts the wrap-around cases -- 12-bit [4095, 0, 1]
is stored as deltas [4095, 1, 1] and masks back to [4095, 0, 1], but
saturating produced [4095, 4095, 4095] -- so the tests lock the modular
semantics.
2026-07-18 16:04:42 -06:00
Aurelia Molzer a9cf5e17b9 Merge pull request #404 from kleinicke/fix-clippy-useless-borrow
Fix clippy 1.97 useless_borrows_in_formatting lint failing CI
2026-07-16 15:59:04 +00:00
florianandClaude Fable 5 6276defac6 Fix clippy 1.97 useless_borrows_in_formatting lint
Rust 1.97's new clippy lint flags the redundant `&` on `self.offset`
in Entry's Debug impl. CI runs clippy with -D warnings in every job,
so this single pre-existing lint currently fails all PR pipelines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:29:05 +02:00
Sergey "Shnatsel" Davidoff 11133cb1e3 upgrade zrip to 0.8.x series 2026-07-06 15:15:04 +01:00
Aurelia Molzer 8ad05b85db Merge pull request #397 from soyosaki/lenient
Fix: add a lenient mode for image decoder
2026-07-03 14:36:35 +00:00
陈启 4a9e1d152b fix: resolve comments 2026-07-03 18:49:16 +08:00
陈启 1c6001ea5d fix: resolve comments 2026-07-03 17:19:48 +08:00
Sergey "Shnatsel" Davidoff c0043c1a84 Use zrip-decode crate instead of ruzstd 2026-06-27 12:47:51 +01:00
陈启 c7a1176381 fix: eliminate clippy errors 2026-06-22 14:13:53 +08:00
陈启 a658c09f09 feat: add lenient decoder example 2026-06-22 12:02:54 +08:00
陈启 0309349e1b fix: add lenient mode for tiff image decoder 2026-06-22 10:59:36 +08:00
陈启 279851a9a9 fix: correct extra bits rgb test assertion based on write extra bits
image logic
2026-06-22 10:58:42 +08:00
Aurelia Molzer 22b4c39254 Merge pull request #394 from powturns/main
Add PhotometricInterpretation unknown tag support
2026-06-02 17:50:20 +00:00
Dustin Thomson f11a1c5393 Add PhotometricInterpretation unknown tag support
Provides a mechanism to support private or extension
photometric interpretations (such as CFA)
2026-05-28 08:48:12 -06:00
Aurelia Molzer c4285aea4a Merge pull request #393 from image-rs/bench-lzw
Fix benches/lzw.rs directory iteration
2026-05-16 15:59:17 +00:00
A. Molzer a1426ca802 Fix benches/lzw.rs directory iteration 2026-05-16 02:28:36 +02:00
Aurelia Molzer 66e6567acc Merge pull request #392 from image-rs/upgrade-weezl
Upgrade weezl to 0.2
2026-05-15 22:51:46 +00:00
A. Molzer 5947d3b72e Make newer clippy happy 2026-05-16 00:46:02 +02:00
A. Molzer 0fdf1e0502 Upgrade weezl to 0.2 2026-05-16 00:40:00 +02:00
Aurelia Molzer cf9eee64ad Merge pull request #383 from lilith/chore/fuzz-dict-and-target
chore: replace decode_image fuzz target with comprehensive decoder exerciser
2026-04-09 22:26:34 +00:00
Lilith River a59db23341 chore: replace decode_image fuzz target with comprehensive decoder exerciser
Replace decode_image with decode_comprehensive, which exercises
per-IFD via a probe_directory helper:
- Tag iteration and value parsing for every tag
- SubIFD, EXIF, and GPS directory traversal via read_directory
- Image metadata (dimensions, colortype, color_map, chunk info)
- Layout queries (image_buffer_layout, image_chunk_buffer_layout)
- Per-chunk reads at first and last indices
- Full image decode via read_image

Three IFD traversal strategies:
- Linear iteration via next_directory (tolerates invalid
  intermediate IFDs)
- Random-access via seek_to_directory (find_nth_ifd code path)
- Image-aware random-access via seek_to_image

Dictionary covers classic TIFF and BigTIFF byte order marks, and
common IFD tags in both LE and BE (image geometry, strips, tiles,
SubIFD, EXIF, GPS, ColorMap).

Strict resource limits (512 KB decode/intermediate, 64 KB IFD).
Bounded iteration (max 8 linear, max 4 seek).
2026-04-09 14:14:35 -06:00
Aurelia Molzer e8103509fa Merge pull request #385 from lilith/fix/colormap-shl-overflow
fix: prevent shift overflow panic in ColorMap size validation
2026-04-09 12:25:51 +00:00
Lilith River 26b18c5184 fix: prevent shift overflow panic in ColorMap size validation
`1usize << bits_per_sample` panics when bits_per_sample >= 64.
Use checked_shl to return a LimitsExceeded error instead.

Found by fuzzing decode_comprehensive with a BE TIFF containing
PhotometricInterpretation=RGBPalette and BitsPerSample=83.
2026-04-09 04:46:05 -06:00
Lilith River b0712855b1 ci: add nightly fuzz workflow (60s on push, 5min nightly) 2026-04-09 04:43:12 -06:00
Lilith River ea6e2a8d80 feat: add decode_comprehensive fuzz target with safe metadata probing
New fuzz target exercises TiffHeader::parse, dimensions/colortype
probing, chunk-by-chunk reading, and multi-IFD iteration. Guards
get_chunk_type/chunk_dimensions behind dimensions() success check
to avoid documented panics on non-image IFDs.
2026-04-09 04:43:12 -06:00
Lilith River 032421634f chore: add fuzz dictionary for TIFF
Format-specific dictionary with LE/BE byte order marks, TIFF magic
number, and common IFD tag numbers as 2-byte LE and BE pairs.
Also add coverage to fuzz/.gitignore.
2026-04-09 04:43:12 -06:00
Aurelia Molzer d760bd3442 Merge pull request #384 from image-rs/renovate-decode-iteration
Fix decode example/fuzz with new iteration
2026-04-08 18:49:52 +00:00
A. Molzer a70e05e51e Fix decode example/fuzz with new iteration 2026-04-08 20:43:12 +02:00
Aurelia Molzer dc25c4eaea Merge pull request #382 from lilith/fix/fax-overflow-panic
fix: prevent u16 overflow panics in Group3 fax run-length decode
2026-04-08 05:42:03 +00:00
Lilith River d7961a2fcf fix: prevent u16 overflow panic in Group3 fax run-length decode
Fuzz testing found that crafted TIFF files with corrupt Group3 fax data
could cause a u16 addition overflow when accumulating run lengths. Use
checked_add and break on overflow (treating as corrupt data) instead of
panicking.
2026-04-07 17:45:59 -06:00
Aurelia Molzer 2166fd591a Merge pull request #380 from lilith/fix/ifd-limits-and-packbits
fix: enforce ifd_value_size limits and detect PackBits under-decode
2026-04-07 18:13:40 +00:00
Aurelia Molzer 7380611a7b Merge pull request #381 from lilith/refactor/typed-predictor-slices
refactor: use typed slices for float predictor output
2026-04-07 13:03:16 +00:00
Lilith River 149492ca6e refactor: use typed slices for float predictor output
Change predict_f32/f16/f64 to take &mut [[u8; N]] output instead of
&mut [u8], making sample alignment a compile-time guarantee rather
than a silent runtime truncation. The caller uses as_chunks_mut at the
boundary, with a debug_assert that the remainder is empty.

This follows 197g's suggestion in #376 to move alignment validation
to the caller via typed slices, keeping the predictor functions clean
for future SIMD dispatch.
2026-04-07 03:03:23 -06:00
Lilith River e4c3c1fcfd fix: enforce ifd_value_size limits and detect PackBits under-decode
Two input validation fixes:

- vec_with_capacity now checks raw IFD value bytes against
  ifd_value_size before allocating. Previously only the in-memory
  Value representation was bounded (by decoding_buffer_size), so a
  crafted IFD with a large count of small-typed values could allocate
  based on raw byte count without limit checking. Also fix ASCII value
  parsing to check ifd_value_size instead of decoding_buffer_size.

- LogLuv PackBits decoder now verifies that the expected number of
  pixels was actually decoded per plane, returning UnexpectedEof if
  the RLE stream ends short.
2026-04-07 02:53:43 -06:00
Aurelia Molzer 6a3282e9f2 Merge pull request #379 from lilith/main
Optimize horizontal and float predictor decode (4-43% faster)
2026-04-06 17:25:25 +00:00
Lilith River fa8a911f79 perf: optimize horizontal and float predictor decode
Horizontal predictor (one_byte_predict, all integer TIFFs with
Predictor=2): rewrite with explicit [u8; N] accumulator so LLVM unrolls
and keeps each channel in a register. For N=3 (RGB8), this replaces a
scalar 1-byte-per-iteration loop with a 2x-unrolled 3-register loop.

Float predictor (Predictor=3, f16/f32/f64): split deinterleave into
per-format functions with plane slicing up front; f32 batches 4 samples
through a u128 wide store to eliminate per-sample bounds checks. Thread
a reusable scratch buffer through expand_chunk to eliminate per-row
heap allocation.

Benchmarked on real 21MP photos and GeoTIFF DEMs (vs upstream main):

  Strip RGB8 deflate+hpred:      253ms -> 224ms (-11%)
  Strip RGB8 LZW:                405ms -> 370ms (-9%)
  Tiled RGB8 deflate+hpred:      241ms -> 222ms (-8%)
  Strip RGB f32 deflate+fpred:   726ms -> 527ms (-27%)
  Gray f32 tiled 512:            290ms -> 166ms (-43%)
  Copernicus DEM GeoTIFF:        239ms -> 152ms (-37%)
  USGS DEM GeoTIFF:              284ms -> 198ms (-30%)
2026-04-06 08:49:00 -06:00
Lilith River dc94cd4436 bench: add comprehensive decode benchmarks
Add decode benchmark covering 16 TIFF configurations with both a real
photo corpus (if available) and synthetic in-memory images:

Strip: uncompressed, PackBits, LZW, LZW+hpred, deflate+hpred, RGB16
deflate+hpred, gray8 LZW, gray8 deflate+hpred, RGB f32 deflate+fpred,
gray f32 deflate+fpred, palette8 LZW, bilevel fax4.
Tiled: RGB8 LZW/deflate/JPEG 256, gray f32 deflate+fpred 512.

Synthetic benchmarks isolate predictor and compression overhead with
controlled 2048x2048 images. A callgrind profiling example is included
for instruction-level analysis.
2026-04-05 22:00:22 -06:00
Aurelia Molzer a42b16ab72 Merge pull request #377 from image-rs/change-reader-of-decoder
Transfer decoder to new reader
2026-04-05 08:05:41 +00:00
Aurelia Molzer 8246b55e8f Merge pull request #378 from image-rs/forgotten-dbg-statement
Cleanup dbg! left in error path
2026-04-04 21:16:36 +00:00
A. Molzer 399469f512 Cleanup dbg! left in error path 2026-04-04 23:13:07 +02:00
A. Molzer 740c5cdeea Transfer decoder to new reader 2026-04-04 15:45:31 +02:00
Aurelia Molzer 86ac5b601e Merge pull request #375 from image-rs/image-as-option
Make decoder's `Image` an option
2026-04-04 11:57:46 +00:00
A. Molzer 97d357d446 Prepare ImageDecoder split, avoid as_mut|unwrap 2026-04-04 10:20:10 +02:00
A. Molzer 61867b46f9 Make decoder's Image an option
Instead all methods with mutable self will initialize it on-demand.
There are only few methods that take `&self` and can not do that. For
those that have no error/option return we panic but both of these could
panic previously already. This should be fine by documentation alone. In
particular, the image size can be queried safely.
2026-04-04 10:20:08 +02:00
Aurelia Molzer 0f1f075abb Merge pull request #372 from image-rs/deprecate-and-hide-tag-reading
Deprecate and hide direct tag reading
2026-04-04 08:13:45 +00:00
Aurelia Molzer 5576417abe Merge pull request #374 from image-rs/value-read-provider
Add a `Read`-er for entry values
2026-03-31 16:47:14 +00:00
A. Molzer 52b9cc7a19 Rename EntryBytesReader module 2026-03-31 18:35:19 +02:00
A. Molzer 006f0b0fa4 Directly implement Seek::stream_position 2026-03-31 18:34:16 +02:00