Subject: bug#32932: 27.0.50; render bugs on macOS Mojave

From: Alan Third <[email-masked]>
Date: Sat, 1 Feb 2020 21:20:34 +0000

On Sat, Feb 01, 2020 at 08:29:10AM -0800, Aaron Jensen wrote:
> On Sat, Feb 1, 2020 at 6:22 AM Alan Third <[email-masked]> wrote:
> >
> > I’m now stumped because I can’t actually find any image type in the
> > documentation that isn’t immutable. I’m sure there must be something.
> > The Mac port appears to be using IOSurfaces, but as far as I can tell
> > they can be changed underneath you so aren’t much use for just
> > updating little bits of the Emacs frame at a time. I must be
> > misunderstanding something.
> 
> I know nothing, but have you looked into
> https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_layers/dq_layers.html
> ?

Can you try the attached patch? It looks like it’s faster here, but I
can’t really tell.
-- 
Alan Third
>From ad2feeba4284ce7ef7518781c26bd2a98389372a Mon Sep 17 00:00:00 2001
From: Alan Third <[email-masked]>
Date: Sat, 1 Feb 2020 21:17:29 +0000
Subject: [PATCH] Use CGLayer instead of NSBitmapImageRep (bug#32932)

---
 src/nsterm.h |  2 +-
 src/nsterm.m | 67 ++++++++++++++++++++++------------------------------
 2 files changed, 29 insertions(+), 40 deletions(-)

diff --git a/src/nsterm.h b/src/nsterm.h
index 980ca534cf..fbcd29be12 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -418,7 +418,7 @@ #define NSTRACE_UNSILENCE()
    NSWindow *nonfs_window;
    BOOL fs_is_native;
 #ifdef NS_IMPL_COCOA
-   NSBitmapImageRep *drawingBuffer;
+   CGLayerRef drawingBuffer;
 #endif
 @public
    struct frame *emacsframe;
diff --git a/src/nsterm.m b/src/nsterm.m
index 9d427b9b38..94662a24fe 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1141,7 +1141,6 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
 
 #ifdef NS_IMPL_COCOA
   [NSGraphicsContext setCurrentContext:nil];
-  [view display];
 #else
   block_input ();
 
@@ -2853,7 +2852,9 @@ so some key presses (TAB) are swallowed by the system.  */
   ns_unfocus (f);
 
   /* as of 2006/11 or so this is now needed */
-  ns_redraw_scroll_bars (f);
+  /* FIXME: I don't see any reason for this and removing it makes no
+     difference here.  Do we need it for GNUstep?  */
+  //ns_redraw_scroll_bars (f);
   unblock_input ();
 }
 
@@ -3169,18 +3170,6 @@ so some key presses (TAB) are swallowed by the system.  */
 
       NSTRACE_RECT ("fromRect", fromRect);
 
-      /* Because we're drawing into an offscreen buffer which isn't
-         flipped, the images come out upside down.  To work around it
-         we need to do some fancy transforms.  */
-      {
-        NSAffineTransform *transform = [NSAffineTransform transform];
-        [transform translateXBy:0 yBy:NSMaxY(imageRect)];
-        [transform scaleXBy:1 yBy:-1];
-        [transform concat];
-
-        imageRect.origin.y = 0;
-      }
-
       [img drawInRect: imageRect
              fromRect: fromRect
             operation: NSCompositingOperationSourceOver
@@ -3938,11 +3927,6 @@ Function modeled after x_draw_glyph_string_box ().
 
       NSAffineTransform *doTransform = [NSAffineTransform transform];
 
-      /* We have to flip the image around the X axis as the offscreen
-         bitmap we're drawing to is flipped.  */
-      [doTransform scaleXBy:1 yBy:-1];
-      [doTransform translateXBy:0 yBy:-[img size].height];
-
       /* ImageMagick images don't have transforms.  */
       if (img->transform)
         [doTransform appendTransform:img->transform];
@@ -4838,7 +4822,7 @@ in certain situations (rapid incoming events).
   if (NILP (window->vertical_scroll_bar))
     {
       if (width > 0 && height > 0)
-	ns_clear_frame_area (f, left, top, width, height);
+        ns_clear_frame_area (f, left, top, width, height);
 
       bar = [[EmacsScroller alloc] initFrame: r window: win];
       wset_vertical_scroll_bar (window, make_mint_ptr (bar));
@@ -8239,10 +8223,15 @@ - (void)createDrawingBufferWithRect:(NSRect)rect
      retain the old method of drawing direct to the EmacsView.  */
 {
 #ifdef NS_IMPL_COCOA
+  NSGraphicsContext *screen;
+
   if (drawingBuffer != nil)
-    [drawingBuffer release];
+    CGLayerRelease (drawingBuffer);
 
-  drawingBuffer = [[self bitmapImageRepForCachingDisplayInRect:rect] retain];
+  screen = [NSGraphicsContext graphicsContextWithBitmapImageRep:
+                    [self bitmapImageRepForCachingDisplayInRect:rect]];
+
+  drawingBuffer = CGLayerCreateWithContext ([screen CGContext], rect.size, nil);
 #endif
 }
 
@@ -8250,11 +8239,12 @@ - (void)createDrawingBufferWithRect:(NSRect)rect
 #ifdef NS_IMPL_COCOA
 - (void)focusOnDrawingBuffer
 {
-  /* Creating the graphics context each time is very slow, but it
-     doesn't seem possible to cache and reuse it.  */
-  [NSGraphicsContext
-    setCurrentContext:
-      [NSGraphicsContext graphicsContextWithBitmapImageRep:drawingBuffer]];
+  NSGraphicsContext *buf =
+    [NSGraphicsContext
+        graphicsContextWithCGContext:CGLayerGetContext (drawingBuffer)
+                             flipped:YES];
+
+  [NSGraphicsContext setCurrentContext:buf];
 }

@@ -8284,13 +8274,16 @@ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect
   NSTRACE_RECT ("Destination", dstRect);
 
 #ifdef NS_IMPL_COCOA
-  [drawingBuffer drawInRect:dstRect
-                   fromRect:srcRect
-                  operation:NSCompositingOperationCopy
-                   fraction:1.0
-             respectFlipped:NO
-                      hints:nil];
+  CGPoint offset = CGPointMake (NSMinX (dstRect) - NSMinX (srcRect),
+                                NSMinY (dstRect) - NSMinY (srcRect));
+  [[NSGraphicsContext currentContext] saveGraphicsState];
 
+  NSRectClip (dstRect);
+
+  CGContextDrawLayerAtPoint ([[NSGraphicsContext currentContext] CGContext],
+                             offset, drawingBuffer);
+
+  [[NSGraphicsContext currentContext] restoreGraphicsState];
   [self setNeedsDisplayInRect:dstRect];
 #else
   hide_bell();              // Ensure the bell image isn't scrolled.
@@ -8313,12 +8306,8 @@ - (void)drawRect: (NSRect)rect
     return;
 
 #ifdef NS_IMPL_COCOA
-  [drawingBuffer drawInRect:rect
-                   fromRect:rect
-                  operation:NSCompositingOperationSourceOver
-                   fraction:1
-             respectFlipped:NO
-                      hints:nil];
+  CGContextRef ctx = [[NSGraphicsContext currentContext] CGContext];
+  CGContextDrawLayerAtPoint (ctx, CGPointZero, drawingBuffer);
 #else
   int x = NSMinX (rect), y = NSMinY (rect);
   int width = NSWidth (rect), height = NSHeight (rect);
-- 
2.24.0