Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect VP8X Canvas Dimensions in Animated WebP Files #145

Open
skidder opened this issue Aug 23, 2024 · 2 comments
Open

Incorrect VP8X Canvas Dimensions in Animated WebP Files #145

skidder opened this issue Aug 23, 2024 · 2 comments

Comments

@skidder
Copy link

skidder commented Aug 23, 2024

Summary

Piexif incorrectly sets the VP8X canvas dimensions for certain animated WebP files, leading to a "Frame exceeds canvas" error when frames exceed the dimensions of the last frame.

Steps to Reproduce:

  1. Process an animated WebP file using Piexif where the frames vary in size.
  2. Ensure that the last frame is smaller than some of the previous frames.
  3. Run a tool like webpinfo on the output file to observe the error.

This can be reproduced with the pil_animated2.webp file in my fork: 5a12974

Input Test File

→ webpinfo -diag tests/images/pil_animated2.webp
File: tests/images/pil_animated2.webp
RIFF HEADER:
  File size:  37342
Chunk VP8X at offset     12, length     18
  ICCP: 0
  Alpha: 1
  EXIF: 0
  XMP: 0
  Animation: 1
  Canvas size 400 x 400
Chunk ANIM at offset     30, length     14
  Background color:(ARGB) ff 00 00 00
  Loop count      : 0
Chunk ANMF at offset     44, length   5190
  Offset_X: 0
  Offset_Y: 0
  Width: 400
  Height: 400
  Duration: 70
  Dispose: 0
  Blend: 1
Chunk VP8L at offset     68, length   5166
  Width: 400
  Height: 400
  Alpha: 0
  Animation: 0
  Format: Lossless (2)
  
  ...
  
Chunk ANMF at offset  34824, length   2518
  Offset_X: 0
  Offset_Y: 6
  Width: 320
  Height: 382
  Duration: 70
  Dispose: 0
  Blend: 0
Chunk VP8L at offset  34848, length   2494
  Width: 320
  Height: 382
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
No error detected.

Output Test File

→ webpinfo -diag tests/images/out/i_pil_animated2.webp
File: tests/images/out/i_pil_animated2.webp
RIFF HEADER:
  File size:  37400
Chunk VP8X at offset     12, length     18
  ICCP: 0
  Alpha: 0
  EXIF: 1
  XMP: 0
  Animation: 1
  Canvas size 320 x 382
Chunk EXIF at offset     30, length     58
Chunk ANIM at offset     88, length     14
  Background color:(ARGB) ff 00 00 00
  Loop count      : 0
Chunk ANMF at offset    102, length   5190
  Offset_X: 0
  Offset_Y: 0
  Width: 400
  Height: 400
  Duration: 70
  Dispose: 0
  Blend: 1
Error: Frame exceeds canvas in ANMF chunk.
Errors detected.

Expected Behavior:

The VP8X chunk should reflect the maximum dimensions across all frames to correctly represent the entire canvas, preventing any "Frame exceeds canvas" errors.

Actual Behavior:

The VP8X chunk dimensions are set based on the last frame processed. If this frame is smaller than earlier frames, the overall canvas size is incorrect, leading to errors.

This issue affects users processing animated WebP files with frames of varying sizes. Ensuring the correct canvas size in the VP8X chunk is critical for maintaining compatibility and avoiding errors in tools that validate WebP file structure.

Technical Details

The issue arises because the set_vp8x function in the piexif library sets the width and height based on the last frame it processes. This approach fails to account for previous frames that may be larger, resulting in an incorrect canvas size.

WebP Image (OK)

For non-animated WebP files, the VP8X chunk correctly represents the image dimensions:

+--------------------------+
|   WebP Container          |
|                          |
|  +--------------------+  |
|  |     VP8X Chunk     |  |  <-- Flags and size info
|  +--------------------+  |
|                          |
|  +--------------------+  |
|  |    VP8L Chunk      |  |  <-- Image data (Lossless)
|  +--------------------+  |
|                          |
+--------------------------+

Animated WebP (Broken)

For animated WebP files, the VP8X chunk incorrectly sets the canvas dimensions based on the last frame processed, even if earlier frames are larger:

+--------------------------+
|   WebP Container          |
|                          |
|  +--------------------+  |
|  |     VP8X Chunk     |  |  <-- Flags and canvas size info (incorrectly set)
|  +--------------------+  |
|                          |
|  +--------------------+  |
|  |     ANIM Chunk     |  |  <-- Animation control info
|  +--------------------+  |
|                          |
|  +--------------------+  |
|  |    ANMF Chunk      |  |  <-- Frame 1 (larger than last frame)
|  |  +--------------+  |  |
|  |  | VP8L Chunk   |  |  |  <-- Image data
|  +--------------------+  |
|                          |
|  +--------------------+  |
|  |    ANMF Chunk      |  |  <-- Frame 2 (smaller, last frame processed)
|  |  +--------------+  |  |
|  |  | VP8L Chunk   |  |  |  <-- Image data
|  +--------------------+  |
|                          |
+--------------------------+

Proposal:

Modify the set_vp8x function to track the maximum width and height across all frames. The VP8X chunk should be updated to reflect these maximum dimensions, ensuring that the canvas size accommodates all frames.

I have a commit in my fork that appears to resolve this: 7c6522a

@nico
Copy link

nico commented Sep 6, 2024

Modify the set_vp8x function to track the maximum width and height across all frames.

That isn't necessarily enough. Some webp files have a canvas that's larger than all frames.

Here's a file where the canvas is 16x16 but the two animation frames are 12x10 and 8x2 respectively:

test.webp.zip

 % ~/Downloads/libwebp-1.4.0-rc1-mac-arm64/bin/webpinfo ~/Downloads/test.webp
File: /Users/thakis/Downloads/test.webp
RIFF HEADER:
  File size:    154
Chunk VP8X at offset     12, length     18
  ICCP: 0
  Alpha: 1
  EXIF: 0
  XMP: 0
  Animation: 1
  Canvas size 16 x 16
Chunk ANIM at offset     30, length     14
  Background color:(ARGB) ff ff ff ff
  Loop count      : 0
Chunk ANMF at offset     44, length     64
  Offset_X: 2
  Offset_Y: 4
  Width: 12
  Height: 10
  Duration: 100
  Dispose: 0
  Blend: 1
Chunk VP8L at offset     68, length     40
  Width: 12
  Height: 10
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
Chunk ANMF at offset    108, length     46
  Offset_X: 4
  Offset_Y: 4
  Width: 8
  Height: 2
  Duration: 100
  Dispose: 0
  Blend: 1
Chunk VP8L at offset    132, length     22
  Width: 8
  Height: 2
  Alpha: 1
  Animation: 0
  Format: Lossless (2)
No error detected.

(This images was drawn by myself and saved using aseprite. Feel free to use it in tests, I hereby put it in the public domain.)

@skidder
Copy link
Author

skidder commented Sep 11, 2024

That isn't necessarily enough. Some webp files have a canvas that's larger than all frames.

Here's a file where the canvas is 16x16 but the two animation frames are 12x10 and 8x2 respectively:

@nico Thanks for the follow-up and that interesting test case! It's right to include examples of WebPs with canvases bigger than their frames - definitely adds another layer to think about.

I've taken a good look at your test fix, and I think it's actually handling these cases pretty well for spec-compliant WebPs. Here's the gist:

  • The code checks all chunks (VP8X, VP8, VP8L, ANMF) and finds the biggest dimensions.
  • If there's already a VP8X chunk (like in your test.webp), it considers those dimensions too.
  • It then creates a new VP8X chunk using the largest size it found, so all frames should fit.

For your 16x16 canvas example with smaller frames:

  • If there's a VP8X chunk saying it's 16x16, we'll use that size since it's bigger than the frames.
  • If there's no VP8X chunk, we'll use the size of the largest frame, which works too.

I've thrown a bunch of different WebPs at it, including some funky animated ones, and it seems to be handling them fine.
That said, if you've got any other tricky WebPs or a nuance to this case that I've missed, please let me know.

Thanks again for diving into this!

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

No branches or pull requests

2 participants