diff options
Diffstat (limited to 'macosx/tkMacOSXImage.c')
-rw-r--r-- | macosx/tkMacOSXImage.c | 620 |
1 files changed, 619 insertions, 1 deletions
diff --git a/macosx/tkMacOSXImage.c b/macosx/tkMacOSXImage.c index 07a20ac..934a50e 100644 --- a/macosx/tkMacOSXImage.c +++ b/macosx/tkMacOSXImage.c @@ -1,7 +1,8 @@ /* * 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 2001-2009, Apple Inc. @@ -13,6 +14,7 @@ */ #include "tkMacOSXPrivate.h" +#include "tkMacOSXConstants.h" #include "xbytes.h" #pragma mark XImage handling @@ -597,6 +599,622 @@ XPutImage( return Success; } + +/* ---------------------------------------------------------------------------*/ + +/* + * Implementation of a Tk image type which provide access to NSImages + * for use in buttons etc. + */ + +/* + * Forward declarations. + */ + +typedef struct TkNSImageInstance TkNSImageInstance; +typedef struct TkNSImageMaster TkNSImageMaster; + +/* + * The following data structure represents a particular use of an nsimage + * in a widget. + */ + +struct TkNSImageInstance { + TkNSImageMaster *masterPtr; /* Pointer to the master for the image. */ + NSImage *image; /* Pointer to a named NSImage.*/ + TkNSImageInstance *nextPtr; /* First in the list of instances associated + * with this master. */ +}; + +/* + * The following data structure represents the master for an nsimage: + */ + +struct TkNSImageMaster { + Tk_ImageMaster tkMaster; /* Tk's token for image master. */ + Tcl_Interp *interp; /* Interpreter for application. */ + int width, height; /* Dimensions of the image. */ + double alpha; /* Transparency, between 0.0 and 1.0*/ + bool pressed; /* Image is for use in a pressed button.*/ + char *imageName ; /* Malloc'ed image name. */ + char *source; /* Malloc'ed name of the NSimage. */ + char *as; /* Malloc'ed description of source */ + int flags; /* Sundry flags, defined below. */ + TkNSImageInstance *instancePtr; /* Start of list of instances associated + * with this master. */ + NSImage *image; /* The underlying NSImage object. */ + NSImage *darkModeImage; /* A modified image to use in Dark Mode. */ +}; + +/* + * Bit definitions for the flags field of a TkNSImageMaster. + * 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 TkNSImageCreate(Tcl_Interp *interp, + const char *name, int argc, Tcl_Obj *const objv[], + const Tk_ImageType *typePtr, Tk_ImageMaster master, + ClientData *clientDataPtr); +static ClientData TkNSImageGet(Tk_Window tkwin, ClientData clientData); +static void TkNSImageDisplay(ClientData clientData, + Display *display, Drawable drawable, + int imageX, int imageY, int width, + int height, int drawableX, + int drawableY); +static void TkNSImageFree(ClientData clientData, Display *display); +static void TkNSImageDelete(ClientData clientData); + +static Tk_ImageType TkNSImageType = { + "nsimage", /* name of image type */ + TkNSImageCreate, /* createProc */ + TkNSImageGet, /* getProc */ + TkNSImageDisplay, /* displayProc */ + TkNSImageFree, /* freeProc */ + TkNSImageDelete, /* deleteProc */ + NULL, /* postscriptPtr */ + NULL, /* nextPtr */ + NULL +}; + +/* + * Information used for parsing configuration specifications: + */ +#define DEF_SOURCE "" +#define DEF_AS "name" +#define DEF_HEIGHT "32" +#define DEF_WIDTH "32" +#define DEF_ALPHA "1.0" +#define DEF_PRESSED "0" + +static const Tk_OptionSpec systemImageOptions[] = { + {TK_OPTION_STRING, "-source", NULL, NULL, DEF_SOURCE, + -1, Tk_Offset(TkNSImageMaster, source), 0, NULL, 0}, + {TK_OPTION_STRING, "-as", NULL, NULL, DEF_AS, + -1, Tk_Offset(TkNSImageMaster, as), 0, NULL, 0}, + {TK_OPTION_INT, "-width", NULL, NULL, DEF_WIDTH, + -1, Tk_Offset(TkNSImageMaster, width), 0, NULL, 0}, + {TK_OPTION_INT, "-height", NULL, NULL, DEF_HEIGHT, + -1, Tk_Offset(TkNSImageMaster, height), 0, NULL, 0}, + {TK_OPTION_DOUBLE, "-alpha", NULL, NULL, DEF_ALPHA, + -1, Tk_Offset(TkNSImageMaster, alpha), 0, NULL, 0}, + {TK_OPTION_BOOLEAN, "-pressed", NULL, NULL, DEF_PRESSED, + -1, Tk_Offset(TkNSImageMaster, pressed), 0, 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]; +} + +/* + *---------------------------------------------------------------------- + * + * TkNSImageConfigureMaster -- + * + * 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 masterPtr->interp's result. + * + * Side effects: + * Existing instances of the image will be redisplayed to match the new + * configuration options. + * + *---------------------------------------------------------------------- + */ + +static int +TkNSImageConfigureMaster( + Tcl_Interp *interp, /* Interpreter to use for reporting errors. */ + TkNSImageMaster *masterPtr, /* Pointer to data structure describing + * overall photo image to (re)configure. */ + int 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; + + if (asOption == NULL) { + asOption = Tcl_NewStringObj("-as", -1); + Tcl_IncrRefCount(asOption); + } + + if (Tk_SetOptions(interp, (char *) masterPtr, optionTable, objc, objv, + NULL, NULL, NULL) != TCL_OK){ + goto errorExit; + } + + if (masterPtr->source == NULL || masterPtr->source[0] == '0') { + Tcl_SetObjResult(interp, Tcl_NewStringObj("-source is required.", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "SYSTEM", "BAD_VALUE", NULL); + goto errorExit; + } + + objPtr = Tk_GetOptionValue(interp, (char *) masterPtr, 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.", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "SYSTEM", "BAD_VALUE", NULL); + goto errorExit; + } + + NSString *source = [[NSString alloc] initWithUTF8String: masterPtr->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 = [[NSWorkspace sharedWorkspace] iconForFileType:source]; + break; + default: + newImage = NULL; + break; + } + [source release]; + if (newImage) { + NSSize size = NSMakeSize(masterPtr->width, masterPtr->height); + [newImage setSize:size]; + [masterPtr->image release]; + [masterPtr->darkModeImage release]; + masterPtr->image = [newImage retain]; + masterPtr->darkModeImage = [[masterPtr->image copy] retain]; + if ([masterPtr->darkModeImage isTemplate]) { + + /* + * For a template image the Dark Mode version should be white. + */ + + NSRect rect = {NSZeroPoint, size}; + [masterPtr->darkModeImage lockFocus]; + [[NSColor whiteColor] set]; + NSRectFillUsingOperation(rect, NSCompositeSourceAtop); + [masterPtr->darkModeImage unlockFocus]; + } else if (masterPtr->pressed) { + + /* + * Non-template pressed images are darker in Light Mode and lighter + * in Dark Mode. + */ + + TintImage(masterPtr->image, [NSColor blackColor], 0.2); + TintImage(masterPtr->darkModeImage, [NSColor whiteColor], 0.5); + } + } else { + Tcl_SetObjResult(interp, Tcl_NewStringObj("Unknown named NSImage.\n" + "Try omitting ImageName, " + "e.g. use NSCaution for NSImageNameCaution.", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "SYSTEM", "BAD_VALUE", NULL); + goto errorExit; + } + + /* + * Inform the generic image code that the image has (potentially) changed. + */ + + Tk_ImageChanged(masterPtr->tkMaster, 0, 0, masterPtr->width, + masterPtr->height, masterPtr->width, masterPtr->height); + masterPtr->flags &= ~IMAGE_CHANGED; + + return TCL_OK; + + errorExit: + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * TkNSImageObjCmd -- + * + * This function implements the configure and cget commands for an + * nsimage instance. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * The image may be reconfigured. + * + *---------------------------------------------------------------------- + */ + +static int +TkNSImageObjCmd( + ClientData clientData, /* Information about the image master. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + TkNSImageMaster *masterPtr = 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(masterPtr); + switch (index) { + case CGET: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "option"); + return TCL_ERROR; + } + objPtr = Tk_GetOptionValue(interp, (char *)masterPtr, optionTable, + objv[2], NULL); + if (objPtr == NULL) { + goto error; + } + Tcl_SetObjResult(interp, objPtr); + break; + case CONFIGURE: + if (objc == 2) { + objPtr = Tk_GetOptionInfo(interp, (char *)masterPtr, optionTable, + NULL, NULL); + if (objPtr == NULL) { + goto error; + } + Tcl_SetObjResult(interp, objPtr); + break; + } else if (objc == 3) { + objPtr = Tk_GetOptionInfo(interp, (char *)masterPtr, optionTable, + objv[2], NULL); + if (objPtr == NULL) { + goto error; + } + Tcl_SetObjResult(interp, objPtr); + break; + } else { + TkNSImageConfigureMaster(interp, masterPtr, objc - 2, objv + 2); + break; + } + default: + break; + } + + Tcl_Release(masterPtr); + return TCL_OK; + + error: + Tcl_Release(masterPtr); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * TkNSImageCreate -- + * + * Allocate and initialize an nsimage master. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * The data structure for a new image is allocated. + * + *---------------------------------------------------------------------- + */ + +static int +TkNSImageCreate( + Tcl_Interp *interp, /* Interpreter for application using image. */ + const char *name, /* Name to use for image. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[], /* Argument strings for options (not + * including image name or type). */ + const Tk_ImageType *typePtr, /* Pointer to our type record (not used). */ + Tk_ImageMaster master, /* Token for image, to be used in callbacks. */ + ClientData *clientDataPtr) /* Store manager's token for image here; it + * will be returned in later callbacks. */ +{ + TkNSImageMaster *masterPtr; + Tk_OptionTable optionTable = Tk_CreateOptionTable(interp, systemImageOptions); + + masterPtr = ckalloc(sizeof(TkNSImageMaster)); + masterPtr->tkMaster = master; + masterPtr->interp = interp; + masterPtr->imageName = ckalloc(strlen(name) + 1); + strcpy(masterPtr->imageName, name); + masterPtr->flags = 0; + masterPtr->instancePtr = NULL; + masterPtr->image = NULL; + masterPtr->darkModeImage = NULL; + masterPtr->source = NULL; + masterPtr->as = NULL; + + /* + * Process configuration options given in the image create command. + */ + + if (Tk_InitOptions(interp, (char *) masterPtr, optionTable, NULL) != TCL_OK + || TkNSImageConfigureMaster(interp, masterPtr, objc, objv) != TCL_OK) { + TkNSImageDelete(masterPtr); + return TCL_ERROR; + } + + Tcl_CreateObjCommand(interp, name, TkNSImageObjCmd, masterPtr, NULL); + + *clientDataPtr = masterPtr; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkNSImageGet -- + * + * 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 ClientData +TkNSImageGet( + Tk_Window tkwin, /* Token for window in which image will be + * used. */ + ClientData clientData) /* Pointer to TkNSImageMaster for image. */ +{ + TkNSImageMaster *masterPtr = (TkNSImageMaster *) clientData; + TkNSImageInstance *instPtr; + + instPtr = ckalloc(sizeof(TkNSImageInstance)); + instPtr->masterPtr = masterPtr; + return instPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TkNSImageDisplay -- + * + * Display or redisplay an nsimage in the given drawable. + * + * Results: + * None. + * + * Side effects: + * The image gets drawn. + * + *---------------------------------------------------------------------- + */ + +static void +TkNSImageDisplay( + ClientData clientData, /* Pointer to TkNSImageInstance for image. */ + Display *display, /* Display to use for drawing. */ + 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; + TkNSImageInstance *instPtr = (TkNSImageInstance *) clientData; + TkNSImageMaster *masterPtr = instPtr->masterPtr; + TkMacOSXDrawingContext dc; + NSRect dstRect = NSMakeRect(macWin->xOff + drawableX, + macWin->yOff + drawableY, width, height); + NSRect srcRect = NSMakeRect(imageX, imageY, width, height); + NSImage *image = TkMacOSXInDarkMode(tkwin) ? masterPtr->darkModeImage : + masterPtr->image; + + if (TkMacOSXSetupDrawingContext(drawable, NULL, 1, &dc)) { + if (dc.context) { + NSGraphicsContext *savedContext = NSGraphicsContext.currentContext; + NSGraphicsContext.currentContext = [NSGraphicsContext + graphicsContextWithCGContext:dc.context flipped: YES]; + [image drawInRect:dstRect + fromRect:srcRect + operation:NSCompositeSourceOver + fraction:masterPtr->alpha + respectFlipped:YES + hints:nil]; + NSGraphicsContext.currentContext = savedContext; + } + TkMacOSXRestoreDrawingContext(&dc); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkNSImageFree -- + * + * Deallocate an instance of an nsimage. + * + * Results: + * None. + * + * Side effects: + * Information related to the instance is freed. + * + *---------------------------------------------------------------------- + */ + +static void +TkNSImageFree( + ClientData clientData, /* Pointer to TkNSImageInstance for instance. */ + Display *display) /* Display where image was to be drawn. */ +{ + TkNSImageInstance *instPtr = (TkNSImageInstance *) clientData; + ckfree(instPtr); +} + +/* + *---------------------------------------------------------------------- + * + * TkNSImageDelete -- + * + * Deallocate an nsimage master. + * + * Results: + * None. + * + * Side effects: + * NSImages are released and memory is freed. + * + *---------------------------------------------------------------------- + */ + +static void +TkNSImageDelete( + ClientData clientData) /* Pointer to TkNSImageMaster for image. When + * this function is called, no more instances + * exist. */ +{ + TkNSImageMaster *masterPtr = (TkNSImageMaster *) clientData; + + Tcl_DeleteCommand(masterPtr->interp, masterPtr->imageName); + ckfree(masterPtr->imageName); + ckfree(masterPtr->source); + ckfree(masterPtr->as); + [masterPtr->image release]; + [masterPtr->darkModeImage release]; + ckfree(masterPtr); +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXNSImage_Init -- + * + * Adds the TkNSImage 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 command: + * image create system -source ?-width? ?-height? ?-alpha? ?-pressed? + * + *---------------------------------------------------------------------- + */ + +int +TkMacOSXNSImage_Init( + Tcl_Interp *interp) /* Interpreter for application. */ +{ + Tk_CreateImageType(&TkNSImageType); + return 1; +} /* * Local Variables: * mode: objc |