diff options
Diffstat (limited to 'generic/tkImgPhoto.c')
-rw-r--r-- | generic/tkImgPhoto.c | 3164 |
1 files changed, 542 insertions, 2622 deletions
diff --git a/generic/tkImgPhoto.c b/generic/tkImgPhoto.c index f6fee84..1bd0142 100644 --- a/generic/tkImgPhoto.c +++ b/generic/tkImgPhoto.c @@ -18,209 +18,7 @@ * Australian National University. */ -#include "tkInt.h" -#include <ctype.h> - -#ifdef __WIN32__ -#include "tkWinInt.h" -#elif defined(__CYGWIN__) -#include "tkUnixInt.h" -#endif - -/* - * Declaration for internal Xlib function used here: - */ - -extern int _XInitImageFuncPtrs(XImage *image); - -/* - * A signed 8-bit integral type. If chars are unsigned and the compiler isn't - * an ANSI one, then we have to use short instead (which wastes space) to get - * signed behavior. - */ - -#if defined(__STDC__) || defined(_AIX) - typedef signed char schar; -#else -# ifndef __CHAR_UNSIGNED__ - typedef char schar; -# else - typedef short schar; -# endif -#endif - -/* - * An unsigned 32-bit integral type, used for pixel values. We use int rather - * than long here to accommodate those systems where longs are 64 bits. - */ - -typedef unsigned int pixel; - -/* - * The maximum number of pixels to transmit to the server in a single - * XPutImage call. - */ - -#define MAX_PIXELS 65536 - -/* - * The set of colors required to display a photo image in a window depends on: - * - the visual used by the window - * - the palette, which specifies how many levels of each primary color to - * use, and - * - the gamma value for the image. - * - * Pixel values allocated for specific colors are valid only for the colormap - * in which they were allocated. Sets of pixel values allocated for displaying - * photos are re-used in other windows if possible, that is, if the display, - * colormap, palette and gamma values match. A hash table is used to locate - * these sets of pixel values, using the following data structure as key: - */ - -typedef struct { - Display *display; /* Qualifies the colormap resource ID. */ - Colormap colormap; /* Colormap that the windows are using. */ - double gamma; /* Gamma exponent value for images. */ - Tk_Uid palette; /* Specifies how many shades of each primary - * we want to allocate. */ -} ColorTableId; - -/* - * For a particular (display, colormap, palette, gamma) combination, a data - * structure of the following type is used to store the allocated pixel values - * and other information: - */ - -typedef struct ColorTable { - ColorTableId id; /* Information used in selecting this color - * table. */ - int flags; /* See below. */ - int refCount; /* Number of instances using this map. */ - int liveRefCount; /* Number of instances which are actually in - * use, using this map. */ - int numColors; /* Number of colors allocated for this map. */ - - XVisualInfo visualInfo; /* Information about the visual for windows - * using this color table. */ - - pixel redValues[256]; /* Maps 8-bit values of red intensity to a - * pixel value or index in pixelMap. */ - pixel greenValues[256]; /* Ditto for green intensity. */ - pixel blueValues[256]; /* Ditto for blue intensity. */ - unsigned long *pixelMap; /* Actual pixel values allocated. */ - - unsigned char colorQuant[3][256]; - /* Maps 8-bit intensities to quantized - * intensities. The first index is 0 for red, - * 1 for green, 2 for blue. */ -} ColorTable; - -/* - * Bit definitions for the flags field of a ColorTable. - * BLACK_AND_WHITE: 1 means only black and white colors are - * available. - * COLOR_WINDOW: 1 means a full 3-D color cube has been - * allocated. - * DISPOSE_PENDING: 1 means a call to DisposeColorTable has been - * scheduled as an idle handler, but it hasn't - * been invoked yet. - * MAP_COLORS: 1 means pixel values should be mapped through - * pixelMap. - */ - -#ifdef COLOR_WINDOW -#undef COLOR_WINDOW -#endif - -#define BLACK_AND_WHITE 1 -#define COLOR_WINDOW 2 -#define DISPOSE_PENDING 4 -#define MAP_COLORS 8 - -/* - * Definition of the data associated with each photo image master. - */ - -typedef struct PhotoMaster { - Tk_ImageMaster tkMaster; /* Tk's token for image master. NULL means the - * image is being deleted. */ - Tcl_Interp *interp; /* Interpreter associated with the application - * using this image. */ - Tcl_Command imageCmd; /* Token for image command (used to delete it - * when the image goes away). NULL means the - * image command has already been deleted. */ - int flags; /* Sundry flags, defined below. */ - int width, height; /* Dimensions of image. */ - int userWidth, userHeight; /* User-declared image dimensions. */ - Tk_Uid palette; /* User-specified default palette for - * instances of this image. */ - double gamma; /* Display gamma value to correct for. */ - char *fileString; /* Name of file to read into image. */ - Tcl_Obj *dataString; /* Object to use as contents of image. */ - Tcl_Obj *format; /* User-specified format of data in image file - * or string value. */ - unsigned char *pix32; /* Local storage for 32-bit image. */ - int ditherX, ditherY; /* Location of first incorrectly dithered - * pixel in image. */ - TkRegion validRegion; /* Tk region indicating which parts of the - * image have valid image data. */ - struct PhotoInstance *instancePtr; - /* First in the list of instances associated - * with this master. */ -} PhotoMaster; - -/* - * Bit definitions for the flags field of a PhotoMaster. - * COLOR_IMAGE: 1 means that the image has different color - * components. - * IMAGE_CHANGED: 1 means that the instances of this image need - * to be redithered. - * COMPLEX_ALPHA: 1 means that the instances of this image have - * alpha values that aren't 0 or 255, and so need - * the copy-merge-replace renderer . - */ - -#define COLOR_IMAGE 1 -#define IMAGE_CHANGED 2 -#define COMPLEX_ALPHA 4 - -/* - * Flag to OR with the compositing rule to indicate that the source, despite - * having an alpha channel, has simple alpha. - */ - -#define SOURCE_IS_SIMPLE_ALPHA_PHOTO 0x10000000 - -/* - * The following data structure represents all of the instances of a photo - * image in windows on a given screen that are using the same colormap. - */ - -typedef struct PhotoInstance { - PhotoMaster *masterPtr; /* Pointer to master for image. */ - Display *display; /* Display for windows using this instance. */ - Colormap colormap; /* The image may only be used in windows with - * this particular colormap. */ - struct PhotoInstance *nextPtr; - /* Pointer to the next instance in the list of - * instances associated with this master. */ - int refCount; /* Number of instances using this structure. */ - Tk_Uid palette; /* Palette for these particular instances. */ - double gamma; /* Gamma value for these instances. */ - Tk_Uid defaultPalette; /* Default palette to use if a palette is not - * specified for the master. */ - ColorTable *colorTablePtr; /* Pointer to information about colors - * allocated for image display in windows like - * this one. */ - Pixmap pixels; /* X pixmap containing dithered image. */ - int width, height; /* Dimensions of the pixmap. */ - schar *error; /* Error image, used in dithering. */ - XImage *imagePtr; /* Image structure for converted pixels. */ - XVisualInfo visualInfo; /* Information about the visual that these - * windows are using. */ - GC gc; /* Graphics context for writing images to the - * pixmap. */ -} PhotoInstance; +#include "tkImgPhoto.h" /* * The following data structure is used to return information from @@ -301,16 +99,10 @@ static const char *const optionNames[] = { * Functions used in the type record for photo images. */ -static int ImgPhotoCreate(Tcl_Interp *interp, char *name, - int objc, Tcl_Obj *CONST objv[], - Tk_ImageType *typePtr, Tk_ImageMaster master, +static int ImgPhotoCreate(Tcl_Interp *interp, const char *name, + int objc, Tcl_Obj *const objv[], + const Tk_ImageType *typePtr, Tk_ImageMaster master, ClientData *clientDataPtr); -static ClientData ImgPhotoGet(Tk_Window tkwin, ClientData clientData); -static void ImgPhotoDisplay(ClientData clientData, - Display *display, Drawable drawable, - int imageX, int imageY, int width, int height, - int drawableX, int drawableY); -static void ImgPhotoFree(ClientData clientData, Display *display); static void ImgPhotoDelete(ClientData clientData); static int ImgPhotoPostscript(ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, @@ -324,12 +116,13 @@ static int ImgPhotoPostscript(ClientData clientData, Tk_ImageType tkPhotoImageType = { "photo", /* name */ ImgPhotoCreate, /* createProc */ - ImgPhotoGet, /* getProc */ - ImgPhotoDisplay, /* displayProc */ - ImgPhotoFree, /* freeProc */ + TkImgPhotoGet, /* getProc */ + TkImgPhotoDisplay, /* displayProc */ + TkImgPhotoFree, /* freeProc */ ImgPhotoDelete, /* deleteProc */ ImgPhotoPostscript, /* postscriptProc */ - NULL /* nextPtr */ + NULL, /* nextPtr */ + NULL }; typedef struct ThreadSpecificData { @@ -340,7 +133,7 @@ typedef struct ThreadSpecificData { /* Pointer to the first in the list of known * photo image formats.*/ int initialized; /* Set to 1 if we've initialized the - * strucuture. */ + * structure. */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; @@ -357,45 +150,27 @@ static Tcl_ThreadDataKey dataKey; * Information used for parsing configuration specifications: */ -static Tk_ConfigSpec configSpecs[] = { +static const Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_STRING, "-file", NULL, NULL, - NULL, Tk_Offset(PhotoMaster, fileString), TK_CONFIG_NULL_OK}, + NULL, Tk_Offset(PhotoMaster, fileString), TK_CONFIG_NULL_OK, NULL}, {TK_CONFIG_DOUBLE, "-gamma", NULL, NULL, - DEF_PHOTO_GAMMA, Tk_Offset(PhotoMaster, gamma), 0}, + DEF_PHOTO_GAMMA, Tk_Offset(PhotoMaster, gamma), 0, NULL}, {TK_CONFIG_INT, "-height", NULL, NULL, - DEF_PHOTO_HEIGHT, Tk_Offset(PhotoMaster, userHeight), 0}, + DEF_PHOTO_HEIGHT, Tk_Offset(PhotoMaster, userHeight), 0, NULL}, {TK_CONFIG_UID, "-palette", NULL, NULL, - DEF_PHOTO_PALETTE, Tk_Offset(PhotoMaster, palette), 0}, + DEF_PHOTO_PALETTE, Tk_Offset(PhotoMaster, palette), 0, NULL}, {TK_CONFIG_INT, "-width", NULL, NULL, - DEF_PHOTO_WIDTH, Tk_Offset(PhotoMaster, userWidth), 0}, - {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} + DEF_PHOTO_WIDTH, Tk_Offset(PhotoMaster, userWidth), 0, NULL}, + {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0, NULL} }; /* - * Hash table used to hash from (display, colormap, palette, gamma) to - * ColorTable address. - */ - -static Tcl_HashTable imgPhotoColorHash; -static int imgPhotoColorHashInitialized; -#define N_COLOR_HASH (sizeof(ColorTableId) / sizeof(int)) - -/* - * Implementation of the Porter-Duff Source-Over compositing rule. - */ - -#define PD_SRC_OVER(srcColor,srcAlpha,dstColor,dstAlpha) \ - (srcColor*srcAlpha/255) + dstAlpha*(255-srcAlpha)/255*dstColor/255 -#define PD_SRC_OVER_ALPHA(srcAlpha,dstAlpha) \ - (srcAlpha + (255-srcAlpha)*dstAlpha/255) - -/* * Forward declarations */ static void PhotoFormatThreadExitProc(ClientData clientData); static int ImgPhotoCmd(ClientData clientData, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int ParseSubcommandOptions( struct SubcommandOptions *optPtr, Tcl_Interp *interp, int allowedOptions, @@ -404,47 +179,24 @@ static void ImgPhotoCmdDeletedProc(ClientData clientData); static int ImgPhotoConfigureMaster(Tcl_Interp *interp, PhotoMaster *masterPtr, int objc, Tcl_Obj *const objv[], int flags); -static void ImgPhotoConfigureInstance(PhotoInstance *instancePtr); static int ToggleComplexAlphaIfNeeded(PhotoMaster *mPtr); -static void ImgPhotoBlendComplexAlpha(XImage *bgImg, - PhotoInstance *iPtr, int xOffset, int yOffset, - int width, int height); static int ImgPhotoSetSize(PhotoMaster *masterPtr, int width, int height); -static void ImgPhotoInstanceSetSize(PhotoInstance *instancePtr); static int ImgStringWrite(Tcl_Interp *interp, Tcl_Obj *formatString, Tk_PhotoImageBlock *blockPtr); static char * ImgGetPhoto(PhotoMaster *masterPtr, Tk_PhotoImageBlock *blockPtr, struct SubcommandOptions *optPtr); -static int IsValidPalette(PhotoInstance *instancePtr, - const char *palette); -static int CountBits(pixel mask); -static void GetColorTable(PhotoInstance *instancePtr); -static void FreeColorTable(ColorTable *colorPtr, int force); -static void AllocateColors(ColorTable *colorPtr); -static void DisposeColorTable(ClientData clientData); -static void DisposeInstance(ClientData clientData); -static int ReclaimColors(ColorTableId *id, int numColors); static int MatchFileFormat(Tcl_Interp *interp, Tcl_Channel chan, - char *fileName, Tcl_Obj *formatString, + const char *fileName, Tcl_Obj *formatString, Tk_PhotoImageFormat **imageFormatPtr, int *widthPtr, int *heightPtr, int *oldformat); static int MatchStringFormat(Tcl_Interp *interp, Tcl_Obj *data, Tcl_Obj *formatString, Tk_PhotoImageFormat **imageFormatPtr, int *widthPtr, int *heightPtr, int *oldformat); -static Tcl_ObjCmdProc * PhotoOptionFind(Tcl_Interp *interp, Tcl_Obj *obj); -static void DitherInstance(PhotoInstance *instancePtr, int x, - int y, int width, int height); -static void PhotoOptionCleanupProc(ClientData clientData, - Tcl_Interp *interp); - -#undef MIN -#define MIN(a, b) ((a) < (b)? (a): (b)) -#undef MAX -#define MAX(a, b) ((a) > (b)? (a): (b)) +static const char * GetExtension(const char *path); /* *---------------------------------------------------------------------- @@ -467,19 +219,19 @@ PhotoFormatThreadExitProc( ClientData clientData) /* not used */ { Tk_PhotoImageFormat *freePtr; - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); while (tsdPtr->oldFormatList != NULL) { freePtr = tsdPtr->oldFormatList; tsdPtr->oldFormatList = tsdPtr->oldFormatList->nextPtr; - ckfree((char *) freePtr); + ckfree(freePtr); } while (tsdPtr->formatList != NULL) { freePtr = tsdPtr->formatList; tsdPtr->formatList = tsdPtr->formatList->nextPtr; - ckfree((char *) freePtr->name); - ckfree((char *) freePtr); + ckfree((char *)freePtr->name); + ckfree(freePtr); } } @@ -504,20 +256,20 @@ PhotoFormatThreadExitProc( void Tk_CreateOldPhotoImageFormat( - Tk_PhotoImageFormat *formatPtr) + const Tk_PhotoImageFormat *formatPtr) /* Structure describing the format. All of the * fields except "nextPtr" must be filled in * by caller. */ { Tk_PhotoImageFormat *copyPtr; - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); if (!tsdPtr->initialized) { tsdPtr->initialized = 1; Tcl_CreateThreadExitHandler(PhotoFormatThreadExitProc, NULL); } - copyPtr = (Tk_PhotoImageFormat *) ckalloc(sizeof(Tk_PhotoImageFormat)); + copyPtr = ckalloc(sizeof(Tk_PhotoImageFormat)); *copyPtr = *formatPtr; copyPtr->nextPtr = tsdPtr->oldFormatList; tsdPtr->oldFormatList = copyPtr; @@ -525,20 +277,20 @@ Tk_CreateOldPhotoImageFormat( void Tk_CreatePhotoImageFormat( - Tk_PhotoImageFormat *formatPtr) + const Tk_PhotoImageFormat *formatPtr) /* Structure describing the format. All of the * fields except "nextPtr" must be filled in * by caller. */ { Tk_PhotoImageFormat *copyPtr; - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); if (!tsdPtr->initialized) { tsdPtr->initialized = 1; Tcl_CreateThreadExitHandler(PhotoFormatThreadExitProc, NULL); } - copyPtr = (Tk_PhotoImageFormat *) ckalloc(sizeof(Tk_PhotoImageFormat)); + copyPtr = ckalloc(sizeof(Tk_PhotoImageFormat)); *copyPtr = *formatPtr; if (isupper((unsigned char) *formatPtr->name)) { copyPtr->nextPtr = tsdPtr->oldFormatList; @@ -574,11 +326,11 @@ static int ImgPhotoCreate( Tcl_Interp *interp, /* Interpreter for application containing * image. */ - char *name, /* Name to use for image. */ + const char *name, /* Name to use for image. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[], /* Argument objects for options (doesn't + Tcl_Obj *const objv[], /* Argument objects for options (doesn't * include image name or type). */ - Tk_ImageType *typePtr, /* Pointer to our type record (not used). */ + const Tk_ImageType *typePtr,/* Pointer to our type record (not used). */ Tk_ImageMaster master, /* Token for image, to be used by us in later * callbacks. */ ClientData *clientDataPtr) /* Store manager's token for image here; it @@ -590,12 +342,12 @@ ImgPhotoCreate( * Allocate and initialize the photo image master record. */ - masterPtr = (PhotoMaster *) ckalloc(sizeof(PhotoMaster)); + masterPtr = ckalloc(sizeof(PhotoMaster)); memset(masterPtr, 0, sizeof(PhotoMaster)); masterPtr->tkMaster = master; masterPtr->interp = interp; masterPtr->imageCmd = Tcl_CreateObjCommand(interp, name, ImgPhotoCmd, - (ClientData) masterPtr, ImgPhotoCmdDeletedProc); + masterPtr, ImgPhotoCmdDeletedProc); masterPtr->palette = NULL; masterPtr->pix32 = NULL; masterPtr->instancePtr = NULL; @@ -606,11 +358,11 @@ ImgPhotoCreate( */ if (ImgPhotoConfigureMaster(interp, masterPtr, objc, objv, 0) != TCL_OK) { - ImgPhotoDelete((ClientData) masterPtr); + ImgPhotoDelete(masterPtr); return TCL_ERROR; } - *clientDataPtr = (ClientData) masterPtr; + *clientDataPtr = masterPtr; return TCL_OK; } @@ -637,9 +389,9 @@ ImgPhotoCmd( ClientData clientData, /* Information about photo master. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - static const char *photoOptions[] = { + static const char *const photoOptions[] = { "blank", "cget", "configure", "copy", "data", "get", "put", "read", "redither", "transparency", "write", NULL }; @@ -649,7 +401,7 @@ ImgPhotoCmd( PHOTO_WRITE }; - PhotoMaster *masterPtr = (PhotoMaster *) clientData; + PhotoMaster *masterPtr = clientData; int result, index, x, y, width, height, dataWidth, dataHeight, listObjc; struct SubcommandOptions options; Tcl_Obj **listObjv, **srcObjv; @@ -657,25 +409,21 @@ ImgPhotoCmd( Tk_PhotoImageBlock block; Tk_Window tkwin; Tk_PhotoImageFormat *imageFormat; - int imageWidth, imageHeight, matched, length, oldformat = 0; + size_t length; + int imageWidth, imageHeight, matched, oldformat = 0; Tcl_Channel chan; Tk_PhotoHandle srcHandle; - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); + Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], photoOptions, "option", 0, &index) != TCL_OK) { - Tcl_ObjCmdProc *proc; - proc = PhotoOptionFind(interp, objv[1]); - if (proc == NULL) { - return TCL_ERROR; - } - return proc(clientData, interp, objc, objv); + return TCL_ERROR; } switch ((enum PhotoOptions) index) { @@ -693,18 +441,19 @@ ImgPhotoCmd( } case PHOTO_CGET: { - char *arg; + const char *arg; if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "option"); return TCL_ERROR; } - arg = Tcl_GetStringFromObj(objv[2], &length); - if (strncmp(arg,"-data", (unsigned) length) == 0) { + arg = Tcl_GetString(objv[2]); + length = objv[2]->length; + if (strncmp(arg,"-data", length) == 0) { if (masterPtr->dataString) { Tcl_SetObjResult(interp, masterPtr->dataString); } - } else if (strncmp(arg,"-format", (unsigned) length) == 0) { + } else if (strncmp(arg,"-format", length) == 0) { if (masterPtr->format) { Tcl_SetObjResult(interp, masterPtr->format); } @@ -748,22 +497,31 @@ ImgPhotoCmd( return TCL_OK; } else if (objc == 3) { - char *arg = Tcl_GetStringFromObj(objv[2], &length); + const char *arg = Tcl_GetString(objv[2]); - if (length > 1 && !strncmp(arg, "-data", (unsigned) length)) { + length = objv[2]->length; + if (length > 1 && !strncmp(arg, "-data", length)) { Tcl_AppendResult(interp, "-data {} {} {}", NULL); if (masterPtr->dataString) { - Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), + /* + * TODO: Modifying result is bad! + */ + + Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), masterPtr->dataString); } else { Tcl_AppendResult(interp, " {}", NULL); } return TCL_OK; } else if (length > 1 && - !strncmp(arg, "-format", (unsigned) length)) { + !strncmp(arg, "-format", length)) { Tcl_AppendResult(interp, "-format {} {} {}", NULL); if (masterPtr->format) { - Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), + /* + * TODO: Modifying result is bad! + */ + + Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), masterPtr->format); } else { Tcl_AppendResult(interp, " {}", NULL); @@ -773,9 +531,10 @@ ImgPhotoCmd( return Tk_ConfigureInfo(interp, Tk_MainWindow(interp), configSpecs, (char *) masterPtr, arg, 0); } + } else { + return ImgPhotoConfigureMaster(interp, masterPtr, objc-2, objv+2, + TK_CONFIG_ARGV_ONLY); } - return ImgPhotoConfigureMaster(interp, masterPtr, objc-2, objv+2, - TK_CONFIG_ARGV_ONLY); case PHOTO_COPY: /* @@ -806,17 +565,21 @@ ImgPhotoCmd( srcHandle = Tk_FindPhoto(interp, Tcl_GetString(options.name)); if (srcHandle == NULL) { - Tcl_AppendResult(interp, "image \"", - Tcl_GetString(options.name), "\" doesn't", - " exist or is not a photo image", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "image \"%s\" doesn't exist or is not a photo image", + Tcl_GetString(options.name))); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "PHOTO", + Tcl_GetString(options.name), NULL); return TCL_ERROR; } Tk_PhotoGetImage(srcHandle, &block); if ((options.fromX2 > block.width) || (options.fromY2 > block.height) || (options.fromX2 > block.width) || (options.fromY2 > block.height)) { - Tcl_AppendResult(interp, "coordinates for -from option extend ", - "outside source image", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "coordinates for -from option extend outside source image", + -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", "BAD_FROM", NULL); return TCL_ERROR; } @@ -868,8 +631,9 @@ ImgPhotoCmd( if (options.options & OPT_SHRINK) { if (ImgPhotoSetSize(masterPtr, options.toX2, options.toY2) != TCL_OK) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, TK_PHOTO_ALLOC_FAILURE_MESSAGE, NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); + Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); return TCL_ERROR; } } @@ -909,15 +673,16 @@ ImgPhotoCmd( return TCL_ERROR; } if ((options.name != NULL) || (index < objc)) { - Tcl_WrongNumArgs(interp, 2, objv, "?options?"); + Tcl_WrongNumArgs(interp, 2, objv, "?-option value ...?"); return TCL_ERROR; } if ((options.fromX > masterPtr->width) || (options.fromY > masterPtr->height) || (options.fromX2 > masterPtr->width) || (options.fromY2 > masterPtr->height)) { - Tcl_AppendResult(interp, "coordinates for -from option extend ", - "outside image", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "coordinates for -from option extend outside image", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", "BAD_FROM", NULL); return TCL_ERROR; } @@ -925,7 +690,7 @@ ImgPhotoCmd( * Fill in default values for unspecified parameters. */ - if (((options.options & OPT_FROM) == 0) || (options.fromX2 < 0)) { + if (!(options.options & OPT_FROM) || (options.fromX2 < 0)) { options.fromX2 = masterPtr->width; options.fromY2 = masterPtr->height; } @@ -963,9 +728,12 @@ ImgPhotoCmd( } } if (stringWriteProc == NULL) { - Tcl_AppendResult(interp, "image string format \"", - Tcl_GetString(options.format), "\" is ", - (matched ? "not supported" : "unknown"), NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "image string format \"%s\" is %s", + Tcl_GetString(options.format), + (matched ? "not supported" : "unknown"))); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "PHOTO_FORMAT", + Tcl_GetString(options.format), NULL); return TCL_ERROR; } } else { @@ -980,23 +748,25 @@ ImgPhotoCmd( if (oldformat) { Tcl_DString buffer; + typedef int (*OldStringWriteProc)(Tcl_Interp *interp, + Tcl_DString *dataPtr, const char *formatString, + Tk_PhotoImageBlock *blockPtr); Tcl_DStringInit(&buffer); - result = ((int (*) (Tcl_Interp *interp, - Tcl_DString *dataPtr, char *formatString, - Tk_PhotoImageBlock *blockPtr)) stringWriteProc) - (interp, &buffer, Tcl_GetString(options.format), &block); + result = ((OldStringWriteProc) stringWriteProc)(interp, &buffer, + Tcl_GetString(options.format), &block); if (result == TCL_OK) { Tcl_DStringResult(interp, &buffer); } else { Tcl_DStringFree(&buffer); } } else { - - result = ((int (*) (Tcl_Interp *interp, + typedef int (*NewStringWriteProc)(Tcl_Interp *interp, Tcl_Obj *formatString, Tk_PhotoImageBlock *blockPtr, - void *dummy)) stringWriteProc) - (interp, options.format, &block, NULL); + void *dummy); + + result = ((NewStringWriteProc) stringWriteProc)(interp, + options.format, &block, NULL); } if (options.background) { Tk_FreeColor(options.background); @@ -1012,7 +782,7 @@ ImgPhotoCmd( * photo get command - first parse and check parameters. */ - char string[TCL_INTEGER_SPACE * 3]; + Tcl_Obj *channels[3]; if (objc != 4) { Tcl_WrongNumArgs(interp, 2, objv, "x y"); @@ -1024,8 +794,11 @@ ImgPhotoCmd( } if ((x < 0) || (x >= masterPtr->width) || (y < 0) || (y >= masterPtr->height)) { - Tcl_AppendResult(interp, Tcl_GetString(objv[0]), " get: ", - "coordinates out of range", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "%s get: coordinates out of range", + Tcl_GetString(objv[0]))); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", "COORDINATES", + NULL); return TCL_ERROR; } @@ -1034,9 +807,10 @@ ImgPhotoCmd( */ pixelPtr = masterPtr->pix32 + (y * masterPtr->width + x) * 4; - sprintf(string, "%d %d %d", pixelPtr[0], pixelPtr[1], - pixelPtr[2]); - Tcl_AppendResult(interp, string, NULL); + channels[0] = Tcl_NewIntObj(pixelPtr[0]); + channels[1] = Tcl_NewIntObj(pixelPtr[1]); + channels[2] = Tcl_NewIntObj(pixelPtr[2]); + Tcl_SetObjResult(interp, Tcl_NewListObj(3, channels)); return TCL_OK; } @@ -1053,7 +827,7 @@ ImgPhotoCmd( return TCL_ERROR; } if ((options.name == NULL) || (index < objc)) { - Tcl_WrongNumArgs(interp, 2, objv, "data ?options?"); + Tcl_WrongNumArgs(interp, 2, objv, "data ?-option value ...?"); return TCL_ERROR; } @@ -1062,7 +836,7 @@ ImgPhotoCmd( &imageHeight, &oldformat) == TCL_OK) { Tcl_Obj *format, *data; - if (((options.options & OPT_TO) == 0) || (options.toX2 < 0)) { + if (!(options.options & OPT_TO) || (options.toX2 < 0)) { options.toX2 = options.toX + imageWidth; options.toY2 = options.toY + imageHeight; } @@ -1080,10 +854,9 @@ ImgPhotoCmd( } data = (Tcl_Obj *) Tcl_GetString(data); } - if ((*imageFormat->stringReadProc)(interp, data, - format, (Tk_PhotoHandle) masterPtr, - options.toX, options.toY, imageWidth, imageHeight, - 0, 0) != TCL_OK) { + if (imageFormat->stringReadProc(interp, data, format, + (Tk_PhotoHandle) masterPtr, options.toX, options.toY, + imageWidth, imageHeight, 0, 0) != TCL_OK) { return TCL_ERROR; } masterPtr->flags |= IMAGE_CHANGED; @@ -1116,17 +889,32 @@ ImgPhotoCmd( break; } dataWidth = listObjc; - pixelPtr = (unsigned char *) - ckalloc((unsigned) dataWidth * dataHeight * 3); + /* + * Memory allocation overflow protection. + * May not be able to trigger/ demo / test this. + */ + + if (dataWidth > (int)((UINT_MAX/3) / dataHeight)) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "photo image dimensions exceed Tcl memory limits", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", + "OVERFLOW", NULL); + break; + } + + pixelPtr = ckalloc(dataWidth * dataHeight * 3); block.pixelPtr = pixelPtr; } else if (listObjc != dataWidth) { - Tcl_AppendResult(interp, "all elements of color list must", - " have the same number of elements", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "all elements of color list must have the same" + " number of elements", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", + "NON_RECTANGULAR", NULL); break; } for (x = 0; x < dataWidth; ++x) { - char *colorString = Tcl_GetString(listObjv[x]); + const char *colorString = Tcl_GetString(listObjv[x]); XColor color; int tmpr, tmpg, tmpb; @@ -1164,8 +952,9 @@ ImgPhotoCmd( if (!TkParseColor(Tk_Display(tkwin), Tk_Colormap(tkwin), colorString, &color)) { - Tcl_AppendResult(interp, "can't parse color \"", - colorString, "\"", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't parse color \"%s\"", colorString)); + Tcl_SetErrorCode(interp, "TK", "VALUE", "COLOR", NULL); break; } *pixelPtr++ = color.red >> 8; @@ -1178,7 +967,7 @@ ImgPhotoCmd( } if (y < dataHeight || dataHeight == 0 || dataWidth == 0) { if (block.pixelPtr != NULL) { - ckfree((char *) block.pixelPtr); + ckfree(block.pixelPtr); } if (y < dataHeight) { return TCL_ERROR; @@ -1203,11 +992,11 @@ ImgPhotoCmd( block.offset[1] = 1; block.offset[2] = 2; block.offset[3] = 0; - result = Tk_PhotoPutBlock(interp, (ClientData)masterPtr, &block, + result = Tk_PhotoPutBlock(interp, masterPtr, &block, options.toX, options.toY, options.toX2 - options.toX, options.toY2 - options.toY, TK_PHOTO_COMPOSITE_SET); - ckfree((char *) block.pixelPtr); + ckfree(block.pixelPtr); return result; case PHOTO_READ: { @@ -1227,7 +1016,7 @@ ImgPhotoCmd( return TCL_ERROR; } if ((options.name == NULL) || (index < objc)) { - Tcl_WrongNumArgs(interp, 2, objv, "fileName ?options?"); + Tcl_WrongNumArgs(interp, 2, objv, "fileName ?-option value ...?"); return TCL_ERROR; } @@ -1236,8 +1025,9 @@ ImgPhotoCmd( */ if (Tcl_IsSafe(interp)) { - Tcl_AppendResult(interp, "can't get image from a file in a", - " safe interpreter", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "can't get image from a file in a safe interpreter", -1)); + Tcl_SetErrorCode(interp, "TK", "SAFE", "PHOTO_FILE", NULL); return TCL_ERROR; } @@ -1275,12 +1065,14 @@ ImgPhotoCmd( if ((options.fromX > imageWidth) || (options.fromY > imageHeight) || (options.fromX2 > imageWidth) || (options.fromY2 > imageHeight)) { - Tcl_AppendResult(interp, "coordinates for -from option extend ", - "outside source image", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "coordinates for -from option extend outside source image", + -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", "BAD_FROM", NULL); Tcl_Close(NULL, chan); return TCL_ERROR; } - if (((options.options & OPT_FROM) == 0) || (options.fromX2 < 0)) { + if (!(options.options & OPT_FROM) || (options.fromX2 < 0)) { width = imageWidth - options.fromX; height = imageHeight - options.fromY; } else { @@ -1296,7 +1088,9 @@ ImgPhotoCmd( if (ImgPhotoSetSize(masterPtr, options.toX + width, options.toY + height) != TCL_OK) { Tcl_ResetResult(interp); - Tcl_AppendResult(interp, TK_PHOTO_ALLOC_FAILURE_MESSAGE, NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); + Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); return TCL_ERROR; } } @@ -1310,7 +1104,7 @@ ImgPhotoCmd( if (oldformat && format) { format = (Tcl_Obj *) Tcl_GetString(format); } - result = (*imageFormat->fileReadProc)(interp, chan, + result = imageFormat->fileReadProc(interp, chan, Tcl_GetString(options.name), format, (Tk_PhotoHandle) masterPtr, options.toX, options.toY, width, height, options.fromX, options.fromY); @@ -1356,7 +1150,7 @@ ImgPhotoCmd( return TCL_OK; case PHOTO_TRANS: { - static const char *photoTransOptions[] = { + static const char *const photoTransOptions[] = { "get", "set", NULL }; enum transOptions { @@ -1364,7 +1158,7 @@ ImgPhotoCmd( }; if (objc < 3) { - Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?"); + Tcl_WrongNumArgs(interp, 2, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[2], photoTransOptions, "option", @@ -1386,9 +1180,12 @@ ImgPhotoCmd( return TCL_ERROR; } if ((x < 0) || (x >= masterPtr->width) - || (y < 0) || (y >= masterPtr->height)) { - Tcl_AppendResult(interp, Tcl_GetString(objv[0]), - " transparency get: coordinates out of range", NULL); + || (y < 0) || (y >= masterPtr->height)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "%s transparency get: coordinates out of range", + Tcl_GetString(objv[0]))); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", "COORDINATES", + NULL); return TCL_ERROR; } @@ -1403,8 +1200,8 @@ ImgPhotoCmd( TkClipBox(testRegion, &testBox); TkDestroyRegion(testRegion); - Tcl_SetBooleanObj(Tcl_GetObjResult(interp), - (testBox.width==0 && testBox.height==0)); + Tcl_SetObjResult(interp, Tcl_NewBooleanObj( + testBox.width==0 && testBox.height==0)); return TCL_OK; } @@ -1424,8 +1221,11 @@ ImgPhotoCmd( } if ((x < 0) || (x >= masterPtr->width) || (y < 0) || (y >= masterPtr->height)) { - Tcl_AppendResult(interp, Tcl_GetString(objv[0]), - " transparency set: coordinates out of range", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "%s transparency set: coordinates out of range", + Tcl_GetString(objv[0]))); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", "COORDINATES", + NULL); return TCL_ERROR; } @@ -1479,15 +1279,18 @@ ImgPhotoCmd( case PHOTO_WRITE: { char *data; + const char *fmtString; Tcl_Obj *format; + int usedExt; /* * Prevent file system access in safe interpreters. */ if (Tcl_IsSafe(interp)) { - Tcl_AppendResult(interp, "can't write image to a file in a", - " safe interpreter", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "can't write image to a file in a safe interpreter", -1)); + Tcl_SetErrorCode(interp, "TK", "SAFE", "PHOTO_FILE", NULL); return TCL_ERROR; } @@ -1505,26 +1308,36 @@ ImgPhotoCmd( return TCL_ERROR; } if ((options.name == NULL) || (index < objc)) { - Tcl_WrongNumArgs(interp, 2, objv, "fileName ?options?"); + Tcl_WrongNumArgs(interp, 2, objv, "fileName ?-option value ...?"); return TCL_ERROR; } if ((options.fromX > masterPtr->width) || (options.fromY > masterPtr->height) || (options.fromX2 > masterPtr->width) || (options.fromY2 > masterPtr->height)) { - Tcl_AppendResult(interp, "coordinates for -from option extend ", - "outside image", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "coordinates for -from option extend outside image", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", "BAD_FROM", NULL); return TCL_ERROR; } /* - * Fill in default values for unspecified parameters. + * Fill in default values for unspecified parameters. Note that a + * missing -format flag results in us having a guess from the file + * extension. [Bug 2983824] */ if (!(options.options & OPT_FROM) || (options.fromX2 < 0)) { options.fromX2 = masterPtr->width; options.fromY2 = masterPtr->height; } + if (options.format == NULL) { + fmtString = GetExtension(Tcl_GetString(options.name)); + usedExt = (fmtString != NULL); + } else { + fmtString = Tcl_GetString(options.format); + usedExt = 0; + } /* * Search for an appropriate image file format handler, and give an @@ -1532,11 +1345,12 @@ ImgPhotoCmd( */ matched = 0; + redoFormatLookup: for (imageFormat = tsdPtr->formatList; imageFormat != NULL; imageFormat = imageFormat->nextPtr) { - if ((options.format == NULL) - || (strncasecmp(Tcl_GetString(options.format), - imageFormat->name, strlen(imageFormat->name)) == 0)) { + if ((fmtString == NULL) + || (strncasecmp(fmtString, imageFormat->name, + strlen(imageFormat->name)) == 0)) { matched = 1; if (imageFormat->fileWriteProc != NULL) { break; @@ -1547,9 +1361,9 @@ ImgPhotoCmd( oldformat = 1; for (imageFormat = tsdPtr->oldFormatList; imageFormat != NULL; imageFormat = imageFormat->nextPtr) { - if ((options.format == NULL) - || (strncasecmp(Tcl_GetString(options.format), - imageFormat->name, strlen(imageFormat->name)) == 0)) { + if ((fmtString == NULL) + || (strncasecmp(fmtString, imageFormat->name, + strlen(imageFormat->name)) == 0)) { matched = 1; if (imageFormat->fileWriteProc != NULL) { break; @@ -1557,19 +1371,32 @@ ImgPhotoCmd( } } } + if (usedExt && !matched) { + /* + * If we didn't find one and we're using file extensions as the + * basis for the guessing, go back and look again without + * prejudice. Supports old broken code. + */ + + usedExt = 0; + fmtString = NULL; + goto redoFormatLookup; + } if (imageFormat == NULL) { - if (options.format == NULL) { - Tcl_AppendResult(interp, "no available image file format ", - "has file writing capability", NULL); + if (fmtString == NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "no available image file format has file writing" + " capability", -1)); } else if (!matched) { - Tcl_AppendResult(interp, "image file format \"", - Tcl_GetString(options.format), - "\" is unknown", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "image file format \"%s\" is unknown", fmtString)); } else { - Tcl_AppendResult(interp, "image file format \"", - Tcl_GetString(options.format), - "\" has no file writing capability", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "image file format \"%s\" has no file writing capability", + fmtString)); } + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "PHOTO_FORMAT", + fmtString, NULL); return TCL_ERROR; } @@ -1582,7 +1409,7 @@ ImgPhotoCmd( if (oldformat && format) { format = (Tcl_Obj *) Tcl_GetString(options.format); } - result = (*imageFormat->fileWriteProc)(interp, + result = imageFormat->fileWriteProc(interp, Tcl_GetString(options.name), format, &block); if (options.background) { Tk_FreeColor(options.background); @@ -1601,6 +1428,36 @@ ImgPhotoCmd( /* *---------------------------------------------------------------------- * + * GetExtension -- + * + * Return the extension part of a path, or NULL if there is no extension. + * The returned string will be a substring of the argument string, so + * should not be ckfree()d directly. No side effects. + * + *---------------------------------------------------------------------- + */ + +static const char * +GetExtension( + const char *path) +{ + char c; + const char *extension = NULL; + + for (; (c=*path++) != '\0' ;) { + if (c == '.') { + extension = path; + } + } + if (extension != NULL && extension[0] == '\0') { + extension = NULL; + } + return extension; +} + +/* + *---------------------------------------------------------------------- + * * ParseSubcommandOptions -- * * This function is invoked to process one of the options which may be @@ -1630,10 +1487,17 @@ ParseSubcommandOptions( int objc, /* Number of arguments in objv[]. */ Tcl_Obj *const objv[]) /* Arguments to be parsed. */ { - int index, c, bit, currentBit, length; + static const char *const compositingRules[] = { + "overlay", "set", /* Note that these must match the + * TK_PHOTO_COMPOSITE_* constants. */ + NULL + }; + size_t length; + int index, c, bit, currentBit; int values[4], numValues, maxValues, argIndex; - char *option; + const char *option, *expandedOption, *needed; const char *const *listPtr; + Tcl_Obj *msgObj; for (index = *optIndexPtr; index < objc; *optIndexPtr = ++index) { /* @@ -1641,7 +1505,8 @@ ParseSubcommandOptions( * optPtr->name. */ - option = Tcl_GetStringFromObj(objv[index], &length); + expandedOption = option = Tcl_GetString(objv[index]); + length = objv[index]->length; if (option[0] != '-') { if (optPtr->name == NULL) { optPtr->name = objv[index]; @@ -1659,10 +1524,10 @@ ParseSubcommandOptions( currentBit = 1; for (listPtr = optionNames; *listPtr != NULL; ++listPtr) { if ((c == *listPtr[0]) - && (strncmp(option, *listPtr, (size_t) length) == 0)) { + && (strncmp(option, *listPtr, length) == 0)) { + expandedOption = *listPtr; if (bit != 0) { - bit = 0; /* An ambiguous option. */ - break; + goto unknownOrAmbiguousOption; } bit = currentBit; } @@ -1674,28 +1539,12 @@ ParseSubcommandOptions( * in the interpreter and return. */ - if ((allowedOptions & bit) == 0) { - if (optPtr->name == NULL) { - optPtr->name = objv[index]; - continue; - } - Tcl_AppendResult(interp, "unrecognized option \"", - Tcl_GetString(objv[index]), - "\": must be ", NULL); - bit = 1; - for (listPtr = optionNames; *listPtr != NULL; ++listPtr) { - if ((allowedOptions & bit) != 0) { - if ((allowedOptions & (bit - 1)) != 0) { - Tcl_AppendResult(interp, ", ", NULL); - if ((allowedOptions & ~((bit << 1) - 1)) == 0) { - Tcl_AppendResult(interp, "or ", NULL); - } - } - Tcl_AppendResult(interp, *listPtr, NULL); - } - bit <<= 1; + if (!(allowedOptions & bit)) { + if (optPtr->name != NULL) { + goto unknownOrAmbiguousOption; } - return TCL_ERROR; + optPtr->name = objv[index]; + continue; } /* @@ -1708,16 +1557,13 @@ ParseSubcommandOptions( * The -background option takes a single XColor value. */ - if (index + 1 < objc) { - *optIndexPtr = ++index; - optPtr->background = Tk_GetColor(interp, Tk_MainWindow(interp), - Tk_GetUid(Tcl_GetString(objv[index]))); - if (!optPtr->background) { - return TCL_ERROR; - } - } else { - Tcl_AppendResult(interp, "the \"-background\" option ", - "requires a value", NULL); + if (index + 1 >= objc) { + goto oneValueRequired; + } + *optIndexPtr = ++index; + optPtr->background = Tk_GetColor(interp, Tk_MainWindow(interp), + Tk_GetUid(Tcl_GetString(objv[index]))); + if (!optPtr->background) { return TCL_ERROR; } } else if (bit == OPT_FORMAT) { @@ -1726,45 +1572,31 @@ ParseSubcommandOptions( * parsing this is outside the scope of this function. */ - if (index + 1 < objc) { - *optIndexPtr = ++index; - optPtr->format = objv[index]; - } else { - Tcl_AppendResult(interp, "the \"-format\" option ", - "requires a value", NULL); - return TCL_ERROR; + if (index + 1 >= objc) { + goto oneValueRequired; } + *optIndexPtr = ++index; + optPtr->format = objv[index]; } else if (bit == OPT_COMPOSITE) { /* * The -compositingrule option takes a single value from a * well-known set. */ - if (index + 1 < objc) { - /* - * Note that these must match the TK_PHOTO_COMPOSITE_* - * constants. - */ - - static const char *compositingRules[] = { - "overlay", "set", NULL - }; - - index++; - if (Tcl_GetIndexFromObj(interp, objv[index], compositingRules, - "compositing rule", 0, &optPtr->compositingRule) - != TCL_OK) { - return TCL_ERROR; - } - *optIndexPtr = index; - } else { - Tcl_AppendResult(interp, "the \"-compositingrule\" option ", - "requires a value", NULL); + if (index + 1 >= objc) { + goto oneValueRequired; + } + index++; + if (Tcl_GetIndexFromObj(interp, objv[index], compositingRules, + "compositing rule", 0, &optPtr->compositingRule) + != TCL_OK) { return TCL_ERROR; } + *optIndexPtr = index; } else if ((bit != OPT_SHRINK) && (bit != OPT_GRAYSCALE)) { - char *val; - maxValues = ((bit == OPT_FROM) || (bit == OPT_TO))? 4: 2; + const char *val; + + maxValues = ((bit == OPT_FROM) || (bit == OPT_TO)) ? 4 : 2; argIndex = index + 1; for (numValues = 0; numValues < maxValues; ++numValues) { if (argIndex >= objc) { @@ -1780,14 +1612,11 @@ ParseSubcommandOptions( } else { break; } - ++argIndex; + argIndex++; } if (numValues == 0) { - Tcl_AppendResult(interp, "the \"", option, "\" option ", - "requires one ", maxValues == 2? "or two": "to four", - " integer values", NULL); - return TCL_ERROR; + goto manyValuesRequired; } *optIndexPtr = (index += numValues); @@ -1811,9 +1640,8 @@ ParseSubcommandOptions( case OPT_FROM: if ((values[0] < 0) || (values[1] < 0) || ((numValues > 2) && ((values[2] < 0) || (values[3] < 0)))) { - Tcl_AppendResult(interp, "value(s) for the -from", - " option must be non-negative", NULL); - return TCL_ERROR; + needed = "non-negative"; + goto numberOutOfRange; } if (numValues <= 2) { optPtr->fromX = values[0]; @@ -1834,9 +1662,8 @@ ParseSubcommandOptions( case OPT_TO: if ((values[0] < 0) || (values[1] < 0) || ((numValues > 2) && ((values[2] < 0) || (values[3] < 0)))) { - Tcl_AppendResult(interp, "value(s) for the -to", - " option must be non-negative", NULL); - return TCL_ERROR; + needed = "non-negative"; + goto numberOutOfRange; } if (numValues <= 2) { optPtr->toX = values[0]; @@ -1852,9 +1679,8 @@ ParseSubcommandOptions( break; case OPT_ZOOM: if ((values[0] <= 0) || (values[1] <= 0)) { - Tcl_AppendResult(interp, "value(s) for the -zoom", - " option must be positive", NULL); - return TCL_ERROR; + needed = "positive"; + goto numberOutOfRange; } optPtr->zoomX = values[0]; optPtr->zoomY = values[1]; @@ -1868,8 +1694,50 @@ ParseSubcommandOptions( optPtr->options |= bit; } - return TCL_OK; + + /* + * Exception generation. + */ + + oneValueRequired: + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "the \"%s\" option requires a value", expandedOption)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", "MISSING_VALUE", NULL); + return TCL_ERROR; + + manyValuesRequired: + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "the \"%s\" option requires one %s integer values", + expandedOption, (maxValues == 2) ? "or two": "to four")); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", "MISSING_VALUE", NULL); + return TCL_ERROR; + + numberOutOfRange: + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "value(s) for the %s option must be %s", expandedOption, needed)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", "BAD_VALUE", NULL); + return TCL_ERROR; + + unknownOrAmbiguousOption: + msgObj = Tcl_ObjPrintf("unrecognized option \"%s\": must be ", option); + bit = 1; + for (listPtr = optionNames; *listPtr != NULL; ++listPtr) { + if (allowedOptions & bit) { + if (allowedOptions & (bit - 1)) { + if (allowedOptions & ~((bit << 1) - 1)) { + Tcl_AppendToObj(msgObj, ", ", -1); + } else { + Tcl_AppendToObj(msgObj, ", or ", -1); + } + } + Tcl_AppendToObj(msgObj, *listPtr, -1); + } + bit <<= 1; + } + Tcl_SetObjResult(interp, msgObj); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", "BAD_OPTION", NULL); + return TCL_ERROR; } /* @@ -1906,34 +1774,42 @@ ImgPhotoConfigureMaster( const char *oldFileString, *oldPaletteString; Tcl_Obj *oldData, *data = NULL, *oldFormat, *format = NULL; Tcl_Obj *tempdata, *tempformat; - int length, i, j, result, imageWidth, imageHeight, oldformat; + size_t length; + int i, j, result, imageWidth, imageHeight, oldformat; double oldGamma; Tcl_Channel chan; Tk_PhotoImageFormat *imageFormat; const char **args; - args = (const char **) ckalloc((objc + 1) * sizeof(char *)); + args = ckalloc((objc + 1) * sizeof(char *)); for (i = 0, j = 0; i < objc; i++,j++) { - args[j] = Tcl_GetStringFromObj(objv[i], &length); + args[j] = Tcl_GetString(objv[i]); + length = objv[i]->length; if ((length > 1) && (args[j][0] == '-')) { if ((args[j][1] == 'd') && - !strncmp(args[j], "-data", (size_t) length)) { + !strncmp(args[j], "-data", length)) { if (++i < objc) { data = objv[i]; j--; } else { - Tcl_AppendResult(interp, - "value for \"-data\" missing", NULL); + ckfree(args); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "value for \"-data\" missing", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", + "MISSING_VALUE", NULL); return TCL_ERROR; } } else if ((args[j][1] == 'f') && - !strncmp(args[j], "-format", (size_t) length)) { + !strncmp(args[j], "-format", length)) { if (++i < objc) { format = objv[i]; j--; } else { - Tcl_AppendResult(interp, - "value for \"-format\" missing", NULL); + ckfree(args); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "value for \"-format\" missing", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", + "MISSING_VALUE", NULL); return TCL_ERROR; } } @@ -1969,10 +1845,10 @@ ImgPhotoConfigureMaster( if (Tk_ConfigureWidget(interp, Tk_MainWindow(interp), configSpecs, j, args, (char *) masterPtr, flags) != TCL_OK) { - ckfree((char *) args); + ckfree(args); goto errorExit; } - ckfree((char *) args); + ckfree(args); /* * Regard the empty string for -file, -data or -format as the null value. @@ -1987,9 +1863,10 @@ ImgPhotoConfigureMaster( * Force into ByteArray format, which most (all) image handlers will * use anyway. Empty length means ignore the -data option. */ + int bytesize; - (void) Tcl_GetByteArrayFromObj(data, &length); - if (length) { + (void) Tcl_GetByteArrayFromObj(data, &bytesize); + if (bytesize) { Tcl_IncrRefCount(data); } else { data = NULL; @@ -2005,8 +1882,8 @@ ImgPhotoConfigureMaster( * object. */ - (void) Tcl_GetStringFromObj(format, &length); - if (length) { + (void) Tcl_GetString(format); + if (format->length) { Tcl_IncrRefCount(format); } else { format = NULL; @@ -2023,8 +1900,9 @@ ImgPhotoConfigureMaster( if (ImgPhotoSetSize(masterPtr, masterPtr->width, masterPtr->height) != TCL_OK) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, TK_PHOTO_ALLOC_FAILURE_MESSAGE, NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); + Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); goto errorExit; } @@ -2036,15 +1914,16 @@ ImgPhotoConfigureMaster( if ((masterPtr->fileString != NULL) && ((masterPtr->fileString != oldFileString) || (masterPtr->format != oldFormat))) { - /* * Prevent file system access in a safe interpreter. */ if (Tcl_IsSafe(interp)) { Tcl_ResetResult(interp); - Tcl_AppendResult(interp, - "can't get image from a file in a safe interpreter", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "can't get image from a file in a safe interpreter", + -1)); + Tcl_SetErrorCode(interp, "TK", "SAFE", "PHOTO_FILE", NULL); goto errorExit; } @@ -2068,15 +1947,16 @@ ImgPhotoConfigureMaster( result = ImgPhotoSetSize(masterPtr, imageWidth, imageHeight); if (result != TCL_OK) { Tcl_Close(NULL, chan); - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, TK_PHOTO_ALLOC_FAILURE_MESSAGE, NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); + Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); goto errorExit; } tempformat = masterPtr->format; if (oldformat && tempformat) { tempformat = (Tcl_Obj *) Tcl_GetString(tempformat); } - result = (*imageFormat->fileReadProc)(interp, chan, + result = imageFormat->fileReadProc(interp, chan, masterPtr->fileString, tempformat, (Tk_PhotoHandle) masterPtr, 0, 0, imageWidth, imageHeight, 0, 0); Tcl_Close(NULL, chan); @@ -2098,8 +1978,9 @@ ImgPhotoConfigureMaster( goto errorExit; } if (ImgPhotoSetSize(masterPtr, imageWidth, imageHeight) != TCL_OK) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, TK_PHOTO_ALLOC_FAILURE_MESSAGE, NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); + Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); goto errorExit; } tempformat = masterPtr->format; @@ -2110,9 +1991,9 @@ ImgPhotoConfigureMaster( } tempdata = (Tcl_Obj *) Tcl_GetString(tempdata); } - if ((*imageFormat->stringReadProc)(interp, tempdata, - tempformat, (Tk_PhotoHandle) masterPtr, - 0, 0, imageWidth, imageHeight, 0, 0) != TCL_OK) { + if (imageFormat->stringReadProc(interp, tempdata, tempformat, + (Tk_PhotoHandle) masterPtr, 0, 0, imageWidth, imageHeight, + 0, 0) != TCL_OK) { goto errorExit; } @@ -2141,7 +2022,7 @@ ImgPhotoConfigureMaster( for (instancePtr = masterPtr->instancePtr; instancePtr != NULL; instancePtr = instancePtr->nextPtr) { - ImgPhotoConfigureInstance(instancePtr); + TkImgPhotoConfigureInstance(instancePtr); } /* @@ -2176,333 +2057,6 @@ ImgPhotoConfigureMaster( /* *---------------------------------------------------------------------- * - * ImgPhotoConfigureInstance -- - * - * This function is called to create displaying information for a photo - * image instance based on the configuration information in the master. - * It is invoked both when new instances are created and when the master - * is reconfigured. - * - * Results: - * None. - * - * Side effects: - * Generates errors via Tcl_BackgroundError if there are problems in - * setting up the instance. - * - *---------------------------------------------------------------------- - */ - -static void -ImgPhotoConfigureInstance( - PhotoInstance *instancePtr) /* Instance to reconfigure. */ -{ - PhotoMaster *masterPtr = instancePtr->masterPtr; - XImage *imagePtr; - int bitsPerPixel; - ColorTable *colorTablePtr; - XRectangle validBox; - - /* - * If the -palette configuration option has been set for the master, use - * the value specified for our palette, but only if it is a valid palette - * for our windows. Use the gamma value specified the master. - */ - - if ((masterPtr->palette && masterPtr->palette[0]) - && IsValidPalette(instancePtr, masterPtr->palette)) { - instancePtr->palette = masterPtr->palette; - } else { - instancePtr->palette = instancePtr->defaultPalette; - } - instancePtr->gamma = masterPtr->gamma; - - /* - * If we don't currently have a color table, or if the one we have no - * longer applies (e.g. because our palette or gamma has changed), get a - * new one. - */ - - colorTablePtr = instancePtr->colorTablePtr; - if ((colorTablePtr == NULL) - || (instancePtr->colormap != colorTablePtr->id.colormap) - || (instancePtr->palette != colorTablePtr->id.palette) - || (instancePtr->gamma != colorTablePtr->id.gamma)) { - /* - * Free up our old color table, and get a new one. - */ - - if (colorTablePtr != NULL) { - colorTablePtr->liveRefCount -= 1; - FreeColorTable(colorTablePtr, 0); - } - GetColorTable(instancePtr); - - /* - * Create a new XImage structure for sending data to the X server, if - * necessary. - */ - - if (instancePtr->colorTablePtr->flags & BLACK_AND_WHITE) { - bitsPerPixel = 1; - } else { - bitsPerPixel = instancePtr->visualInfo.depth; - } - - if ((instancePtr->imagePtr == NULL) - || (instancePtr->imagePtr->bits_per_pixel != bitsPerPixel)) { - if (instancePtr->imagePtr != NULL) { - XDestroyImage(instancePtr->imagePtr); - } - imagePtr = XCreateImage(instancePtr->display, - instancePtr->visualInfo.visual, (unsigned) bitsPerPixel, - (bitsPerPixel > 1? ZPixmap: XYBitmap), 0, NULL, - 1, 1, 32, 0); - instancePtr->imagePtr = imagePtr; - - /* - * We create images using the local host's endianness, rather than - * the endianness of the server; otherwise we would have to - * byte-swap any 16 or 32 bit values that we store in the image - * if the server's endianness is different from ours. - */ - - if (imagePtr != NULL) { -#ifdef WORDS_BIGENDIAN - imagePtr->byte_order = MSBFirst; -#else - imagePtr->byte_order = LSBFirst; -#endif - _XInitImageFuncPtrs(imagePtr); - } - } - } - - /* - * If the user has specified a width and/or height for the master which is - * different from our current width/height, set the size to the values - * specified by the user. If we have no pixmap, we do this also, since it - * has the side effect of allocating a pixmap for us. - */ - - if ((instancePtr->pixels == None) || (instancePtr->error == NULL) - || (instancePtr->width != masterPtr->width) - || (instancePtr->height != masterPtr->height)) { - ImgPhotoInstanceSetSize(instancePtr); - } - - /* - * Redither this instance if necessary. - */ - - if ((masterPtr->flags & IMAGE_CHANGED) - || (instancePtr->colorTablePtr != colorTablePtr)) { - TkClipBox(masterPtr->validRegion, &validBox); - if ((validBox.width > 0) && (validBox.height > 0)) { - DitherInstance(instancePtr, validBox.x, validBox.y, - validBox.width, validBox.height); - } - } -} - -/* - *---------------------------------------------------------------------- - * - * ImgPhotoGet -- - * - * This function is called for each use of a photo image in a widget. - * - * Results: - * The return value is a token for the instance, which is passed back to - * us in calls to ImgPhotoDisplay and ImgPhotoFree. - * - * Side effects: - * A data structure is set up for the instance (or, an existing instance - * is re-used for the new one). - * - *---------------------------------------------------------------------- - */ - -static ClientData -ImgPhotoGet( - Tk_Window tkwin, /* Window in which the instance will be - * used. */ - ClientData masterData) /* Pointer to our master structure for the - * image. */ -{ - PhotoMaster *masterPtr = (PhotoMaster *) masterData; - PhotoInstance *instancePtr; - Colormap colormap; - int mono, nRed, nGreen, nBlue, numVisuals; - XVisualInfo visualInfo, *visInfoPtr; - char buf[TCL_INTEGER_SPACE * 3]; - XColor *white, *black; - XGCValues gcValues; - - /* - * Table of "best" choices for palette for PseudoColor displays with - * between 3 and 15 bits/pixel. - */ - - static const int paletteChoice[13][3] = { - /* #red, #green, #blue */ - {2, 2, 2, /* 3 bits, 8 colors */}, - {2, 3, 2, /* 4 bits, 12 colors */}, - {3, 4, 2, /* 5 bits, 24 colors */}, - {4, 5, 3, /* 6 bits, 60 colors */}, - {5, 6, 4, /* 7 bits, 120 colors */}, - {7, 7, 4, /* 8 bits, 198 colors */}, - {8, 10, 6, /* 9 bits, 480 colors */}, - {10, 12, 8, /* 10 bits, 960 colors */}, - {14, 15, 9, /* 11 bits, 1890 colors */}, - {16, 20, 12, /* 12 bits, 3840 colors */}, - {20, 24, 16, /* 13 bits, 7680 colors */}, - {26, 30, 20, /* 14 bits, 15600 colors */}, - {32, 32, 30, /* 15 bits, 30720 colors */} - }; - - /* - * See if there is already an instance for windows using the same - * colormap. If so then just re-use it. - */ - - colormap = Tk_Colormap(tkwin); - for (instancePtr = masterPtr->instancePtr; instancePtr != NULL; - instancePtr = instancePtr->nextPtr) { - if ((colormap == instancePtr->colormap) - && (Tk_Display(tkwin) == instancePtr->display)) { - /* - * Re-use this instance. - */ - - if (instancePtr->refCount == 0) { - /* - * We are resurrecting this instance. - */ - - Tcl_CancelIdleCall(DisposeInstance, (ClientData) instancePtr); - if (instancePtr->colorTablePtr != NULL) { - FreeColorTable(instancePtr->colorTablePtr, 0); - } - GetColorTable(instancePtr); - } - instancePtr->refCount++; - return (ClientData) instancePtr; - } - } - - /* - * The image isn't already in use in a window with the same colormap. Make - * a new instance of the image. - */ - - instancePtr = (PhotoInstance *) ckalloc(sizeof(PhotoInstance)); - instancePtr->masterPtr = masterPtr; - instancePtr->display = Tk_Display(tkwin); - instancePtr->colormap = Tk_Colormap(tkwin); - Tk_PreserveColormap(instancePtr->display, instancePtr->colormap); - instancePtr->refCount = 1; - instancePtr->colorTablePtr = NULL; - instancePtr->pixels = None; - instancePtr->error = NULL; - instancePtr->width = 0; - instancePtr->height = 0; - instancePtr->imagePtr = 0; - instancePtr->nextPtr = masterPtr->instancePtr; - masterPtr->instancePtr = instancePtr; - - /* - * Obtain information about the visual and decide on the default palette. - */ - - visualInfo.screen = Tk_ScreenNumber(tkwin); - visualInfo.visualid = XVisualIDFromVisual(Tk_Visual(tkwin)); - visInfoPtr = XGetVisualInfo(Tk_Display(tkwin), - VisualScreenMask | VisualIDMask, &visualInfo, &numVisuals); - if (visInfoPtr == NULL) { - Tcl_Panic("ImgPhotoGet couldn't find visual for window"); - } - - nRed = 2; - nGreen = nBlue = 0; - mono = 1; - instancePtr->visualInfo = *visInfoPtr; - switch (visInfoPtr->class) { - case DirectColor: - case TrueColor: - nRed = 1 << CountBits(visInfoPtr->red_mask); - nGreen = 1 << CountBits(visInfoPtr->green_mask); - nBlue = 1 << CountBits(visInfoPtr->blue_mask); - mono = 0; - break; - case PseudoColor: - case StaticColor: - if (visInfoPtr->depth > 15) { - nRed = 32; - nGreen = 32; - nBlue = 32; - mono = 0; - } else if (visInfoPtr->depth >= 3) { - const int *ip = paletteChoice[visInfoPtr->depth - 3]; - - nRed = ip[0]; - nGreen = ip[1]; - nBlue = ip[2]; - mono = 0; - } - break; - case GrayScale: - case StaticGray: - nRed = 1 << visInfoPtr->depth; - break; - } - XFree((char *) visInfoPtr); - - if (mono) { - sprintf(buf, "%d", nRed); - } else { - sprintf(buf, "%d/%d/%d", nRed, nGreen, nBlue); - } - instancePtr->defaultPalette = Tk_GetUid(buf); - - /* - * Make a GC with background = black and foreground = white. - */ - - white = Tk_GetColor(masterPtr->interp, tkwin, "white"); - black = Tk_GetColor(masterPtr->interp, tkwin, "black"); - gcValues.foreground = (white != NULL)? white->pixel: - WhitePixelOfScreen(Tk_Screen(tkwin)); - gcValues.background = (black != NULL)? black->pixel: - BlackPixelOfScreen(Tk_Screen(tkwin)); - Tk_FreeColor(white); - Tk_FreeColor(black); - gcValues.graphics_exposures = False; - instancePtr->gc = Tk_GetGC(tkwin, - GCForeground|GCBackground|GCGraphicsExposures, &gcValues); - - /* - * Set configuration options and finish the initialization of the - * instance. This will also dither the image if necessary. - */ - - ImgPhotoConfigureInstance(instancePtr); - - /* - * If this is the first instance, must set the size of the image. - */ - - if (instancePtr->nextPtr == NULL) { - Tk_ImageChanged(masterPtr->tkMaster, 0, 0, 0, 0, - masterPtr->width, masterPtr->height); - } - - return (ClientData) instancePtr; -} - -/* - *---------------------------------------------------------------------- - * * ToggleComplexAlphaIfNeeded -- * * This function is called when an image is modified to check if any @@ -2522,8 +2076,8 @@ static int ToggleComplexAlphaIfNeeded( PhotoMaster *mPtr) { - size_t len = MAX(mPtr->userWidth, mPtr->width) * - MAX(mPtr->userHeight, mPtr->height) * 4; + size_t len = (size_t)MAX(mPtr->userWidth, mPtr->width) * + (size_t)MAX(mPtr->userHeight, mPtr->height) * 4; unsigned char *c = mPtr->pix32; unsigned char *end = c + len; @@ -2533,6 +2087,9 @@ ToggleComplexAlphaIfNeeded( */ mPtr->flags &= ~COMPLEX_ALPHA; + if (c == NULL) { + return 0; + } c += 3; /* Start at first alpha byte. */ for (; c < end; c += 4) { if (*c && *c != 255) { @@ -2546,351 +2103,6 @@ ToggleComplexAlphaIfNeeded( /* *---------------------------------------------------------------------- * - * ImgPhotoBlendComplexAlpha -- - * - * This function is called when an image with partially transparent - * pixels must be drawn over another image. It blends the photo data onto - * a local copy of the surface that we are drawing on, *including* the - * pixels drawn by everything that should be drawn underneath the image. - * - * Much of this code has hard-coded values in for speed because this - * routine is performance critical for complex image drawing. - * - * Results: - * None. - * - * Side effects: - * Background image passed in gets drawn over with image data. - * - * Notes: - * This should work on all platforms that set mask and shift data - * properly from the visualInfo. RGB is really only a 24+ bpp version - * whereas RGB15 is the correct version and works for 15bpp+, but it - * slower, so it's only used for 15bpp+. - * - * Note that Win32 pre-defines those operations that we really need. - * - *---------------------------------------------------------------------- - */ - -#ifndef __WIN32__ -#define GetRValue(rgb) (UCHAR(((rgb) & red_mask) >> red_shift)) -#define GetGValue(rgb) (UCHAR(((rgb) & green_mask) >> green_shift)) -#define GetBValue(rgb) (UCHAR(((rgb) & blue_mask) >> blue_shift)) -#define RGB(r, g, b) ((unsigned)( \ - (UCHAR(r) << red_shift) | \ - (UCHAR(g) << green_shift) | \ - (UCHAR(b) << blue_shift) )) -#define RGB15(r, g, b) ((unsigned)( \ - (((r) * red_mask / 255) & red_mask) | \ - (((g) * green_mask / 255) & green_mask) | \ - (((b) * blue_mask / 255) & blue_mask) )) -#endif /* !__WIN32__ */ - -static void -ImgPhotoBlendComplexAlpha( - XImage *bgImg, /* Background image to draw on. */ - PhotoInstance *iPtr, /* Image instance to draw. */ - int xOffset, int yOffset, /* X & Y offset into image instance to - * draw. */ - int width, int height) /* Width & height of image to draw. */ -{ - int x, y, line; - unsigned long pixel; - unsigned char r, g, b, alpha, unalpha, *masterPtr; - unsigned char *alphaAr = iPtr->masterPtr->pix32; - - /* - * This blending is an integer version of the Source-Over compositing rule - * (see Porter&Duff, "Compositing Digital Images", proceedings of SIGGRAPH - * 1984) that has been hard-coded (for speed) to work with targetting a - * solid surface. - * - * The 'unalpha' field must be 255-alpha; it is separated out to encourage - * more efficient compilation. - */ - -#define ALPHA_BLEND(bgPix, imgPix, alpha, unalpha) \ - ((bgPix * unalpha + imgPix * alpha) / 255) - - /* - * We have to get the mask and shift info from the visual on non-Win32 so - * that the macros Get*Value(), RGB() and RGB15() work correctly. This - * might be cached for better performance. - */ - -#ifndef __WIN32__ - unsigned long red_mask, green_mask, blue_mask; - unsigned long red_shift, green_shift, blue_shift; - Visual *visual = iPtr->visualInfo.visual; - - red_mask = visual->red_mask; - green_mask = visual->green_mask; - blue_mask = visual->blue_mask; - red_shift = 0; - green_shift = 0; - blue_shift = 0; - while ((0x0001 & (red_mask >> red_shift)) == 0) { - red_shift++; - } - while ((0x0001 & (green_mask >> green_shift)) == 0) { - green_shift++; - } - while ((0x0001 & (blue_mask >> blue_shift)) == 0) { - blue_shift++; - } -#endif /* !__WIN32__ */ - - /* - * Only UNIX requires the special case for <24bpp. It varies with 3 extra - * shifts and uses RGB15. The 24+bpp version could also then be further - * optimized. - */ - -#if !(defined(__WIN32__) || defined(MAC_OSX_TK)) - if (bgImg->depth < 24) { - unsigned char red_mlen, green_mlen, blue_mlen; - - red_mlen = 8 - CountBits(red_mask >> red_shift); - green_mlen = 8 - CountBits(green_mask >> green_shift); - blue_mlen = 8 - CountBits(blue_mask >> blue_shift); - for (y = 0; y < height; y++) { - line = (y + yOffset) * iPtr->masterPtr->width; - for (x = 0; x < width; x++) { - masterPtr = alphaAr + ((line + x + xOffset) * 4); - alpha = masterPtr[3]; - - /* - * Ignore pixels that are fully transparent - */ - - if (alpha) { - /* - * We could perhaps be more efficient than XGetPixel for - * 24 and 32 bit displays, but this seems "fast enough". - */ - - r = masterPtr[0]; - g = masterPtr[1]; - b = masterPtr[2]; - if (alpha != 255) { - /* - * Only blend pixels that have some transparency - */ - - unsigned char ra, ga, ba; - - pixel = XGetPixel(bgImg, x, y); - ra = GetRValue(pixel) << red_mlen; - ga = GetGValue(pixel) << green_mlen; - ba = GetBValue(pixel) << blue_mlen; - unalpha = 255 - alpha; /* Calculate once. */ - r = ALPHA_BLEND(ra, r, alpha, unalpha); - g = ALPHA_BLEND(ga, g, alpha, unalpha); - b = ALPHA_BLEND(ba, b, alpha, unalpha); - } - XPutPixel(bgImg, x, y, RGB15(r, g, b)); - } - } - } - return; - } -#endif /* !__WIN32__ && !MAC_OSX_TK */ - - for (y = 0; y < height; y++) { - line = (y + yOffset) * iPtr->masterPtr->width; - for (x = 0; x < width; x++) { - masterPtr = alphaAr + ((line + x + xOffset) * 4); - alpha = masterPtr[3]; - - /* - * Ignore pixels that are fully transparent - */ - - if (alpha) { - /* - * We could perhaps be more efficient than XGetPixel for 24 - * and 32 bit displays, but this seems "fast enough". - */ - - r = masterPtr[0]; - g = masterPtr[1]; - b = masterPtr[2]; - if (alpha != 255) { - /* - * Only blend pixels that have some transparency - */ - - unsigned char ra, ga, ba; - - pixel = XGetPixel(bgImg, x, y); - ra = GetRValue(pixel); - ga = GetGValue(pixel); - ba = GetBValue(pixel); - unalpha = 255 - alpha; /* Calculate once. */ - r = ALPHA_BLEND(ra, r, alpha, unalpha); - g = ALPHA_BLEND(ga, g, alpha, unalpha); - b = ALPHA_BLEND(ba, b, alpha, unalpha); - } - XPutPixel(bgImg, x, y, RGB(r, g, b)); - } - } - } -#undef ALPHA_BLEND -} - -/* - *---------------------------------------------------------------------- - * - * ImgPhotoDisplay -- - * - * This function is invoked to draw a photo image. - * - * Results: - * None. - * - * Side effects: - * A portion of the image gets rendered in a pixmap or window. - * - *---------------------------------------------------------------------- - */ - -static void -ImgPhotoDisplay( - ClientData clientData, /* Pointer to PhotoInstance structure for - * instance to be displayed. */ - Display *display, /* Display on which to draw image. */ - Drawable drawable, /* Pixmap or window in which to draw image. */ - int imageX, int imageY, /* Upper-left corner of region within image to - * draw. */ - int width, int height, /* Dimensions of region within image to - * draw. */ - int drawableX,int drawableY)/* Coordinates within drawable that correspond - * to imageX and imageY. */ -{ - PhotoInstance *instancePtr = (PhotoInstance *) clientData; - XVisualInfo visInfo = instancePtr->visualInfo; - - /* - * If there's no pixmap, it means that an error occurred while creating - * the image instance so it can't be displayed. - */ - - if (instancePtr->pixels == None) { - return; - } - - if ((instancePtr->masterPtr->flags & COMPLEX_ALPHA) - && visInfo.depth >= 15 - && (visInfo.class == DirectColor || visInfo.class == TrueColor)) { - Tk_ErrorHandler handler; - XImage *bgImg = NULL; - - /* - * Create an error handler to suppress the case where the input was - * not properly constrained, which can cause an X error. [Bug 979239] - */ - - handler = Tk_CreateErrorHandler(display, -1, -1, -1, NULL, - (ClientData) NULL); - - /* - * Pull the current background from the display to blend with - */ - - bgImg = XGetImage(display, drawable, drawableX, drawableY, - (unsigned int)width, (unsigned int)height, AllPlanes, ZPixmap); - if (bgImg == NULL) { - Tk_DeleteErrorHandler(handler); - /* We failed to get the image so draw without blending alpha. It's the best we can do */ - goto fallBack; - } - - ImgPhotoBlendComplexAlpha(bgImg, instancePtr, imageX, imageY, width, - height); - - /* - * Color info is unimportant as we only do this operation for depth >= - * 15. - */ - - TkPutImage(NULL, 0, display, drawable, instancePtr->gc, - bgImg, 0, 0, drawableX, drawableY, - (unsigned int) width, (unsigned int) height); - XDestroyImage(bgImg); - Tk_DeleteErrorHandler(handler); - } else { - /* - * masterPtr->region describes which parts of the image contain valid - * data. We set this region as the clip mask for the gc, setting its - * origin appropriately, and use it when drawing the image. - */ - - fallBack: - TkSetRegion(display, instancePtr->gc, - instancePtr->masterPtr->validRegion); - XSetClipOrigin(display, instancePtr->gc, drawableX - imageX, - drawableY - imageY); - XCopyArea(display, instancePtr->pixels, drawable, instancePtr->gc, - imageX, imageY, (unsigned) width, (unsigned) height, - drawableX, drawableY); - XSetClipMask(display, instancePtr->gc, None); - XSetClipOrigin(display, instancePtr->gc, 0, 0); - } - XFlush(display); -} - -/* - *---------------------------------------------------------------------- - * - * ImgPhotoFree -- - * - * This function is called when a widget ceases to use a particular - * instance of an image. We don't actually get rid of the instance until - * later because we may be about to get this instance again. - * - * Results: - * None. - * - * Side effects: - * Internal data structures get cleaned up, later. - * - *---------------------------------------------------------------------- - */ - -static void -ImgPhotoFree( - ClientData clientData, /* Pointer to PhotoInstance structure for - * instance to be displayed. */ - Display *display) /* Display containing window that used - * image. */ -{ - PhotoInstance *instancePtr = (PhotoInstance *) clientData; - ColorTable *colorPtr; - - instancePtr->refCount -= 1; - if (instancePtr->refCount > 0) { - return; - } - - /* - * There are no more uses of the image within this widget. Decrement the - * count of live uses of its color table, so that its colors can be - * reclaimed if necessary, and set up an idle call to free the instance - * structure. - */ - - colorPtr = instancePtr->colorTablePtr; - if (colorPtr != NULL) { - colorPtr->liveRefCount -= 1; - } - - Tcl_DoWhenIdle(DisposeInstance, (ClientData) instancePtr); -} - -/* - *---------------------------------------------------------------------- - * * ImgPhotoDelete -- * * This function is called by the image code to delete the master @@ -2910,22 +2122,22 @@ ImgPhotoDelete( ClientData masterData) /* Pointer to PhotoMaster structure for image. * Must not have any more instances. */ { - PhotoMaster *masterPtr = (PhotoMaster *) masterData; + PhotoMaster *masterPtr = masterData; PhotoInstance *instancePtr; while ((instancePtr = masterPtr->instancePtr) != NULL) { if (instancePtr->refCount > 0) { Tcl_Panic("tried to delete photo image when instances still exist"); } - Tcl_CancelIdleCall(DisposeInstance, (ClientData) instancePtr); - DisposeInstance((ClientData) instancePtr); + Tcl_CancelIdleCall(TkImgDisposeInstance, instancePtr); + TkImgDisposeInstance(instancePtr); } masterPtr->tkMaster = NULL; if (masterPtr->imageCmd != NULL) { Tcl_DeleteCommandFromToken(masterPtr->interp, masterPtr->imageCmd); } if (masterPtr->pix32 != NULL) { - ckfree((char *) masterPtr->pix32); + ckfree(masterPtr->pix32); } if (masterPtr->validRegion != NULL) { TkDestroyRegion(masterPtr->validRegion); @@ -2937,7 +2149,7 @@ ImgPhotoDelete( Tcl_DecrRefCount(masterPtr->format); } Tk_FreeOptions(configSpecs, (char *) masterPtr, NULL, 0); - ckfree((char *) masterPtr); + ckfree(masterPtr); } /* @@ -2962,7 +2174,7 @@ ImgPhotoCmdDeletedProc( ClientData clientData) /* Pointer to PhotoMaster structure for * image. */ { - PhotoMaster *masterPtr = (PhotoMaster *) clientData; + PhotoMaster *masterPtr = clientData; masterPtr->imageCmd = NULL; if (masterPtr->tkMaster != NULL) { @@ -3008,6 +2220,10 @@ ImgPhotoSetSize( height = masterPtr->userHeight; } + if (width > INT_MAX / 4) { + /* Pitch overflows int */ + return TCL_ERROR; + } pitch = width * 4; /* @@ -3017,11 +2233,12 @@ ImgPhotoSetSize( if ((width != masterPtr->width) || (height != masterPtr->height) || (masterPtr->pix32 == NULL)) { - /* - * Not a u-long, but should be one. - */ + unsigned newPixSize; - unsigned /*long*/ newPixSize = (unsigned /*long*/) (height * pitch); + if (pitch && height > (int)(UINT_MAX / pitch)) { + return TCL_ERROR; + } + newPixSize = height * pitch; /* * Some mallocs() really hate allocating zero bytes. [Bug 619544] @@ -3030,7 +2247,7 @@ ImgPhotoSetSize( if (newPixSize == 0) { newPix32 = NULL; } else { - newPix32 = (unsigned char *) attemptckalloc(newPixSize); + newPix32 = attemptckalloc(newPixSize); if (newPix32 == NULL) { return TCL_ERROR; } @@ -3073,14 +2290,14 @@ ImgPhotoSetSize( if ((masterPtr->pix32 != NULL) && ((width == masterPtr->width) || (width == validBox.width))) { if (validBox.y > 0) { - memset(newPix32, 0, (size_t) (validBox.y * pitch)); + memset(newPix32, 0, ((size_t) validBox.y * pitch)); } h = validBox.y + validBox.height; if (h < height) { - memset(newPix32 + h*pitch, 0, (size_t) ((height - h) * pitch)); + memset(newPix32 + h*pitch, 0, ((size_t) (height - h) * pitch)); } } else { - memset(newPix32, 0, (size_t) (height * pitch)); + memset(newPix32, 0, ((size_t)height * pitch)); } if (masterPtr->pix32 != NULL) { @@ -3097,7 +2314,7 @@ ImgPhotoSetSize( offset = validBox.y * pitch; memcpy(newPix32 + offset, masterPtr->pix32 + offset, - (size_t) (validBox.height * pitch)); + ((size_t)validBox.height * pitch)); } else if ((validBox.width > 0) && (validBox.height > 0)) { /* @@ -3108,13 +2325,13 @@ ImgPhotoSetSize( srcPtr = masterPtr->pix32 + (validBox.y * masterPtr->width + validBox.x) * 4; for (h = validBox.height; h > 0; h--) { - memcpy(destPtr, srcPtr, (size_t) (validBox.width * 4)); + memcpy(destPtr, srcPtr, ((size_t)validBox.width * 4)); destPtr += width * 4; srcPtr += masterPtr->width * 4; } } - ckfree((char *) masterPtr->pix32); + ckfree(masterPtr->pix32); } masterPtr->pix32 = newPix32; @@ -3149,7 +2366,7 @@ ImgPhotoSetSize( for (instancePtr = masterPtr->instancePtr; instancePtr != NULL; instancePtr = instancePtr->nextPtr) { - ImgPhotoInstanceSetSize(instancePtr); + TkImgPhotoInstanceSetSize(instancePtr); } return TCL_OK; @@ -3158,867 +2375,6 @@ ImgPhotoSetSize( /* *---------------------------------------------------------------------- * - * ImgPhotoInstanceSetSize -- - * - * This function reallocates the instance pixmap and dithering error - * array for a photo instance, as necessary, to change the image's size - * to `width' x `height' pixels. - * - * Results: - * None. - * - * Side effects: - * Storage gets reallocated, here and in the X server. - * - *---------------------------------------------------------------------- - */ - -static void -ImgPhotoInstanceSetSize( - PhotoInstance *instancePtr) /* Instance whose size is to be changed. */ -{ - PhotoMaster *masterPtr; - schar *newError, *errSrcPtr, *errDestPtr; - int h, offset; - XRectangle validBox; - Pixmap newPixmap; - - masterPtr = instancePtr->masterPtr; - TkClipBox(masterPtr->validRegion, &validBox); - - if ((instancePtr->width != masterPtr->width) - || (instancePtr->height != masterPtr->height) - || (instancePtr->pixels == None)) { - newPixmap = Tk_GetPixmap(instancePtr->display, - RootWindow(instancePtr->display, - instancePtr->visualInfo.screen), - (masterPtr->width > 0) ? masterPtr->width: 1, - (masterPtr->height > 0) ? masterPtr->height: 1, - instancePtr->visualInfo.depth); - if (!newPixmap) { - Tcl_Panic("Fail to create pixmap with Tk_GetPixmap in ImgPhotoInstanceSetSize.\n"); - } - - /* - * The following is a gross hack needed to properly support colormaps - * under Windows. Before the pixels can be copied to the pixmap, the - * relevent colormap must be associated with the drawable. Normally we - * can infer this association from the window that was used to create - * the pixmap. However, in this case we're using the root window, so - * we have to be more explicit. - */ - - TkSetPixmapColormap(newPixmap, instancePtr->colormap); - - if (instancePtr->pixels != None) { - /* - * Copy any common pixels from the old pixmap and free it. - */ - - XCopyArea(instancePtr->display, instancePtr->pixels, newPixmap, - instancePtr->gc, validBox.x, validBox.y, - validBox.width, validBox.height, validBox.x, validBox.y); - Tk_FreePixmap(instancePtr->display, instancePtr->pixels); - } - instancePtr->pixels = newPixmap; - } - - if ((instancePtr->width != masterPtr->width) - || (instancePtr->height != masterPtr->height) - || (instancePtr->error == NULL)) { - - if (masterPtr->height > 0 && masterPtr->width > 0) { - newError = (schar *) ckalloc((unsigned) - masterPtr->height * masterPtr->width * 3 * sizeof(schar)); - - /* - * Zero the new array so that we don't get bogus error values - * propagating into areas we dither later. - */ - - if ((instancePtr->error != NULL) - && ((instancePtr->width == masterPtr->width) - || (validBox.width == masterPtr->width))) { - if (validBox.y > 0) { - memset(newError, 0, (size_t) - validBox.y * masterPtr->width * 3 * sizeof(schar)); - } - h = validBox.y + validBox.height; - if (h < masterPtr->height) { - memset(newError + h*masterPtr->width*3, 0, - (size_t) (masterPtr->height - h) - * masterPtr->width * 3 * sizeof(schar)); - } - } else { - memset(newError, 0, (size_t) - masterPtr->height * masterPtr->width *3*sizeof(schar)); - } - } else { - newError = NULL; - } - - if (instancePtr->error != NULL) { - /* - * Copy the common area over to the new array and free the old - * array. - */ - - if (masterPtr->width == instancePtr->width) { - offset = validBox.y * masterPtr->width * 3; - memcpy(newError + offset, instancePtr->error + offset, - (size_t) (validBox.height - * masterPtr->width * 3 * sizeof(schar))); - - } else if (validBox.width > 0 && validBox.height > 0) { - errDestPtr = newError + - (validBox.y * masterPtr->width + validBox.x) * 3; - errSrcPtr = instancePtr->error + - (validBox.y * instancePtr->width + validBox.x) * 3; - - for (h = validBox.height; h > 0; --h) { - memcpy(errDestPtr, errSrcPtr, - validBox.width * 3 * sizeof(schar)); - errDestPtr += masterPtr->width * 3; - errSrcPtr += instancePtr->width * 3; - } - } - ckfree((char *) instancePtr->error); - } - - instancePtr->error = newError; - } - - instancePtr->width = masterPtr->width; - instancePtr->height = masterPtr->height; -} - -/* - *---------------------------------------------------------------------- - * - * IsValidPalette -- - * - * This function is called to check whether a value given for the - * -palette option is valid for a particular instance of a photo image. - * - * Results: - * A boolean value: 1 if the palette is acceptable, 0 otherwise. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static int -IsValidPalette( - PhotoInstance *instancePtr, /* Instance to which the palette specification - * is to be applied. */ - const char *palette) /* Palette specification string. */ -{ - int nRed, nGreen, nBlue, mono, numColors; - char *endp; - - /* - * First parse the specification: it must be of the form %d or %d/%d/%d. - */ - - nRed = strtol(palette, &endp, 10); - if ((endp == palette) || ((*endp != 0) && (*endp != '/')) - || (nRed < 2) || (nRed > 256)) { - return 0; - } - - if (*endp == 0) { - mono = 1; - nGreen = nBlue = nRed; - } else { - palette = endp + 1; - nGreen = strtol(palette, &endp, 10); - if ((endp == palette) || (*endp != '/') || (nGreen < 2) - || (nGreen > 256)) { - return 0; - } - palette = endp + 1; - nBlue = strtol(palette, &endp, 10); - if ((endp == palette) || (*endp != 0) || (nBlue < 2) - || (nBlue > 256)) { - return 0; - } - mono = 0; - } - - switch (instancePtr->visualInfo.class) { - case DirectColor: - case TrueColor: - if ((nRed > (1 << CountBits(instancePtr->visualInfo.red_mask))) - || (nGreen>(1<<CountBits(instancePtr->visualInfo.green_mask))) - || (nBlue>(1<<CountBits(instancePtr->visualInfo.blue_mask)))) { - return 0; - } - break; - case PseudoColor: - case StaticColor: - numColors = nRed; - if (!mono) { - numColors *= nGreen*nBlue; - } - if (numColors > (1 << instancePtr->visualInfo.depth)) { - return 0; - } - break; - case GrayScale: - case StaticGray: - if (!mono || (nRed > (1 << instancePtr->visualInfo.depth))) { - return 0; - } - break; - } - - return 1; -} - -/* - *---------------------------------------------------------------------- - * - * CountBits -- - * - * This function counts how many bits are set to 1 in `mask'. - * - * Results: - * The integer number of bits. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static int -CountBits( - pixel mask) /* Value to count the 1 bits in. */ -{ - int n; - - for (n=0 ; mask!=0 ; mask&=mask-1) { - n++; - } - return n; -} - -/* - *---------------------------------------------------------------------- - * - * GetColorTable -- - * - * This function is called to allocate a table of colormap information - * for an instance of a photo image. Only one such table is allocated for - * all photo instances using the same display, colormap, palette and - * gamma values, so that the application need only request a set of - * colors from the X server once for all such photo widgets. This - * function maintains a hash table to find previously-allocated - * ColorTables. - * - * Results: - * None. - * - * Side effects: - * A new ColorTable may be allocated and placed in the hash table, and - * have colors allocated for it. - * - *---------------------------------------------------------------------- - */ - -static void -GetColorTable( - PhotoInstance *instancePtr) /* Instance needing a color table. */ -{ - ColorTable *colorPtr; - Tcl_HashEntry *entry; - ColorTableId id; - int isNew; - - /* - * Look for an existing ColorTable in the hash table. - */ - - memset(&id, 0, sizeof(id)); - id.display = instancePtr->display; - id.colormap = instancePtr->colormap; - id.palette = instancePtr->palette; - id.gamma = instancePtr->gamma; - if (!imgPhotoColorHashInitialized) { - Tcl_InitHashTable(&imgPhotoColorHash, N_COLOR_HASH); - imgPhotoColorHashInitialized = 1; - } - entry = Tcl_CreateHashEntry(&imgPhotoColorHash, (char *) &id, &isNew); - - if (!isNew) { - /* - * Re-use the existing entry. - */ - - colorPtr = (ColorTable *) Tcl_GetHashValue(entry); - } else { - /* - * No color table currently available; need to make one. - */ - - colorPtr = (ColorTable *) ckalloc(sizeof(ColorTable)); - - /* - * The following line of code should not normally be needed due to the - * assignment in the following line. However, it compensates for bugs - * in some compilers (HP, for example) where sizeof(ColorTable) is 24 - * but the assignment only copies 20 bytes, leaving 4 bytes - * uninitialized; these cause problems when using the id for lookups - * in imgPhotoColorHash, and can result in core dumps. - */ - - memset(&colorPtr->id, 0, sizeof(ColorTableId)); - colorPtr->id = id; - Tk_PreserveColormap(colorPtr->id.display, colorPtr->id.colormap); - colorPtr->flags = 0; - colorPtr->refCount = 0; - colorPtr->liveRefCount = 0; - colorPtr->numColors = 0; - colorPtr->visualInfo = instancePtr->visualInfo; - colorPtr->pixelMap = NULL; - Tcl_SetHashValue(entry, colorPtr); - } - - colorPtr->refCount++; - colorPtr->liveRefCount++; - instancePtr->colorTablePtr = colorPtr; - if (colorPtr->flags & DISPOSE_PENDING) { - Tcl_CancelIdleCall(DisposeColorTable, (ClientData) colorPtr); - colorPtr->flags &= ~DISPOSE_PENDING; - } - - /* - * Allocate colors for this color table if necessary. - */ - - if ((colorPtr->numColors == 0) - && ((colorPtr->flags & BLACK_AND_WHITE) == 0)) { - AllocateColors(colorPtr); - } -} - -/* - *---------------------------------------------------------------------- - * - * FreeColorTable -- - * - * This function is called when an instance ceases using a color table. - * - * Results: - * None. - * - * Side effects: - * If no other instances are using this color table, a when-idle handler - * is registered to free up the color table and the colors allocated for - * it. - * - *---------------------------------------------------------------------- - */ - -static void -FreeColorTable( - ColorTable *colorPtr, /* Pointer to the color table which is no - * longer required by an instance. */ - int force) /* Force free to happen immediately. */ -{ - colorPtr->refCount--; - if (colorPtr->refCount > 0) { - return; - } - - if (force) { - if ((colorPtr->flags & DISPOSE_PENDING) != 0) { - Tcl_CancelIdleCall(DisposeColorTable, (ClientData) colorPtr); - colorPtr->flags &= ~DISPOSE_PENDING; - } - DisposeColorTable((ClientData) colorPtr); - } else if ((colorPtr->flags & DISPOSE_PENDING) == 0) { - Tcl_DoWhenIdle(DisposeColorTable, (ClientData) colorPtr); - colorPtr->flags |= DISPOSE_PENDING; - } -} - -/* - *---------------------------------------------------------------------- - * - * AllocateColors -- - * - * This function allocates the colors required by a color table, and sets - * up the fields in the color table data structure which are used in - * dithering. - * - * Results: - * None. - * - * Side effects: - * Colors are allocated from the X server. Fields in the color table data - * structure are updated. - * - *---------------------------------------------------------------------- - */ - -static void -AllocateColors( - ColorTable *colorPtr) /* Pointer to the color table requiring colors - * to be allocated. */ -{ - int i, r, g, b, rMult, mono; - int numColors, nRed, nGreen, nBlue; - double fr, fg, fb, igam; - XColor *colors; - unsigned long *pixels; - - /* - * 16-bit intensity value for i/n of full intensity. - */ -#define CFRAC(i, n) ((i) * 65535 / (n)) - - /* As for CFRAC, but apply exponent of g. */ -#define CGFRAC(i, n, g) ((int)(65535 * pow((double)(i) / (n), (g)))) - - /* - * First parse the palette specification to get the required number of - * shades of each primary. - */ - - mono = sscanf(colorPtr->id.palette, "%d/%d/%d", &nRed, &nGreen, &nBlue) - <= 1; - igam = 1.0 / colorPtr->id.gamma; - - /* - * Each time around this loop, we reduce the number of colors we're trying - * to allocate until we succeed in allocating all of the colors we need. - */ - - for (;;) { - /* - * If we are using 1 bit/pixel, we don't need to allocate any colors - * (we just use the foreground and background colors in the GC). - */ - - if (mono && (nRed <= 2)) { - colorPtr->flags |= BLACK_AND_WHITE; - return; - } - - /* - * Calculate the RGB coordinates of the colors we want to allocate and - * store them in *colors. - */ - - if ((colorPtr->visualInfo.class == DirectColor) - || (colorPtr->visualInfo.class == TrueColor)) { - - /* - * Direct/True Color: allocate shades of red, green, blue - * independently. - */ - - if (mono) { - numColors = nGreen = nBlue = nRed; - } else { - numColors = MAX(MAX(nRed, nGreen), nBlue); - } - colors = (XColor *) ckalloc(numColors * sizeof(XColor)); - - for (i = 0; i < numColors; ++i) { - if (igam == 1.0) { - colors[i].red = CFRAC(i, nRed - 1); - colors[i].green = CFRAC(i, nGreen - 1); - colors[i].blue = CFRAC(i, nBlue - 1); - } else { - colors[i].red = CGFRAC(i, nRed - 1, igam); - colors[i].green = CGFRAC(i, nGreen - 1, igam); - colors[i].blue = CGFRAC(i, nBlue - 1, igam); - } - } - } else { - /* - * PseudoColor, StaticColor, GrayScale or StaticGray visual: we - * have to allocate each color in the color cube separately. - */ - - numColors = (mono) ? nRed: (nRed * nGreen * nBlue); - colors = (XColor *) ckalloc(numColors * sizeof(XColor)); - - if (!mono) { - /* - * Color display using a PseudoColor or StaticColor visual. - */ - - i = 0; - for (r = 0; r < nRed; ++r) { - for (g = 0; g < nGreen; ++g) { - for (b = 0; b < nBlue; ++b) { - if (igam == 1.0) { - colors[i].red = CFRAC(r, nRed - 1); - colors[i].green = CFRAC(g, nGreen - 1); - colors[i].blue = CFRAC(b, nBlue - 1); - } else { - colors[i].red = CGFRAC(r, nRed - 1, igam); - colors[i].green = CGFRAC(g, nGreen - 1, igam); - colors[i].blue = CGFRAC(b, nBlue - 1, igam); - } - i++; - } - } - } - } else { - /* - * Monochrome display - allocate the shades of grey we want. - */ - - for (i = 0; i < numColors; ++i) { - if (igam == 1.0) { - r = CFRAC(i, numColors - 1); - } else { - r = CGFRAC(i, numColors - 1, igam); - } - colors[i].red = colors[i].green = colors[i].blue = r; - } - } - } - - /* - * Now try to allocate the colors we've calculated. - */ - - pixels = (unsigned long *) ckalloc(numColors * sizeof(unsigned long)); - for (i = 0; i < numColors; ++i) { - if (!XAllocColor(colorPtr->id.display, colorPtr->id.colormap, - &colors[i])) { - /* - * Can't get all the colors we want in the default colormap; - * first try freeing colors from other unused color tables. - */ - - if (!ReclaimColors(&colorPtr->id, numColors - i) - || !XAllocColor(colorPtr->id.display, - colorPtr->id.colormap, &colors[i])) { - /* - * Still can't allocate the color. - */ - - break; - } - } - pixels[i] = colors[i].pixel; - } - - /* - * If we didn't get all of the colors, reduce the resolution of the - * color cube, free the ones we got, and try again. - */ - - if (i >= numColors) { - break; - } - XFreeColors(colorPtr->id.display, colorPtr->id.colormap, pixels, i, 0); - ckfree((char *) colors); - ckfree((char *) pixels); - - if (!mono) { - if ((nRed == 2) && (nGreen == 2) && (nBlue == 2)) { - /* - * Fall back to 1-bit monochrome display. - */ - - mono = 1; - } else { - /* - * Reduce the number of shades of each primary to about 3/4 of - * the previous value. This should reduce the total number of - * colors required to about half the previous value for - * PseudoColor displays. - */ - - nRed = (nRed * 3 + 2) / 4; - nGreen = (nGreen * 3 + 2) / 4; - nBlue = (nBlue * 3 + 2) / 4; - } - } else { - /* - * Reduce the number of shades of gray to about 1/2. - */ - - nRed = nRed / 2; - } - } - - /* - * We have allocated all of the necessary colors: fill in various fields - * of the ColorTable record. - */ - - if (!mono) { - colorPtr->flags |= COLOR_WINDOW; - - /* - * The following is a hairy hack. We only want to index into the - * pixelMap on colormap displays. However, if the display is on - * Windows, then we actually want to store the index not the value - * since we will be passing the color table into the TkPutImage call. - */ - -#ifndef __WIN32__ - if ((colorPtr->visualInfo.class != DirectColor) - && (colorPtr->visualInfo.class != TrueColor)) { - colorPtr->flags |= MAP_COLORS; - } -#endif /* __WIN32__ */ - } - - colorPtr->numColors = numColors; - colorPtr->pixelMap = pixels; - - /* - * Set up quantization tables for dithering. - */ - - rMult = nGreen * nBlue; - for (i = 0; i < 256; ++i) { - r = (i * (nRed - 1) + 127) / 255; - if (mono) { - fr = (double) colors[r].red / 65535.0; - if (colorPtr->id.gamma != 1.0 ) { - fr = pow(fr, colorPtr->id.gamma); - } - colorPtr->colorQuant[0][i] = (int)(fr * 255.99); - colorPtr->redValues[i] = colors[r].pixel; - } else { - g = (i * (nGreen - 1) + 127) / 255; - b = (i * (nBlue - 1) + 127) / 255; - if ((colorPtr->visualInfo.class == DirectColor) - || (colorPtr->visualInfo.class == TrueColor)) { - colorPtr->redValues[i] = - colors[r].pixel & colorPtr->visualInfo.red_mask; - colorPtr->greenValues[i] = - colors[g].pixel & colorPtr->visualInfo.green_mask; - colorPtr->blueValues[i] = - colors[b].pixel & colorPtr->visualInfo.blue_mask; - } else { - r *= rMult; - g *= nBlue; - colorPtr->redValues[i] = r; - colorPtr->greenValues[i] = g; - colorPtr->blueValues[i] = b; - } - fr = (double) colors[r].red / 65535.0; - fg = (double) colors[g].green / 65535.0; - fb = (double) colors[b].blue / 65535.0; - if (colorPtr->id.gamma != 1.0) { - fr = pow(fr, colorPtr->id.gamma); - fg = pow(fg, colorPtr->id.gamma); - fb = pow(fb, colorPtr->id.gamma); - } - colorPtr->colorQuant[0][i] = (int)(fr * 255.99); - colorPtr->colorQuant[1][i] = (int)(fg * 255.99); - colorPtr->colorQuant[2][i] = (int)(fb * 255.99); - } - } - - ckfree((char *) colors); -} - -/* - *---------------------------------------------------------------------- - * - * DisposeColorTable -- - * - * Release a color table and its associated resources. - * - * Results: - * None. - * - * Side effects: - * The colors in the argument color table are freed, as is the color - * table structure itself. The color table is removed from the hash table - * which is used to locate color tables. - * - *---------------------------------------------------------------------- - */ - -static void -DisposeColorTable( - ClientData clientData) /* Pointer to the ColorTable whose - * colors are to be released. */ -{ - ColorTable *colorPtr = (ColorTable *) clientData; - Tcl_HashEntry *entry; - - if (colorPtr->pixelMap != NULL) { - if (colorPtr->numColors > 0) { - XFreeColors(colorPtr->id.display, colorPtr->id.colormap, - colorPtr->pixelMap, colorPtr->numColors, 0); - Tk_FreeColormap(colorPtr->id.display, colorPtr->id.colormap); - } - ckfree((char *) colorPtr->pixelMap); - } - - entry = Tcl_FindHashEntry(&imgPhotoColorHash, (char *) &colorPtr->id); - if (entry == NULL) { - Tcl_Panic("DisposeColorTable couldn't find hash entry"); - } - Tcl_DeleteHashEntry(entry); - - ckfree((char *) colorPtr); -} - -/* - *---------------------------------------------------------------------- - * - * ReclaimColors -- - * - * This function is called to try to free up colors in the colormap used - * by a color table. It looks for other color tables with the same - * colormap and with a zero live reference count, and frees their colors. - * It only does so if there is the possibility of freeing up at least - * `numColors' colors. - * - * Results: - * The return value is TRUE if any colors were freed, FALSE otherwise. - * - * Side effects: - * ColorTables which are not currently in use may lose their color - * allocations. - * - *---------------------------------------------------------------------- - */ - -static int -ReclaimColors( - ColorTableId *id, /* Pointer to information identifying - * the color table which needs more colors. */ - int numColors) /* Number of colors required. */ -{ - Tcl_HashSearch srch; - Tcl_HashEntry *entry; - ColorTable *colorPtr; - int nAvail = 0; - - /* - * First scan through the color hash table to get an upper bound on how - * many colors we might be able to free. - */ - - entry = Tcl_FirstHashEntry(&imgPhotoColorHash, &srch); - while (entry != NULL) { - colorPtr = (ColorTable *) Tcl_GetHashValue(entry); - if ((colorPtr->id.display == id->display) - && (colorPtr->id.colormap == id->colormap) - && (colorPtr->liveRefCount == 0 )&& (colorPtr->numColors != 0) - && ((colorPtr->id.palette != id->palette) - || (colorPtr->id.gamma != id->gamma))) { - /* - * We could take this guy's colors off him. - */ - - nAvail += colorPtr->numColors; - } - entry = Tcl_NextHashEntry(&srch); - } - - /* - * nAvail is an (over)estimate of the number of colors we could free. - */ - - if (nAvail < numColors) { - return 0; - } - - /* - * Scan through a second time freeing colors. - */ - - entry = Tcl_FirstHashEntry(&imgPhotoColorHash, &srch); - while ((entry != NULL) && (numColors > 0)) { - colorPtr = (ColorTable *) Tcl_GetHashValue(entry); - if ((colorPtr->id.display == id->display) - && (colorPtr->id.colormap == id->colormap) - && (colorPtr->liveRefCount == 0) && (colorPtr->numColors != 0) - && ((colorPtr->id.palette != id->palette) - || (colorPtr->id.gamma != id->gamma))) { - /* - * Free the colors that this ColorTable has. - */ - - XFreeColors(colorPtr->id.display, colorPtr->id.colormap, - colorPtr->pixelMap, colorPtr->numColors, 0); - numColors -= colorPtr->numColors; - colorPtr->numColors = 0; - ckfree((char *) colorPtr->pixelMap); - colorPtr->pixelMap = NULL; - } - - entry = Tcl_NextHashEntry(&srch); - } - return 1; /* We freed some colors. */ -} - -/* - *---------------------------------------------------------------------- - * - * DisposeInstance -- - * - * This function is called to finally free up an instance of a photo - * image which is no longer required. - * - * Results: - * None. - * - * Side effects: - * The instance data structure and the resources it references are freed. - * - *---------------------------------------------------------------------- - */ - -static void -DisposeInstance( - ClientData clientData) /* Pointer to the instance whose resources are - * to be released. */ -{ - PhotoInstance *instancePtr = (PhotoInstance *) clientData; - PhotoInstance *prevPtr; - - if (instancePtr->pixels != None) { - Tk_FreePixmap(instancePtr->display, instancePtr->pixels); - } - if (instancePtr->gc != None) { - Tk_FreeGC(instancePtr->display, instancePtr->gc); - } - if (instancePtr->imagePtr != NULL) { - XDestroyImage(instancePtr->imagePtr); - } - if (instancePtr->error != NULL) { - ckfree((char *) instancePtr->error); - } - if (instancePtr->colorTablePtr != NULL) { - FreeColorTable(instancePtr->colorTablePtr, 1); - } - - if (instancePtr->masterPtr->instancePtr == instancePtr) { - instancePtr->masterPtr->instancePtr = instancePtr->nextPtr; - } else { - for (prevPtr = instancePtr->masterPtr->instancePtr; - prevPtr->nextPtr != instancePtr; prevPtr = prevPtr->nextPtr) { - /* Empty loop body. */ - } - prevPtr->nextPtr = instancePtr->nextPtr; - } - Tk_FreeColormap(instancePtr->display, instancePtr->colormap); - ckfree((char *) instancePtr); -} - -/* - *---------------------------------------------------------------------- - * * MatchFileFormat -- * * This function is called to find a photo image file format handler @@ -4042,7 +2398,7 @@ static int MatchFileFormat( Tcl_Interp *interp, /* Interpreter to use for reporting errors. */ Tcl_Channel chan, /* The image file, open for reading. */ - char *fileName, /* The name of the image file. */ + const char *fileName, /* The name of the image file. */ Tcl_Obj *formatObj, /* User-specified format string, or NULL. */ Tk_PhotoImageFormat **imageFormatPtr, /* A pointer to the photo image format record @@ -4054,9 +2410,9 @@ MatchFileFormat( { int matched = 0, useoldformat = 0; Tk_PhotoImageFormat *formatPtr; - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - char *formatString = NULL; + const char *formatString = NULL; if (formatObj) { formatString = Tcl_GetString(formatObj); @@ -4076,15 +2432,18 @@ MatchFileFormat( } matched = 1; if (formatPtr->fileMatchProc == NULL) { - Tcl_AppendResult(interp, "-file option isn't supported for ", - formatString, " images", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "-file option isn't supported for %s images", + formatString)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", + "NOT_FILE_FORMAT", NULL); return TCL_ERROR; } } if (formatPtr->fileMatchProc != NULL) { (void) Tcl_Seek(chan, Tcl_LongAsWide(0L), SEEK_SET); - if ((*formatPtr->fileMatchProc)(chan, fileName, formatObj, + if (formatPtr->fileMatchProc(chan, fileName, formatObj, widthPtr, heightPtr, interp)) { if (*widthPtr < 1) { *widthPtr = 1; @@ -4107,14 +2466,17 @@ MatchFileFormat( } matched = 1; if (formatPtr->fileMatchProc == NULL) { - Tcl_AppendResult(interp, "-file option isn't supported", - " for ", formatString, " images", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "-file option isn't supported for %s images", + formatString)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", + "NOT_FILE_FORMAT", NULL); return TCL_ERROR; } } if (formatPtr->fileMatchProc != NULL) { (void) Tcl_Seek(chan, Tcl_LongAsWide(0L), SEEK_SET); - if ((*formatPtr->fileMatchProc)(chan, fileName, (Tcl_Obj *) + if (formatPtr->fileMatchProc(chan, fileName, (Tcl_Obj *) formatString, widthPtr, heightPtr, interp)) { if (*widthPtr < 1) { *widthPtr = 1; @@ -4130,12 +2492,17 @@ MatchFileFormat( if (formatPtr == NULL) { if ((formatObj != NULL) && !matched) { - Tcl_AppendResult(interp, "image file format \"", formatString, - "\" is not supported", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "image file format \"%s\" is not supported", + formatString)); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "PHOTO_FORMAT", + formatString, NULL); } else { - Tcl_AppendResult(interp, - "couldn't recognize data in image file \"", fileName, "\"", - NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "couldn't recognize data in image file \"%s\"", + fileName)); + Tcl_SetErrorCode(interp, "TK", "PHOTO", "IMAGE", + "UNRECOGNIZED_DATA", NULL); } return TCL_ERROR; } @@ -4183,9 +2550,9 @@ MatchStringFormat( { int matched = 0, useoldformat = 0; Tk_PhotoImageFormat *formatPtr; - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - char *formatString = NULL; + const char *formatString = NULL; if (formatObj) { formatString = Tcl_GetString(formatObj); @@ -4205,15 +2572,18 @@ MatchStringFormat( } matched = 1; if (formatPtr->stringMatchProc == NULL) { - Tcl_AppendResult(interp, "-data option isn't supported for ", - formatString, " images", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "-data option isn't supported for %s images", + formatString)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", + "NOT_DATA_FORMAT", NULL); return TCL_ERROR; } } if ((formatPtr->stringMatchProc != NULL) && (formatPtr->stringReadProc != NULL) - && (*formatPtr->stringMatchProc)(data, formatObj, - widthPtr, heightPtr, interp)) { + && formatPtr->stringMatchProc(data, formatObj, + widthPtr, heightPtr, interp)) { break; } } @@ -4229,14 +2599,17 @@ MatchStringFormat( } matched = 1; if (formatPtr->stringMatchProc == NULL) { - Tcl_AppendResult(interp, "-data option isn't supported", - " for ", formatString, " images", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "-data option isn't supported for %s images", + formatString)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", + "NOT_DATA_FORMAT", NULL); return TCL_ERROR; } } if ((formatPtr->stringMatchProc != NULL) && (formatPtr->stringReadProc != NULL) - && (*formatPtr->stringMatchProc)( + && formatPtr->stringMatchProc( (Tcl_Obj *) Tcl_GetString(data), (Tcl_Obj *) formatString, widthPtr, heightPtr, interp)) { @@ -4246,10 +2619,15 @@ MatchStringFormat( } if (formatPtr == NULL) { if ((formatObj != NULL) && !matched) { - Tcl_AppendResult(interp, "image format \"", formatString, - "\" is not supported", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "image format \"%s\" is not supported", formatString)); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "PHOTO_FORMAT", + formatString, NULL); } else { - Tcl_AppendResult(interp, "couldn't recognize image data", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "couldn't recognize image data", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "PHOTO", + "UNRECOGNIZED_DATA", NULL); } return TCL_ERROR; } @@ -4283,16 +2661,16 @@ Tk_PhotoHandle Tk_FindPhoto( Tcl_Interp *interp, /* Interpreter (application) in which image * exists. */ - CONST char *imageName) /* Name of the desired photo image. */ + const char *imageName) /* Name of the desired photo image. */ { - ClientData clientData; - Tk_ImageType *typePtr; + const Tk_ImageType *typePtr; + ClientData clientData = + Tk_GetImageMasterData(interp, imageName, &typePtr); - clientData = Tk_GetImageMasterData(interp, imageName, &typePtr); if ((typePtr == NULL) || (typePtr->name != tkPhotoImageType.name)) { return NULL; } - return (Tk_PhotoHandle) clientData; + return clientData; } /* @@ -4330,14 +2708,21 @@ Tk_PhotoPutBlock( int compRule) /* Compositing rule to use when processing * transparent pixels. */ { - register PhotoMaster *masterPtr; + register PhotoMaster *masterPtr = (PhotoMaster *) handle; int xEnd, yEnd, greenOffset, blueOffset, alphaOffset; int wLeft, hLeft, wCopy, hCopy, pitch; unsigned char *srcPtr, *srcLinePtr, *destPtr, *destLinePtr; int sourceIsSimplePhoto = compRule & SOURCE_IS_SIMPLE_ALPHA_PHOTO; XRectangle rect; - masterPtr = (PhotoMaster *) handle; + /* + * Zero-sized blocks never cause any changes. [Bug 3078902] + */ + + if (blockPtr->height == 0 || blockPtr->width == 0) { + return TCL_OK; + } + compRule &= ~SOURCE_IS_SIMPLE_ALPHA_PHOTO; if ((masterPtr->userWidth != 0) && ((x + width) > masterPtr->userWidth)) { @@ -4359,8 +2744,9 @@ Tk_PhotoPutBlock( if (ImgPhotoSetSize(masterPtr, MAX(xEnd, masterPtr->width), MAX(yEnd, masterPtr->height)) == TCL_ERROR) { if (interp != NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, TK_PHOTO_ALLOC_FAILURE_MESSAGE, NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); + Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); } return TCL_ERROR; } @@ -4419,7 +2805,7 @@ Tk_PhotoPutBlock( && (blockPtr->pitch == pitch))) && (compRule == TK_PHOTO_COMPOSITE_SET)) { memmove(destLinePtr, blockPtr->pixelPtr + blockPtr->offset[0], - (size_t) (height * width * 4)); + ((size_t)height * width * 4)); /* * We know there's an alpha offset and we're setting the data, so skip @@ -4451,7 +2837,7 @@ Tk_PhotoPutBlock( && (blueOffset == 2) && (alphaOffset == 3) && (width <= blockPtr->width) && compRuleSet) { - memcpy(destLinePtr, srcLinePtr, (size_t) (width * 4)); + memcpy(destLinePtr, srcLinePtr, ((size_t)width * 4)); srcLinePtr += blockPtr->pitch; destLinePtr += pitch; continue; @@ -4624,27 +3010,32 @@ Tk_PhotoPutBlock( * Check if display code needs alpha blending... */ - if (!sourceIsSimplePhoto && (width == 1) && (height == 1)) { + if (!sourceIsSimplePhoto && (height == 1)) { /* - * Optimize the single pixel case if we can. This speeds up code that - * builds up large simple-alpha images by single pixels. We don't - * negate COMPLEX_ALPHA in this case. [Bug 1409140] + * Optimize the single span case if we can. This speeds up code that + * builds up large simple-alpha images by scan-lines or individual + * pixels. We don't negate COMPLEX_ALPHA in this case. [Bug 1409140] + * [Patch 1539990] */ if (!(masterPtr->flags & COMPLEX_ALPHA)) { - unsigned char newAlpha; + register int x1; - destLinePtr = masterPtr->pix32 + (y * masterPtr->width + x) * 4; - newAlpha = destLinePtr[3]; + for (x1=x ; x1<x+width ; x1++) { + register unsigned char newAlpha; - if (newAlpha && newAlpha != 255) { - masterPtr->flags |= COMPLEX_ALPHA; + destLinePtr = masterPtr->pix32 + (y*masterPtr->width + x1)*4; + newAlpha = destLinePtr[3]; + if (newAlpha && newAlpha != 255) { + masterPtr->flags |= COMPLEX_ALPHA; + break; + } } } } else if ((alphaOffset != 0) || (masterPtr->flags & COMPLEX_ALPHA)) { /* * Check for partial transparency if alpha pixels are specified, or - * rescan if we already knew such pixels existed. To restrict this + * rescan if we already knew such pixels existed. To restrict this * Toggle to only checking the changed pixels requires knowing where * the alpha pixels are. */ @@ -4712,6 +3103,14 @@ Tk_PhotoPutZoomedBlock( int pitch, xRepeat, yRepeat, blockXSkip, blockYSkip, sourceIsSimplePhoto; XRectangle rect; + /* + * Zero-sized blocks never cause any changes. [Bug 3078902] + */ + + if (blockPtr->height == 0 || blockPtr->width == 0) { + return TCL_OK; + } + if (zoomX==1 && zoomY==1 && subsampleX==1 && subsampleY==1) { return Tk_PhotoPutBlock(interp, handle, blockPtr, x, y, width, height, compRule); @@ -4738,11 +3137,13 @@ Tk_PhotoPutZoomedBlock( yEnd = y + height; if ((xEnd > masterPtr->width) || (yEnd > masterPtr->height)) { int sameSrc = (blockPtr->pixelPtr == masterPtr->pix32); + if (ImgPhotoSetSize(masterPtr, MAX(xEnd, masterPtr->width), MAX(yEnd, masterPtr->height)) == TCL_ERROR) { if (interp != NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, TK_PHOTO_ALLOC_FAILURE_MESSAGE, NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); + Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); } return TCL_ERROR; } @@ -4753,7 +3154,7 @@ Tk_PhotoPutZoomedBlock( } if ((y < masterPtr->ditherY) || ((y == masterPtr->ditherY) - && (x < masterPtr->ditherX))) { + && (x < masterPtr->ditherX))) { /* * The dithering isn't correct past the start of this block. */ @@ -4927,7 +3328,7 @@ Tk_PhotoPutZoomedBlock( if (!sourceIsSimplePhoto && (width == 1) && (height == 1)) { /* * Optimize the single pixel case if we can. This speeds up code that - * builds up large simple-alpha images by single pixels. We don't + * builds up large simple-alpha images by single pixels. We don't * negate COMPLEX_ALPHA in this case. [Bug 1409140] */ if (!(masterPtr->flags & COMPLEX_ALPHA)) { @@ -4943,7 +3344,7 @@ Tk_PhotoPutZoomedBlock( } else if ((alphaOffset != 0) || (masterPtr->flags & COMPLEX_ALPHA)) { /* * Check for partial transparency if alpha pixels are specified, or - * rescan if we already knew such pixels existed. To restrict this + * rescan if we already knew such pixels existed. To restrict this * Toggle to only checking the changed pixels requires knowing where * the alpha pixels are. */ @@ -5001,7 +3402,7 @@ Tk_DitherPhoto( for (instancePtr = masterPtr->instancePtr; instancePtr != NULL; instancePtr = instancePtr->nextPtr) { - DitherInstance(instancePtr, x, y, width, height); + TkImgDitherInstance(instancePtr, x, y, width, height); } /* @@ -5047,345 +3448,6 @@ Tk_DitherPhoto( /* *---------------------------------------------------------------------- * - * DitherInstance -- - * - * This function is called to update an area of an instance's pixmap by - * dithering the corresponding area of the master. - * - * Results: - * None. - * - * Side effects: - * The instance's pixmap gets updated. - * - *---------------------------------------------------------------------- - */ - -static void -DitherInstance( - PhotoInstance *instancePtr, /* The instance to be updated. */ - int xStart, int yStart, /* Coordinates of the top-left pixel in the - * block to be dithered. */ - int width, int height) /* Dimensions of the block to be dithered. */ -{ - PhotoMaster *masterPtr = instancePtr->masterPtr; - ColorTable *colorPtr = instancePtr->colorTablePtr; - XImage *imagePtr; - int nLines, bigEndian, i, c, x, y, xEnd, doDithering = 1; - int bitsPerPixel, bytesPerLine, lineLength; - unsigned char *srcLinePtr; - schar *errLinePtr; - pixel firstBit, word, mask; - - /* - * Turn dithering off in certain cases where it is not needed (TrueColor, - * DirectColor with many colors). - */ - - if ((colorPtr->visualInfo.class == DirectColor) - || (colorPtr->visualInfo.class == TrueColor)) { - int nRed, nGreen, nBlue, result; - - result = sscanf(colorPtr->id.palette, "%d/%d/%d", &nRed, - &nGreen, &nBlue); - if ((nRed >= 256) - && ((result == 1) || ((nGreen >= 256) && (nBlue >= 256)))) { - doDithering = 0; - } - } - - /* - * First work out how many lines to do at a time, then how many bytes - * we'll need for pixel storage, and allocate it. - */ - - nLines = (MAX_PIXELS + width - 1) / width; - if (nLines < 1) { - nLines = 1; - } - if (nLines > height ) { - nLines = height; - } - - imagePtr = instancePtr->imagePtr; - if (imagePtr == NULL) { - return; /* We must be really tight on memory. */ - } - bitsPerPixel = imagePtr->bits_per_pixel; - bytesPerLine = ((bitsPerPixel * width + 31) >> 3) & ~3; - imagePtr->width = width; - imagePtr->height = nLines; - imagePtr->bytes_per_line = bytesPerLine; - imagePtr->data = (char *) - ckalloc((unsigned) (imagePtr->bytes_per_line * nLines)); - bigEndian = imagePtr->bitmap_bit_order == MSBFirst; - firstBit = bigEndian? (1 << (imagePtr->bitmap_unit - 1)): 1; - - lineLength = masterPtr->width * 3; - srcLinePtr = masterPtr->pix32 + (yStart * masterPtr->width + xStart) * 4; - errLinePtr = instancePtr->error + yStart * lineLength + xStart * 3; - xEnd = xStart + width; - - /* - * Loop over the image, doing at most nLines lines before updating the - * screen image. - */ - - for (; height > 0; height -= nLines) { - unsigned char *dstLinePtr = (unsigned char *) imagePtr->data; - int yEnd; - - if (nLines > height) { - nLines = height; - } - yEnd = yStart + nLines; - for (y = yStart; y < yEnd; ++y) { - unsigned char *srcPtr = srcLinePtr; - schar *errPtr = errLinePtr; - unsigned char *destBytePtr = dstLinePtr; - pixel *destLongPtr = (pixel *) dstLinePtr; - - if (colorPtr->flags & COLOR_WINDOW) { - /* - * Color window. We dither the three components independently, - * using Floyd-Steinberg dithering, which propagates errors - * from the quantization of pixels to the pixels below and to - * the right. - */ - - for (x = xStart; x < xEnd; ++x) { - int col[3]; - - if (doDithering) { - for (i = 0; i < 3; ++i) { - /* - * Compute the error propagated into this pixel - * for this component. If e[x,y] is the array of - * quantization error values, we compute - * 7/16 * e[x-1,y] + 1/16 * e[x-1,y-1] - * + 5/16 * e[x,y-1] + 3/16 * e[x+1,y-1] - * and round it to an integer. - * - * The expression ((c + 2056) >> 4) - 128 computes - * round(c / 16), and works correctly on machines - * without a sign-extending right shift. - */ - - c = (x > 0) ? errPtr[-3] * 7: 0; - if (y > 0) { - if (x > 0) { - c += errPtr[-lineLength-3]; - } - c += errPtr[-lineLength] * 5; - if ((x + 1) < masterPtr->width) { - c += errPtr[-lineLength+3] * 3; - } - } - - /* - * Add the propagated error to the value of this - * component, quantize it, and store the - * quantization error. - */ - - c = ((c + 2056) >> 4) - 128 + *srcPtr++; - if (c < 0) { - c = 0; - } else if (c > 255) { - c = 255; - } - col[i] = colorPtr->colorQuant[i][c]; - *errPtr++ = c - col[i]; - } - } else { - /* - * Output is virtually continuous in this case, so - * don't bother dithering. - */ - - col[0] = *srcPtr++; - col[1] = *srcPtr++; - col[2] = *srcPtr++; - } - srcPtr++; - - /* - * Translate the quantized component values into an X - * pixel value, and store it in the image. - */ - - i = colorPtr->redValues[col[0]] - + colorPtr->greenValues[col[1]] - + colorPtr->blueValues[col[2]]; - if (colorPtr->flags & MAP_COLORS) { - i = colorPtr->pixelMap[i]; - } - switch (bitsPerPixel) { - case NBBY: - *destBytePtr++ = i; - break; -#ifndef __WIN32__ - /* - * This case is not valid for Windows because the - * image format is different from the pixel format in - * Win32. Eventually we need to fix the image code in - * Tk to use the Windows native image ordering. This - * would speed up the image code for all of the common - * sizes. - */ - - case NBBY * sizeof(pixel): - *destLongPtr++ = i; - break; -#endif - default: - XPutPixel(imagePtr, x - xStart, y - yStart, - (unsigned) i); - } - } - - } else if (bitsPerPixel > 1) { - /* - * Multibit monochrome window. The operation here is similar - * to the color window case above, except that there is only - * one component. If the master image is in color, use the - * luminance computed as - * 0.344 * red + 0.5 * green + 0.156 * blue. - */ - - for (x = xStart; x < xEnd; ++x) { - c = (x > 0) ? errPtr[-1] * 7: 0; - if (y > 0) { - if (x > 0) { - c += errPtr[-lineLength-1]; - } - c += errPtr[-lineLength] * 5; - if (x + 1 < masterPtr->width) { - c += errPtr[-lineLength+1] * 3; - } - } - c = ((c + 2056) >> 4) - 128; - - if ((masterPtr->flags & COLOR_IMAGE) == 0) { - c += srcPtr[0]; - } else { - c += (unsigned)(srcPtr[0] * 11 + srcPtr[1] * 16 - + srcPtr[2] * 5 + 16) >> 5; - } - srcPtr += 4; - - if (c < 0) { - c = 0; - } else if (c > 255) { - c = 255; - } - i = colorPtr->colorQuant[0][c]; - *errPtr++ = c - i; - i = colorPtr->redValues[i]; - switch (bitsPerPixel) { - case NBBY: - *destBytePtr++ = i; - break; -#ifndef __WIN32__ - /* - * This case is not valid for Windows because the - * image format is different from the pixel format in - * Win32. Eventually we need to fix the image code in - * Tk to use the Windows native image ordering. This - * would speed up the image code for all of the common - * sizes. - */ - - case NBBY * sizeof(pixel): - *destLongPtr++ = i; - break; -#endif - default: - XPutPixel(imagePtr, x - xStart, y - yStart, - (unsigned) i); - } - } - } else { - /* - * 1-bit monochrome window. This is similar to the multibit - * monochrome case above, except that the quantization is - * simpler (we only have black = 0 and white = 255), and we - * produce an XY-Bitmap. - */ - - word = 0; - mask = firstBit; - for (x = xStart; x < xEnd; ++x) { - /* - * If we have accumulated a whole word, store it in the - * image and start a new word. - */ - - if (mask == 0) { - *destLongPtr++ = word; - mask = firstBit; - word = 0; - } - - c = (x > 0) ? errPtr[-1] * 7: 0; - if (y > 0) { - if (x > 0) { - c += errPtr[-lineLength-1]; - } - c += errPtr[-lineLength] * 5; - if (x + 1 < masterPtr->width) { - c += errPtr[-lineLength+1] * 3; - } - } - c = ((c + 2056) >> 4) - 128; - - if ((masterPtr->flags & COLOR_IMAGE) == 0) { - c += srcPtr[0]; - } else { - c += (unsigned)(srcPtr[0] * 11 + srcPtr[1] * 16 - + srcPtr[2] * 5 + 16) >> 5; - } - srcPtr += 4; - - if (c < 0) { - c = 0; - } else if (c > 255) { - c = 255; - } - if (c >= 128) { - word |= mask; - *errPtr++ = c - 255; - } else { - *errPtr++ = c; - } - mask = bigEndian? (mask >> 1): (mask << 1); - } - *destLongPtr = word; - } - srcLinePtr += masterPtr->width * 4; - errLinePtr += lineLength; - dstLinePtr += bytesPerLine; - } - - /* - * Update the pixmap for this instance with the block of pixels that - * we have just computed. - */ - - TkPutImage(colorPtr->pixelMap, colorPtr->numColors, - instancePtr->display, instancePtr->pixels, - instancePtr->gc, imagePtr, 0, 0, xStart, yStart, - (unsigned) width, (unsigned) nLines); - yStart = yEnd; - } - - ckfree(imagePtr->data); - imagePtr->data = NULL; -} - -/* - *---------------------------------------------------------------------- - * * Tk_PhotoBlank -- * * This function is called to clear an entire photo image. @@ -5425,14 +3487,10 @@ Tk_PhotoBlank( */ memset(masterPtr->pix32, 0, - (size_t) (masterPtr->width * masterPtr->height * 4)); + ((size_t)masterPtr->width * masterPtr->height * 4)); for (instancePtr = masterPtr->instancePtr; instancePtr != NULL; instancePtr = instancePtr->nextPtr) { - if (instancePtr->error) { - memset(instancePtr->error, 0, - (size_t) (masterPtr->width * masterPtr->height - * 3 * sizeof(schar))); - } + TkImgResetDither(instancePtr); } /* @@ -5482,8 +3540,9 @@ Tk_PhotoExpand( if (ImgPhotoSetSize(masterPtr, MAX(width, masterPtr->width), MAX(height, masterPtr->height)) == TCL_ERROR) { if (interp != NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, TK_PHOTO_ALLOC_FAILURE_MESSAGE, NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); + Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); } return TCL_ERROR; } @@ -5556,8 +3615,9 @@ Tk_PhotoSetSize( if (ImgPhotoSetSize(masterPtr, ((width > 0) ? width: masterPtr->width), ((height > 0) ? height: masterPtr->height)) == TCL_ERROR) { if (interp != NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, TK_PHOTO_ALLOC_FAILURE_MESSAGE, NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + TK_PHOTO_ALLOC_FAILURE_MESSAGE, -1)); + Tcl_SetErrorCode(interp, "TK", "MALLOC", NULL); } return TCL_ERROR; } @@ -5672,8 +3732,14 @@ ImgGetPhoto( if ((greenOffset||blueOffset) && !(optPtr->options & OPT_GRAYSCALE)) { newPixelSize += 2; } - data = ckalloc((unsigned int) (newPixelSize * - blockPtr->width * blockPtr->height)); + + if (blockPtr->height > (int)((UINT_MAX/newPixelSize)/blockPtr->width)) { + return NULL; + } + data = attemptckalloc(newPixelSize*blockPtr->width*blockPtr->height); + if (data == NULL) { + return NULL; + } srcPtr = blockPtr->pixelPtr + blockPtr->offset[0]; destPtr = (unsigned char *) data; if (!greenOffset && !blueOffset) { @@ -5768,13 +3834,13 @@ ImgGetPhoto( blockPtr->pixelSize = newPixelSize; blockPtr->pitch = newPixelSize * blockPtr->width; blockPtr->offset[0] = 0; - if (newPixelSize>2) { - blockPtr->offset[1]= 1; - blockPtr->offset[2]= 2; + if (newPixelSize > 2) { + blockPtr->offset[1] = 1; + blockPtr->offset[2] = 2; blockPtr->offset[3]= 3; } else { - blockPtr->offset[1]= 0; - blockPtr->offset[2]= 0; + blockPtr->offset[1] = 0; + blockPtr->offset[2] = 0; blockPtr->offset[3]= 1; } return data; @@ -5805,33 +3871,31 @@ ImgStringWrite( Tcl_Obj *formatString, Tk_PhotoImageBlock *blockPtr) { - int row, col; - char *line, *linePtr; - unsigned char *pixelPtr; int greenOffset, blueOffset; - Tcl_DString data; + Tcl_Obj *data; greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; - Tcl_DStringInit(&data); + data = Tcl_NewObj(); if ((blockPtr->width > 0) && (blockPtr->height > 0)) { - line = (char *) ckalloc((unsigned int) ((8 * blockPtr->width) + 2)); + int row, col; + for (row=0; row<blockPtr->height; row++) { - pixelPtr = blockPtr->pixelPtr + blockPtr->offset[0] + - row * blockPtr->pitch; - linePtr = line; + Tcl_Obj *line = Tcl_NewObj(); + unsigned char *pixelPtr = blockPtr->pixelPtr + blockPtr->offset[0] + + row * blockPtr->pitch; + for (col=0; col<blockPtr->width; col++) { - sprintf(linePtr, " #%02x%02x%02x", *pixelPtr, + Tcl_AppendPrintfToObj(line, "%s#%02x%02x%02x", + col ? " " : "", *pixelPtr, pixelPtr[greenOffset], pixelPtr[blueOffset]); pixelPtr += blockPtr->pixelSize; - linePtr += 8; } - Tcl_DStringAppendElement(&data, line+1); + Tcl_ListObjAppendElement(NULL, data, line); } - ckfree (line); } - Tcl_DStringResult(interp, &data); + Tcl_SetObjResult(interp, data); return TCL_OK; } @@ -5878,150 +3942,6 @@ Tk_PhotoGetImage( } /* - *---------------------------------------------------------------------- - * - * PhotoOptionFind -- - * - * Finds a specific Photo option. - * - * Results: - * None. - * - * Side effects: - * After commands are removed. - * - *---------------------------------------------------------------------- - */ - -typedef struct OptionAssocData { - struct OptionAssocData *nextPtr; - /* Pointer to next OptionAssocData. */ - Tcl_ObjCmdProc *command; /* Command associated with this option. */ - char name[1]; /* Name of option (remaining chars) */ -} OptionAssocData; - -static Tcl_ObjCmdProc * -PhotoOptionFind( - Tcl_Interp *interp, /* Interpreter that is being deleted. */ - Tcl_Obj *obj) /* Name of option to be found. */ -{ - int length; - char *name = Tcl_GetStringFromObj(obj, &length); - char *prevname = NULL; - Tcl_ObjCmdProc *proc = NULL; - OptionAssocData *list = (OptionAssocData *) Tcl_GetAssocData(interp, - "photoOption", NULL); - - while (list != NULL) { - if (strncmp(name, list->name, (unsigned) length) == 0) { - if (proc != NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "ambiguous option \"", name, - "\": must be ", prevname, NULL); - while (list->nextPtr != NULL) { - Tcl_AppendResult(interp, prevname, ", ",NULL); - list = list->nextPtr; - prevname = list->name; - } - Tcl_AppendResult(interp, ", or", prevname, NULL); - return NULL; - } - proc = list->command; - prevname = list->name; - } - list = list->nextPtr; - } - if (proc != NULL) { - Tcl_ResetResult(interp); - } - return proc; -} - -/* - *---------------------------------------------------------------------- - * - * PhotoOptionCleanupProc -- - * - * This function is invoked whenever an interpreter is deleted to cleanup - * the AssocData for "photoVisitor". - * - * Results: - * None. - * - * Side effects: - * Photo Visitor options are removed. - * - *---------------------------------------------------------------------- - */ - -static void -PhotoOptionCleanupProc( - ClientData clientData, /* Points to "photoVisitor" AssocData for the - * interpreter. */ - Tcl_Interp *interp) /* Interpreter that is being deleted. */ -{ - OptionAssocData *list = (OptionAssocData *) clientData; - - while (list != NULL) { - register OptionAssocData *ptr; - - list = (ptr = list)->nextPtr; - ckfree((char *) ptr); - } -} - -/* - *-------------------------------------------------------------- - * - * Tk_CreatePhotoOption -- - * - * This function may be invoked to add a new kind of photo option to the - * core photo command supported by Tk. - * - * Results: - * None. - * - * Side effects: - * From now on, the new option will be useable by the photo command. - * - *-------------------------------------------------------------- - */ - -MODULE_SCOPE void -Tk_CreatePhotoOption( - Tcl_Interp *interp, /* Interpreter. */ - CONST char *name, /* Option name. */ - Tcl_ObjCmdProc *proc) /* Function to execute command. */ -{ - OptionAssocData *typePtr2, *prevPtr, *ptr; - OptionAssocData *list = (OptionAssocData *) - Tcl_GetAssocData(interp, "photoOption", NULL); - - /* - * If there's already a photo option with the given name, remove it. - */ - - for (typePtr2 = list, prevPtr = NULL; typePtr2 != NULL; - prevPtr = typePtr2, typePtr2 = typePtr2->nextPtr) { - if (strcmp(typePtr2->name, name) == 0) { - if (prevPtr == NULL) { - list = typePtr2->nextPtr; - } else { - prevPtr->nextPtr = typePtr2->nextPtr; - } - ckfree((char *) typePtr2); - break; - } - } - ptr = (OptionAssocData *) ckalloc(sizeof(OptionAssocData) + strlen(name)); - strcpy(&(ptr->name[0]), name); - ptr->command = proc; - ptr->nextPtr = list; - Tcl_SetAssocData(interp, "photoOption", PhotoOptionCleanupProc, - (ClientData) ptr); -} - -/* *-------------------------------------------------------------- * * TkPostscriptPhoto -- @@ -6050,7 +3970,7 @@ ImgPhotoPostscript( { Tk_PhotoImageBlock block; - Tk_PhotoGetImage((Tk_PhotoHandle) clientData, &block); + Tk_PhotoGetImage(clientData, &block); block.pixelPtr += y * block.pitch + x * block.pixelSize; return Tk_PostscriptPhoto(interp, &block, psInfo, width, height); |