[PATCH] ui/cocoa: fix artifacts from scaled partial redraws

Stefan Reinauer via qemu development posted 1 patch 1 month, 1 week ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260614000409.63735-1-stefan.reinauer@coreboot.org
Maintainers: Peter Maydell <peter.maydell@linaro.org>, "Philippe Mathieu-Daudé" <philmd@mailo.com>, Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>, "Marc-André Lureau" <marcandre.lureau@redhat.com>
ui/cocoa.m | 25 +++++++------------------
1 file changed, 7 insertions(+), 18 deletions(-)
[PATCH] ui/cocoa: fix artifacts from scaled partial redraws
Posted by Stefan Reinauer via qemu development 1 month, 1 week ago
Under zoom-to-fit, Cocoa redrew each dirty rect by cropping the
guest CGImage and drawing that crop into the dirty destination
rect. With non-integer scales, adjacent partial updates could sample
the source with different phase. Moving cursor updates then showed
one-line stretch/skew artifacts at the dirty band boundary.

Keep using AppKit's dirty-region clipping, but draw the full guest
image into the view bounds. Each partial update then uses the same
source transform while drawing remains limited to invalidated
regions.

Signed-off-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
---
 ui/cocoa.m | 25 +++++++------------------
 1 file changed, 7 insertions(+), 18 deletions(-)

diff --git a/ui/cocoa.m b/ui/cocoa.m
index c5e639ab98..f294c20720 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -551,24 +551,13 @@ - (void) drawRect:(NSRect) rect
             0, //interpolate
             kCGRenderingIntentDefault //intent
         );
-        // selective drawing code (draws only dirty rectangles) (OS X >= 10.4)
-        const NSRect *rectList;
-        NSInteger rectCount;
-        int i;
-        CGImageRef clipImageRef;
-        CGRect clipRect;
-
-        [self getRectsBeingDrawn:&rectList count:&rectCount];
-        for (i = 0; i < rectCount; i++) {
-            clipRect = rectList[i];
-            clipRect.origin.y = (float)h - (clipRect.origin.y + clipRect.size.height);
-            clipImageRef = CGImageCreateWithImageInRect(
-                                                        imageRef,
-                                                        clipRect
-                                                        );
-            CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef);
-            CGImageRelease (clipImageRef);
-        }
+        NSRect imageRect = NSMakeRect(0, 0, w, h);
+
+        /*
+         * AppKit already clips drawing to the dirty region. Draw the full
+         * guest image so scaled partial updates use a stable source transform.
+         */
+        CGContextDrawImage(viewContextRef, cgrect(imageRect), imageRef);
         CGImageRelease (imageRef);
         CGDataProviderRelease(dataProviderRef);
     }
-- 
2.50.1 (Apple Git-155)
Re: [PATCH] ui/cocoa: fix artifacts from scaled partial redraws
Posted by Akihiko Odaki 1 month, 1 week ago
On 2026/06/14 9:04, Stefan Reinauer wrote:
> Under zoom-to-fit, Cocoa redrew each dirty rect by cropping the
> guest CGImage and drawing that crop into the dirty destination
> rect. With non-integer scales, adjacent partial updates could sample
> the source with different phase. Moving cursor updates then showed
> one-line stretch/skew artifacts at the dirty band boundary.

This fix is fine, but I think it's unlikely that "adjacent partial 
updates could sample the source with different phase". You'll observe 
tearing even with integer scales if that is the case. This fix does not 
deal with timing either.

My guess is that some fractions in rectList were rounded in 
CGImageCreateWithImageInRect() or before reaching the function, and that 
caused sampling one line more or less, causing stretch/skew artifacts.

> 
> Keep using AppKit's dirty-region clipping, but draw the full guest
> image into the view bounds. Each partial update then uses the same
> source transform while drawing remains limited to invalidated
> regions.
> 
> Signed-off-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
> ---
>   ui/cocoa.m | 25 +++++++------------------
>   1 file changed, 7 insertions(+), 18 deletions(-)
> 
> diff --git a/ui/cocoa.m b/ui/cocoa.m
> index c5e639ab98..f294c20720 100644
> --- a/ui/cocoa.m
> +++ b/ui/cocoa.m
> @@ -551,24 +551,13 @@ - (void) drawRect:(NSRect) rect
>               0, //interpolate
>               kCGRenderingIntentDefault //intent
>           );
> -        // selective drawing code (draws only dirty rectangles) (OS X >= 10.4)
> -        const NSRect *rectList;
> -        NSInteger rectCount;
> -        int i;
> -        CGImageRef clipImageRef;
> -        CGRect clipRect;
> -
> -        [self getRectsBeingDrawn:&rectList count:&rectCount];
> -        for (i = 0; i < rectCount; i++) {
> -            clipRect = rectList[i];
> -            clipRect.origin.y = (float)h - (clipRect.origin.y + clipRect.size.height);
> -            clipImageRef = CGImageCreateWithImageInRect(
> -                                                        imageRef,
> -                                                        clipRect
> -                                                        );
> -            CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef);
> -            CGImageRelease (clipImageRef);
> -        }
> +        NSRect imageRect = NSMakeRect(0, 0, w, h);
> +
> +        /*
> +         * AppKit already clips drawing to the dirty region. Draw the full
> +         * guest image so scaled partial updates use a stable source transform.
> +         */
> +        CGContextDrawImage(viewContextRef, cgrect(imageRect), imageRef);

Please use CGRectMake().

Regards,
Akihiko Odaki

>           CGImageRelease (imageRef);
>           CGDataProviderRelease(dataProviderRef);
>       }