diff options
author | culler <culler> | 2020-08-24 20:21:53 (GMT) |
---|---|---|
committer | culler <culler> | 2020-08-24 20:21:53 (GMT) |
commit | 2bc6fae00d9ae77272e63ae6c01bf2a127088d3f (patch) | |
tree | 6976cb584f215a5f10ee0bd00e9471adeafac867 | |
parent | e98f41ec7f422fce9089b087b6ca9bfd73697988 (diff) | |
parent | 3b967bf8397e69bc3437a6c6a071516cba20702e (diff) | |
download | tk-2bc6fae00d9ae77272e63ae6c01bf2a127088d3f.zip tk-2bc6fae00d9ae77272e63ae6c01bf2a127088d3f.tar.gz tk-2bc6fae00d9ae77272e63ae6c01bf2a127088d3f.tar.bz2 |
Move XGetImage and XCopyArea to tkMacOSXImage.c and eliminate references to XImage.pixelpower.
-rw-r--r-- | generic/tkImgPhInstance.c | 7 | ||||
-rw-r--r-- | macosx/tkMacOSXDraw.c | 440 | ||||
-rw-r--r-- | macosx/tkMacOSXImage.c | 607 | ||||
-rw-r--r-- | macosx/tkMacOSXMenu.c | 4 | ||||
-rw-r--r-- | macosx/tkMacOSXPort.h | 7 | ||||
-rw-r--r-- | macosx/tkMacOSXPrivate.h | 11 | ||||
-rw-r--r-- | macosx/tkMacOSXWm.c | 2 | ||||
-rw-r--r-- | xlib/X11/Xlib.h | 2 |
8 files changed, 523 insertions, 557 deletions
diff --git a/generic/tkImgPhInstance.c b/generic/tkImgPhInstance.c index c500050..32b09be 100644 --- a/generic/tkImgPhInstance.c +++ b/generic/tkImgPhInstance.c @@ -19,9 +19,7 @@ */ #include "tkImgPhoto.h" -#ifdef MAC_OSX_TK -#define TKPUTIMAGE_CAN_BLEND -#endif +#include "tkPort.h" /* * Declaration for internal Xlib function used here: @@ -409,9 +407,6 @@ 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. - * *---------------------------------------------------------------------- */ #ifndef TKPUTIMAGE_CAN_BLEND diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 87bd90c..8e096bc 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -1,13 +1,13 @@ /* * tkMacOSXDraw.c -- * - * This file contains functions that perform drawing to Xlib windows. Most - * of the functions simply emulate Xlib functions. + * This file contains functions that draw to windows. Many of thees + * functions emulate Xlib functions. * * Copyright (c) 1995-1997 Sun Microsystems, Inc. - * Copyright 2001-2009, Apple Inc. + * Copyright (c) 2001-2009 Apple Inc. * Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net> - * Copyright 2014 Marc Culler. + * Copyright (c) 2014-2020 Marc Culler. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. @@ -50,6 +50,8 @@ static unsigned long transparentColor; */ static void ClipToGC(Drawable d, GC gc, HIShapeRef *clipRgnPtr); +static NSImage *CreateNSImageFromPixmap(Pixmap pixmap, int width, int height); + /* *---------------------------------------------------------------------- @@ -108,384 +110,7 @@ TkMacOSXInitCGDrawing( /* *---------------------------------------------------------------------- * - * TkMacOSXBitmapRepFromDrawableRect - * - * Extract bitmap data from a MacOSX drawable as an NSBitmapImageRep. - * - * This is only used by XGetImage, which is never called. And this - * implementation does not work correctly. Originally it relied on - * [NSBitmapImageRep initWithFocusedViewRect:view_rect] which was - * deprecated by Apple in OSX 10.14 and also required the use of other - * deprecated functions such as [NSView lockFocus]. Apple's suggested - * replacement is [NSView cacheDisplayInRect: toBitmapImageRep:] and that - * is what is being used here. However, that method only works when the - * view has a valid CGContext, and a view is only guaranteed to have a - * valid context during a call to [NSView drawRect]. To further complicate - * matters, cacheDisplayInRect calls [NSView drawRect]. Essentially it is - * asking the view to draw a subrectangle of itself into a special - * graphics context which is linked to the BitmapImageRep. But our - * implementation of [NSView drawRect] does not allow recursive calls. If - * called recursively it returns immediately without doing any drawing. - * So the bottom line is that this function either returns a NULL pointer - * or a black image. To make it useful would require a significant amount - * of rewriting of the drawRect method. Perhaps the next release of OSX - * will include some more helpful ways of doing this. - * - * Results: - * Returns an NSBitmapRep representing the image of the given rectangle of - * the given drawable. This object is retained. The caller is responsible - * for releasing it. - * - * NOTE: The x,y coordinates should be relative to a coordinate system - * with origin at the top left, as used by XImage and CGImage, not bottom - * left as used by NSView. - * - * Side effects: - * None - * - *---------------------------------------------------------------------- - */ - -NSBitmapImageRep * -TkMacOSXBitmapRepFromDrawableRect( - Drawable drawable, - int x, - int y, - unsigned int width, - unsigned int height) -{ - MacDrawable *mac_drawable = (MacDrawable *) drawable; - CGContextRef cg_context = NULL; - CGImageRef cg_image = NULL, sub_cg_image = NULL; - NSBitmapImageRep *bitmap_rep = NULL; - NSView *view = NULL; - if (mac_drawable->flags & TK_IS_PIXMAP) { - /* - * This MacDrawable is a bitmap, so its view is NULL. - */ - - CGRect image_rect = CGRectMake(x, y, width, height); - - cg_context = TkMacOSXGetCGContextForDrawable(drawable); - cg_image = CGBitmapContextCreateImage((CGContextRef) cg_context); - sub_cg_image = CGImageCreateWithImageInRect(cg_image, image_rect); - if (sub_cg_image) { - bitmap_rep = [NSBitmapImageRep alloc]; - [bitmap_rep initWithCGImage:sub_cg_image]; - } - if (cg_image) { - CGImageRelease(cg_image); - } - } else if (TkMacOSXDrawableView(mac_drawable) != NULL) { - TKContentView *tkview = (TKContentView *)view; - - /* - * Convert Tk top-left to NSView bottom-left coordinates. - */ - - int view_height = [view bounds].size.height; - NSRect view_rect = NSMakeRect(x + mac_drawable->xOff, - view_height - height - y - mac_drawable->yOff, - width, height); - - /* - * Attempt to copy from the view to a bitmapImageRep. If the view does - * not have a valid CGContext, doing this will silently corrupt memory - * and make a big mess. So, in that case, we mark the view as needing - * display and return NULL. - */ - - if (view == [NSView focusView]) { - bitmap_rep = [view bitmapImageRepForCachingDisplayInRect: view_rect]; - [bitmap_rep retain]; - [view cacheDisplayInRect:view_rect toBitmapImageRep:bitmap_rep]; - } else { - TkMacOSXDbgMsg("No CGContext - cannot copy from screen to bitmap."); - [tkview addTkDirtyRect:[tkview bounds]]; - return NULL; - } - } else { - TkMacOSXDbgMsg("Invalid source drawable"); - } - return bitmap_rep; -} - -/* - *---------------------------------------------------------------------- - * - * XCopyArea -- - * - * Copies data from one drawable to another. - * - * Results: - * None. - * - * Side effects: - * Data is moved from a window or bitmap to a second window or bitmap. - * - *---------------------------------------------------------------------- - */ - -int -XCopyArea( - Display *display, /* Display. */ - Drawable src, /* Source drawable. */ - Drawable dst, /* Destination drawable. */ - GC gc, /* GC to use. */ - int src_x, /* X & Y, width & height */ - int src_y, /* define the source rectangle */ - unsigned int width, /* that will be copied. */ - unsigned int height, - int dest_x, /* Dest X & Y on dest rect. */ - int dest_y) -{ - TkMacOSXDrawingContext dc; - MacDrawable *srcDraw = (MacDrawable *) src; - NSBitmapImageRep *bitmap_rep = NULL; - CGImageRef img = NULL; - CGRect bounds, srcRect, dstRect; - - display->request++; - if (!width || !height) { - return BadDrawable; - } - - if (!TkMacOSXSetupDrawingContext(dst, gc, &dc)) { - TkMacOSXDbgMsg("Failed to setup drawing context."); - return BadDrawable; - } - - if (!dc.context) { - TkMacOSXDbgMsg("Invalid destination drawable - no context."); - return BadDrawable; - } - - if (srcDraw->flags & TK_IS_PIXMAP) { - img = TkMacOSXCreateCGImageWithDrawable(src); - } else if (TkMacOSXDrawableWindow(src)) { - bitmap_rep = TkMacOSXBitmapRepFromDrawableRect(src, - src_x, src_y, width, height); - if (bitmap_rep) { - img = [bitmap_rep CGImage]; - } - } else { - TkMacOSXDbgMsg("Invalid source drawable - neither window nor pixmap."); - } - - if (img) { - bounds = CGRectMake(0, 0, srcDraw->size.width, srcDraw->size.height); - srcRect = CGRectMake(src_x, src_y, width, height); - dstRect = CGRectMake(dest_x, dest_y, width, height); - TkMacOSXDrawCGImage(dst, gc, dc.context, img, - gc->foreground, gc->background, bounds, srcRect, dstRect); - CFRelease(img); - } else { - TkMacOSXDbgMsg("Failed to construct CGImage."); - } - - TkMacOSXRestoreDrawingContext(&dc); - return Success; -} - -/* - *---------------------------------------------------------------------- - * - * XCopyPlane -- - * - * Copies a bitmap from a source drawable to a destination drawable. The - * plane argument specifies which bit plane of the source contains the - * bitmap. Note that this implementation ignores the gc->function. - * - * Results: - * None. - * - * Side effects: - * Changes the destination drawable. - * - *---------------------------------------------------------------------- - */ - -int -XCopyPlane( - Display *display, /* Display. */ - Drawable src, /* Source drawable. */ - Drawable dst, /* Destination drawable. */ - GC gc, /* GC to use. */ - int src_x, /* X & Y, width & height */ - int src_y, /* define the source rectangle */ - unsigned int width, /* that will be copied. */ - unsigned int height, - int dest_x, /* Dest X & Y on dest rect. */ - int dest_y, - unsigned long plane) /* Which plane to copy. */ -{ - TkMacOSXDrawingContext dc; - MacDrawable *srcDraw = (MacDrawable *) src; - MacDrawable *dstDraw = (MacDrawable *) dst; - CGRect bounds, srcRect, dstRect; - display->request++; - if (!width || !height) { - /* TkMacOSXDbgMsg("Drawing of empty area requested"); */ - return BadDrawable; - } - if (plane != 1) { - Tcl_Panic("Unexpected plane specified for XCopyPlane"); - } - if (srcDraw->flags & TK_IS_PIXMAP) { - if (!TkMacOSXSetupDrawingContext(dst, gc, &dc)) { - return BadDrawable; - } - - CGContextRef context = dc.context; - - if (context) { - CGImageRef img = TkMacOSXCreateCGImageWithDrawable(src); - - if (img) { - TkpClipMask *clipPtr = (TkpClipMask *) gc->clip_mask; - unsigned long imageBackground = gc->background; - - if (clipPtr && clipPtr->type == TKP_CLIP_PIXMAP) { - srcRect = CGRectMake(src_x, src_y, width, height); - CGImageRef mask = TkMacOSXCreateCGImageWithDrawable( - clipPtr->value.pixmap); - CGImageRef submask = CGImageCreateWithImageInRect( - img, srcRect); - CGRect rect = CGRectMake(dest_x, dest_y, width, height); - - rect = CGRectOffset(rect, dstDraw->xOff, dstDraw->yOff); - CGContextSaveGState(context); - - /* - * Move the origin of the destination to top left. - */ - - CGContextTranslateCTM(context, - 0, rect.origin.y + CGRectGetMaxY(rect)); - CGContextScaleCTM(context, 1, -1); - - /* - * Fill with the background color, clipping to the mask. - */ - - CGContextClipToMask(context, rect, submask); - TkMacOSXSetColorInContext(gc, gc->background, dc.context); - CGContextFillRect(context, rect); - - /* - * Fill with the foreground color, clipping to the - * intersection of img and mask. - */ - - CGImageRef subimage = CGImageCreateWithImageInRect( - img, srcRect); - CGContextClipToMask(context, rect, subimage); - TkMacOSXSetColorInContext(gc, gc->foreground, context); - CGContextFillRect(context, rect); - CGContextRestoreGState(context); - CGImageRelease(img); - CGImageRelease(mask); - CGImageRelease(submask); - CGImageRelease(subimage); - } else { - bounds = CGRectMake(0, 0, - srcDraw->size.width, srcDraw->size.height); - srcRect = CGRectMake(src_x, src_y, width, height); - dstRect = CGRectMake(dest_x, dest_y, width, height); - TkMacOSXDrawCGImage(dst, gc, dc.context, img, - gc->foreground, imageBackground, bounds, - srcRect, dstRect); - CGImageRelease(img); - } - } else { - /* no image */ - TkMacOSXDbgMsg("Invalid source drawable"); - } - } else { - TkMacOSXDbgMsg("Invalid destination drawable - " - "could not get a bitmap context."); - } - TkMacOSXRestoreDrawingContext(&dc); - return Success; - } else { - /* - * Source drawable is a Window, not a Pixmap. - */ - - return XCopyArea(display, src, dst, gc, src_x, src_y, width, height, - dest_x, dest_y); - } -} - -/* - *---------------------------------------------------------------------- - * - * TkMacOSXCreateCGImageWithDrawable -- - * - * Create a CGImage from the given Drawable. - * - * Results: - * CGImage, release after use. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -CGImageRef -TkMacOSXCreateCGImageWithDrawable( - Drawable drawable) -{ - CGImageRef img = NULL; - CGContextRef context = TkMacOSXGetCGContextForDrawable(drawable); - - if (context) { - img = CGBitmapContextCreateImage(context); - } - return img; -} - -/* - *---------------------------------------------------------------------- - * - * CreateNSImageWithPixmap -- - * - * Create NSImage for Pixmap. - * - * Results: - * NSImage. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static NSImage * -CreateNSImageWithPixmap( - Pixmap pixmap, - int width, - int height) -{ - CGImageRef cgImage; - NSImage *nsImage; - NSBitmapImageRep *bitmapImageRep; - - cgImage = TkMacOSXCreateCGImageWithDrawable(pixmap); - nsImage = [[NSImage alloc] initWithSize:NSMakeSize(width, height)]; - bitmapImageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage]; - [nsImage addRepresentation:bitmapImageRep]; - [bitmapImageRep release]; - CFRelease(cgImage); - - return nsImage; -} - -/* - *---------------------------------------------------------------------- - * - * TkMacOSXGetNSImageWithTkImage -- + * TkMacOSXGetNSImageFromTkImage -- * * Get autoreleased NSImage for Tk_Image. * @@ -499,7 +124,7 @@ CreateNSImageWithPixmap( */ NSImage * -TkMacOSXGetNSImageWithTkImage( +TkMacOSXGetNSImageFromTkImage( Display *display, Tk_Image image, int width, @@ -512,7 +137,7 @@ TkMacOSXGetNSImageWithTkImage( } pixmap = Tk_GetPixmap(display, None, width, height, 0); Tk_RedrawImage(image, 0, 0, width, height, pixmap, 0, 0); - nsImage = CreateNSImageWithPixmap(pixmap, width, height); + nsImage = CreateNSImageFromPixmap(pixmap, width, height); Tk_FreePixmap(display, pixmap); return [nsImage autorelease]; @@ -521,7 +146,7 @@ TkMacOSXGetNSImageWithTkImage( /* *---------------------------------------------------------------------- * - * TkMacOSXGetNSImageWithBitmap -- + * TkMacOSXGetNSImageFromBitmap -- * * Get autoreleased NSImage for Bitmap. * @@ -535,7 +160,7 @@ TkMacOSXGetNSImageWithTkImage( */ NSImage * -TkMacOSXGetNSImageWithBitmap( +TkMacOSXGetNSImageFromBitmap( Display *display, Pixmap bitmap, GC gc, @@ -551,7 +176,7 @@ TkMacOSXGetNSImageWithBitmap( XSetClipOrigin(display, gc, 0, 0); XCopyPlane(display, bitmap, pixmap, gc, 0, 0, width, height, 0, 0, 1); gc->background = origBackground; - nsImage = CreateNSImageWithPixmap(pixmap, width, height); + nsImage = CreateNSImageFromPixmap(pixmap, width, height); Tk_FreePixmap(display, pixmap); return [nsImage autorelease]; @@ -560,6 +185,47 @@ TkMacOSXGetNSImageWithBitmap( /* *---------------------------------------------------------------------- * + * CreateNSImageFromPixmap -- + * + * Create NSImage for Pixmap. + * + * Results: + * NSImage. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static NSImage * +CreateNSImageFromPixmap( + Pixmap pixmap, + int width, + int height) +{ + CGImageRef cgImage; + NSImage *nsImage; + NSBitmapImageRep *bitmapImageRep; + CGContextRef context = TkMacOSXGetCGContextForDrawable(pixmap); + + if (context) { + cgImage = CGBitmapContextCreateImage(context); + } else { + return NULL; + } + nsImage = [[NSImage alloc] initWithSize:NSMakeSize(width, height)]; + bitmapImageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage]; + [nsImage addRepresentation:bitmapImageRep]; + [bitmapImageRep release]; + CFRelease(cgImage); + + return nsImage; +} + +/* + *---------------------------------------------------------------------- + * * TkMacOSXGetCGContextForDrawable -- * * Get CGContext for given Drawable, creating one if necessary. diff --git a/macosx/tkMacOSXImage.c b/macosx/tkMacOSXImage.c index f7dbb70..d4dcc78 100644 --- a/macosx/tkMacOSXImage.c +++ b/macosx/tkMacOSXImage.c @@ -6,7 +6,7 @@ * Copyright (c) 1995-1997 Sun Microsystems, Inc. * Copyright 2001-2009, Apple Inc. * Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net> - * Copyright 2017-2018 Marc Culler. + * Copyright (c) 2017-2020 Marc Culler. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. @@ -15,6 +15,10 @@ #include "tkMacOSXPrivate.h" #include "xbytes.h" +static CGImageRef CreateCGImageFromPixmap(Drawable pixmap); +static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable, + int x, int y, unsigned int width, unsigned int height); + #pragma mark XImage handling int @@ -131,141 +135,6 @@ TkMacOSXCreateCGImageWithXImage( /* *---------------------------------------------------------------------- * - * XGetImage -- - * - * This function copies data from a pixmap or window into an XImage. It - * is essentially never used. At one time it was called by - * pTkImgPhotoDisplay, but that is no longer the case. Currently it is - * called two places, one of which is requesting an XY image which we do - * not support. It probably does not work correctly -- see the comments - * for TkMacOSXBitmapRepFromDrawableRect. - * - * 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. 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. - * - *---------------------------------------------------------------------- - */ -struct pixel_fmt {int r; int g; int b; int a;}; -static struct pixel_fmt bgra = {2, 1, 0, 3}; -static struct pixel_fmt abgr = {3, 2, 1, 0}; - -XImage * -XGetImage( - Display *display, - Drawable drawable, - int x, - int y, - unsigned int width, - unsigned int height, - unsigned long plane_mask, - int format) -{ - NSBitmapImageRep* bitmap_rep = NULL; - NSUInteger bitmap_fmt = 0; - XImage* imagePtr = NULL; - char* bitmap = NULL; - char R, G, B, A; - int depth = 32, offset = 0, bitmap_pad = 0; - unsigned int bytes_per_row, size, row, n, m; - unsigned int scalefactor=1, scaled_height=height, scaled_width=width; - NSWindow *win = TkMacOSXDrawableWindow(drawable); - static enum {unknown, no, yes} has_retina = unknown; - - if (win && has_retina == unknown) { -#ifdef __clang__ - has_retina = [win respondsToSelector:@selector(backingScaleFactor)] ? - yes : no; -#else - has_retina = no; -#endif - } - - if (has_retina == yes) { - /* - * We only allow scale factors 1 or 2, as Apple currently does. - */ - -#ifdef __clang__ - scalefactor = [win backingScaleFactor] == 2.0 ? 2 : 1; -#endif - scaled_height *= scalefactor; - scaled_width *= scalefactor; - } - - if (format == ZPixmap) { - if (width == 0 || height == 0) { - return NULL; - } - - bitmap_rep = TkMacOSXBitmapRepFromDrawableRect(drawable, - x, y, width, height); - if (!bitmap_rep) { - TkMacOSXDbgMsg("XGetImage: Failed to construct NSBitmapRep"); - return NULL; - } - bitmap_fmt = [bitmap_rep bitmapFormat]; - size = [bitmap_rep bytesPerPlane]; - bytes_per_row = [bitmap_rep bytesPerRow]; - bitmap = ckalloc(size); - if (!bitmap - || (bitmap_fmt != 0 && bitmap_fmt != 1) - || [bitmap_rep samplesPerPixel] != 4 - || [bitmap_rep isPlanar] != 0 - || bytes_per_row < 4 * scaled_width - || size != bytes_per_row * scaled_height) { - TkMacOSXDbgMsg("XGetImage: Unrecognized bitmap format"); - CFRelease(bitmap_rep); - return NULL; - } - memcpy(bitmap, (char *)[bitmap_rep bitmapData], size); - CFRelease(bitmap_rep); - - /* - * When Apple extracts a bitmap from an NSView, it may be in either - * BGRA or ABGR format. For an XImage we need RGBA. - */ - - struct pixel_fmt pixel = bitmap_fmt == 0 ? bgra : abgr; - - for (row = 0, n = 0; row < scaled_height; row++, n += bytes_per_row) { - for (m = n; m < n + 4*scaled_width; m += 4) { - R = *(bitmap + m + pixel.r); - G = *(bitmap + m + pixel.g); - B = *(bitmap + m + pixel.b); - A = *(bitmap + m + pixel.a); - - *(bitmap + m) = R; - *(bitmap + m + 1) = G; - *(bitmap + m + 2) = B; - *(bitmap + m + 3) = A; - } - } - imagePtr = XCreateImage(display, NULL, depth, format, offset, - (char*) bitmap, scaled_width, scaled_height, - bitmap_pad, bytes_per_row); - if (scalefactor == 2) { - imagePtr->pixelpower = 1; - } - } else { - /* - * There are some calls to XGetImage in the generic Tk code which pass - * an XYPixmap rather than a ZPixmap. XYPixmaps should be handled - * here. - */ - TkMacOSXDbgMsg("XGetImage does not handle XYPixmaps at the moment."); - } - return imagePtr; -} - -/* - *---------------------------------------------------------------------- - * * DestroyImage -- * * Destroys storage associated with an image. @@ -473,13 +342,6 @@ XCreateImage( 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; ximage->bitmap_unit = 32; @@ -574,17 +436,8 @@ XPutImage( } if (img) { - /* - * If the XImage has big pixels, the source is rescaled to reflect - * the actual pixel dimensions. This is not currently used, but - * could arise if the image were copied from a retina monitor and - * redrawn on an ordinary monitor. - */ - - int pp = image->pixelpower; - bounds = CGRectMake(0, 0, image->width, image->height); - srcRect = CGRectMake(src_x<<pp, src_y<<pp, width<<pp, height<<pp); + srcRect = CGRectMake(src_x, src_y, width, height); dstRect = CGRectMake(dest_x, dest_y, width, height); TkMacOSXDrawCGImage(drawable, gc, dc.context, img, gc->foreground, gc->background, @@ -618,6 +471,454 @@ TkPutImage( } /* + *---------------------------------------------------------------------- + * + * CreateCGImageFromDrawableRect + * + * Extract image data from a MacOSX drawable as a CGImage. + * + * This is only called by XGetImage and XCopyArea. The Tk core uses + * these functions on some platforms, but on macOS the core does not + * call them with a source drawable which is a window. Such calls are + * used only for double-buffered drawing. Since macOS defines the + * macro TK_NO_DOUBLE_BUFFERING, the generic code never calls XGetImage + * or XCopyArea on macOS. Nonetheless, these function are in the stubs + * table and therefore could be used by extensions. + * + * This implementation does not work correctly. Originally it relied on + * [NSBitmapImageRep initWithFocusedViewRect:view_rect] which was + * deprecated by Apple in OSX 10.14 and also required the use of other + * deprecated functions such as [NSView lockFocus]. Apple's suggested + * replacement is [NSView cacheDisplayInRect: toBitmapImageRep:] and that + * is what is being used here. However, that method only works when the + * view has a valid CGContext, and a view is only guaranteed to have a + * valid context during a call to [NSView drawRect]. To further complicate + * matters, cacheDisplayInRect calls [NSView drawRect]. Essentially it is + * asking the view to draw a subrectangle of itself using a special + * graphics context which is linked to the BitmapImageRep. But our + * implementation of [NSView drawRect] does not allow recursive calls. If + * called recursively it returns immediately without doing any drawing. + * So the bottom line is that this function either returns a NULL pointer + * or a black image. To make it useful would require a significant amount + * of rewriting of the drawRect method. Perhaps the next release of OSX + * will include some more helpful ways of doing this. + * + * Results: + * Returns an NSBitmapRep representing the image of the given rectangle of + * the given drawable. This object is retained. The caller is responsible + * for releasing it. + * + * NOTE: The x,y coordinates should be relative to a coordinate system + * with origin at the top left, as used by XImage and CGImage, not bottom + * left as used by NSView. + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ + +static CGImageRef +CreateCGImageFromDrawableRect( + Drawable drawable, + int x, + int y, + unsigned int width, + unsigned int height) +{ + MacDrawable *mac_drawable = (MacDrawable *) drawable; + CGContextRef cg_context = NULL; + CGImageRef cg_image = NULL, result = NULL; + NSBitmapImageRep *bitmapRep = NULL; + NSView *view = NULL; + if (mac_drawable->flags & TK_IS_PIXMAP) { + /* + * This MacDrawable is a bitmap, so its view is NULL. + */ + + CGRect image_rect = CGRectMake(x, y, width, height); + + cg_context = TkMacOSXGetCGContextForDrawable(drawable); + cg_image = CGBitmapContextCreateImage((CGContextRef) cg_context); + if (cg_image) { + result = CGImageCreateWithImageInRect(cg_image, image_rect); + CGImageRelease(cg_image); + } + } else if (TkMacOSXDrawableView(mac_drawable) != NULL) { + + /* + * Convert Tk top-left to NSView bottom-left coordinates. + */ + + int view_height = [view bounds].size.height; + NSRect view_rect = NSMakeRect(x + mac_drawable->xOff, + view_height - height - y - mac_drawable->yOff, + width, height); + + /* + * Attempt to copy from the view to a bitmapImageRep. If the view does + * not have a valid CGContext, doing this will silently corrupt memory + * and make a big mess. So, in that case, we just return NULL. + */ + + if (view == [NSView focusView]) { + bitmapRep = [view bitmapImageRepForCachingDisplayInRect: view_rect]; + [view cacheDisplayInRect:view_rect toBitmapImageRep:bitmapRep]; + result = [bitmapRep CGImage]; + CFRelease(bitmapRep); + } else { + TkMacOSXDbgMsg("No CGContext - cannot copy from screen to bitmap."); + result = NULL; + } + } else { + TkMacOSXDbgMsg("Invalid source drawable"); + } + return result; +} + +/* + *---------------------------------------------------------------------- + * + * CreateCGImageFromPixmap -- + * + * Create a CGImage from an X Pixmap. + * + * Results: + * CGImage, release after use. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static CGImageRef +CreateCGImageFromPixmap( + Drawable pixmap) +{ + CGImageRef img = NULL; + CGContextRef context = TkMacOSXGetCGContextForDrawable(pixmap); + + if (context) { + img = CGBitmapContextCreateImage(context); + } + return img; +} + +/* + *---------------------------------------------------------------------- + * + * XGetImage -- + * + * This function copies data from a pixmap or window into an XImage. It + * is essentially never used. At one time it was called by + * pTkImgPhotoDisplay, but that is no longer the case. Currently it is + * called two places, one of which is requesting an XY image which we do + * not support. It probably does not work correctly -- see the comments + * for CGImageFromDrawableRect. + * + * 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. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +struct pixel_fmt {int r; int g; int b; int a;}; +static struct pixel_fmt bgra = {2, 1, 0, 3}; +static struct pixel_fmt abgr = {3, 2, 1, 0}; + +XImage * +XGetImage( + Display *display, + Drawable drawable, + int x, + int y, + unsigned int width, + unsigned int height, + unsigned long plane_mask, + int format) +{ + NSBitmapImageRep* bitmapRep = NULL; + NSUInteger bitmap_fmt = 0; + XImage* imagePtr = NULL; + char* bitmap = NULL; + char R, G, B, A; + int depth = 32, offset = 0, bitmap_pad = 0; + unsigned int bytes_per_row, size, row, n, m; + + if (format == ZPixmap) { + CGImageRef cgImage; + if (width == 0 || height == 0) { + return NULL; + } + + cgImage = CreateCGImageFromDrawableRect(drawable, x, y, width, height); + if (cgImage) { + bitmapRep = [NSBitmapImageRep alloc]; + [bitmapRep initWithCGImage:cgImage]; + CFRelease(cgImage); + } else { + TkMacOSXDbgMsg("XGetImage: Failed to construct CGImage"); + return NULL; + } + bitmap_fmt = [bitmapRep bitmapFormat]; + size = [bitmapRep bytesPerPlane]; + bytes_per_row = [bitmapRep bytesPerRow]; + bitmap = ckalloc(size); + if (!bitmap + || (bitmap_fmt != 0 && bitmap_fmt != 1) + || [bitmapRep samplesPerPixel] != 4 + || [bitmapRep isPlanar] != 0 + || bytes_per_row < 4 * width + || size != bytes_per_row * height) { + TkMacOSXDbgMsg("XGetImage: Unrecognized bitmap format"); + CFRelease(bitmapRep); + return NULL; + } + memcpy(bitmap, (char *)[bitmapRep bitmapData], size); + CFRelease(bitmapRep); + + /* + * When Apple extracts a bitmap from an NSView, it may be in either + * BGRA or ABGR format. For an XImage we need RGBA. + */ + + struct pixel_fmt pixel = bitmap_fmt == 0 ? bgra : abgr; + + for (row = 0, n = 0; row < height; row++, n += bytes_per_row) { + for (m = n; m < n + 4*width; m += 4) { + R = *(bitmap + m + pixel.r); + G = *(bitmap + m + pixel.g); + B = *(bitmap + m + pixel.b); + A = *(bitmap + m + pixel.a); + + *(bitmap + m) = R; + *(bitmap + m + 1) = G; + *(bitmap + m + 2) = B; + *(bitmap + m + 3) = A; + } + } + imagePtr = XCreateImage(display, NULL, depth, format, offset, + (char*) bitmap, width, height, + bitmap_pad, bytes_per_row); + } else { + /* + * There are some calls to XGetImage in the generic Tk code which pass + * an XYPixmap rather than a ZPixmap. XYPixmaps should be handled + * here. + */ + TkMacOSXDbgMsg("XGetImage does not handle XYPixmaps at the moment."); + } + return imagePtr; +} + +/* + *---------------------------------------------------------------------- + * + * XCopyArea -- + * + * Copies image data from one drawable to another. + * + * Results: + * None. + * + * Side effects: + * Image data is moved from a window or bitmap to a second window or bitmap. + * + *---------------------------------------------------------------------- + */ + +int +XCopyArea( + Display *display, /* Display. */ + Drawable src, /* Source drawable. */ + Drawable dst, /* Destination drawable. */ + GC gc, /* GC to use. */ + int src_x, /* X & Y, width & height */ + int src_y, /* define the source rectangle */ + unsigned int width, /* that will be copied. */ + unsigned int height, + int dest_x, /* Dest X & Y on dest rect. */ + int dest_y) +{ + TkMacOSXDrawingContext dc; + MacDrawable *srcDraw = (MacDrawable *) src; + CGImageRef img = NULL; + CGRect bounds, srcRect, dstRect; + + display->request++; + if (!width || !height) { + return BadDrawable; + } + + if (!TkMacOSXSetupDrawingContext(dst, gc, &dc)) { + TkMacOSXDbgMsg("Failed to setup drawing context."); + return BadDrawable; + } + + if (!dc.context) { + TkMacOSXDbgMsg("Invalid destination drawable - no context."); + return BadDrawable; + } + + if (srcDraw->flags & TK_IS_PIXMAP) { + img = CreateCGImageFromPixmap(src); + } else if (TkMacOSXDrawableWindow(src)) { + img = CreateCGImageFromDrawableRect(src, src_x, src_y, width, height); + } else { + TkMacOSXDbgMsg("Invalid source drawable - neither window nor pixmap."); + } + + if (img) { + bounds = CGRectMake(0, 0, srcDraw->size.width, srcDraw->size.height); + srcRect = CGRectMake(src_x, src_y, width, height); + dstRect = CGRectMake(dest_x, dest_y, width, height); + TkMacOSXDrawCGImage(dst, gc, dc.context, img, + gc->foreground, gc->background, bounds, srcRect, dstRect); + CFRelease(img); + } else { + TkMacOSXDbgMsg("Failed to construct CGImage."); + } + + TkMacOSXRestoreDrawingContext(&dc); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XCopyPlane -- + * + * Copies a bitmap from a source drawable to a destination drawable. The + * plane argument specifies which bit plane of the source contains the + * bitmap. Note that this implementation ignores the gc->function. + * + * Results: + * None. + * + * Side effects: + * Changes the destination drawable. + * + *---------------------------------------------------------------------- + */ + +int +XCopyPlane( + Display *display, /* Display. */ + Drawable src, /* Source drawable. */ + Drawable dst, /* Destination drawable. */ + GC gc, /* GC to use. */ + int src_x, /* X & Y, width & height */ + int src_y, /* define the source rectangle */ + unsigned int width, /* that will be copied. */ + unsigned int height, + int dest_x, /* Dest X & Y on dest rect. */ + int dest_y, + unsigned long plane) /* Which plane to copy. */ +{ + TkMacOSXDrawingContext dc; + MacDrawable *srcDraw = (MacDrawable *) src; + MacDrawable *dstDraw = (MacDrawable *) dst; + CGRect bounds, srcRect, dstRect; + display->request++; + if (!width || !height) { + /* TkMacOSXDbgMsg("Drawing of empty area requested"); */ + return BadDrawable; + } + if (plane != 1) { + Tcl_Panic("Unexpected plane specified for XCopyPlane"); + } + if (srcDraw->flags & TK_IS_PIXMAP) { + if (!TkMacOSXSetupDrawingContext(dst, gc, &dc)) { + return BadDrawable; + } + + CGContextRef context = dc.context; + + if (context) { + CGImageRef img = CreateCGImageFromPixmap(src); + + if (img) { + TkpClipMask *clipPtr = (TkpClipMask *) gc->clip_mask; + unsigned long imageBackground = gc->background; + + if (clipPtr && clipPtr->type == TKP_CLIP_PIXMAP) { + srcRect = CGRectMake(src_x, src_y, width, height); + CGImageRef mask = CreateCGImageFromPixmap( + clipPtr->value.pixmap); + CGImageRef submask = CGImageCreateWithImageInRect( + img, srcRect); + CGRect rect = CGRectMake(dest_x, dest_y, width, height); + + rect = CGRectOffset(rect, dstDraw->xOff, dstDraw->yOff); + CGContextSaveGState(context); + + /* + * Move the origin of the destination to top left. + */ + + CGContextTranslateCTM(context, + 0, rect.origin.y + CGRectGetMaxY(rect)); + CGContextScaleCTM(context, 1, -1); + + /* + * Fill with the background color, clipping to the mask. + */ + + CGContextClipToMask(context, rect, submask); + TkMacOSXSetColorInContext(gc, gc->background, dc.context); + CGContextFillRect(context, rect); + + /* + * Fill with the foreground color, clipping to the + * intersection of img and mask. + */ + + CGImageRef subimage = CGImageCreateWithImageInRect( + img, srcRect); + CGContextClipToMask(context, rect, subimage); + TkMacOSXSetColorInContext(gc, gc->foreground, context); + CGContextFillRect(context, rect); + CGContextRestoreGState(context); + CGImageRelease(img); + CGImageRelease(mask); + CGImageRelease(submask); + CGImageRelease(subimage); + } else { + bounds = CGRectMake(0, 0, + srcDraw->size.width, srcDraw->size.height); + srcRect = CGRectMake(src_x, src_y, width, height); + dstRect = CGRectMake(dest_x, dest_y, width, height); + TkMacOSXDrawCGImage(dst, gc, dc.context, img, + gc->foreground, imageBackground, bounds, + srcRect, dstRect); + CGImageRelease(img); + } + } else { + /* no image */ + TkMacOSXDbgMsg("Invalid source drawable"); + } + } else { + TkMacOSXDbgMsg("Invalid destination drawable - " + "could not get a bitmap context."); + } + TkMacOSXRestoreDrawingContext(&dc); + return Success; + } else { + /* + * Source drawable is a Window, not a Pixmap. + */ + + return XCopyArea(display, src, dst, gc, src_x, src_y, width, height, + dest_x, dest_y); + } +} + +/* * Local Variables: * mode: objc * c-basic-offset: 4 diff --git a/macosx/tkMacOSXMenu.c b/macosx/tkMacOSXMenu.c index a77ea59..321b4f8 100644 --- a/macosx/tkMacOSXMenu.c +++ b/macosx/tkMacOSXMenu.c @@ -690,7 +690,7 @@ TkpConfigureMenuEntry( if (mePtr->image) { Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight); - image = TkMacOSXGetNSImageWithTkImage(mePtr->menuPtr->display, + image = TkMacOSXGetNSImageFromTkImage(mePtr->menuPtr->display, mePtr->image, imageWidth, imageHeight); } else if (mePtr->bitmapPtr != None) { Pixmap bitmap = Tk_GetBitmapFromObj(mePtr->menuPtr->tkwin, @@ -698,7 +698,7 @@ TkpConfigureMenuEntry( Tk_SizeOfBitmap(mePtr->menuPtr->display, bitmap, &imageWidth, &imageHeight); - image = TkMacOSXGetNSImageWithBitmap(mePtr->menuPtr->display, bitmap, + image = TkMacOSXGetNSImageFromBitmap(mePtr->menuPtr->display, bitmap, gc, imageWidth, imageHeight); if (gc->foreground == defaultFg) { // Use a semantic foreground color by default diff --git a/macosx/tkMacOSXPort.h b/macosx/tkMacOSXPort.h index 4347766..ba47566 100644 --- a/macosx/tkMacOSXPort.h +++ b/macosx/tkMacOSXPort.h @@ -163,6 +163,13 @@ #define TK_DYNAMIC_COLORMAP 0x0fffffff /* + * Inform tkImgPhInstance.c that our tkPutImage can render an image with an + * alpha channel directly into a window. + */ + +#define TKPUTIMAGE_CAN_BLEND + +/* * Used by xcolor.c */ diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index 5c4ae7d..1091d7c 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -4,7 +4,8 @@ * Macros and declarations that are purely internal & private to TkAqua. * * Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net> - * Copyright 2008-2009, Apple Inc. + * Copyright (c) 2008-2009 Apple Inc. + * Copyright (c) 2020 Marc Culler * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. @@ -254,9 +255,6 @@ MODULE_SCOPE WindowClass TkMacOSXWindowClass(TkWindow *winPtr); MODULE_SCOPE int TkMacOSXIsWindowZoomed(TkWindow *winPtr); MODULE_SCOPE int TkGenerateButtonEventForXPointer(Window window); MODULE_SCOPE EventModifiers TkMacOSXModifierState(void); -MODULE_SCOPE NSBitmapImageRep* TkMacOSXBitmapRepFromDrawableRect(Drawable drawable, - int x, int y, unsigned int width, unsigned int height); -MODULE_SCOPE CGImageRef TkMacOSXCreateCGImageWithXImage(XImage *image); MODULE_SCOPE void TkMacOSXDrawCGImage(Drawable d, GC gc, CGContextRef context, CGImageRef image, unsigned long imageForeground, unsigned long imageBackground, CGRect imageBounds, @@ -279,10 +277,9 @@ MODULE_SCOPE HIShapeRef TkMacOSXGetClipRgn(Drawable drawable); MODULE_SCOPE void TkMacOSXInvalidateViewRegion(NSView *view, HIShapeRef rgn); MODULE_SCOPE CGContextRef TkMacOSXGetCGContextForDrawable(Drawable drawable); -MODULE_SCOPE CGImageRef TkMacOSXCreateCGImageWithDrawable(Drawable drawable); -MODULE_SCOPE NSImage* TkMacOSXGetNSImageWithTkImage(Display *display, +MODULE_SCOPE NSImage* TkMacOSXGetNSImageFromTkImage(Display *display, Tk_Image image, int width, int height); -MODULE_SCOPE NSImage* TkMacOSXGetNSImageWithBitmap(Display *display, +MODULE_SCOPE NSImage* TkMacOSXGetNSImageFromBitmap(Display *display, Pixmap bitmap, GC gc, int width, int height); MODULE_SCOPE CGColorRef TkMacOSXCreateCGColor(GC gc, unsigned long pixel); MODULE_SCOPE NSColor* TkMacOSXGetNSColor(GC gc, unsigned long pixel); diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index cbe6f08..aebf82c 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -2581,7 +2581,7 @@ WmIconphotoCmd( Tk_SizeOfImage(tk_icon, &width, &height); if (width != 0 && height != 0) { - newIcon = TkMacOSXGetNSImageWithTkImage(winPtr->display, tk_icon, + newIcon = TkMacOSXGetNSImageFromTkImage(winPtr->display, tk_icon, width, height); } Tk_FreeImage(tk_icon); diff --git a/xlib/X11/Xlib.h b/xlib/X11/Xlib.h index 91aab57..1977939 100644 --- a/xlib/X11/Xlib.h +++ b/xlib/X11/Xlib.h @@ -331,7 +331,7 @@ typedef struct _XImage { 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*/ + int pixelpower; /* No longer used. */ #endif struct funcs { /* image manipulation routines */ struct _XImage *(*create_image)(); |