DrawImage: build a scaled row once, not once per repeat - #11
Merged
Conversation
Migrating the toolkit's widgets onto DrawImage measured far less than the 7.5x the primitive shows on its own: 2.3x for Image, 1.5x for Wallpaper. The reason is that a widget almost never blits 1:1 -- a wallpaper or a photo in a panel is ENLARGED -- and the row-copy fast path required the destination to be the same width as the source, so scaled blits fell through to the per-pixel loop. Enlarging draws several destination rows from ONE source row. Building that row once and copying it to its repeats turns the cost from the destination's height into the source's, which is the whole point of enlarging. Rows that are not repeats are built by scaleRow, which samples without blending -- the row is known opaque before either path is taken. BenchmarkDrawImageScaled 434038 ns/op BenchmarkPerPixelBlitScaled 2078585 ns/op 4.8x on a 500x350 image filling a 1000x700 window. TestDrawImageScaledFastPathMatchesPerPixel compares the fast path against the per-pixel path byte for byte over four ratios -- enlarged evenly and unevenly, shrunk, and wider-but-shorter. 100% statement coverage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Migrating the toolkit's widgets onto
DrawImagemeasured far less than the 7.5× the primitive shows on its own: 2.3× forImage, 1.5× forWallpaper.The reason is that a widget almost never blits 1:1 — a wallpaper or a photo in a panel is enlarged — and the row-copy fast path required the destination to be the same width as the source. Every scaled blit fell through to the per-pixel loop, so the primitive was fast exactly in the case widgets don't hit.
Enlarging draws several destination rows from one source row. Building that row once and copying it to its repeats turns the cost from the destination's height into the source's — which is the whole point of enlarging. Rows that are not repeats go through
scaleRow, which samples without blending, the row being known opaque before either path is taken.BenchmarkDrawImageScaledBenchmarkPerPixelBlitScaled4.8× on a 500×350 image filling a 1000×700 window. The 1:1 case is unchanged.
Correctness
TestDrawImageScaledFastPathMatchesPerPixelcompares the fast path against the per-pixel path byte for byte over four ratios — enlarged evenly, enlarged unevenly, shrunk, and wider-but-shorter. A row-repeat that reuses a row built under different conditions would show up immediately.The repeat is only taken when the row is known opaque and unclipped, because a composited row depends on what was underneath it and cannot be reused.
100% statement coverage.
🤖 Generated with Claude Code