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%)
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.
The bounds `Read + Seek` are not representational so we may remove them
from the struct itself. This also allows us to use more precise bounds
for direct access to the inner stream, especially were that does not
seek.
Groups operations according to their use case.
Funnel users towards the `IfdDecoder` instead. That type does not have
a generic over the underlying reader. It also works with a directory of
your own choosing. So the duplicates that should go are those directly
on the decoder.
Read the ColorMap tag (320) for RGBPalette images and return raw
palette indices as the decoded data with ColorType::Palette(n).
Changes:
- Read and validate ColorMap tag during IFD parsing (3 * 2^bps entries)
- Return ColorType::Palette from colortype() for RGBPalette images
- Accept Palette in expand_chunk validation (standard + sub-byte depths)
- Expose color_map() method on Decoder for palette lookup
- Add tests for 1-bit, 4-bit, 8-bit palette images with checksums
- Add test verifying ColorMap accessibility and structure
The decoder returns raw palette indices; consumers apply the lookup
using the color map returned by Decoder::color_map().
This prepares allowing to iterate over a TIFF file with invalid images
while `Image` maintains invariants. For instance we precalculate the
amount of buffer space and verify that the count of strips makes sense
in the constructor—so we do not have to redo that for every other entry
point.
In particular we avoid mistaking the absence of this tag for an absence
of subsampling. The specification instead requires it to mean half the
samples in both directions—a mode that we do not support yet. Instead of
returning erroneous samples or an error in the data stream (which would
appear too short) we return an error when asked to layout the read
buffer for these images.
We previously handed it a wrapper that was supposedly type-safe but
really internally cast the buffer to bytes. That was rather pointless as
the `_bytes` method could always be used instead. We replace the
semantics by a utility which allocates an appropriate buffer with the
internal limits, wrapping the exact sequence identified previously in
the example as a pattern.
By adding the type interpretation to `BufferLayoutPreference` (simply
computed from the same fields that are trivial to fetch in
`ReadoutLayout` with the rest) we can allocate an appropriate
`DecoderResult` without any `Decoder` instance. We still want a Limits
instance though. This lets a caller read _typed_ data from the `_bytes`
methods which perform less allocations. It also lets them read multiple
planes in a more convenient interface. See, `read_image` will keep its
planar treatment bug for a little bit so you don't accidentally break
during an upgrade.
This also implies less code is being monomorphized, just a bonus.
This is well-defined even if we add sub-sampling support later on. We
can adjust the layout type accordingly with factors per each plane. Note
that the layout, in theory, does not depend on the index of the unit if
we describe all planes for a multi-planar layout. The specification
requires that the subsampling factors divide the tile sizes. However we
have to handle non-planar images here and this leaves us open to other
potential parameters.