From c63cf60ce4983044a8996953ad080292c8799a59 Mon Sep 17 00:00:00 2001
From: Christopher Chavez <chrischavez@gmx.us>
Date: Fri, 14 Aug 2020 08:48:06 -0500
Subject: [PATCH 5/5] Aqua X11 emulation: support Retina window source

Existing programs/libraries expect XGetImage() to return either an
XImage with the requested width and height, or NULL. This means that
a capture of a real window on a Retina display must be downscaled
before being returned by XGetImage() for compatibility.

To support this, a parameter force_1x_scale is added to
CreateCGImageFromDrawableRect(), which if true will downscale the
returned CGImage to the requested width and height. Otherwise,
capturing a window on a Retina display will return a CGImage having
twice the requested width and height.

If programs should be able to retrieve unscaled captures of windows on
Retina displays, then a separate API is needed. See
https://core.tcl-lang.org/tk/info/fcd6717d8b

Not fully tested. Would be particularly interested in a way to test
XCopyArea() between non-pixmaps. XCopyPlane() might be unfinished.
---
 macosx/tkMacOSXImage.c | 37 +++++++++++++++++++++++++++++++++++--
 1 file changed, 35 insertions(+), 2 deletions(-)

diff --git macosx/tkMacOSXImage.c macosx/tkMacOSXImage.c
index 34177586e..47060e2d8 100644
--- macosx/tkMacOSXImage.c
+++ macosx/tkMacOSXImage.c
@@ -17,6 +17,7 @@
 
 static CGImageRef CreateCGImageFromPixmap(Drawable pixmap);
 static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable,
+	   int force_1x_scale,
 	   int x, int y, unsigned int width, unsigned int height);
 
 #pragma mark XImage handling
@@ -498,6 +499,11 @@ TkPutImage(
  *      with origin at the top left, as used by XImage and CGImage, not bottom
  *      left as used by NSView.
  *
+ *	If force_1x_scale is true, then the returned CGImage will be downscaled
+ *	if necessary to have the requested width and height. Othewise, for
+ *	windows on Retina displays, the width and height of the returned CGImage
+ *	will be twice the requested width and height.
+ *
  * Side effects:
  *	If successful, allocates a new CGImage.
  *
@@ -507,6 +513,7 @@ TkPutImage(
 static CGImageRef
 CreateCGImageFromDrawableRect(
     Drawable drawable,
+    int force_1x_scale,
     int x,
     int y,
     unsigned int width,
@@ -531,6 +538,8 @@ CreateCGImageFromDrawableRect(
 	    CGImageRelease(cg_image);
 	}
     } else if ((view = TkMacOSXGetNSViewForDrawable(mac_drawable)) != nil) {
+	CGFloat scaleFactor = TkMacOSXNSWindowBackingScaleFactor(
+		[view window]);
 
 	/*
 	 * Convert Tk top-left to NSView bottom-left coordinates.
@@ -571,6 +580,28 @@ CreateCGImageFromDrawableRect(
 	result = [bitmapRep CGImage];
 	CGImageRetain(result);
 	[bitmapRep release];
+	if (force_1x_scale && (scaleFactor != 1.0)) {
+	    cg_image = result;
+	    result = NULL;
+	    // See http://blog.foundry376.com/2008/07/scaling-a-cgimage/#comment-200
+	    // create context, keeping original image properties
+	    CGColorSpaceRef colorspace = CGImageGetColorSpace(cg_image);
+	    cg_context = CGBitmapContextCreate(NULL, width, height,
+		    CGImageGetBitsPerComponent(cg_image),
+		    CGImageGetBytesPerRow(cg_image),
+		    colorspace,
+		    CGImageGetAlphaInfo(cg_image));
+	    CGColorSpaceRelease(colorspace);
+	    if (cg_context) {
+		// draw image to context (resizing it)
+		CGContextDrawImage(cg_context, CGRectMake(0, 0, width, height),
+			cg_image);
+		// extract resulting image from context
+		result = CGBitmapContextCreateImage(cg_context);
+		CGContextRelease(cg_context);
+	    }
+	    CGImageRelease(cg_image);
+	}
     } else {
 	TkMacOSXDbgMsg("Invalid source drawable");
     }
@@ -697,7 +728,8 @@ XGetImage(
 	    return NULL;
 	}
 
-	cgImage = CreateCGImageFromDrawableRect(drawable, x, y, width, height);
+	// Request 1x-scale image for compatibility
+	cgImage = CreateCGImageFromDrawableRect(drawable, 1, x, y, width, height);
 	if (cgImage) {
 	    bitmapRep = [NSBitmapImageRep alloc];
 	    [bitmapRep initWithCGImage:cgImage];
@@ -805,7 +837,8 @@ XCopyArea(
 	return BadDrawable;
     }
 
-    img = CreateCGImageFromDrawableRect(src, src_x, src_y, width, height);
+    // Use unscaled source (TkMacOSXDrawCGImage() will implicitly downscale)
+    img = CreateCGImageFromDrawableRect(src, 0, src_x, src_y, width, height);
 
     if (img) {
 	dstRect = CGRectMake(dest_x, dest_y, width, height);
-- 
2.24.3 (Apple Git-128)

