Skip to content

TIFF decode: 8-bit Palette + LZW + Predictor=2 produces near-uniform near-black pixel data #3182

Description

@roguestabster01

Description

Decoding a TIFF with 8-bit Palette color (PhotometricInterpretation=3), LZW compression, and Predictor=2 (horizontal differencing) produces near-uniform, near-black pixel data across the entire image, regardless of the actual page content. The same bytes decode correctly in libvips/NetVips (NetVips.Image.NewFromFile), which I used to visually confirm the real page content and rule out a corrupt source file.

Affected files in my case are real scanned document pages (2479x3504, 300dpi) from a document corpus that may contain sensitive information — 64 out of 864 sampled files, all sharing this exact tag combination, all misdecoded the same way. The other 800 files (bilevel Group4 fax-style, or 24-bit RGB LZW) decode fine.

Repro details

Tag values on the affected files (via Image.Load + inspecting decoded pixel data, and cross-checked with Pillow's tag_v2):

  • PhotometricInterpretation (262) = 3 (Palette)
  • BitsPerSample (258) = 8
  • Compression (259) = 5 (LZW)
  • Predictor (317) = 2 (horizontal differencing)
  • RowsPerStrip (278) = image height (single full-height strip, not tiled)
  • ColorMap (320) = a 768-entry (256×3) 16-bit colormap
  • Typical size: 2479×3504

What I tried

I attempted to build a minimal synthetic repro with matching tags (solid 2-color palette, and separately a richer grayscale-gradient palette image), written via Pillow with tiffinfo={317: 2} to force the predictor tag and compression="tiff_lzw". Both synthetic files decode correctly in ImageSharp — only the real, larger, higher-entropy archive files trigger the bug. This suggests the issue may be specific to LZW code-width transitions or something else that only manifests at realistic page size/entropy (my synthetic images were 200x200 / 400x400, far smaller and lower-entropy than the real 2479x3504 scanned pages), rather than a straightforward "predictor not applied" bug — a real minimal repro would help narrow this down but I haven't been able to construct one yet.

Impact

Any blank-page / content detection, thumbnailing, or other logic built on ImageSharp's decoded pixel data for this TIFF variant will silently get garbage (near-uniform near-black) instead of the real page content — no exception is thrown, so it's a silent correctness bug rather than a crash.

Environment

  • SixLabors.ImageSharp 4.1.0
  • .NET 10.0, Windows

Can I attach a repro file?

The real files that trigger this may contain sensitive information, so I can't post one publicly here. Happy to try harder to build a byte-exact synthetic repro, or share a sanitized/cropped file privately if that would help — let me know what would be most useful.

Activity

  1. brianpopow commented on Aug 26, 2026

    @brianpopow
    Collaborator

    The real files that trigger this may contain sensitive information, so I can't post one publicly here.

    Would it be possible that you send me the file via mail, so i could reproduce the issue?

    Maybe it is possible to create a tiff image with similar properties with the command line tool imagemagick?

  2. brianpopow commented on Aug 27, 2026

    @brianpopow
    Collaborator

    It is possible to create a tiff with the same properties like this:

    magick convert img.jpg -define tiff:predictor=2 -compress lzw -colors 256 -type Palette test.tiff
    

    But this does not seem to reproduce any issue when decoding with ImageSharp.

  3. roguestabster01 commented on Aug 29, 2026

    @roguestabster01
    Author

    Thanks — your ImageMagick command was a useful clue. I was able to reduce this to a
    shareable synthetic reproducer, so no private archival file is needed.

    Starting from a colourful JPEG, this command produces a TIFF that decodes correctly in
    ImageSharp:

    magick convert input.jpg -define tiff:predictor=2 -compress lzw -colors 256 -type Palette normal.tiff
    

    It has the same relevant high-level properties as the affected files: 8-bit palette
    colour, LZW compression, Predictor=2, one strip, and a 256×3 ColorMap. The important
    difference is the range of the ColorMap entries:

    • ImageMagick's normal output uses the TIFF-spec 16-bit range (maximum 65535) and
      ImageSharp decodes it correctly.
    • The affected archival files use a ColorMap whose entries only reach 255. Rewriting
      only the synthetic file's 768 ColorMap entries to that 0–255 range makes ImageSharp
      reproduce the problem: a colourful source decodes with maximum RGB channel value 1
      (near-uniform black).

    No image pixels, compression stream, predictor tag, dimensions, or other TIFF tags are
    changed by that rewrite. Other decoders (libvips/NetVips) still show the expected colour
    variation from the rewritten file.

    I can attach the small generated TIFF and a minimal C# test/rewrite helper if that is
    useful. Does this point to the decoder treating ColorMap entries as though they were
    always full-range 16-bit values, rather than scaling the observed 0–255 palette range?

  4. brianpopow commented on Aug 31, 2026

    @brianpopow
    Collaborator

    I can attach the small generated TIFF and a minimal C# test/rewrite helper if that is
    useful.

    @roguestabster01 yes, that would be useful. You can drag and drop the image here in github when you start a new comment.

  5. JimBobSquarePants commented on Sep 14, 2026

    @JimBobSquarePants
    Member

    Thanks, those instructions were enough to reproduce this without the original image.

    I've added a regression test using synthetic TIFFs that differ only in their ColorMap values. The normal 0–65535 palette passes; the 0–255 version fails.

    The decoder currently treats all palette entries as full-range 16-bit values, which explains the near-black output. No additional image is needed.

  6. JimBobSquarePants commented on Sep 14, 2026

    @JimBobSquarePants
    Member

    Fixed via #3189

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions