Skip to content

fix: handle weezl NoProgress on small interlaced GIF output buffers#233

Open
lilith wants to merge 2 commits intoimage-rs:masterfrom
lilith:fix/small-interlaced-decode
Open

fix: handle weezl NoProgress on small interlaced GIF output buffers#233
lilith wants to merge 2 commits intoimage-rs:masterfrom
lilith:fix/small-interlaced-decode

Conversation

@lilith
Copy link
Copy Markdown
Contributor

@lilith lilith commented Apr 13, 2026

Stacked on #232 (regression tests).

Problem

Small interlaced GIFs (7x7, 9x9, 47x63) produced by gifsicle 1.95 are rejected with "image truncated". All decode correctly with giftopnm (netpbm).

Root cause

When decoding interlaced frames, read_into_buffer passes per-row slices to the LZW decoder. For small images (7-47 byte rows), weezl returns NoProgress because the output buffer is too small for its internal state machine to advance.

Two bugs in DecodeSubBlock:

  1. Input skipped (left > 0): NoProgress with consumed=0, bytes_len=0 caused consumed = n, skipping all remaining sub-block input
  2. Premature frame end (left = 0): NoProgress during flush treated as end-of-stream

Fix

lzw_drain_one helper: retry LZW decode with a 1-byte temp buffer to unstick weezl, append the byte to the caller's output via OutputBuffer::append.

Related: image-rs/weezl#72

Test plan

lilith added 2 commits April 13, 2026 06:19
Three valid interlaced GIFs (7x7, 9x9, 47x63) produced by gifsicle 1.95
are rejected with "image truncated". All decode correctly with giftopnm
(netpbm) and are valid per the GIF89a spec.

Root cause: when decoding interlaced frames, read_into_buffer passes
per-row slices to the LZW decoder. For small images (7-47 byte rows),
weezl's NoProgress status triggers two bugs in DecodeSubBlock:
1. Input data is skipped instead of retried with a smaller buffer
2. NoProgress during flush is treated as end-of-stream

Related: image-rs/weezl#72
When decoding interlaced frames, read_into_buffer passes per-row slices
to the LZW decoder. For small images (7-47 byte rows), weezl returns
NoProgress because the output buffer is too small for its internal state
machine to advance.

Two bugs fixed in DecodeSubBlock:

1. Input data skipped (left > 0): When NoProgress with consumed=0 and
   bytes_len=0, the decoder executed `consumed = n`, skipping all
   remaining sub-block input without processing it.

2. Premature frame end (left = 0): During flush, NoProgress with 0
   output was treated as end-of-stream, but weezl still had buffered
   data.

Fix: retry LZW decode with a 1-byte temp buffer (lzw_drain_one) to
unstick weezl, then append the byte to the caller's output.

Related: image-rs/weezl#72
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.

1 participant