From 2ae13e10ac2c3c86584cece38298da049533e803 Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Fri, 15 Jul 2016 10:47:18 +0000 Subject: Fix for image/alpha rendering under hidpi/Retina displays on Mac OS; thanks to Marc Culler for assistance --- generic/tkImgPhInstance.c | 25 +++++++++++++++++++++++-- macosx/tkMacOSXDraw.c | 6 ++++-- macosx/tkMacOSXXStubs.c | 37 ++++++++++++++++++++++++++++++------- xlib/X11/Xlib.h | 3 +++ 4 files changed, 60 insertions(+), 11 deletions(-) diff --git a/generic/tkImgPhInstance.c b/generic/tkImgPhInstance.c index 666a9b0..bd152f2 100644 --- a/generic/tkImgPhInstance.c +++ b/generic/tkImgPhInstance.c @@ -404,6 +404,9 @@ TkImgPhotoGet( * * Note that Win32 pre-defines those operations that we really need. * + * Note that on MacOS, if the background comes from a Retina display + * then it will be twice as wide and twice as high as the photoimage. + * *---------------------------------------------------------------------- */ @@ -433,7 +436,16 @@ BlendComplexAlpha( unsigned long pixel; unsigned char r, g, b, alpha, unalpha, *masterPtr; unsigned char *alphaAr = iPtr->masterPtr->pix32; - +#if defined(MAC_OSX_TK) + /* Background "pixels" are actually 2^pp x 2^pp blocks of subpixels. Each + * block gets blended with the color of one image pixel. Since we iterate + * over the background subpixels, we reset the width and height to the + * subpixel dimensions of the background image we are using. + */ + int pp = bgImg->pixelpower; + width = width << pp; + height = height << pp; +#endif /* * This blending is an integer version of the Source-Over compositing rule * (see Porter&Duff, "Compositing Digital Images", proceedings of SIGGRAPH @@ -532,9 +544,16 @@ BlendComplexAlpha( #endif /* !_WIN32 && !MAC_OSX_TK */ for (y = 0; y < height; y++) { +# if !defined(MAC_OSX_TK) line = (y + yOffset) * iPtr->masterPtr->width; for (x = 0; x < width; x++) { masterPtr = alphaAr + ((line + x + xOffset) * 4); +#else + /* Repeat each image row and column 2^pp times. */ + line = ((y>>pp) + yOffset) * iPtr->masterPtr->width; + for (x = 0; x < width; x++) { + masterPtr = alphaAr + ((line + (x>>pp) + xOffset) * 4); +#endif alpha = masterPtr[3]; /* @@ -635,7 +654,9 @@ TkImgPhotoDisplay( (unsigned int)width, (unsigned int)height, AllPlanes, ZPixmap); if (bgImg == NULL) { Tk_DeleteErrorHandler(handler); - /* We failed to get the image so draw without blending alpha. It's the best we can do */ + /* We failed to get the image, so draw without blending alpha. + * It's the best we can do. + */ goto fallBack; } diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index d9b909b..f48538d 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -399,9 +399,11 @@ TkPutImage( CGImageRef img = CreateCGImageWithXImage(image); if (img) { + /* If the XImage has big pixels, rescale the source dimensions.*/ + int pp = image->pixelpower; DrawCGImage(d, gc, dc.context, img, gc->foreground, gc->background, - CGRectMake(0, 0, image->width, image->height), - CGRectMake(src_x, src_y, width, height), + CGRectMake(0, 0, image->width<height<format = format; ximage->data = data; ximage->obdata = NULL; + /* The default pixelpower is 0. This must be explicitly set to 1 in the + * case of an XImage extracted from a Retina display. + */ + ximage->pixelpower = 0; if (format == ZPixmap) { ximage->bits_per_pixel = 32; @@ -856,7 +860,8 @@ XCreateImage( * Results: * Returns a newly allocated XImage containing the data from the given * rectangle of the given drawable, or NULL if the XImage could not be - * constructed. + * constructed. NOTE: If we are copying from a window on a Retina + * display, the dimensions of the XImage will be 2*width x 2*height. * * Side effects: * None. @@ -885,7 +890,19 @@ XGetImage( int bitmap_pad = 0; int bytes_per_row = 4*width; int size; - TkMacOSXDbgMsg("XGetImage"); + MacDrawable *macDraw = (MacDrawable *) d; + NSWindow *win = TkMacOSXDrawableWindow(d); + /* This code assumes that backing scale factors are integers. Currently + * Retina displays use a scale factor of 2.0 and normal displays use 1.0. + * We do not support any other values here. + */ + int scalefactor = 1; + if (win && [win respondsToSelector:@selector(backingScaleFactor)]) { + scalefactor = ([win backingScaleFactor] == 2.0) ? 2 : 1; + } + int scaled_height = height * scalefactor; + int scaled_width = width * scalefactor; + if (format == ZPixmap) { if (width == 0 || height == 0) { /* This happens all the time. @@ -894,7 +911,7 @@ XGetImage( return NULL; } - bitmap_rep = BitmapRepFromDrawableRect(d, x, y,width, height); + bitmap_rep = BitmapRepFromDrawableRect(d, x, y, width, height); bitmap_fmt = [bitmap_rep bitmapFormat]; if ( bitmap_rep == Nil || @@ -913,7 +930,9 @@ XGetImage( if ( [bitmap_rep isPlanar ] == 0 && [bitmap_rep samplesPerPixel] == 4 ) { bytes_per_row = [bitmap_rep bytesPerRow]; - size = bytes_per_row*height; + assert(bytes_per_row == 4 * scaled_width); + assert([bitmap_rep bytesPerPlane] == bytes_per_row * scaled_height); + size = bytes_per_row*scaled_height; image_data = (char*)[bitmap_rep bitmapData]; if ( image_data ) { int row, n, m; @@ -924,7 +943,7 @@ XGetImage( */ if (bitmap_fmt == 0) { /* BGRA */ - for (row=0, n=0; rowpixelpower = 1; + } [ns_image removeRepresentation:bitmap_rep]; /*releases the rep*/ [ns_image release]; } diff --git a/xlib/X11/Xlib.h b/xlib/X11/Xlib.h index 667bdc7..8d8ec68 100644 --- a/xlib/X11/Xlib.h +++ b/xlib/X11/Xlib.h @@ -330,6 +330,9 @@ typedef struct _XImage { unsigned long green_mask; unsigned long blue_mask; XPointer obdata; /* hook for the object routines to hang on */ +#if defined(MAC_OSX_TK) + int pixelpower; /* n such that pixels are 2^n x 2^n blocks*/ +#endif struct funcs { /* image manipulation routines */ struct _XImage *(*create_image)(); #if NeedFunctionPrototypes -- cgit v0.12