Skip to content

Drop GIF frame rows outside the logical screen and stop on LZW codes past the code tables - #3224

Merged
JimBobSquarePants merged 3 commits into
SixLabors:mainfrom
jafin:jf/gif-lzw-and-frame-geometry
Oct 27, 2026
Merged

JimBobSquarePants merged 3 commits into
SixLabors:mainfrom
jafin:jf/gif-lzw-and-frame-geometry

Conversation

@jafin

@jafin jafin commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

Clip GIF frame rows and columns to the logical screen without losing visible rows from later interlace passes. Skip empty interlace passes in short frames. Frames entirely outside the screen leave their data to the existing sub-block skipper.

Check the first LZW code before indexing the 4096-entry tables. A tolerated minimum code size of 12 can produce code 4098 before any literal, or after a clear code. This previously threw IndexOutOfRangeException in both Load and Identify.

Detected out-of-table codes now throw InvalidImageContentException. Strict and IgnoreAncillary reject the corrupt image data. IgnoreImageData retains completed rows and skips the rest of that frame, then continues the animation. Header and palette parsing stay outside this recovery boundary. Existing canvas-clipping compatibility remains unchanged.

Dispose a partially decoded image when decoding fails. The stored regressions also reproduced an undisposed pixel buffer on the previous failure paths.

Testing

All new test inputs are stored files: 17 GIFs, 2,310 bytes total. No GIF writer, LZW encoder setup or input generation remains in the added tests. The fixtures cover code errors before and after a clear, an invalid code in a hidden interlace row, clipped and short interlace passes, and fully hidden frames. Every geometry fixture has a following frame to verify continued animation parsing.

Headers, palettes, sub-block boundaries and malformed code sequences were independently checked. Pillow verifies every geometry fixture's frame samples and following white-pixel frame. Interlace row order follows GIF89a Appendix E: https://www.w3.org/Graphics/GIF/spec-gif89a.txt

Current main fails 13 of the 19 new regression cases.

Release .NET 10 verification:

  • GIF format tests: 158 passed, including existing deferred-clear, bad-dimension and reference-output cases.
  • Other GIF-related tests and shared format/dimension tests: 60 passed.
  • Final focused regression run: 19 passed.

No tolerances or reference images changed. The branch includes current main.

Fixes #3236.

…past the code tables

An interlaced frame whose lines fall below the logical screen clamped the target row to the
screen height and threw from the row lookup, frames shorter than five lines wrote their lines
to the wrong rows, and a frame starting right of the screen threw while slicing the row.
Lines below the screen are now decoded and discarded only while a later interlace pass can
still reach the screen, and frames starting outside the screen are skipped.

With a minimum code size of 12 the first code after a clear code could index past the
4096-entry LZW tables. Decoding and skipping now stop at such a code.
Reject detected out-of-table LZW codes with InvalidImageContentException.
Only IgnoreImageData recovers by retaining completed rows and using the
existing sub-block skipper. Keep header and palette parsing outside recovery.
Dispose the partially decoded image when decoding fails; the new stored
regressions reproduce the missing cleanup on the original main branch.

Replace all input generation introduced by this PR with 17 stored GIFs
(2310 bytes). Independently verify descriptors, palettes, sub-block boundaries,
and malformed code sequences. Pillow checks every geometry fixture's frame
samples and following white-pixel frame. Cover code errors before and after
clear, hidden-row index skipping, all integrity modes, clipped interlace
passes, short interlace passes, and continued animation parsing.

Current main fails 13 of the 19 new cases. Release .NET 10: 158 GIF tests
and 60 GIF-related/shared decoder tests pass. Final focused run: 19 passed.
No reference images or tolerances changed.
@JimBobSquarePants
JimBobSquarePants merged commit 11af26c into SixLabors:main Oct 27, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants