summaryrefslogtreecommitdiffstats
path: root/macosx/tkMacOSXImage.c
diff options
context:
space:
mode:
Diffstat (limited to 'macosx/tkMacOSXImage.c')
-rw-r--r--macosx/tkMacOSXImage.c810
1 files changed, 775 insertions, 35 deletions
diff --git a/macosx/tkMacOSXImage.c b/macosx/tkMacOSXImage.c
index e51ad8d..01c0b1b 100644
--- a/macosx/tkMacOSXImage.c
+++ b/macosx/tkMacOSXImage.c
@@ -1,12 +1,13 @@
/*
* tkMacOSXImage.c --
*
- * The code in this file provides an interface for XImages,
+ * The code in this file provides an interface for XImages, and
+ * implements the nsimage image type.
*
- * Copyright (c) 1995-1997 Sun Microsystems, Inc.
- * Copyright (c) 2001-2009, Apple Inc.
- * Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net>
- * Copyright (c) 2017-2021 Marc Culler.
+ * Copyright © 1995-1997 Sun Microsystems, Inc.
+ * Copyright © 2001-2009, Apple Inc.
+ * Copyright © 2005-2009 Daniel A. Steffen <das@users.sourceforge.net>
+ * Copyright © 2017-2021 Marc Culler.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -14,12 +15,10 @@
#include "tkMacOSXPrivate.h"
#include "tkMacOSXConstants.h"
+#include "tkMacOSXImage.h"
+#include "tkColor.h"
#include "xbytes.h"
-static CGImageRef CreateCGImageFromPixmap(Drawable pixmap);
-static CGImageRef CreateCGImageFromDrawableRect( Drawable drawable,
- int x, int y, unsigned int width, unsigned int height);
-
/* Pixel formats
*
* Tk uses the XImage structure defined in Xlib.h for storing images. The
@@ -458,15 +457,16 @@ XCreateImage(
* TkPutImage, XPutImage, TkpPutRGBAImage --
*
* These functions, which all have the same signature, copy a rectangular
- * subimage of an XImage into a drawable. The first two are identical on
- * macOS. They assume that the XImage data has the structure of a 32bpp
- * ZPixmap in which the image data is an array of 32bit integers packed
- * with 8 bit values for the Red Green and Blue channels. They ignore the
- * fourth byte. The function TkpPutRGBAImage assumes that the XImage data
- * has been extended by using the fourth byte to store an 8-bit Alpha
- * value. (The Alpha data is assumed not to pre-multiplied). The image
- * is then drawn into the drawable using standard Porter-Duff Source Atop
- * Composition (kCGBlendModeSourceAtop in Apple's Core Graphics).
+ * subimage of an XImage into a drawable. TkPutImage is an alias for
+ * XPutImage, which assumes that the XImage data has the structure of a
+ * 32bpp ZPixmap in which the image data is an array of 32bit integers
+ * packed with 8 bit values for the Red Green and Blue channels. The
+ * fourth byte is ignored. The function TkpPutRGBAImage assumes that the
+ * XImage data has been extended by using the fourth byte to store an
+ * 8-bit Alpha value. (The Alpha data is assumed not to pre-multiplied).
+ * The image is then drawn into the drawable using standard Porter-Duff
+ * Source Atop Composition (kCGBlendModeSourceAtop in Apple's Core
+ * Graphics).
*
* The TkpPutRGBAImage function is used by TkImgPhotoDisplay to render photo
* images if the compile-time variable TK_CAN_RENDER_RGBA is defined in
@@ -555,23 +555,6 @@ int XPutImage(
src_x, src_y, dest_x, dest_y, width, height);
}
-int TkPutImage(
- TCL_UNUSED(unsigned long *),
- TCL_UNUSED(int),
- Display* display,
- Drawable drawable,
- GC gc,
- XImage* image,
- int src_x,
- int src_y,
- int dest_x,
- int dest_y,
- unsigned int width,
- unsigned int height) {
- return TkMacOSXPutImage(IGNORE_ALPHA, display, drawable, gc, image,
- src_x, src_y, dest_x, dest_y, width, height);
-}
-
int TkpPutRGBAImage(
Display* display,
Drawable drawable,
@@ -688,6 +671,57 @@ CreateCGImageFromDrawableRect(
}
return result;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreatePDFFromDrawableRect
+ *
+ * Extract PDF data from a MacOSX drawable.
+ *
+ * Results:
+ * Returns a CFDataRef that can be written to a file.
+ *
+ * NOTE: The x,y coordinates should be relative to a coordinate system
+ * with origin at the bottom left as used by NSView, not top left
+ * as used by XImage and CGImage.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+CFDataRef
+CreatePDFFromDrawableRect(
+ Drawable drawable,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height)
+{
+ MacDrawable *mac_drawable = (MacDrawable *)drawable;
+ NSView *view = TkMacOSXGetNSViewForDrawable(mac_drawable);
+ if (view == nil) {
+ TkMacOSXDbgMsg("Invalid source drawable");
+ return NULL;
+ }
+ NSRect bounds, viewSrcRect;
+
+ /*
+ * Get the child window area in NSView coordinates
+ * (origin at bottom left).
+ */
+
+ bounds = [view bounds];
+ viewSrcRect = NSMakeRect(mac_drawable->xOff + x,
+ bounds.size.height - height - (mac_drawable->yOff + y),
+ width, height);
+ NSData *viewData = [view dataWithPDFInsideRect:viewSrcRect];
+ CFDataRef result = (CFDataRef)viewData;
+ return result;
+}
+
/*
*----------------------------------------------------------------------
@@ -1028,6 +1062,712 @@ XCopyPlane(
}
}
+/* ---------------------------------------------------------------------------*/
+
+/*
+ * Implementation of a Tk image type which provide access to NSImages
+ * for use in buttons etc.
+ */
+
+/*
+ * Forward declarations.
+ */
+
+typedef struct TkMacOSXNSImageInstance TkMacOSXNSImageInstance;
+typedef struct TkMacOSXNSImageModel TkMacOSXNSImageModel;
+
+/*
+ * The following data structure represents a particular use of an nsimage
+ * in a widget.
+ */
+
+struct TkMacOSXNSImageInstance {
+ TkMacOSXNSImageModel *modelPtr; /* Pointer to the model for the image. */
+ NSImage *image; /* Pointer to an NSImage.*/
+ TkMacOSXNSImageInstance *nextPtr; /* First in the list of instances associated
+ * with this model. */
+};
+
+/*
+ * The following data structure represents the model for an nsimage:
+ */
+
+struct TkMacOSXNSImageModel {
+ Tk_ImageModel tkModel; /* Tk's token for image model. */
+ Tcl_Interp *interp; /* Interpreter for application. */
+ int width, height; /* Dimensions of the image. */
+ int radius; /* Radius for rounded corners. */
+ int ring; /* Thickness of the focus ring. */
+ double alpha; /* Transparency, between 0.0 and 1.0*/
+ char *imageName ; /* Malloc'ed image name. */
+ char *source; /* Malloc'ed string describing the image. */
+ char *as; /* Malloc'ed interpretation of source */
+ int flags; /* Sundry flags, defined below. */
+ bool pressed; /* Image is for use in a pressed button.*/
+ bool templ; /* Image is for use as a template.*/
+ TkMacOSXNSImageInstance *instancePtr; /* Start of list of instances associated
+ * with this model. */
+ NSImage *image; /* The underlying NSImage object. */
+ NSImage *darkModeImage; /* A modified image to use in Dark Mode. */
+};
+
+/*
+ * Bit definitions for the flags field of a TkMacOSXNSImageModel.
+ * IMAGE_CHANGED: 1 means that the instances of this image need
+ * to be redisplayed.
+ */
+
+#define IMAGE_CHANGED 1
+
+/*
+ * The type record for nsimage images:
+ */
+
+static int TkMacOSXNSImageCreate(Tcl_Interp *interp,
+ const char *name, Tcl_Size objc, Tcl_Obj *const objv[],
+ const Tk_ImageType *typePtr, Tk_ImageModel model,
+ void **clientDataPtr);
+static void *TkMacOSXNSImageGet(Tk_Window tkwin, void *clientData);
+static void TkMacOSXNSImageDisplay(void *clientData,
+ Display *display, Drawable drawable,
+ int imageX, int imageY, int width,
+ int height, int drawableX,
+ int drawableY);
+static void TkMacOSXNSImageFree(void *clientData, Display *display);
+static void TkMacOSXNSImageDelete(void *clientData);
+
+static Tk_ImageType TkMacOSXNSImageType = {
+ "nsimage", /* name of image type */
+ TkMacOSXNSImageCreate, /* createProc */
+ TkMacOSXNSImageGet, /* getProc */
+ TkMacOSXNSImageDisplay, /* displayProc */
+ TkMacOSXNSImageFree, /* freeProc */
+ TkMacOSXNSImageDelete, /* deleteProc */
+ NULL, /* postscriptPtr */
+ NULL, /* nextPtr */
+ NULL
+};
+
+/*
+ * Default values used for parsing configuration specifications:
+ */
+#define DEF_SOURCE ""
+#define DEF_AS "name"
+#define DEF_HEIGHT "0"
+#define DEF_WIDTH "0"
+#define DEF_RADIUS "0"
+#define DEF_RING "0"
+#define DEF_ALPHA "1.0"
+#define DEF_PRESSED "0"
+#define DEF_TEMPLATE "0"
+
+static const Tk_OptionSpec systemImageOptions[] = {
+ {TK_OPTION_STRING, "-source", NULL, NULL, DEF_SOURCE,
+ -1, offsetof(TkMacOSXNSImageModel, source), 0, NULL, 0},
+ {TK_OPTION_STRING, "-as", NULL, NULL, DEF_AS,
+ -1, offsetof(TkMacOSXNSImageModel, as), 0, NULL, 0},
+ {TK_OPTION_INT, "-width", NULL, NULL, DEF_WIDTH,
+ -1, offsetof(TkMacOSXNSImageModel, width), 0, NULL, 0},
+ {TK_OPTION_INT, "-height", NULL, NULL, DEF_HEIGHT,
+ -1, offsetof(TkMacOSXNSImageModel, height), 0, NULL, 0},
+ {TK_OPTION_INT, "-radius", NULL, NULL, DEF_RADIUS,
+ -1, offsetof(TkMacOSXNSImageModel, radius), 0, NULL, 0},
+ {TK_OPTION_INT, "-ring", NULL, NULL, DEF_RING,
+ -1, offsetof(TkMacOSXNSImageModel, ring), 0, NULL, 0},
+ {TK_OPTION_DOUBLE, "-alpha", NULL, NULL, DEF_ALPHA,
+ -1, offsetof(TkMacOSXNSImageModel, alpha), 0, NULL, 0},
+ {TK_OPTION_BOOLEAN, "-pressed", NULL, NULL, DEF_PRESSED,
+ -1, offsetof(TkMacOSXNSImageModel, pressed), TK_OPTION_VAR(bool), NULL, 0},
+ {TK_OPTION_BOOLEAN, "-template", NULL, NULL, DEF_TEMPLATE,
+ -1, offsetof(TkMacOSXNSImageModel, templ), TK_OPTION_VAR(bool), NULL, 0},
+ {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, NULL, 0}
+};
+
+/*
+ * The -as option specifies how the string provided in the -source
+ * option should be interpreted as a description of an NSImage.
+ * Below are the possible values and their meanings. (The last two
+ * provide the macOS icon for a particular file type.)
+ */
+
+static const char *sourceInterpretations[] = {
+ "name", /* A name for a named NSImage. */
+ "file", /* A path to an image file. */
+ "path", /* A path to a file whose type should be examined. */
+ "filetype", /* A file extension or 4-byte OSCode. */
+};
+
+enum {NAME_SOURCE, FILE_SOURCE, PATH_SOURCE, FILETYPE_SOURCE};
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TintImage --
+ *
+ * Modify an NSImage by blending it with a color. The transparent part of
+ * the image remains transparent. The opaque part of the image is painted
+ * with the color, using the specified alpha value for the transparency of
+ * the color.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The appearance of the NSImage changes.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void TintImage(
+ NSImage *image,
+ NSColor *color,
+ double alpha)
+{
+ NSSize size = [image size];
+ NSRect rect = {NSZeroPoint, size};
+ NSImage *mask = [[[NSImage alloc] initWithSize:size] retain];
+ [mask lockFocus];
+ [color set];
+ NSRectFillUsingOperation(rect, NSCompositeCopy);
+ [image drawInRect:rect
+ fromRect:rect
+ operation:NSCompositeDestinationIn
+ fraction:1.0];
+ [mask unlockFocus];
+ [image lockFocus];
+ [mask drawInRect:rect
+ fromRect:rect
+ operation:NSCompositeSourceOver
+ fraction:alpha];
+ [image unlockFocus];
+ [mask release];
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXNSImageConfigureModel --
+ *
+ * This function is called when an nsimage image is created or
+ * reconfigured. It processes configuration options and resets any
+ * instances of the image.
+ *
+ * Results:
+ * A standard Tcl return value. If TCL_ERROR is returned then an error
+ * message is left in the modelPtr->interp's result.
+ *
+ * Side effects:
+ * Existing instances of the image will be redisplayed to match the new
+ * configuration options.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TkMacOSXNSImageConfigureModel(
+ Tcl_Interp *interp, /* Interpreter to use for reporting errors. */
+ TkMacOSXNSImageModel *modelPtr, /* Pointer to data structure describing
+ * overall photo image to (re)configure. */
+ Tcl_Size objc, /* Number of entries in objv. */
+ Tcl_Obj *const objv[]) /* Pairs of configuration options for image. */
+{
+ Tk_OptionTable optionTable = Tk_CreateOptionTable(interp, systemImageOptions);
+ NSImage *newImage;
+ Tcl_Obj *objPtr;
+ static Tcl_Obj *asOption = NULL;
+ int sourceInterpretation;
+ NSString *source;
+ int oldWidth = modelPtr->width, oldHeight = modelPtr->height;
+
+ if (asOption == NULL) {
+ asOption = Tcl_NewStringObj("-as", TCL_INDEX_NONE);
+ Tcl_IncrRefCount(asOption);
+ }
+
+ modelPtr->width = 0;
+ modelPtr->height = 0;
+ if (Tk_SetOptions(interp, modelPtr, optionTable, objc, objv,
+ NULL, NULL, NULL) != TCL_OK){
+ goto errorExit;
+ }
+ if (modelPtr->width == 0 && modelPtr->height == 0) {
+ modelPtr->width = oldWidth;
+ modelPtr->height = oldHeight;
+ }
+
+ if (modelPtr->source == NULL || modelPtr->source[0] == '0') {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("-source is required.", TCL_INDEX_NONE));
+ Tcl_SetErrorCode(interp, "TK", "IMAGE", "SYSTEM", "BAD_VALUE", NULL);
+ goto errorExit;
+ }
+
+ objPtr = Tk_GetOptionValue(interp, (char *) modelPtr, optionTable,
+ asOption, NULL);
+ if (Tcl_GetIndexFromObj(interp, objPtr, sourceInterpretations, "option",
+ 0, &sourceInterpretation) != TCL_OK) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "Unknown interpretation for source in -as option. "
+ "Should be name, file, path, or filetype.", TCL_INDEX_NONE));
+ Tcl_SetErrorCode(interp, "TK", "IMAGE", "SYSTEM", "BAD_VALUE", NULL);
+ goto errorExit;
+ }
+
+ source = [[NSString alloc] initWithUTF8String: modelPtr->source];
+ switch (sourceInterpretation) {
+ case NAME_SOURCE:
+ newImage = [[NSImage imageNamed:source] copy];
+ break;
+ case FILE_SOURCE:
+ newImage = [[NSImage alloc] initWithContentsOfFile:source];
+ break;
+ case PATH_SOURCE:
+ newImage = [[NSWorkspace sharedWorkspace] iconForFile:source];
+ break;
+ case FILETYPE_SOURCE:
+ newImage = TkMacOSXIconForFileType(source);
+ break;
+ default:
+ newImage = NULL;
+ break;
+ }
+ [source release];
+ if (newImage) {
+ NSSize size = NSMakeSize(modelPtr->width - 2*modelPtr->ring,
+ modelPtr->height - 2*modelPtr->ring);
+ [modelPtr->image release];
+ [modelPtr->darkModeImage release];
+ newImage.size = size;
+ modelPtr->image = [newImage retain];
+ if (modelPtr->templ) {
+ newImage.template = YES;
+ }
+ modelPtr->darkModeImage = [[newImage copy] retain];
+ if ([modelPtr->darkModeImage isTemplate]) {
+
+ /*
+ * For a template image the Dark Mode version should be white.
+ */
+
+ NSRect rect = {NSZeroPoint, size};
+ [modelPtr->darkModeImage lockFocus];
+ [[NSColor whiteColor] set];
+ NSRectFillUsingOperation(rect, NSCompositeSourceAtop);
+ [modelPtr->darkModeImage unlockFocus];
+ } else if (modelPtr->pressed) {
+
+ /*
+ * Non-template pressed images are darker in Light Mode and lighter
+ * in Dark Mode.
+ */
+
+ TintImage(modelPtr->image, [NSColor blackColor], 0.2);
+ TintImage(modelPtr->darkModeImage, [NSColor whiteColor], 0.5);
+ }
+ } else {
+ switch(sourceInterpretation) {
+ case NAME_SOURCE:
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("Unknown named NSImage.\n"
+ "Try omitting ImageName, "
+ "e.g. use NSCaution for NSImageNameCaution.", TCL_INDEX_NONE));
+ Tcl_SetErrorCode(interp, "TK", "IMAGE", "SYSTEM", "BAD_VALUE", NULL);
+ goto errorExit;
+ case FILE_SOURCE:
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "Failed to load image file.\n", TCL_INDEX_NONE));
+ Tcl_SetErrorCode(interp, "TK", "IMAGE", "SYSTEM", "BAD_VALUE", NULL);
+ goto errorExit;
+ default:
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "Unrecognized file type.\n"
+ "If using a filename extension, do not include the dot.\n", TCL_INDEX_NONE));
+ Tcl_SetErrorCode(interp, "TK", "IMAGE", "SYSTEM", "BAD_VALUE", NULL);
+ goto errorExit;
+ }
+ }
+
+ /*
+ * Set the width and height. If only one is specified, set the other one
+ * so as to preserve the aspect ratio. If neither is specified, match the
+ * size of the image.
+ */
+
+ if (modelPtr->width == 0 && modelPtr->height == 0) {
+ CGSize size = [modelPtr->image size];
+ modelPtr->width = (int) size.width;
+ modelPtr->height = (int) size.height;
+ } else {
+ CGSize size = [modelPtr->image size], newsize;
+ CGFloat aspect = size.width && size.height ?
+ size.height / size.width : 1;
+ if (modelPtr->width == 0) {
+ modelPtr->width = (int) ((CGFloat)(modelPtr->height) / aspect);
+ } else if (modelPtr->height == 0) {
+ modelPtr->height = (int) ((CGFloat)(modelPtr->width) * aspect);
+ }
+ newsize = NSMakeSize(modelPtr->width, modelPtr->height);
+ modelPtr->image.size = newsize;
+ modelPtr->darkModeImage.size = newsize;
+ }
+
+ /*
+ * Inform the generic image code that the image has (potentially) changed.
+ */
+
+ Tk_ImageChanged(modelPtr->tkModel, 0, 0, modelPtr->width,
+ modelPtr->height, modelPtr->width, modelPtr->height);
+ modelPtr->flags &= ~IMAGE_CHANGED;
+
+ return TCL_OK;
+
+ errorExit:
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXNSImageObjCmd --
+ *
+ * This function implements the configure and cget commands for an
+ * nsimage instance.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * The image may be reconfigured.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkMacOSXNSImageObjCmd(
+ void *clientData, /* Information about the image model. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ TkMacOSXNSImageModel *modelPtr = (TkMacOSXNSImageModel *)clientData;
+ Tk_OptionTable optionTable = Tk_CreateOptionTable(interp, systemImageOptions);
+ static const char *const options[] = {"cget", "configure", NULL};
+ enum {CGET, CONFIGURE};
+ Tcl_Obj *objPtr;
+ int index;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIndexFromObjStruct(interp, objv[1], options,
+ sizeof(char *), "option", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_Preserve(modelPtr);
+ switch (index) {
+ case CGET:
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "option");
+ return TCL_ERROR;
+ }
+ objPtr = Tk_GetOptionValue(interp, (char *)modelPtr, optionTable,
+ objv[2], NULL);
+ if (objPtr == NULL) {
+ goto error;
+ }
+ Tcl_SetObjResult(interp, objPtr);
+ break;
+ case CONFIGURE:
+ if (objc == 2) {
+ objPtr = Tk_GetOptionInfo(interp, (char *)modelPtr, optionTable,
+ NULL, NULL);
+ if (objPtr == NULL) {
+ goto error;
+ }
+ Tcl_SetObjResult(interp, objPtr);
+ break;
+ } else if (objc == 3) {
+ objPtr = Tk_GetOptionInfo(interp, (char *)modelPtr, optionTable,
+ objv[2], NULL);
+ if (objPtr == NULL) {
+ goto error;
+ }
+ Tcl_SetObjResult(interp, objPtr);
+ break;
+ } else {
+ TkMacOSXNSImageConfigureModel(interp, modelPtr, objc - 2, objv + 2);
+ break;
+ }
+ default:
+ break;
+ }
+
+ Tcl_Release(modelPtr);
+ return TCL_OK;
+
+ error:
+ Tcl_Release(modelPtr);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXNSImageCreate --
+ *
+ * Allocate and initialize an nsimage model.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * The data structure for a new image is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TkMacOSXNSImageCreate(
+ Tcl_Interp *interp, /* Interpreter for application using image. */
+ const char *name, /* Name to use for image. */
+ Tcl_Size objc, /* Number of arguments. */
+ Tcl_Obj *const objv[], /* Argument strings for options (not
+ * including image name or type). */
+ TCL_UNUSED(const Tk_ImageType *), /* typePtr */
+ Tk_ImageModel model, /* Token for image, to be used in callbacks. */
+ void **clientDataPtr) /* Store manager's token for image here; it
+ * will be returned in later callbacks. */
+{
+ TkMacOSXNSImageModel *modelPtr;
+ Tk_OptionTable optionTable = Tk_CreateOptionTable(interp, systemImageOptions);
+
+ modelPtr = (TkMacOSXNSImageModel *)ckalloc(sizeof(TkMacOSXNSImageModel));
+ modelPtr->tkModel = model;
+ modelPtr->interp = interp;
+ modelPtr->imageName = (char *)ckalloc(strlen(name) + 1);
+ strcpy(modelPtr->imageName, name);
+ modelPtr->flags = 0;
+ modelPtr->instancePtr = NULL;
+ modelPtr->image = NULL;
+ modelPtr->darkModeImage = NULL;
+ modelPtr->source = NULL;
+ modelPtr->as = NULL;
+
+ /*
+ * Process configuration options given in the image create command.
+ */
+
+ if (Tk_InitOptions(interp, (char *) modelPtr, optionTable, NULL) != TCL_OK
+ || TkMacOSXNSImageConfigureModel(interp, modelPtr, objc, objv) != TCL_OK) {
+ TkMacOSXNSImageDelete(modelPtr);
+ return TCL_ERROR;
+ }
+ Tcl_CreateObjCommand(interp, name, TkMacOSXNSImageObjCmd, modelPtr, NULL);
+ *clientDataPtr = modelPtr;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXNSImageGet --
+ *
+ * Allocate and initialize an nsimage instance.
+ *
+ * Results:
+ * The return value is a token for the image instance, which is used in
+ * future callbacks to ImageDisplay and ImageFree.
+ *
+ * Side effects:
+ * A new new nsimage instance is created.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void *
+TkMacOSXNSImageGet(
+ TCL_UNUSED(Tk_Window), /* tkwin */
+ void *clientData) /* Pointer to TkMacOSXNSImageModel for image. */
+{
+ TkMacOSXNSImageModel *modelPtr = (TkMacOSXNSImageModel *) clientData;
+ TkMacOSXNSImageInstance *instPtr;
+
+ instPtr = (TkMacOSXNSImageInstance *)ckalloc(sizeof(TkMacOSXNSImageInstance));
+ instPtr->modelPtr = modelPtr;
+ return instPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXNSImageDisplay --
+ *
+ * Display or redisplay an nsimage in the given drawable.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The image gets drawn.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TkMacOSXNSImageDisplay(
+ void *clientData, /* Pointer to TkMacOSXNSImageInstance for image. */
+ TCL_UNUSED(Display *), /* display */
+ Drawable drawable, /* Where to draw or redraw image. */
+ int imageX, int imageY, /* Origin of area to redraw, relative to
+ * origin of image. */
+ int width, int height, /* Dimensions of area to redraw. */
+ int drawableX, int drawableY)
+ /* Coordinates in drawable corresponding to
+ * imageX and imageY. */
+{
+ MacDrawable *macWin = (MacDrawable *) drawable;
+ Tk_Window tkwin = (Tk_Window) macWin->winPtr;
+ TkMacOSXNSImageInstance *instPtr = (TkMacOSXNSImageInstance *) clientData;
+ TkMacOSXNSImageModel *modelPtr = instPtr->modelPtr;
+ TkMacOSXDrawingContext dc;
+ NSRect dstRect = NSMakeRect(macWin->xOff + drawableX,
+ macWin->yOff + drawableY, width, height);
+ NSRect srcRect = NSMakeRect(imageX, imageY, width, height);
+ NSImage *image = TkMacOSXInDarkMode(tkwin) ? modelPtr->darkModeImage :
+ modelPtr->image;
+ int ring = modelPtr->ring;
+ int radius = modelPtr->radius;
+
+ if (TkMacOSXSetupDrawingContext(drawable, NULL, &dc)) {
+ if (dc.context) {
+ CGRect clipRect = CGRectMake(
+ dstRect.origin.x - srcRect.origin.x + ring,
+ dstRect.origin.y - srcRect.origin.y + ring,
+ modelPtr->width - 2*ring,
+ modelPtr->height - 2*ring);
+ CGPathRef path = CGPathCreateWithRoundedRect(clipRect, radius, radius, NULL);
+ CGContextSaveGState(dc.context);
+ CGContextBeginPath(dc.context);
+ CGContextAddPath(dc.context, path);
+ CGContextClip(dc.context);
+ NSGraphicsContext *savedContext = NSGraphicsContext.currentContext;
+ NSGraphicsContext.currentContext = [NSGraphicsContext
+ graphicsContextWithCGContext:dc.context flipped:YES];
+ [image drawInRect:clipRect
+ fromRect:srcRect
+ operation:NSCompositeSourceOver
+ fraction:modelPtr->alpha
+ respectFlipped:YES
+ hints:nil];
+ CGContextRestoreGState(dc.context);
+
+ /*
+ * Draw the focus ring.
+ */
+
+ if (ring) {
+ CGRect ringRect = CGRectInset(clipRect, -ring, -ring);
+ CGPathRef ringPath = CGPathCreateWithRoundedRect(ringRect,
+ radius + ring, radius + ring, NULL);
+ CGContextSaveGState(dc.context);
+ CGContextAddPath(dc.context, path);
+ CGContextAddPath(dc.context, ringPath);
+ CGContextSetFillColorWithColor(dc.context,
+ controlAccentColor().CGColor);
+ CGContextEOFillPath(dc.context);
+ CGContextRestoreGState(dc.context);
+ CFRelease(ringPath);
+ }
+ CFRelease(path);
+ NSGraphicsContext.currentContext = savedContext;
+ }
+ TkMacOSXRestoreDrawingContext(&dc);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXNSImageFree --
+ *
+ * Deallocate an instance of an nsimage.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information related to the instance is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TkMacOSXNSImageFree(
+ void *clientData, /* Pointer to TkMacOSXNSImageInstance for instance. */
+ TCL_UNUSED(Display *)) /* display */
+{
+ TkMacOSXNSImageInstance *instPtr = (TkMacOSXNSImageInstance *) clientData;
+ ckfree(instPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXNSImageDelete --
+ *
+ * Deallocate an nsimage model.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * NSImages are released and memory is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TkMacOSXNSImageDelete(
+ void *clientData) /* Pointer to TkMacOSXNSImageModel for image. When
+ * this function is called, no more instances
+ * exist. */
+{
+ TkMacOSXNSImageModel *modelPtr = (TkMacOSXNSImageModel *) clientData;
+
+ Tcl_DeleteCommand(modelPtr->interp, modelPtr->imageName);
+ ckfree(modelPtr->imageName);
+ ckfree(modelPtr->source);
+ ckfree(modelPtr->as);
+ [modelPtr->image release];
+ [modelPtr->darkModeImage release];
+ ckfree(modelPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkMacOSXNSImage_Init --
+ *
+ * Adds the TkMacOSXNSImage type to Tk.
+ *
+ * Results:
+ * Returns a standard Tcl completion code, and leaves an error message in
+ * the interp's result if an error occurs.
+ *
+ * Side effects:
+ * Creates the image create nsrect ... command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkMacOSXNSImage_Init(
+ TCL_UNUSED(Tcl_Interp *)) /* interp */
+{
+ Tk_CreateImageType(&TkMacOSXNSImageType);
+ return 1;
+}
+
/*
* Local Variables:
* mode: objc