Attachment "ttk-image-macos-fixes.diff" to
ticket [7caf9e9e]
added by
mtmcp_
2026-05-30 16:54:39.
Index: generic/ttk/ttkImage.c
==================================================================
--- generic/ttk/ttkImage.c
+++ generic/ttk/ttkImage.c
@@ -261,10 +261,20 @@
/*------------------------------------------------------------------------
* +++ Image element definition.
*/
+/*
+ * TK_CAN_XCOPYAREA_PIXMAP marks platforms whose XCopyArea accepts a Pixmap as
+ * source or destination, which the element pixmap cache below relies on. The
+ * macOS (Aqua) XCopyArea returns BadDrawable for a Pixmap, so it is excluded.
+ */
+
+#ifndef MAC_OSX_TK
+#define TK_CAN_XCOPYAREA_PIXMAP
+#endif
+
typedef struct { /* ClientData for image elements */
Ttk_ImageSpec *imageSpec; /* Image(s) to use */
int minWidth; /* Minimum width; overrides image width */
int minHeight; /* Minimum height; overrides image height */
Ttk_Sticky sticky; /* -stickiness specification */
@@ -274,26 +284,32 @@
#ifdef TILE_07_COMPAT
Ttk_ResourceCache cache; /* Resource cache for images */
Ttk_StateMap imageMap; /* State-based lookup table for images */
#endif
+#ifdef TK_CAN_XCOPYAREA_PIXMAP
Pixmap cachedPixmap; /* Cached composed element, or None */
int cachedWidth; /* Width of cached pixmap */
int cachedHeight; /* Height of cached pixmap */
int cachedX; /* X position of cached render */
int cachedY; /* Y position of cached render */
Ttk_State cachedState; /* State at which pixmap was rendered */
Display *cachedDisplay; /* Display owning cachedPixmap */
+#endif
} ImageData;
+#ifdef TK_CAN_XCOPYAREA_PIXMAP
static void InvalidateImageCache(ImageData *imageData)
{
if (imageData->cachedPixmap != None) {
Tk_FreePixmap(imageData->cachedDisplay, imageData->cachedPixmap);
imageData->cachedPixmap = None;
imageData->cachedWidth = 0;
imageData->cachedHeight = 0;
+ imageData->cachedX = 0;
+ imageData->cachedY = 0;
+ imageData->cachedState = 0;
}
}
static void ImageElementImageChanged(
void *clientData,
@@ -305,15 +321,18 @@
TCL_UNUSED(int))
{
ImageData *imageData = (ImageData *)clientData;
InvalidateImageCache(imageData);
}
+#endif /* TK_CAN_XCOPYAREA_PIXMAP */
static void FreeImageData(void *clientData)
{
ImageData *imageData = (ImageData *)clientData;
+#ifdef TK_CAN_XCOPYAREA_PIXMAP
InvalidateImageCache(imageData);
+#endif
if (imageData->imageSpec) { TtkFreeImageSpec(imageData->imageSpec); }
#ifdef TILE_07_COMPAT
if (imageData->imageMap) { Tcl_DecrRefCount(imageData->imageMap); }
#endif
Tcl_Free(clientData);
@@ -376,10 +395,18 @@
}
Tk_SizeOfImage(image, &imgWidth, &imgHeight);
src = Ttk_MakeBox(0, 0, imgWidth, imgHeight);
dst = Ttk_StickBox(b, imgWidth, imgHeight, imageData->sticky);
+
+#ifdef TK_CAN_XCOPYAREA_PIXMAP
+ /*
+ * Element pixmap cache. Compiled in only where XCopyArea accepts a
+ * Pixmap; on macOS it returns BadDrawable for a Pixmap source or
+ * destination, so the blits below would draw nothing and that platform
+ * falls through to the direct draw.
+ */
/* Fast path: blit cached pixmap if size, position, and state match. */
if (imageData->cachedPixmap != None
&& imageData->cachedWidth == dst.width
&& imageData->cachedHeight == dst.height
@@ -440,12 +467,13 @@
dst.x, dst.y);
Tk_FreeGC(display, gc);
return;
}
}
+#endif /* TK_CAN_XCOPYAREA_PIXMAP */
- /* Fallback: window not realized or pixmap alloc failed. */
+ /* Direct draw: cache disabled, window not realized, or alloc failed. */
Ttk_Tile(tkwin, d, image, src, dst, imageData->border);
}
static const Ttk_ElementSpec ImageElementSpec =
{
@@ -481,18 +509,22 @@
"Must supply a base image", -1));
Tcl_SetErrorCode(interp, "TTK", "IMAGE", "BASE", (char *)NULL);
return TCL_ERROR;
}
- imageData = (ImageData *)ckalloc(sizeof(*imageData));
+ imageData = (ImageData *)Tcl_Alloc(sizeof(*imageData));
memset(imageData, 0, sizeof(*imageData));
- imageData->cachedPixmap = None;
+#ifdef TK_CAN_XCOPYAREA_PIXMAP
+ imageData->cachedPixmap = None;
imageSpec = TtkGetImageSpecEx(interp, Tk_MainWindow(interp), objv[0],
ImageElementImageChanged, imageData);
+#else
+ imageSpec = TtkGetImageSpec(interp, Tk_MainWindow(interp), objv[0]);
+#endif
if (!imageSpec) {
- ckfree(imageData);
+ Tcl_Free(imageData);
return TCL_ERROR;
}
imageData->imageSpec = imageSpec;
imageData->minWidth = imageData->minHeight = -1;