feat: gif->gif memory usage optimizations; add gifski option - #40
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #40 +/- ##
==========================================
+ Coverage 93.80% 95.32% +1.52%
==========================================
Files 17 17
Lines 855 1134 +279
Branches 144 188 +44
==========================================
+ Hits 802 1081 +279
Misses 48 48
Partials 5 5
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
fdintino
force-pushed
the
feat/gif-memory-opt-gifski
branch
4 times, most recently
from
July 2, 2026 15:02
f3d97c3 to
c9762dd
Compare
fdintino
marked this pull request as ready for review
July 2, 2026 15:04
fdintino
force-pushed
the
feat/gif-memory-opt-gifski
branch
from
July 2, 2026 15:20
c9762dd to
4b0a6b4
Compare
fdintino
force-pushed
the
feat/gif-memory-opt-gifski
branch
from
July 2, 2026 16:22
4b0a6b4 to
175ca03
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This fixes excessive python heap usage when the engine processed animated GIFs (a 1920x1080 125-frame GIF would use 6 GiB of memory in-process, likely causing OOM). It also adds an optional, faster and higher-quality gifski encoding pipeline. All new code has test coverage, and not an insignificant amount of existing code has additional test coverage.
The two sources of unnecessary memory usage and how they were fixed:
The
probe()function decoded every frame just to read metadata. This is unavoidable when using Pillow because their GifImagePlugin loads every frame into memory. In our case, where we only need the metadata, this can be avoided.This was fixed by adding a
parse_giffunction that follows Pillow's parsing logic as closely as it can while skipping frame loading and the parsing of metadata unnecessary for the gif pipeline (tile, palette, disposal, mode). Pillow's own tests for the metadata parsing have been copied over into this repository to check for correctness, including those testing error handling of malformed GIFs.When both the input and output formats are GIF, the code would load the decoded, original-size GIF frames into memory, use Pillow to perform the crop and resize operations, then pipe them into
gifsicleviastdin. It now uses ffmpeg to perform crops and resizing first, saving the intermediate files on disk in temporary files (so no part of the original geometry operations loads the entire decoded GIF into memory). When gifsicle is enabled the optimization is run file-to-file, so only the final image data is ever fully loaded into memory.Some additional related changes:
Added gifski pipeline (
FFMPEG_GIF_PIPELINE="gifski")This allows users to optionally use gifski if the binary is available and the conditions allow it. Gifski is much faster and produces higher perceptual quality than gifsicle. At small-to-moderate output sizes,
gifskigifs are also generally smaller than gifsicle's. But its quantizer's memory usage scales with output dimensions, and at larger dimensions it underperforms gifsicle on output file size, so use of gifski is gated byGIFSKI_MAX_TARGET_PIXELSto account for both limitations. Gifski does not support animated gifs with variable frame durations, so those get passed through the old pipeline as well.Added
MAX_ANIMATED_GIF_PIXELSgateAs an additional safety check, allows users to return a 400 if an input animated gif's total pixel size across frames exceeds a configurable threshold and the output format is also gif. The default value is
0, which disables any such restrictions. As you can see from the measurement table below, the non-gifski encoding options might be prohibitively slow for large images. Users can make trade-offs between response time, peak memory usage, and output file sizes by togglingGIFSKI_MAX_TARGET_PIXELSandMAX_ANIMATED_GIF_PIXELS.Measurements
I've done before-and-after tests using a 1920x1080x125 source GIF with
FFMPEG_USE_GIFSICLE_ENGINE=TrueandGIFSICLE_ARGS=['--lossy=80']