Import Tk 8.6.11

This commit is contained in:
Steve Dower
2021-03-30 00:54:10 +01:00
parent 42c69189d9
commit 070b8750b0
403 changed files with 21608 additions and 16269 deletions

View File

@@ -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
@@ -128,141 +132,6 @@ TkMacOSXCreateCGImageWithXImage(
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 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;
}
/*
*----------------------------------------------------------------------
*
@@ -300,7 +169,11 @@ DestroyImage(
* Get a single pixel from an image.
*
* Results:
* Returns the 32 bit pixel value.
* The XColor structure contains an unsigned long field named pixel which
* identifies the color. This function returns the unsigned long that
* would be used as the pixel value of an XColor that has the same red
* green and blue components as the XImage pixel at the specified
* location.
*
* Side effects:
* None.
@@ -316,13 +189,18 @@ ImageGetPixel(
{
unsigned char r = 0, g = 0, b = 0;
/*
* Compute 8 bit red green and blue values, which are passed as inputs to
* TkMacOSXRGBPixel to produce the pixel value.
*/
if (image && image->data) {
unsigned char *srcPtr = ((unsigned char*) image->data)
+ (y * image->bytes_per_line)
+ (((image->xoffset + x) * image->bits_per_pixel) / NBBY);
switch (image->bits_per_pixel) {
case 32:
case 32: /* 8 bits per channel */
r = (*((unsigned int*) srcPtr) >> 16) & 0xff;
g = (*((unsigned int*) srcPtr) >> 8) & 0xff;
b = (*((unsigned int*) srcPtr) ) & 0xff;
@@ -332,12 +210,12 @@ ImageGetPixel(
r = srcPtr[1]; g = srcPtr[2]; b = srcPtr[3];
}*/
break;
case 16:
case 16: /* 5 bits per channel */
r = (*((unsigned short*) srcPtr) >> 7) & 0xf8;
g = (*((unsigned short*) srcPtr) >> 2) & 0xf8;
b = (*((unsigned short*) srcPtr) << 3) & 0xf8;
break;
case 8:
case 8: /* 2 bits per channel */
r = (*srcPtr << 2) & 0xc0;
g = (*srcPtr << 4) & 0xc0;
b = (*srcPtr << 6) & 0xc0;
@@ -345,7 +223,7 @@ ImageGetPixel(
g |= g >> 2 | g >> 4 | g >> 6;
b |= b >> 2 | b >> 4 | b >> 6;
break;
case 4: {
case 4: { /* 1 bit per channel */
unsigned char c = (x % 2) ? *srcPtr : (*srcPtr >> 4);
r = (c & 0x04) ? 0xff : 0;
@@ -353,12 +231,13 @@ ImageGetPixel(
b = (c & 0x01) ? 0xff : 0;
break;
}
case 1:
case 1: /* Black-white bitmap. */
r = g = b = ((*srcPtr) & (0x80 >> (x % 8))) ? 0xff : 0;
break;
}
}
return (PIXEL_MAGIC << 24) | (r << 16) | (g << 8) | b;
return TkMacOSXRGBPixel(r, g, b);
}
/*
@@ -463,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;
@@ -542,13 +414,13 @@ XPutImage(
int dest_x, /* Destination X & Y. */
int dest_y,
unsigned int width, /* Same width & height for both */
unsigned int height) /* distination and source. */
unsigned int height) /* destination and source. */
{
TkMacOSXDrawingContext dc;
MacDrawable *macDraw = (MacDrawable *) drawable;
MacDrawable *macDraw = (MacDrawable *)drawable;
display->request++;
if (!TkMacOSXSetupDrawingContext(drawable, gc, 1, &dc)) {
if (!TkMacOSXSetupDrawingContext(drawable, gc, &dc)) {
return BadDrawable;
}
if (dc.context) {
@@ -564,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,
@@ -607,6 +470,454 @@ TkPutImage(
return XPutImage(display, d, gc, image, src_x, src_y, dest_x, dest_y, width, height);
}
/*
*----------------------------------------------------------------------
*
* 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 = nil;
NSView *view = nil;
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 (TkMacOSXGetNSViewForDrawable(mac_drawable) != nil) {
/*
* 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 = nil;
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 (TkMacOSXGetNSWindowForDrawable(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