Skip to content

TIFF: only adjust strip/tile byte counts for uncompressed data#4444

Merged
sbesson merged 1 commit into
ome:developfrom
melissalinkert:gh-4442
Jun 24, 2026
Merged

TIFF: only adjust strip/tile byte counts for uncompressed data#4444
sbesson merged 1 commit into
ome:developfrom
melissalinkert:gh-4442

Conversation

@melissalinkert

Copy link
Copy Markdown
Member

Fixes #4442.

Without this change, showinf on the TIFF file linked to #4442 throws:

Exception in thread "main" loci.formats.FormatException: io.airlift.compress.MalformedInputException: Invalid magic prefix: d: offset=16400
	at ome.codecs.ZstdCodec.decompress(ZstdCodec.java:106)
	at ome.codecs.ZstdCodec.decompress(ZstdCodec.java:85)
	at loci.formats.codec.WrappedCodec.decompress(WrappedCodec.java:86)
	at loci.formats.codec.ZstdCodec.decompress(ZstdCodec.java:37)
	at loci.formats.tiff.TiffCompression.decompress(TiffCompression.java:288)
	at loci.formats.tiff.TiffParser.getTile(TiffParser.java:845)
	at loci.formats.tiff.TiffParser.getSamples(TiffParser.java:1135)
	at loci.formats.tiff.TiffParser.getSamples(TiffParser.java:885)
	at loci.formats.in.MinimalTiffReader.openBytes(MinimalTiffReader.java:312)
	at loci.formats.in.TiffDelegateReader.openBytes(TiffDelegateReader.java:71)
	at loci.formats.FormatReader.openBytes(FormatReader.java:922)
	at loci.formats.ImageReader.openBytes(ImageReader.java:450)
	at loci.formats.ReaderWrapper.openBytes(ReaderWrapper.java:336)
	at loci.formats.gui.BufferedImageReader.openImage(BufferedImageReader.java:86)
	at loci.formats.tools.ImageInfo.readPixels(ImageInfo.java:840)
	at loci.formats.tools.ImageInfo.testRead(ImageInfo.java:1074)
	at loci.formats.tools.ImageInfo.main(ImageInfo.java:1165)
Caused by: io.airlift.compress.MalformedInputException: Invalid magic prefix: d: offset=16400
	at io.airlift.compress.zstd.ZstdFrameDecompressor.verifyMagic(ZstdFrameDecompressor.java:976)
	at io.airlift.compress.zstd.ZstdFrameDecompressor.decompress(ZstdFrameDecompressor.java:153)
	at io.airlift.compress.zstd.ZstdDecompressor.decompress(ZstdDecompressor.java:44)
	at ome.codecs.ZstdCodec.decompress(ZstdCodec.java:103)
	... 16 more

tiffdump reports:

test1.tif:
Magic: 0x4949 <little-endian> Version: 0x2b <BigTIFF>
OffsetSize: 0x8 Unused: 0
Directory 0: offset 16400 (0x4010) next 0 (0)
ImageWidth (256) LONG (4) 1<128>
ImageLength (257) LONG (4) 1<128>
BitsPerSample (258) SHORT (3) 1<16>
Compression (259) SHORT (3) 1<50000>
Photometric (262) SHORT (3) 1<1>
ImageDescription (270) ASCII (2) 94<ImageJ=1.11a\nimages=1\nsl ...>
SamplesPerPixel (277) SHORT (3) 1<1>
Software (305) ASCII (2) 13<tiffwrite_rs\0>
DateTime (306) ASCII (2) 20<2026:06:05 17:25:50\0>
TileWidth (322) SHORT (3) 1<128>
TileLength (323) SHORT (3) 1<128>
TileOffsets (324) SHORT (3) 1<16>
TileByteCounts (325) SHORT (3) 1<16384>

As I believe we do have examples of TIFF variants (Zeiss LSM?) that store uncompressed strips with byte counts that do not reflect the bytes per pixel, I did not remove the correction in line 778, but it should now be bypassed for compressed strips/tiles. With this change, showinf on the test file opens the image without error.

@sbesson sbesson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed that using the sample file of the accompanying issue showinf fails with Bio-Formats 8.5.0 with an io.airlift.compress.MalformedInputException while decompressing the tile.

With this PR included, the reading of the metadata and pixel data works as expected

sbesson@Sebastien-GS-MacBook-Pro-2025 bioformats % ./tools/showinf ~/Downloads/test1.tif -autoscale
Checking file format [Tagged Image File Format]
Initializing reader
TiffDelegateReader initializing /Users/sbesson/Downloads/test1.tif
Reading IFDs
Populating metadata
Checking comment style
Populating OME metadata
Initialization took 0.034s

Reading core metadata
filename = /Users/sbesson/Downloads/test1.tif
Series count = 1
Series #0 :
	Image count = 1
	RGB = false (1) 
	Interleaved = false
	Indexed = false (false color)
	Width = 128
	Height = 128
	SizeZ = 1
	SizeT = 1
	SizeC = 1
	Tile size = 128 x 128
	Thumbnail size = 128 x 128
	Endianness = intel (little)
	Dimension order = XYCZT (certain)
	Pixel type = uint16
	Valid bits per pixel = 16
	Metadata complete = true
	Thumbnail series = false
	-----
	Plane #0 <=> Z 0, C 0, T 0


Reading pixel data (0-0)
	Read 1/1 planes (100%)
[done]
0.028s elapsed (28.0ms per plane)

Launching image viewer

Reading global metadata
BitsPerSample: 16
Color mode: grayscale
Compression: Zstandard
DateTime: 2026:06:05 17:25:50
ImageJ: 1.11a
ImageLength: 128
ImageWidth: 128
MetaDataPhotometricInterpretation: Monochrome
MetaMorph: no
NumberOfChannels: 1
PhotometricInterpretation: BlackIsZero
SamplesPerPixel: 1
Software: tiffwrite_rs
TileByteCounts: 16384
TileLength: 128
TileOffsets: 16
TileWidth: 128
Unit: micron
hyperstack: true
loop: false

Reading metadata

The nightly repository builds have been passing with this change - https://bf-testing-results.s3.amazonaws.com/2026/2026-06-23/build-status.log.

Before merging, we should ensure this scenario is covered either by some unit test and/or by adding a representative sample to the curated QA repository to prevent future regression.

@melissalinkert

Copy link
Copy Markdown
Member Author

curated/tiff/samples/melissa/zstd-tile-size/ has a newly generated test file (see readme) which should reproduce the original issue and read successfully with this fix. I'd expect that to be included in tonight's tests.

@sbesson sbesson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The synthetic samples was successfully included in the nightly repository builds - https://bf-testing-results.s3.amazonaws.com/2026/2026-06-24/tiff.log

@sbesson sbesson merged commit dcdbd99 into ome:develop Jun 24, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TiffParser incorrectly multiplies stripByteCounts

2 participants