Repository navigation
Integer resampling, vertical pass first - #68
Merged
Merged
Conversation
Resizing used float arithmetic through loops over the filter taps, in premultiplied alpha, horizontal pass first: 49 ms to halve a 1920x1080 photo, slower than Jimp's bilinear (which reads 2x2 pixels without antialiasing, where ours reads 4x4). - Weights are integers with 14 fractional bits (rounded so they still sum to one), every output index gets the same number of taps, and the passes unroll by it. - The vertical pass runs first: its weights are the same for a whole row, so it's the cheaper one to run on the larger image. - Opaque images skip premultiplying. With non-negative filters (bilinear, area) and up to 4 taps per output row, red and blue share a 32-bit multiplication (8-bit weights, 16-bit lanes). The pass notices pixels that aren't opaque and the image is done premultiplied instead. - The box filter's weights could all be 0 when enlarging (a pixel center right on the filter's edge): those pixels came out transparent, now they take the pixel in range. Results differ from before by at most 1 per channel, checked on 1313 corpus images x 27 sizes/modes and on noise (the worst case for rounded weights). Larger differences are only in the color of pixels with alpha 0 or 1 and the transparent lines that are fixed. Every other operation is unchanged (hashes compared with master), the test targets that resize are regenerated. Halving the photo: 15 ms bilinear, 9 ms area (3.5x faster), bicubic and images with alpha 20-40% faster. clone() copies in the array's constructor instead of into a zeroed array: about 50% faster. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJgRK5qpwJEbHPuFv6kgAQ
This was referenced Oct 16, 2026
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.
Resizing was the one benchmark row where ImageScript was slower than Jimp. On GitHub's runner the numbers were 30.6 vs. 87.1 ops/s for halving a 1920×1080 photo bilinearly.
Jimp's bilinear reads 2×2 pixels per output pixel, without antialiasing. Ours is a proper filter like Pillow's and reads 4×4. That makes it slower by design, but the bigger cost was the implementation: float arithmetic in a loop over the filter taps, with the horizontal pass first.
Changes
clone()copies in the array's constructor instead of into a zeroed array, which makes it about 50% faster.RESIZE_AREA, the box filter's weights could all be 0, so whole rows and columns came out transparent. Those pixels now take the pixel in range.Speed (this LXC, CPU time)
The benchmark run locally on the same machine:
Results
The output is no longer bit-identical: it differs from 3.2.0 by at most 1 per channel. This was checked on:
Larger differences show up in only two places:
Every other operation is unchanged (hashes compared with master over the corpus).
tests/targets/{resize,effects,readme}.pngare regenerated: resize differs by ≤1, and effects like sharpen and color-dodge amplify that to a few levels in some pixels.The changelog entry is under 3.2.1.
🤖 Generated with Claude Code
https://claude.ai/code/session_01YJgRK5qpwJEbHPuFv6kgAQ