[GTK] Stop decoding the image file on every draw at zoom != 100 - #3510
Open
vogella wants to merge 1 commit into
Open
[GTK] Stop decoding the image file on every draw at zoom != 100#3510vogella wants to merge 1 commit into
vogella wants to merge 1 commit into
Conversation
The internal drawImage that every public overload funnels into read the source dimensions from srcImage.getImageData(), which is getImageData(100). At currentDeviceZoom != 100 that misses the zoom == currentDeviceZoom fast path and loads new ImageData(fileName): a full open and full decode, for SVG a full re-parse and re-rasterize, to obtain two integers the Image already knows. The result was used for nothing else, the drawing itself goes through srcImage.surface. An ImageGcDrawer image paid the same way, by running the drawer callback once per draw. Take the dimensions from the Image, falling back to ImageData for images wrapped around a native handle by Image.gtk_new, which carry none. Those have no provider, so the fallback reads the cairo surface and never a file. Cocoa already takes its dimensions from NSImage.size() and win32 from getBounds(), so this brings GTK in line with both. Opens per draw over 100 draws of one Image at zoom 200, strace on Linux/GTK: 9 arg overload, PNG 1.00 -> 0.00 5 arg overload, PNG 2.00 -> 1.00 The open left on the 5 arg overload is the CachedImageAtSize path of issue 3505. Rendering is unchanged wherever the Image dimensions agree with the decoded ones, verified as identical SHA-256 of the drawn ImageData over both overloads, three files and zoom 100, 150 and 200, and again for an ImageFileNameProvider returning one path at every zoom. Two cases change, both where getBounds() and getImageData() already disagreed. For an asset set that is not exactly proportional, 16 pixels at 100% and 33 at 200%, the width field is round(33/2) = 17 while getImageData(100) gives 16, so at zoom 200 the unscaled draw now paints the last point row and column that were previously clipped. For a provider handing out the same file at every zoom, a 16 pixel file at zoom 200 gives bounds 8 and getImageData 16, so passing getImageData() dimensions as the source rectangle now raises ERROR_INVALID_ARGUMENT where it previously drew. In both cases the new value is the one that agrees with getBounds(). Fixes eclipse-platform#3507
Contributor
Test Results 211 files - 1 211 suites - 1 27m 10s ⏱️ - 1m 17s For more details on these errors, see this check. Results for commit 3443bc6. ± Comparison against base commit 9ca368d. This pull request removes 57 and adds 1 tests. Note that renamed tests count towards both. |
This was referenced Aug 13, 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.
The internal
drawImagethat every public overload funnels into read the source dimensions fromsrcImage.getImageData(), which isgetImageData(100).At a device zoom other than 100 that misses the fast path and does
new ImageData(fileName), a full open and decode of the image file, for SVG a full re-parse and re-rasterize, just to obtain two integers theImagealready knows.An
ImageGcDrawerimage paid the same way, by running the drawer callback once per draw.Taking the dimensions from the
Imageremoves that, with a fallback for images wrapped around a native handle byImage.gtk_new, which carry none and have no provider, so the fallback reads the in-memory cairo surface and never a file.Measured with
strace -f -e trace=openatover 100 draws of oneImageat zoom 200, opens per draw fall from 1.00 to 0.00 for the 9 argument overload and from 2.00 to 1.00 for the 5 argument one; the remaining open there is theCachedImageAtSizepath of #3505, addressed separately in #3506.Cocoa already takes its dimensions from
NSImage.size()and win32 fromgetBounds(), so this brings GTK in line with both.Two cases change behavior, both where
getBounds()andgetImageData()already disagreed, and in both the new value is the one that agrees withgetBounds().A non-proportional asset set (16 pixels at 100%, 33 at 200%) now paints the last point row and column that were previously clipped, and a provider handing out the same file at every zoom now validates the source rectangle against the bounds, so passing
getImageData()dimensions there raisesERROR_INVALID_ARGUMENTwhere it previously drew.Everything else renders identically, verified as matching SHA-256 of the drawn
ImageDataacross both overloads, three files and zoom 100, 150 and 200.The new test deletes the image file after the first draw, so any further read fails loudly; it was confirmed to fail before the change and pass after.
It runs on all platforms although the fix is GTK only, so the win32 and macOS workflow results are worth a look rather than a rubber stamp.
Fixes #3507