Tk Source Code

Artifact [3f1965cc]
Login

Artifact 3f1965cc7dfd8f36941146f329197be222ddb742942cc603bd821ee82fa5ec1f:

Attachment "ttk-image-macos-fixes.diff" to ticket [7caf9e9e] added by mtmcp_ 2026-05-30 16:54:39. (unpublished)
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;