Fix PictureBox StretchImage repaint perf regression by caching scaled bitmap#14559
Open
LeafShi1 wants to merge 3 commits into
Open
Fix PictureBox StretchImage repaint perf regression by caching scaled bitmap#14559LeafShi1 wants to merge 3 commits into
LeafShi1 wants to merge 3 commits into
Conversation
…on-aware bitmap caching with memory cap and lazy invalidation, while preserving animation behavior and updating tests accordingly.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR addresses a reported performance regression in PictureBox when SizeMode=StretchImage by introducing a cache of the scaled bitmap to avoid repeated high-cost scaling during frequent repaints (e.g., resizing).
Changes:
- Add a per-
PictureBoxscaled-bitmap cache keyed by destination size andGraphics.InterpolationMode, and render cached results via a non-scaling draw path. - Invalidate/dispose the cache on image changes,
SizeModechanges, and control disposal; bypass caching for animated images and oversized targets. - Add unit tests validating cache reuse/recreation behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/System.Windows.Forms/System/Windows/Forms/Controls/PictureBox/PictureBox.cs | Adds scaled bitmap caching logic to optimize StretchImage painting. |
| src/test/unit/System.Windows.Forms/System/Windows/Forms/PictureBoxTests.cs | Adds unit tests for the new StretchImage cache behavior (reuse, recreation, bypass). |
Comments suppressed due to low confidence (2)
src/System.Windows.Forms/System/Windows/Forms/Controls/PictureBox/PictureBox.cs:1172
- TryGetStretchImageCache returns false for non-cacheable scenarios (e.g., CanCacheStretchImage is false for large sizes, or ImageAnimator.CanAnimate is true) without clearing any previously created _stretchedImageCache. If a cache was built at a smaller size and the control later becomes non-cacheable (e.g., resized above the threshold), the old bitmap stays allocated indefinitely even though it will never be used. Consider disposing the existing cache when the method determines caching is not applicable for the current state.
if (_sizeMode != PictureBoxSizeMode.StretchImage
|| _image is null
|| _imageInstallationType == ImageInstallationType.ErrorOrInitial
|| drawingRect.Width <= 0
|| drawingRect.Height <= 0
|| ImageAnimator.CanAnimate(_image)
|| !CanCacheStretchImage(drawingRect.Size))
{
return false;
src/System.Windows.Forms/System/Windows/Forms/Controls/PictureBox/PictureBox.cs:1196
- CanCacheStretchImage uses (long)width * height * 4 to estimate allocation size. For extremely large dimensions, the multiplication can overflow Int64 (e.g., width≈height≈int.MaxValue), potentially making the expression appear below the threshold and leading to an attempt to allocate an impossibly large Bitmap. Consider using checked arithmetic or (ulong) multiplication with explicit overflow handling/clamping before comparing to MaxCachedStretchImageBytes.
private static bool CanCacheStretchImage(Size drawingSize)
=> (long)drawingSize.Width * drawingSize.Height * sizeof(uint) <= MaxCachedStretchImageBytes;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…icit destination rectangle and GraphicsUnit.Pixel
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.
Fixes #14009
Proposed changes
StretchImage(key: source image + destination size).OnPaint, when cache is hit, render via non-scaling path (DrawImageUnscaled).SizeModechange,Resize, andDispose.ImageAnimator.CanAnimate) to preserve frame update semantics.Customer Impact
StretchImageRegression?
Risk
Screenshots
Before
On a page containing a PictureBox that uses StretchImage, pressing A to run 1000 refreshes takes about 11348ms.
After
The same 1000 refreshes complete much faster.
Test methodology
Test environment(s)
Microsoft Reviewers: Open in CodeFlow