diff options
-rw-r--r-- | generic/tk.h | 10 | ||||
-rw-r--r-- | generic/tkImgGIF.c | 156 | ||||
-rw-r--r-- | generic/tkImgPNG.c | 25 | ||||
-rw-r--r-- | generic/tkImgPhoto.c | 109 | ||||
-rw-r--r-- | generic/tkImgSVGnano.c | 1547 | ||||
-rw-r--r-- | generic/tkInt.h | 2 | ||||
-rw-r--r-- | generic/tkWindow.c | 2 | ||||
-rw-r--r-- | tests/imgPhoto.test | 228 | ||||
-rw-r--r-- | tests/imgSVGnano.test | 116 |
9 files changed, 418 insertions, 1777 deletions
diff --git a/generic/tk.h b/generic/tk.h index e8bcae4..910c22a 100644 --- a/generic/tk.h +++ b/generic/tk.h @@ -1445,22 +1445,20 @@ typedef struct Tk_PhotoImageFormatVersion3 Tk_PhotoImageFormatVersion3; typedef int (Tk_ImageFileMatchProcVersion3) (Tcl_Interp *interp, Tcl_Channel chan, const char *fileName, Tcl_Obj *format, Tcl_Obj *metadataIn, int *widthPtr, int *heightPtr, - Tcl_Obj *metadataOut, int *closeChannelPtr, - Tcl_DString *driverInternalPtr); + Tcl_Obj *metadataOut); typedef int (Tk_ImageStringMatchProcVersion3) (Tcl_Interp *interp, Tcl_Obj *dataObj, Tcl_Obj *format, Tcl_Obj *metadataIn, int *widthPtr, - int *heightPtr, Tcl_Obj *metadataOut, Tcl_DString *driverInternalPtr); + int *heightPtr, Tcl_Obj *metadataOut); typedef int (Tk_ImageFileReadProcVersion3) (Tcl_Interp *interp, Tcl_Channel chan, const char *fileName, Tcl_Obj *format, Tcl_Obj *metadataIn, Tk_PhotoHandle imageHandle, int destX, int destY, int width, int height, int srcX, int srcY, - Tcl_Obj *metadataOut, Tcl_DString *driverInternalPtr); + Tcl_Obj *metadataOut); typedef int (Tk_ImageStringReadProcVersion3) (Tcl_Interp *interp, Tcl_Obj *dataObj, Tcl_Obj *format, Tcl_Obj *metadataIn, Tk_PhotoHandle imageHandle, int destX, int destY, int width, int height, - int srcX, int srcY, Tcl_Obj *metadataOut, - Tcl_DString *driverInternalPtr); + int srcX, int srcY, Tcl_Obj *metadataOut); typedef int (Tk_ImageFileWriteProcVersion3) (Tcl_Interp *interp, const char *fileName, Tcl_Obj *format, Tcl_Obj *metadataIn, Tk_PhotoImageBlock *blockPtr); diff --git a/generic/tkImgGIF.c b/generic/tkImgGIF.c index 06a44ac..7dc78d8 100644 --- a/generic/tkImgGIF.c +++ b/generic/tkImgGIF.c @@ -121,25 +121,21 @@ typedef size_t (WriteBytesFunc) (ClientData clientData, const char *bytes, static int FileMatchGIF(Tcl_Interp *interp, Tcl_Channel chan, const char *fileName, Tcl_Obj *format, Tcl_Obj *metadataInObj, int *widthPtr, - int *heightPtr, Tcl_Obj *metadataOutObj, - int *closeChannelPtr, Tcl_DString *driverInternal); + int *heightPtr, Tcl_Obj *metadataOutObj); static int FileReadGIF(Tcl_Interp *interp, Tcl_Channel chan, const char *fileName, Tcl_Obj *format, Tcl_Obj *metadataInObj, Tk_PhotoHandle imageHandle, int destX, int destY, int width, int height, - int srcX, int srcY, Tcl_Obj *metadataOutObj, - Tcl_DString *driverInternal); + int srcX, int srcY, Tcl_Obj *metadataOutObj); static int StringMatchGIF(Tcl_Interp *interp, Tcl_Obj *dataObj, Tcl_Obj *format, Tcl_Obj *metadataInObj, int *widthPtr, int *heightPtr, - Tcl_Obj *metadataOutObj, - Tcl_DString *driverInternal); + Tcl_Obj *metadataOutObj); static int StringReadGIF(Tcl_Interp *interp, Tcl_Obj *dataObj, Tcl_Obj *format, Tcl_Obj *metadataInObj, Tk_PhotoHandle imageHandle, int destX, int destY, int width, int height, - int srcX, int srcY, Tcl_Obj *metadataOutObj, - Tcl_DString *driverInternal); + int srcX, int srcY, Tcl_Obj *metadataOutObj); static int FileWriteGIF(Tcl_Interp *interp, const char *filename, Tcl_Obj *format, Tcl_Obj *metadataInObj, Tk_PhotoImageBlock *blockPtr); @@ -368,9 +364,7 @@ FileMatchGIF( int *widthPtr, int *heightPtr, /* The dimensions of the image are returned * here if the file is a valid raw GIF file. */ - Tcl_Obj *metadataOutObj, /* metadata return dict, may be NULL */ - int *closeChannelPtr, /* Return if the channel may be closed */ - Tcl_DString *driverInternal)/* memory passed to FileReadGIF */ + Tcl_Obj *metadataOutObj) /* metadata return dict, may be NULL */ { GIFImageConfig gifConf; (void)fileName; @@ -414,8 +408,7 @@ FileReadGIF( * written to. */ int srcX, int srcY, /* Coordinates of top-left pixel to be used in * image being read. */ - Tcl_Obj *metadataOutObj, /* metadata return dict, may be NULL */ - Tcl_DString *driverInternal)/* memory passed from FileMatchGIF */ + Tcl_Obj *metadataOutObj) /* metadata return dict, may be NULL */ { int fileWidth, fileHeight, imageWidth, imageHeight; unsigned int nBytes; @@ -859,8 +852,7 @@ StringMatchGIF( Tcl_Obj *metadataInObj, /* metadata input, may be NULL */ int *widthPtr, /* where to put the string width */ int *heightPtr, /* where to put the string height */ - Tcl_Obj *metadataOutObj, /* metadata return dict, may be NULL */ - Tcl_DString *driverInternal)/* memory to pass to StringReadGIF */ + Tcl_Obj *metadataOutObj) /* metadata return dict, may be NULL */ { unsigned char *data, header[10]; TkSizeT got, length; @@ -933,8 +925,7 @@ StringReadGIF( int destX, int destY, /* The rectangular region of the */ int width, int height, /* image to copy */ int srcX, int srcY, - Tcl_Obj *metadataOutObj, /* metadata return dict, may be NULL */ - Tcl_DString *driverInternal)/* memory passed from StringReadGIF */ + Tcl_Obj *metadataOutObj) /* metadata return dict, may be NULL */ { MFile handle, *hdlPtr = &handle; TkSizeT length; @@ -964,7 +955,7 @@ StringReadGIF( return FileReadGIF(interp, (Tcl_Channel) hdlPtr, xferFormat, format, metadataInObj, imageHandle, destX, destY, width, height, srcX, srcY, - metadataOutObj, driverInternal); + metadataOutObj); } /* @@ -1058,8 +1049,6 @@ ReadColorMap( * The transparent color is set if present in current extensions * The data of the following extensions are saved to the metadata dict: * - Application extension -* - XMP data is stored in key "XMP" -* - any other under the key Application_<name><code> * - Comment extension in key "comment" * Plain text extensions are currently ignored. * @@ -1077,9 +1066,9 @@ DoExtension( { int count; /* Prepare extension name - * Maximum string size: "Application_"(12) + App(8) + Code(3) + trailing zero + * Maximum string size: "comment" + Code(3) + trailing zero */ - char extensionStreamName[24]; + char extensionStreamName[8]; extensionStreamName[0] = '\0'; switch (label) { @@ -1099,84 +1088,6 @@ DoExtension( strcpy(extensionStreamName,"comment"); /* copy the extension data below */ break; - case 0xff: /* Application Extension */ - /* Length: 11 - * Application Identifier: 8 bytes - * Application Authentication code: 3 Bytes - */ - count = GetDataBlock(gifConfPtr, chan, buf); - if (count != 11) { - return -1; - } - /* Detect XMP extension */ - if (NULL != metadataOutObj - && 0 == memcmp(buf,"XMP DataXMP",11)) { - /* XMP format does not use the block structure of GIF - * The data is utf-8 which never contains 0's - * A magic trailer of 258 bytes is added with the following data: - * 0x01 0xff 0xfe ... 0x01 0x00 0x00 - */ - Tcl_Encoding encoding; - Tcl_DString recodedDString; - Tcl_DString dataDString; - int length; - int result; - unsigned char lastbyte = 1; - Tcl_DStringInit(&dataDString); - - for (;;) { - unsigned char byte; - if (1 != Fread(gifConfPtr, &byte, 1, 1, chan)) { - /* read error */ - Tcl_DStringFree(&dataDString); - return -1; - } - Tcl_DStringAppend(&dataDString,(char *)&byte,1); - /* check for end of xmp header */ - if (byte == 0 && lastbyte == 0) { - break; - } - lastbyte = byte; - } - - /* check if trailer of 258 bytes is present */ - length = Tcl_DStringLength(&dataDString); - if (length < 258) { - Tcl_DStringFree(&dataDString); - return -1; - } - /* Remove the trailer from the data */ - length -= 258; - /* save the utf-8 data in the metadata dict key "XMP" */ - encoding = Tcl_GetEncoding(NULL, "utf-8"); - Tcl_DStringInit(&recodedDString); - Tcl_ExternalToUtfDString(encoding, Tcl_DStringValue(&dataDString), length, &recodedDString); - result = Tcl_DictObjPut(NULL, metadataOutObj, - Tcl_NewStringObj("XMP",-1), - Tcl_NewStringObj(Tcl_DStringValue(&recodedDString), - Tcl_DStringLength(&recodedDString))); - Tcl_DStringFree(&recodedDString); - Tcl_DStringFree(&dataDString); - Tcl_FreeEncoding(encoding); - if ( TCL_OK != result ) { - return -1; - } - return 0; - } else { - /* - * Other extension - * Name the extension: Application_xxxxxxxxxxx - * 012345678901234567890123 - */ - /* Untested code commented out, no use case - */ - /* - strcpy(extensionStreamName,"Application_"); - memcpy(extensionStreamName+12,buf,11); - extensionStreamName[23]='\0'; - */ - } - break; } /* Add extension to dict */ if (NULL != metadataOutObj @@ -2131,51 +2042,6 @@ CommonWriteGIF( writeProc(handle, (char *) &c, 1); } } - - /* - * Check and code XMP block - */ - - if (TCL_ERROR == Tcl_DictObjGet(interp, metadataInObj, - Tcl_NewStringObj("XMP",-1), - &itemData)) { - return TCL_ERROR; - } - if (itemData != NULL) { - Tcl_Encoding encoding; - Tcl_DString recodedDString; - char * itemString; - int itemLength; - int trailerChar; - - /* write header */ - writeProc(handle, "\x21\xff\x0bXMP DataXMP", 14); - - /* write utf-8 coded data */ - encoding = Tcl_GetEncoding(NULL, "utf-8"); - Tcl_DStringInit(&recodedDString); - itemString = Tcl_GetStringFromObj(itemData, &itemLength); - Tcl_UtfToExternalDString(encoding, itemString, itemLength, - &recodedDString); - writeProc(handle, Tcl_DStringValue(&recodedDString), - Tcl_DStringLength(&recodedDString)); - Tcl_DStringFree(&recodedDString); - Tcl_FreeEncoding(encoding); - - /* XMP format does not use the block structure of GIF - * The data is utf-8 which never contains 0's - * A magic trailer of 258 bytes is added with the following data: - * 0x01 0xff 0xfe ... 0x01 0x00 0x00 - */ - c = 1; - writeProc(handle, (char *) &c, 1); - for (trailerChar = 0xff; trailerChar >= 0; trailerChar--) { - c = (unsigned char)trailerChar; - writeProc(handle, (char *) &c, 1); - } - c = 0; - writeProc(handle, (char *) &c, 1); - } } c = GIF_TERMINATOR; writeProc(handle, (char *) &c, 1); diff --git a/generic/tkImgPNG.c b/generic/tkImgPNG.c index 91eb393..1ead8c0 100644 --- a/generic/tkImgPNG.c +++ b/generic/tkImgPNG.c @@ -212,14 +212,12 @@ static int EncodePNG(Tcl_Interp *interp, static int FileMatchPNG(Tcl_Interp *interp, Tcl_Channel chan, const char *fileName, Tcl_Obj *fmtObj, Tcl_Obj *metadataInObj, int *widthPtr, - int *heightPtr, Tcl_Obj *metadataOut, - int *closeChannelPtr, Tcl_DString *driverInternal); + int *heightPtr, Tcl_Obj *metadataOut); static int FileReadPNG(Tcl_Interp *interp, Tcl_Channel chan, const char *fileName, Tcl_Obj *fmtObj, Tcl_Obj *metadataInObj, Tk_PhotoHandle imageHandle, int destX, int destY, int width, int height, - int srcX, int srcY, Tcl_Obj *metadataOutPtr, - Tcl_DString *driverInternal); + int srcX, int srcY, Tcl_Obj *metadataOutPtr); static int FileWritePNG(Tcl_Interp *interp, const char *filename, Tcl_Obj *fmtObj, Tcl_Obj *metadataInObj, Tk_PhotoImageBlock *blockPtr); @@ -254,14 +252,12 @@ static int SkipChunk(Tcl_Interp *interp, PNGImage *pngPtr, static int StringMatchPNG(Tcl_Interp *interp, Tcl_Obj *pObjData, Tcl_Obj *fmtObj, Tcl_Obj *metadataInObj, int *widthPtr, int *heightPtr, - Tcl_Obj *metadataOutObj, - Tcl_DString *driverInternal); + Tcl_Obj *metadataOutObj); static int StringReadPNG(Tcl_Interp *interp, Tcl_Obj *pObjData, Tcl_Obj *fmtObj, Tcl_Obj *metadataInObj, Tk_PhotoHandle imageHandle, int destX, int destY, int width, int height, - int srcX, int srcY, Tcl_Obj *metadataOutObj, - Tcl_DString *driverInternal); + int srcX, int srcY, Tcl_Obj *metadataOutObj); static int StringWritePNG(Tcl_Interp *interp, Tcl_Obj *fmtObj, Tcl_Obj *metadataInObj, @@ -2827,9 +2823,7 @@ FileMatchPNG( int *widthPtr, int *heightPtr, /* The dimensions of the image are returned * here if the file is a valid raw GIF file. */ - Tcl_Obj *metadataOutObj, /* metadata return dict, may be NULL */ - int *closeChannelPtr, /* Return if the channel may be closed */ - Tcl_DString *driverInternal)/* memory passed to FileReadGIF */ + Tcl_Obj *metadataOutObj) /* metadata return dict, may be NULL */ { PNGImage png; int match = 0; @@ -2882,8 +2876,7 @@ FileReadPNG( * written to. */ int srcX, int srcY, /* Coordinates of top-left pixel to be used in * image being read. */ - Tcl_Obj *metadataOutObj, /* metadata return dict, may be NULL */ - Tcl_DString *driverInternal)/* memory passed from FileMatchGIF */ + Tcl_Obj *metadataOutObj) /* metadata return dict, may be NULL */ { PNGImage png; int result = TCL_ERROR; @@ -2941,8 +2934,7 @@ StringMatchPNG( Tcl_Obj *metadataInObj, /* metadata input, may be NULL */ int *widthPtr, /* where to put the string width */ int *heightPtr, /* where to put the string height */ - Tcl_Obj *metadataOutObj, /* metadata return dict, may be NULL */ - Tcl_DString *driverInternal)/* memory to pass to StringReadGIF */ + Tcl_Obj *metadataOutObj) /* metadata return dict, may be NULL */ { PNGImage png; int match = 0; @@ -2990,8 +2982,7 @@ StringReadPNG( int destX, int destY, /* The rectangular region of the */ int width, int height, /* image to copy */ int srcX, int srcY, - Tcl_Obj *metadataOutObj, /* metadata return dict, may be NULL */ - Tcl_DString *driverInternal)/* memory passed from StringReadGIF */ + Tcl_Obj *metadataOutObj) /* metadata return dict, may be NULL */ { PNGImage png; int result = TCL_ERROR; diff --git a/generic/tkImgPhoto.c b/generic/tkImgPhoto.c index f41cadd..92e4670 100644 --- a/generic/tkImgPhoto.c +++ b/generic/tkImgPhoto.c @@ -206,17 +206,14 @@ static int MatchFileFormat(Tcl_Interp *interp, Tcl_Channel chan, Tcl_Obj *metadataOutObj, Tk_PhotoImageFormat **imageFormatPtr, Tk_PhotoImageFormatVersion3 **imageFormatVersion3Ptr, - int *widthPtr, int *heightPtr, int *oldformat, - int *closeChannelPtr, - Tcl_DString *driverInternalPtr); + int *widthPtr, int *heightPtr, int *oldformat); static int MatchStringFormat(Tcl_Interp *interp, Tcl_Obj *data, Tcl_Obj *formatString, Tcl_Obj *metadataInObj, Tcl_Obj *metadataOutObj, Tk_PhotoImageFormat **imageFormatPtr, Tk_PhotoImageFormatVersion3 **imageFormatVersion3Ptr, - int *widthPtr, int *heightPtr, int *oldformat, - Tcl_DString *driverInternalPtr); + int *widthPtr, int *heightPtr, int *oldformat); static const char * GetExtension(const char *path); /* @@ -981,9 +978,8 @@ ImgPhotoCmd( } case PHOTO_PUT: { - int result; Tcl_Obj *format, *data; - Tcl_DString driverInternalDString; + /* * photo put command - first parse the options. */ @@ -1002,12 +998,6 @@ ImgPhotoCmd( Tcl_WrongNumArgs(interp, 2, objv, "data ?-option value ...?"); return TCL_ERROR; } - - /* - * Prepare memory connection between format match and read function - */ - - Tcl_DStringInit(&driverInternalDString); /* * See if there's a format that can read the data @@ -1015,11 +1005,9 @@ ImgPhotoCmd( if (MatchStringFormat(interp, objv[2], options.format, options.metadata, NULL, &imageFormat, - &imageFormatVersion3, &imageWidth, &imageHeight, &oldformat, - &driverInternalDString) + &imageFormatVersion3, &imageWidth, &imageHeight, &oldformat) != TCL_OK) { - result = TCL_ERROR; - goto putCleanup; + return TCL_ERROR; } if (!(options.options & OPT_TO) || (options.toX2 < 0)) { @@ -1046,8 +1034,7 @@ ImgPhotoCmd( (Tk_PhotoHandle) masterPtr, options.toX, options.toY, options.toX2 - options.toX, options.toY2 - options.toY, 0, 0) != TCL_OK) { - result = TCL_ERROR; - goto putCleanup; + return TCL_ERROR; } } else { if (imageFormatVersion3->stringReadProc(interp, data, format, @@ -1055,10 +1042,9 @@ ImgPhotoCmd( (Tk_PhotoHandle) masterPtr, options.toX, options.toY, options.toX2 - options.toX, options.toY2 - options.toY, 0, 0, - NULL, &driverInternalDString) + NULL) != TCL_OK) { - result = TCL_ERROR; - goto putCleanup; + return TCL_ERROR; } } @@ -1070,17 +1056,11 @@ ImgPhotoCmd( */ masterPtr->flags |= IMAGE_CHANGED; - result = TCL_OK; -putCleanup: - Tcl_DStringFree(&driverInternalDString); - return result; - + return TCL_OK; } case PHOTO_READ: { Tcl_Obj *format; int result; - int closeChannel = 0; - Tcl_DString driverInternalDString; /* * photo read command - first parse the options specified. @@ -1132,26 +1112,14 @@ putCleanup: return TCL_ERROR; } - /* - * Prepare memory connection between format match and read function - */ - - Tcl_DStringInit(&driverInternalDString); - closeChannel = 0; if (MatchFileFormat(interp, chan, Tcl_GetString(options.name), options.format, options.metadata, NULL, &imageFormat, - &imageFormatVersion3, &imageWidth, &imageHeight, &oldformat, - &closeChannel, &driverInternalDString) + &imageFormatVersion3, &imageWidth, &imageHeight, &oldformat) != TCL_OK) { result = TCL_ERROR; goto readCleanup; } - - if (closeChannel) { - Tcl_Close(NULL, chan); - chan = NULL; - } /* * Check the values given for the -from option. @@ -1210,10 +1178,9 @@ putCleanup: Tcl_GetString(options.name), format, options.metadata, (Tk_PhotoHandle) masterPtr, options.toX, options.toY, width, height, options.fromX, - options.fromY, NULL, &driverInternalDString); + options.fromY, NULL); } readCleanup: - Tcl_DStringFree(&driverInternalDString); if (chan != NULL) { Tcl_Close(NULL, chan); } @@ -1998,8 +1965,6 @@ ImgPhotoConfigureMaster( Tk_PhotoImageFormat *imageFormat; Tk_PhotoImageFormatVersion3 *imageFormatVersion3; const char **args; - Tcl_DString driverInternalDString; - int closeChannel; args = (const char **)ckalloc((objc + 1) * sizeof(char *)); for (i = 0, j = 0; i < objc; i++,j++) { @@ -2049,12 +2014,6 @@ ImgPhotoConfigureMaster( } /* - * Prepare memory connection between format match and read function - */ - - Tcl_DStringInit(&driverInternalDString); - - /* * Save the current values for fileString and dataString, so we can tell * if the user specifies them anew. IMPORTANT: if the format changes we * have to interpret "-file" and "-data" again as well! It might be that @@ -2197,8 +2156,6 @@ ImgPhotoConfigureMaster( goto errorExit; } - closeChannel = 0; - /* * Flag that we want the metadata result dict */ @@ -2215,15 +2172,10 @@ ImgPhotoConfigureMaster( (MatchFileFormat(interp, chan, masterPtr->fileString, masterPtr->format, masterPtr->metadata, metadataOutObj, &imageFormat, &imageFormatVersion3, - &imageWidth, &imageHeight, &oldformat, &closeChannel, - &driverInternalDString) != TCL_OK)) { + &imageWidth, &imageHeight, &oldformat) != TCL_OK)) { Tcl_Close(NULL, chan); goto errorExit; } - if (closeChannel) { - Tcl_Close(NULL, chan); - chan = NULL; - } result = ImgPhotoSetSize(masterPtr, imageWidth, imageHeight); if (result != TCL_OK) { Tcl_Close(NULL, chan); @@ -2246,11 +2198,10 @@ ImgPhotoConfigureMaster( masterPtr->fileString, tempformat, masterPtr->metadata, (Tk_PhotoHandle) masterPtr, 0, 0, imageWidth, imageHeight, 0, 0, - metadataOutObj, &driverInternalDString); - } - if (chan != NULL) { - Tcl_Close(NULL, chan); + metadataOutObj); } + + Tcl_Close(NULL, chan); if (result != TCL_OK) { goto errorExit; } @@ -2273,7 +2224,7 @@ ImgPhotoConfigureMaster( if (MatchStringFormat(interp, masterPtr->dataString, masterPtr->format, masterPtr->metadata, metadataOutObj, &imageFormat, &imageFormatVersion3, &imageWidth, - &imageHeight, &oldformat, &driverInternalDString) != TCL_OK) { + &imageHeight, &oldformat) != TCL_OK) { goto errorExit; } if (ImgPhotoSetSize(masterPtr, imageWidth, imageHeight) != TCL_OK) { @@ -2300,8 +2251,7 @@ ImgPhotoConfigureMaster( if (imageFormatVersion3->stringReadProc(interp, tempdata, tempformat, masterPtr->metadata, (Tk_PhotoHandle) masterPtr, 0, 0, - imageWidth, imageHeight, 0, 0, metadataOutObj, - &driverInternalDString) != TCL_OK) { + imageWidth, imageHeight, 0, 0, metadataOutObj) != TCL_OK) { goto errorExit; } } @@ -2352,7 +2302,7 @@ ImgPhotoConfigureMaster( } } } - + /* * Enforce a reasonable value for gamma. */ @@ -2385,7 +2335,6 @@ ImgPhotoConfigureMaster( masterPtr->height, masterPtr->width, masterPtr->height); masterPtr->flags &= ~IMAGE_CHANGED; - Tcl_DStringInit(&driverInternalDString); if (oldData != NULL) { Tcl_DecrRefCount(oldData); } @@ -2401,7 +2350,6 @@ ImgPhotoConfigureMaster( return TCL_OK; errorExit: - Tcl_DStringInit(&driverInternalDString); if (oldData != NULL) { Tcl_DecrRefCount(oldData); } @@ -2776,12 +2724,10 @@ MatchFileFormat( int *widthPtr, int *heightPtr, /* The dimensions of the image are returned * here. */ - int *oldformat, /* Returns 1 if the old image API is used. */ - int *closeChannelPtr, /* Is set to 1 if channel can be closed */ - Tcl_DString *driverInternalPtr)/* Memory to be passed to the corresponding - * ReadFileFormat function */ + int *oldformat) /* Returns 1 if the old image API is used. */ { - int matched = 0, useoldformat = 0; + int matched = 0; + int useoldformat = 0; Tk_PhotoImageFormat *formatPtr; Tk_PhotoImageFormatVersion3 *formatVersion3Ptr; ThreadSpecificData *tsdPtr = (ThreadSpecificData *) @@ -2907,7 +2853,7 @@ if (formatPtr == NULL) { if (formatVersion3Ptr->fileMatchProc(interp, chan, fileName, formatObj, metadataInObj, widthPtr, heightPtr, - metadataOutObj, closeChannelPtr, driverInternalPtr)) { + metadataOutObj)) { if (*widthPtr < 1) { *widthPtr = 1; } @@ -2917,9 +2863,7 @@ if (formatPtr == NULL) { *imageFormatVersion3Ptr = formatVersion3Ptr; *imageFormatPtr = NULL; *oldformat = 0; - if (! *closeChannelPtr ) { - (void) Tcl_Seek(chan, Tcl_LongAsWide(0L), SEEK_SET); - } + (void) Tcl_Seek(chan, Tcl_LongAsWide(0L), SEEK_SET); return TCL_OK; } @@ -3002,10 +2946,7 @@ MatchStringFormat( int *widthPtr, int *heightPtr, /* The dimensions of the image are returned * here. */ - int *oldformat, /* Returns 1 if the old image API is used. */ - Tcl_DString *driverInternalPtr) - /* Memory to be passed to the corresponding - * ReadFileFormat function */ + int *oldformat) /* Returns 1 if the old image API is used. */ { int matched = 0, useoldformat = 0; Tk_PhotoImageFormat *formatPtr, *defaultFormatPtr = NULL; @@ -3125,7 +3066,7 @@ MatchStringFormat( && (formatVersion3Ptr->stringReadProc != NULL) && formatVersion3Ptr->stringMatchProc(interp, data, formatObj, metadataInObj, widthPtr, heightPtr, - metadataOutObj, driverInternalPtr)) { + metadataOutObj)) { break; } diff --git a/generic/tkImgSVGnano.c b/generic/tkImgSVGnano.c index c4eceb0..7575f86 100644 --- a/generic/tkImgSVGnano.c +++ b/generic/tkImgSVGnano.c @@ -27,94 +27,13 @@ #define NANOSVGRAST_IMPLEMENTATION #include "nanosvgrast.h" -/* - * Serialized data version - * This consists of "svg" plus binary '1' at byte locations in an int. - * It serves as indication, version and endinaess check - */ - -#define STRUCTURE_VERSION ('s'+256*'v'+65535*'g'+16777216*1) - -/* - * Serialized image data header - */ - -typedef struct { - unsigned int structureVersion; - float dpi; - float width; // Width of the image. - float height; // Height of the image. - int shapeCount; - int pathCount; - int ptsCount; - int gradientCount; - int gradientStopCount; -} serializedHeader; - -/* - * Result of options parsing and first block in driver internal DString - */ +/* Additional parameters to nsvgRasterize() */ typedef struct { double scale; int scaleToHeight; int scaleToWidth; - float dpi; - int svgBlobFollows; -} optionsStruct; - -/* - * Serialized structures from NSCG - * All pointers are replaced by array indexes - */ - -typedef struct NSVGgradientSerialized { - float xform[6]; - char spread; - float fx, fy; - int nstops; - int stops; -} NSVGgradientSerialized; - -typedef struct NSVGpaintSerialized { - char type; - union { - unsigned int color; - int gradient; - }; -} NSVGpaintSerialized; - - -typedef struct NSVGpathSerialized -{ - int pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... - int npts; // Total number of bezier points. - // Caution: pair of floats - char closed; // Flag indicating if shapes should be treated as closed. - float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. - int next; // Pointer to next path, or NULL if last element. -} NSVGpathSerialized; - -typedef struct NSVGshapeSerialized -{ - char id[64]; // Optional 'id' attr of the shape or its group - NSVGpaintSerialized fill; // Fill paint - NSVGpaintSerialized stroke; // Stroke paint - float opacity; // Opacity of the shape. - float strokeWidth; // Stroke width (scaled). - float strokeDashOffset; // Stroke dash offset (scaled). - float strokeDashArray[8]; // Stroke dash array (scaled). - char strokeDashCount; // Number of dash values in dash array. - char strokeLineJoin; // Stroke join type. - char strokeLineCap; // Stroke cap type. - float miterLimit; // Miter limit - char fillRule; // Fill rule, see NSVGfillRule. - unsigned char flags; // Logical or of NSVG_FLAGS_* flags - float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. - int paths; // Linked list of paths in the image. - int next; // Pointer to next shape, or NULL if last element. -} NSVGshapeSerialized; - +} RastOpts; /* * Per interp cache of last NSVGimage which was matched to @@ -130,65 +49,45 @@ typedef struct { ClientData dataOrChan; Tcl_DString formatString; NSVGimage *nsvgImage; + RastOpts ropts; } NSVGcache; -static int FileMatchSVG(Tcl_Interp *interp, Tcl_Channel chan, - const char *fileName, Tcl_Obj *formatObj, - Tcl_Obj *metadataInObj, int *widthPtr, - int *heightPtr, Tcl_Obj *metadataOutObj, - int *closeChannelPtr, - Tcl_DString *driverInternal); +static int FileMatchSVG(Tcl_Channel chan, const char *fileName, + Tcl_Obj *format, int *widthPtr, int *heightPtr, + Tcl_Interp *interp); static int FileReadSVG(Tcl_Interp *interp, Tcl_Channel chan, const char *fileName, Tcl_Obj *format, - Tcl_Obj *metadataIn, Tk_PhotoHandle imageHandle, - int destX, int destY, int width, int height, - int srcX, int srcY, - Tcl_Obj *metadataOut, Tcl_DString *driverInternal); -static int StringMatchSVG(Tcl_Interp *interp, Tcl_Obj *dataObj, - Tcl_Obj *format, Tcl_Obj *metadataIn, int *widthPtr, - int *heightPtr, Tcl_Obj *metadataOutObj, - Tcl_DString *driverInternal); + Tk_PhotoHandle imageHandle, int destX, int destY, + int width, int height, int srcX, int srcY); +static int StringMatchSVG(Tcl_Obj *dataObj, Tcl_Obj *format, + int *widthPtr, int *heightPtr, Tcl_Interp *interp); static int StringReadSVG(Tcl_Interp *interp, Tcl_Obj *dataObj, - Tcl_Obj *format, Tcl_Obj *metadataIn, - Tk_PhotoHandle imageHandle, + Tcl_Obj *format, Tk_PhotoHandle imageHandle, int destX, int destY, int width, int height, - int srcX, int srcY, - Tcl_Obj *metadataOut, Tcl_DString *driverInternal); -static int ParseOptions(Tcl_Interp *interp, Tcl_Obj *formatObj, - optionsStruct *optionsPtr); -static NSVGimage * ParseSVG(Tcl_Interp *interp, Tcl_Obj *dataObj, float dpi); + int srcX, int srcY); +static NSVGimage * ParseSVGWithOptions(Tcl_Interp *interp, + const char *input, TkSizeT length, Tcl_Obj *format, + RastOpts *ropts); static int RasterizeSVG(Tcl_Interp *interp, - Tk_PhotoHandle imageHandle, - char *svgBlob, optionsStruct * optionsPtr, + Tk_PhotoHandle imageHandle, NSVGimage *nsvgImage, int destX, int destY, int width, int height, - int srcX, int srcY); -static double GetScaleFromParameters( - serializedHeader *serializedHeaderPtr, - optionsStruct * optionsPtr, int *widthPtr, - int *heightPtr); -static void SerializeNSVGImage(Tcl_DString *driverInternalPtr, - NSVGimage *nsvgImage); -static void SerializePath(struct NSVGpath *pathPtr, - serializedHeader *serializedHeaderPtr, - Tcl_DString *pathDStringPtr, - Tcl_DString *ptrDStringPtr); -static struct NSVGpaintSerialized SerializePaint(struct NSVGpaint *paintPtr, - serializedHeader *serializedHeaderPtr, - Tcl_DString *gradientDStringPtr, - Tcl_DString *gradientStopDStringPtr); -static char * StringCheckMetadata(Tcl_Obj *dataObj, Tcl_Obj *metadataInObj, - float dpi, int *LengthPtr); -static int SaveSVGBLOBToMetadata(Tcl_Interp *interp, Tcl_Obj *metadataOutObj, - Tcl_DString *driverInternalPtr); -static void nsvgRasterizeSerialized(NSVGrasterizer* r, char *svgBlobPtr, float tx, - float ty, float scale, unsigned char* dst, int w, - int h, int stride); + int srcX, int srcY, RastOpts *ropts); +static double GetScaleFromParameters(NSVGimage *nsvgImage, + RastOpts *ropts, int *widthPtr, int *heightPtr); +static NSVGcache * GetCachePtr(Tcl_Interp *interp); +static int CacheSVG(Tcl_Interp *interp, ClientData dataOrChan, + Tcl_Obj *formatObj, NSVGimage *nsvgImage, + RastOpts *ropts); +static NSVGimage * GetCachedSVG(Tcl_Interp *interp, ClientData dataOrChan, + Tcl_Obj *formatObj, RastOpts *ropts); +static void CleanCache(Tcl_Interp *interp); +static void FreeCache(ClientData clientData, Tcl_Interp *interp); /* * The format record for the SVG nano file format: */ -Tk_PhotoImageFormatVersion3 tkImgFmtSVGnano = { +Tk_PhotoImageFormat tkImgFmtSVGnano = { "svg", /* name */ FileMatchSVG, /* fileMatchProc */ StringMatchSVG, /* stringMatchProc */ @@ -219,381 +118,45 @@ Tk_PhotoImageFormatVersion3 tkImgFmtSVGnano = { static int FileMatchSVG( - Tcl_Interp *interp, /* interpreter pointer */ - Tcl_Channel chan, /* The image file, open for reading. */ - const char *fileName, /* The name of the image file. */ - Tcl_Obj *formatObj, /* User-specified format object, or NULL. */ - Tcl_Obj *metadataInObj, /* metadata input, may be NULL */ + Tcl_Channel chan, + const char *fileName, + Tcl_Obj *formatObj, int *widthPtr, int *heightPtr, - /* The dimensions of the image are returned - * here if the file is a valid raw GIF file. */ - Tcl_Obj *metadataOut, /* metadata return dict, may be NULL */ - int *closeChannelPtr, /* Return if the channel may be closed */ - Tcl_DString *driverInternalPtr) - /* memory passed to FileReadGIF */ + Tcl_Interp *interp) { - Tcl_Obj *dataObj; + TkSizeT length; + Tcl_Obj *dataObj = Tcl_NewObj(); + const char *data; + RastOpts ropts; NSVGimage *nsvgImage; - serializedHeader *serializedHeaderPtr; - optionsStruct options; - (void)fileName; - /* - * Parse the options. Unfortunately, any error can not be returned. - */ - - if (TCL_OK != ParseOptions(interp, formatObj, &options) ) { - return 0; - } - - /* - * Read the file data into a TCL object - */ - - dataObj = Tcl_NewObj(); + CleanCache(interp); if (Tcl_ReadChars(chan, dataObj, -1, 0) == TCL_IO_FAILURE) { /* in case of an error reading the file */ Tcl_DecrRefCount(dataObj); return 0; } - - /* - * Parse the SVG data - */ - - nsvgImage = ParseSVG(interp, dataObj, options.dpi); + data = TkGetStringFromObj(dataObj, &length); + nsvgImage = ParseSVGWithOptions(interp, data, length, formatObj, &ropts); Tcl_DecrRefCount(dataObj); if (nsvgImage != NULL) { - - /* - * On successful parse, save the data in the header structure - */ - - Tcl_DStringSetLength(driverInternalPtr, - sizeof(optionsStruct)+sizeof(serializedHeader)); - - options.svgBlobFollows = 1; - memcpy( Tcl_DStringValue(driverInternalPtr), &options, - sizeof(optionsStruct)); - - serializedHeaderPtr = (serializedHeader *) - (Tcl_DStringValue(driverInternalPtr) + sizeof(optionsStruct)); - - serializedHeaderPtr->width = nsvgImage->width; - serializedHeaderPtr->height = nsvgImage->height; - serializedHeaderPtr->dpi = options.dpi; - GetScaleFromParameters(serializedHeaderPtr, &options, widthPtr, - heightPtr); + GetScaleFromParameters(nsvgImage, &ropts, widthPtr, heightPtr); if ((*widthPtr <= 0.0) || (*heightPtr <= 0.0)) { nsvgDelete(nsvgImage); return 0; } - - /* - * Serialize the NSVGImage structure - * As the DString is resized, serializedHeaderPtr may get invalid - */ - - SerializeNSVGImage(driverInternalPtr, nsvgImage); - nsvgDelete(nsvgImage); + if (!CacheSVG(interp, chan, formatObj, nsvgImage, &ropts)) { + nsvgDelete(nsvgImage); + } return 1; } return 0; - } /* *---------------------------------------------------------------------- * - * SerializeNSVGImage -- - * - * This function saves the NSVGimage structure into the DString. - * - * Results: - * none. - * - * Side effects: - * The DString size is changed and thus, the value pointer may - * change. - * - *---------------------------------------------------------------------- - */ - -static void SerializeNSVGImage(Tcl_DString *driverInternalPtr, - NSVGimage *nsvgImage) { - serializedHeader *serializedHeaderPtr; - Tcl_DString shapeDString, pathDString, ptsDString, gradientDString, - gradientStopDString; - NSVGshape *shapePtr; - - serializedHeaderPtr = (serializedHeader *) - (Tcl_DStringValue(driverInternalPtr) + sizeof(optionsStruct)); - Tcl_DStringInit(&shapeDString); - Tcl_DStringInit(&pathDString); - Tcl_DStringInit(&ptsDString); - Tcl_DStringInit(&gradientDString); - Tcl_DStringInit(&gradientStopDString); - - serializedHeaderPtr->structureVersion = STRUCTURE_VERSION; - serializedHeaderPtr->shapeCount = 0; - serializedHeaderPtr->pathCount = 0; - serializedHeaderPtr->ptsCount = 0; - serializedHeaderPtr->gradientCount = 0; - serializedHeaderPtr->gradientStopCount = 0; - - for ( shapePtr = nsvgImage->shapes; shapePtr != NULL; - shapePtr = shapePtr->next) { - NSVGshapeSerialized shapeSerialized; - - /* - * Copy serialized shape fix data - */ - - memcpy(shapeSerialized.id, shapePtr->id, 64 * sizeof(char)); - - shapeSerialized.fill = SerializePaint(&(shapePtr->fill), - serializedHeaderPtr, &gradientDString, &gradientStopDString); - - shapeSerialized.stroke = SerializePaint(&(shapePtr->stroke), - serializedHeaderPtr, &gradientDString, &gradientStopDString); - - shapeSerialized.opacity = shapePtr->opacity; - shapeSerialized.strokeWidth = shapePtr->strokeWidth; - shapeSerialized.strokeDashOffset = shapePtr->strokeDashOffset; - memcpy(shapeSerialized.strokeDashArray, shapePtr->strokeDashArray, - 8*sizeof(float)); - shapeSerialized.strokeDashCount = shapePtr->strokeDashCount; - shapeSerialized.strokeLineJoin = shapePtr->strokeLineJoin; - shapeSerialized.strokeLineCap = shapePtr->strokeLineCap; - shapeSerialized.miterLimit = shapePtr->miterLimit; - shapeSerialized.fillRule = shapePtr->fillRule; - shapeSerialized.flags = shapePtr->flags; - memcpy(shapeSerialized.bounds, shapePtr->bounds, 4*sizeof(float)); - - /* - * Serialize the paths linked list - */ - - if ( shapePtr->paths == NULL ) { - shapeSerialized.paths = -1; - } else { - shapeSerialized.paths = serializedHeaderPtr->pathCount; - SerializePath(shapePtr->paths, serializedHeaderPtr, &pathDString, - &ptsDString); - } - - /* - * generate next array position and save to DString - */ - - serializedHeaderPtr->shapeCount++; - shapeSerialized.next = - shapePtr->next == NULL ? -1: - serializedHeaderPtr->shapeCount; - - Tcl_DStringAppend(&shapeDString, - (const char *)&shapeSerialized, sizeof(NSVGshapeSerialized)); - } - - /* - * Write the DStrings into the driver memory one after the other - * Note: serializedHeaderPtr may get invalid due to DString resize - */ - - if (Tcl_DStringLength(&shapeDString) > 0) { - Tcl_DStringAppend(driverInternalPtr, - Tcl_DStringValue(&shapeDString), - Tcl_DStringLength(&shapeDString)); - } - Tcl_DStringFree(&shapeDString); - - if (Tcl_DStringLength(&pathDString) > 0) { - Tcl_DStringAppend(driverInternalPtr, - Tcl_DStringValue(&pathDString), - Tcl_DStringLength(&pathDString)); - } - Tcl_DStringFree(&pathDString); - - if (Tcl_DStringLength(&ptsDString) > 0) { - Tcl_DStringAppend(driverInternalPtr, - Tcl_DStringValue(&ptsDString), - Tcl_DStringLength(&ptsDString)); - } - Tcl_DStringFree(&ptsDString); - - if (Tcl_DStringLength(&gradientDString) > 0) { - Tcl_DStringAppend(driverInternalPtr, - Tcl_DStringValue(&gradientDString), - Tcl_DStringLength(&gradientDString)); - } - Tcl_DStringFree(&gradientDString); - - if (Tcl_DStringLength(&gradientStopDString) > 0) { - Tcl_DStringAppend(driverInternalPtr, - Tcl_DStringValue(&gradientStopDString), - Tcl_DStringLength(&gradientStopDString)); - } - Tcl_DStringFree(&gradientStopDString); -} - -/* - *---------------------------------------------------------------------- - * - * SerializePaint -- - * - * This function transforms the NSVGpaint structure to a serialize - * version. - * The child structures gradient and gradientStop are serialized into - * their DString memory. - * - * Results: - * NSVGPaintSerialized structure. - * - * Side effects: - * The DString size is changed and thus, the value pointer may - * change. - * - *---------------------------------------------------------------------- - */ - -static struct NSVGpaintSerialized SerializePaint(struct NSVGpaint *paintPtr, - serializedHeader *serializedHeaderPtr, - Tcl_DString *gradientDStringPtr, - Tcl_DString *gradientStopDStringPtr) -{ - struct NSVGpaintSerialized paintSerialized; - - paintSerialized.type = paintPtr->type; - - if (paintPtr->type == NSVG_PAINT_LINEAR_GRADIENT - || paintPtr->type == NSVG_PAINT_RADIAL_GRADIENT) { - - /* - * Gradient union pointer present - */ - - NSVGgradient* gradientPtr; - NSVGgradientSerialized gradientSerialized; - - gradientPtr = paintPtr->gradient; - memcpy(&(gradientSerialized.xform), gradientPtr->xform, - 6 * sizeof(float) ); - gradientSerialized.spread = gradientPtr->spread; - gradientSerialized.fx = gradientPtr->fx; - gradientSerialized.fy = gradientPtr->fy; - - /* - * Copy gradient stop array to DString - */ - - gradientSerialized.nstops = gradientPtr->nstops; - if ( gradientPtr->nstops == 0 ) { - gradientSerialized.stops = -1; - } else { - gradientSerialized.stops = serializedHeaderPtr->gradientStopCount; - Tcl_DStringAppend(gradientStopDStringPtr, - (const char *)gradientPtr->stops, - gradientPtr->nstops * sizeof(NSVGgradientStop)); - - (serializedHeaderPtr->gradientStopCount) += gradientPtr->nstops; - } - (serializedHeaderPtr->gradientStopCount) += gradientPtr->nstops; - - paintSerialized.gradient = serializedHeaderPtr->gradientCount; - Tcl_DStringAppend(gradientDStringPtr, - (const char *) & gradientSerialized, - sizeof(NSVGgradientSerialized)); - (serializedHeaderPtr->gradientCount) ++; - - } else { - - /* - * Color union or nothing present - */ - - paintSerialized.color = paintPtr->color; - } - return paintSerialized; -} -/* - *---------------------------------------------------------------------- - * - * SerializePath -- - * - * This function serializes a linked list of NSVGpath structure into - * the corresponding DString array. - * - * Results: - * none - * - * Side effects: - * The DString size is changed and thus, the value pointer may - * change. - * - *---------------------------------------------------------------------- - */ - -static void SerializePath(struct NSVGpath *pathPtr, - serializedHeader *serializedHeaderPtr, - Tcl_DString *pathDStringPtr, - Tcl_DString *ptsDStringPtr) -{ - - /* - * loop over path linked list - */ - - for (;pathPtr != NULL; pathPtr = pathPtr->next) { - NSVGpathSerialized pathSerialized; - int index; - - /* - * Save points in the ptr dstring. - * The first index and the count is saved. - */ - - pathSerialized.npts = pathPtr->npts; - if (pathPtr->npts == 0) { - pathSerialized.pts = -1; - } else { - - /* - * Attention: npts is a pair of floats - */ - - pathSerialized.pts = serializedHeaderPtr->ptsCount; - for (index = 0; index < (pathPtr->npts) * 2; index++) { - float ptCurrent; - ptCurrent = pathPtr->pts[index]; - Tcl_DStringAppend(ptsDStringPtr, - (const char *)&ptCurrent, sizeof(float)); - (serializedHeaderPtr->ptsCount)++; - } - } - - /* - * Copy the other items of the path structure - */ - - pathSerialized.closed = pathPtr->closed; - memcpy(pathSerialized.bounds, pathPtr->bounds, 4*sizeof(float)); - - /* - * Build the next item and add to DString - */ - - serializedHeaderPtr->pathCount++; - pathSerialized.next = (pathPtr->next == NULL) ? -1 : - serializedHeaderPtr->pathCount; - - Tcl_DStringAppend(pathDStringPtr, - (const char *)&pathSerialized, sizeof(NSVGpathSerialized)); - } -} -/* - *---------------------------------------------------------------------- - * * FileReadSVG -- * * This function is called by the photo image type to read SVG format @@ -612,48 +175,41 @@ static void SerializePath(struct NSVGpath *pathPtr, static int FileReadSVG( - Tcl_Interp *interp, /* Interpreter to use for reporting errors. */ - Tcl_Channel chan, /* The image file, open for reading. */ - const char *fileName, /* The name of the image file. */ - Tcl_Obj *formatObj, /* User-specified format object, or NULL. */ - Tcl_Obj *metadataInObj, /* metadata input, may be NULL */ - Tk_PhotoHandle imageHandle, /* The photo image to write into. */ - int destX, int destY, /* Coordinates of top-left pixel in photo - * image to be written to. */ - int width, int height, /* Dimensions of block of photo image to be - * written to. */ - int srcX, int srcY, /* Coordinates of top-left pixel to be used in - * image being read. */ - Tcl_Obj *metadataOutObj, /* metadata return dict, may be NULL */ - Tcl_DString *driverInternalPtr) - /* memory passed from FileMatchGIF */ + Tcl_Interp *interp, + Tcl_Channel chan, + const char *fileName, + Tcl_Obj *formatObj, + Tk_PhotoHandle imageHandle, + int destX, int destY, + int width, int height, + int srcX, int srcY) { - int result; - optionsStruct * optionsPtr; - char * svgBlob; + TkSizeT length; + const char *data; + RastOpts ropts; + NSVGimage *nsvgImage = GetCachedSVG(interp, chan, formatObj, &ropts); (void)fileName; - - - optionsPtr = (optionsStruct *) Tcl_DStringValue(driverInternalPtr); - svgBlob = Tcl_DStringValue(driverInternalPtr) + sizeof(optionsStruct); - - /* - * Raster the parsed SVG from the SVGBLOB in the driver internal DString - */ - - result = RasterizeSVG(interp, imageHandle, svgBlob, optionsPtr, destX, - destY, width, height, srcX, srcY); - /* - * On success, output the SVGBLOB as metadata - */ + if (nsvgImage == NULL) { + Tcl_Obj *dataObj = Tcl_NewObj(); - if (result == TCL_OK) { - result = SaveSVGBLOBToMetadata(interp, metadataOutObj, - driverInternalPtr); + if (Tcl_ReadChars(chan, dataObj, -1, 0) == TCL_IO_FAILURE) { + /* in case of an error reading the file */ + Tcl_DecrRefCount(dataObj); + Tcl_SetObjResult(interp, Tcl_NewStringObj("read error", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "READ_ERROR", NULL); + return TCL_ERROR; + } + data = TkGetStringFromObj(dataObj, &length); + nsvgImage = ParseSVGWithOptions(interp, data, length, formatObj, + &ropts); + Tcl_DecrRefCount(dataObj); + if (nsvgImage == NULL) { + return TCL_ERROR; + } } - - return result; + return RasterizeSVG(interp, imageHandle, nsvgImage, destX, destY, + width, height, srcX, srcY, &ropts); } /* @@ -676,95 +232,28 @@ FileReadSVG( static int StringMatchSVG( - Tcl_Interp *interp, /* interpreter to report errors */ - Tcl_Obj *dataObj, /* the object containing the image data */ - Tcl_Obj *formatObj, /* the image format object, or NULL */ - Tcl_Obj *metadataInObj, /* metadata input, may be NULL */ - int *widthPtr, /* where to put the string width */ - int *heightPtr, /* where to put the string height */ - Tcl_Obj *metadataOut, /* metadata return dict, may be NULL */ - Tcl_DString *driverInternalPtr) - /* memory to pass to StringReadGIF */ + Tcl_Obj *dataObj, + Tcl_Obj *formatObj, + int *widthPtr, int *heightPtr, + Tcl_Interp *interp) { TkSizeT length; + const char *data; + RastOpts ropts; NSVGimage *nsvgImage; - serializedHeader *serializedHeaderPtr; - char * svgBlobPtr; - optionsStruct options; - - /* - * Parse the options. Unfortunately, any error can not be returned. - */ - - if (TCL_OK != ParseOptions(interp, formatObj, &options) ) { - return 0; - } - - /* - * Check for the special data to indicate that the metadata should be used. - * On special data, get the serialized header structure and check it. - */ - - svgBlobPtr = StringCheckMetadata( dataObj, metadataInObj, options.dpi, - &length); - - if (NULL != svgBlobPtr) { - serializedHeaderPtr = (serializedHeader *) svgBlobPtr; - } else { - Tcl_DStringSetLength(driverInternalPtr, - sizeof(optionsStruct)+sizeof(serializedHeader)); - - options.svgBlobFollows = 1; - memcpy( Tcl_DStringValue(driverInternalPtr), &options, - sizeof(optionsStruct)); - - serializedHeaderPtr = (serializedHeader *) - (Tcl_DStringValue(driverInternalPtr) + sizeof(optionsStruct)); - } - - /* - * Use the metadata svg blob if present to return width and height - */ - - if (NULL != svgBlobPtr) { - /* - * Save the options struct in the driver internal DString - */ - options.svgBlobFollows = 0; - Tcl_DStringSetLength(driverInternalPtr, sizeof(optionsStruct)); - memcpy(Tcl_DStringValue(driverInternalPtr), &options, - sizeof(optionsStruct)); - - GetScaleFromParameters(serializedHeaderPtr, &options, widthPtr, - heightPtr); - return 1; - } - - /* - * Check the passed data object and serialize it on success. - */ - - nsvgImage = ParseSVG(interp, dataObj, options.dpi); + CleanCache(interp); + data = TkGetStringFromObj(dataObj, &length); + nsvgImage = ParseSVGWithOptions(interp, data, length, formatObj, &ropts); if (nsvgImage != NULL) { - serializedHeaderPtr->width = nsvgImage->width; - serializedHeaderPtr->height = nsvgImage->height; - serializedHeaderPtr->dpi = options.dpi; - GetScaleFromParameters(serializedHeaderPtr, &options, widthPtr, - heightPtr); + GetScaleFromParameters(nsvgImage, &ropts, widthPtr, heightPtr); if ((*widthPtr <= 0.0) || (*heightPtr <= 0.0)) { nsvgDelete(nsvgImage); return 0; } - - /* - * Serialize the NSVGImage structure - * As the DString is resized, serializedHeaderPtr may get invalid - */ - - SerializeNSVGImage(driverInternalPtr, nsvgImage); - - nsvgDelete(nsvgImage); + if (!CacheSVG(interp, dataObj, formatObj, nsvgImage, &ropts)) { + nsvgDelete(nsvgImage); + } return 1; } return 0; @@ -773,68 +262,6 @@ StringMatchSVG( /* *---------------------------------------------------------------------- * - * StringCheckMetadata -- - * - * Check the passed tring for a metadata serialized structure. - * - * Results: - * The svg blob pointer on success, NULL otherwise. - * - * Side effects: - * The file is saved in the internal cache for further use. - * - *---------------------------------------------------------------------- - */ - -static char * StringCheckMetadata( - Tcl_Obj *dataObj, /* the object containing the image data */ - Tcl_Obj *metadataInObj, /* metadata input, may be NULL */ - float dpi, /* options dpi must match svgblob */ - TkSizeT *lengthOutPtr) /* output data length on success */ -{ - TkSizeT length; - const char *data; - serializedHeader *serializedHeaderPtr; - char * svgBlobPtr; - Tcl_Obj *itemData; - - /* - * Check for the special data to indicate that the metadata should be used. - * On special data, get the serialized header structure and check it. - */ - - if (NULL == metadataInObj) { - return NULL; - } - - data = TkGetStringFromObj(dataObj, &length); - if (0 != strcmp(data, "<svg data=\"metadata\" />") ) { - return NULL; - } - if (TCL_ERROR == Tcl_DictObjGet(NULL, metadataInObj, - Tcl_NewStringObj("SVGBLOB",-1), &itemData)) { - return NULL; - } - if (itemData == NULL) { - return NULL; - } - svgBlobPtr = Tcl_GetByteArrayFromObj(itemData, &length); - if (length < sizeof(serializedHeader) ) { - return NULL; - } - serializedHeaderPtr = (serializedHeader *)svgBlobPtr; - if (serializedHeaderPtr->structureVersion != STRUCTURE_VERSION - || serializedHeaderPtr->dpi != dpi - ) { - return NULL; - } - *lengthOutPtr = length; - return svgBlobPtr; -} - -/* - *---------------------------------------------------------------------- - * * StringReadSVG -- * * This function is called by the photo image type to read SVG format @@ -852,112 +279,59 @@ static char * StringCheckMetadata( static int StringReadSVG( - Tcl_Interp *interp, /* interpreter for reporting errors in */ - Tcl_Obj *dataObj, /* object containing the image */ - Tcl_Obj *formatObj, /* format object, or NULL */ - Tcl_Obj *metadataInObj, /* metadata input, may be NULL */ - Tk_PhotoHandle imageHandle, /* the image to write this data into */ - int destX, int destY, /* The rectangular region of the */ - int width, int height, /* image to copy */ - int srcX, int srcY, - Tcl_Obj *metadataOutObj, /* metadata return dict, may be NULL */ - Tcl_DString *driverInternalPtr) - /* memory passed from StringReadSVG */ + Tcl_Interp *interp, + Tcl_Obj *dataObj, + Tcl_Obj *formatObj, + Tk_PhotoHandle imageHandle, + int destX, int destY, + int width, int height, + int srcX, int srcY) { - int result; TkSizeT length; - char * svgBlobPtr; - optionsStruct * optionsPtr; - Tcl_Obj *itemData; - - optionsPtr = (optionsStruct *) Tcl_DStringValue(driverInternalPtr); - if (optionsPtr->svgBlobFollows) { - svgBlobPtr = Tcl_DStringValue(driverInternalPtr)+ sizeof(optionsStruct); - } else { - if (NULL == metadataInObj - || TCL_ERROR == Tcl_DictObjGet(NULL, metadataInObj, - Tcl_NewStringObj("SVGBLOB",-1), &itemData) - || itemData == NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "internal error: -metadata missing", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "BAD_SCALE", - NULL); - return TCL_ERROR; - } - - svgBlobPtr = Tcl_GetByteArrayFromObj(itemData, &length); - } - - result = RasterizeSVG(interp, imageHandle, - svgBlobPtr, optionsPtr, destX, destY, width, height, srcX, srcY); - if (result != TCL_OK) { - return result; - } + const char *data; + RastOpts ropts; + NSVGimage *nsvgImage = GetCachedSVG(interp, dataObj, formatObj, &ropts); - if (!optionsPtr->svgBlobFollows) { - return TCL_OK; + if (nsvgImage == NULL) { + data = TkGetStringFromObj(dataObj, &length); + nsvgImage = ParseSVGWithOptions(interp, data, length, formatObj, + &ropts); } - - return SaveSVGBLOBToMetadata(interp, metadataOutObj, driverInternalPtr); -} - -/* - *---------------------------------------------------------------------- - * - * SaveSVGBLOBToMetadata -- - * - * Copy the driver internal DString into the metadata key SVGBLOB. - * - * Results: - * A TCL result value. - * - * Side effects: - * Change the output metadata. - * - *---------------------------------------------------------------------- - */ - -static int SaveSVGBLOBToMetadata( - Tcl_Interp *interp, /* interpreter for reporting errors in */ - Tcl_Obj *metadataOutObj, /* metadata return dict, may be NULL */ - Tcl_DString *driverInternalPtr) - /* memory passed from xxxReadSVG */ -{ - if (metadataOutObj == NULL) { - return TCL_OK; + if (nsvgImage == NULL) { + return TCL_ERROR; } - return Tcl_DictObjPut(interp, metadataOutObj, - Tcl_NewStringObj("SVGBLOB",-1), - Tcl_NewByteArrayObj( - Tcl_DStringValue(driverInternalPtr) + sizeof(optionsStruct), - Tcl_DStringLength(driverInternalPtr) - sizeof(optionsStruct))); + return RasterizeSVG(interp, imageHandle, nsvgImage, destX, destY, + width, height, srcX, srcY, &ropts); } /* *---------------------------------------------------------------------- * - * ParseOptions -- + * ParseSVGWithOptions -- * - * Parse the options given in the -format parameter. + * This function is called to parse the given input string as SVG. * * Results: - * A normal tcl result + * Return a newly create NSVGimage on success, and NULL otherwise. * * Side effects: * *---------------------------------------------------------------------- */ -static int -ParseOptions( +static NSVGimage * +ParseSVGWithOptions( Tcl_Interp *interp, + const char *input, + TkSizeT length, Tcl_Obj *formatObj, - optionsStruct * optionsPtr) + RastOpts *ropts) { Tcl_Obj **objv = NULL; int objc = 0; - double dpi; + double dpi = 96.0; char *inputCopy = NULL; + NSVGimage *nsvgImage; int parameterScaleSeen = 0; static const char *const fmtOptions[] = { "-dpi", "-scale", "-scaletoheight", "-scaletowidth", NULL @@ -967,16 +341,29 @@ ParseOptions( }; /* + * The parser destroys the original input string, + * therefore first duplicate. + */ + + inputCopy = (char *)attemptckalloc(length+1); + if (inputCopy == NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("cannot alloc data buffer", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "OUT_OF_MEMORY", NULL); + goto error; + } + memcpy(inputCopy, input, length); + inputCopy[length] = '\0'; + + /* * Process elements of format specification as a list. */ - optionsPtr->dpi = 96.0; - optionsPtr->scale = 1.0; - optionsPtr->scaleToHeight = 0; - optionsPtr->scaleToWidth = 0; + ropts->scale = 1.0; + ropts->scaleToHeight = 0; + ropts->scaleToWidth = 0; if ((formatObj != NULL) && Tcl_ListObjGetElements(interp, formatObj, &objc, &objv) != TCL_OK) { - return TCL_ERROR;; + goto error; } for (; objc > 0 ; objc--, objv++) { int optIndex; @@ -991,12 +378,14 @@ ParseOptions( if (Tcl_GetIndexFromObjStruct(interp, objv[0], fmtOptions, sizeof(char *), "option", 0, &optIndex) == TCL_ERROR) { - return TCL_ERROR;; + goto error; } if (objc < 2) { + ckfree(inputCopy); + inputCopy = NULL; Tcl_WrongNumArgs(interp, 1, objv, "value"); - return TCL_ERROR;; + goto error; } objc--; @@ -1014,7 +403,7 @@ ParseOptions( "only one of -scale, -scaletoheight, -scaletowidth may be given", -1)); Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "BAD_SCALE", NULL); - return TCL_ERROR;; + goto error; } parameterScaleSeen = 1; break; @@ -1028,174 +417,72 @@ ParseOptions( switch ((enum fmtOptions) optIndex) { case OPT_DPI: if (Tcl_GetDoubleFromObj(interp, objv[0], &dpi) == TCL_ERROR) { - return TCL_ERROR;; + goto error; } if (dpi < 0.0) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "-dpi value must be positive", -1)); Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "BAD_DPI", NULL); - return TCL_ERROR;; + goto error; } - optionsPtr->dpi = (float)dpi; break; case OPT_SCALE: - if (Tcl_GetDoubleFromObj(interp, objv[0], - &(optionsPtr->scale)) == + if (Tcl_GetDoubleFromObj(interp, objv[0], &ropts->scale) == TCL_ERROR) { - return TCL_ERROR;; + goto error; } - if (optionsPtr->scale <= 0.0) { + if (ropts->scale <= 0.0) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "-scale value must be positive", -1)); Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "BAD_SCALE", NULL); - return TCL_ERROR;; + goto error; } break; case OPT_SCALE_TO_HEIGHT: - if (Tcl_GetIntFromObj(interp, objv[0], - &(optionsPtr->scaleToHeight)) == TCL_ERROR) { - return TCL_ERROR;; + if (Tcl_GetIntFromObj(interp, objv[0], &ropts->scaleToHeight) == + TCL_ERROR) { + goto error; } - if (optionsPtr->scaleToHeight <= 0) { + if (ropts->scaleToHeight <= 0) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "-scaletoheight value must be positive", -1)); Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "BAD_SCALE", NULL); - return TCL_ERROR;; + goto error; } break; case OPT_SCALE_TO_WIDTH: - if (Tcl_GetIntFromObj(interp, objv[0], - &(optionsPtr->scaleToWidth)) == TCL_ERROR) { - return TCL_ERROR;; + if (Tcl_GetIntFromObj(interp, objv[0], &ropts->scaleToWidth) == + TCL_ERROR) { + goto error; } - if (optionsPtr->scaleToWidth <= 0) { + if (ropts->scaleToWidth <= 0) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "-scaletowidth value must be positive", -1)); Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "BAD_SCALE", NULL); - return TCL_ERROR;; + goto error; } break; } } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ParseSVG -- - * - * This function is called to parse the given input string as SVG. - * - * Results: - * Return a newly create NSVGimage on success, and NULL otherwise. - * - * Side effects: - * - *---------------------------------------------------------------------- - */ - -static NSVGimage * -ParseSVG( - Tcl_Interp *interp, - Tcl_Obj *dataObj, - float dpi) -{ - const char *input; - char *inputCopy = NULL; - NSVGimage *nsvgImage; - TkSizeT length; - - /* - * The parser destroys the original input string, - * therefore first duplicate. - */ - - input = TkGetStringFromObj(dataObj, &length); - inputCopy = (char *)attemptckalloc(length+1); - if (inputCopy == NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("cannot alloc data buffer", -1)); - Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "OUT_OF_MEMORY", NULL); - return NULL; - } - memcpy(inputCopy, input, length); - inputCopy[length] = '\0'; - - nsvgImage = nsvgParse(inputCopy, "px", dpi); + nsvgImage = nsvgParse(inputCopy, "px", (float) dpi); if (nsvgImage == NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj("cannot parse SVG image", -1)); Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "PARSE_ERROR", NULL); - ckfree(inputCopy); - return NULL; + goto error; } ckfree(inputCopy); return nsvgImage; -} -/* - *---------------------------------------------------------------------- - * - * GetScaleFromParameters -- - * - * Get the scale value from the already parsed parameters -scale, - * -scaletoheight and -scaletowidth. - * - * The image width and height is also returned. - * - * Results: - * The evaluated or configured scale value, or 0.0 on failure - * - * Side effects: - * heightPtr and widthPtr are set to height and width of the image. - * - *---------------------------------------------------------------------- - */ - -static double -GetScaleFromParameters( - serializedHeader *serializedHeaderPtr, - optionsStruct * optionsPtr, - int *widthPtr, - int *heightPtr) -{ - double scale; - int width, height; - - if ((serializedHeaderPtr->width == 0.0) || (serializedHeaderPtr->height == 0.0)) { - width = height = 0; - scale = 1.0; - } else if (optionsPtr->scaleToHeight > 0) { - /* - * Fixed height - */ - height = optionsPtr->scaleToHeight; - scale = height / serializedHeaderPtr->height; - width = (int) ceil(serializedHeaderPtr->width * scale); - } else if (optionsPtr->scaleToWidth > 0) { - /* - * Fixed width - */ - width = optionsPtr->scaleToWidth; - scale = width / serializedHeaderPtr->width; - height = (int) ceil(serializedHeaderPtr->height * scale); - } else { - /* - * Scale factor - */ - scale = optionsPtr->scale; - width = (int) ceil(serializedHeaderPtr->width * scale); - height = (int) ceil(serializedHeaderPtr->height * scale); +error: + if (inputCopy != NULL) { + ckfree(inputCopy); } - - *heightPtr = height; - *widthPtr = width; - return scale; + return NULL; } /* @@ -1221,11 +508,11 @@ static int RasterizeSVG( Tcl_Interp *interp, Tk_PhotoHandle imageHandle, - char *svgBlobPtr, - optionsStruct * optionsPtr, + NSVGimage *nsvgImage, int destX, int destY, int width, int height, - int srcX, int srcY) + int srcX, int srcY, + RastOpts *ropts) { int w, h, c; NSVGrasterizer *rast; @@ -1234,16 +521,15 @@ RasterizeSVG( double scale; (void)srcX; (void)srcY; - - scale = GetScaleFromParameters((serializedHeader *) svgBlobPtr, optionsPtr, - &w, &h); + + scale = GetScaleFromParameters(nsvgImage, ropts, &w, &h); rast = nsvgCreateRasterizer(); if (rast == NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj("cannot initialize rasterizer", -1)); Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "RASTERIZER_ERROR", NULL); - return TCL_ERROR; + goto cleanAST; } imgData = (unsigned char *)attemptckalloc(w * h *4); if (imgData == NULL) { @@ -1251,8 +537,7 @@ RasterizeSVG( Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "OUT_OF_MEMORY", NULL); goto cleanRAST; } - - nsvgRasterizeSerialized(rast, svgBlobPtr, 0, 0, + nsvgRasterize(rast, nsvgImage, 0, 0, (float) scale, imgData, w, h, w * 4); /* transfer the data to a photo block */ svgblock.pixelPtr = imgData; @@ -1273,6 +558,7 @@ RasterizeSVG( } ckfree(imgData); nsvgDeleteRasterizer(rast); + nsvgDelete(nsvgImage); return TCL_OK; cleanimg: @@ -1281,369 +567,244 @@ cleanimg: cleanRAST: nsvgDeleteRasterizer(rast); +cleanAST: + nsvgDelete(nsvgImage); return TCL_ERROR; } /* *---------------------------------------------------------------------- * - * Rasterize Serialized -- + * GetScaleFromParameters -- * - * Fiunctions of svgnrast.h which requires modification due to the - * serialized data structure. + * Get the scale value from the already parsed parameters -scale, + * -scaletoheight and -scaletowidth. * - * Results: + * The image width and height is also returned. * + * Results: + * The evaluated or configured scale value, or 0.0 on failure * * Side effects: + * heightPtr and widthPtr are set to height and width of the image. * *---------------------------------------------------------------------- */ -static void nsvg__flattenShapeSerialized(NSVGrasterizer* r, int pathIndex, - NSVGpathSerialized *pathSerializedPtr, float *ptsSerializedPtr, - float scale) +static double +GetScaleFromParameters( + NSVGimage *nsvgImage, + RastOpts *ropts, + int *widthPtr, + int *heightPtr) { - int i, j; - NSVGpathSerialized* path; + double scale; + int width, height; - for (; pathIndex != -1; pathIndex = pathSerializedPtr[pathIndex].next) { - path = &(pathSerializedPtr[pathIndex]); - r->npoints = 0; - // Flatten path - nsvg__addPathPoint(r, - ptsSerializedPtr[path->pts]*scale, - ptsSerializedPtr[path->pts+1]*scale, 0); - for (i = 0; i < path->npts-1; i += 3) { - float* p = &ptsSerializedPtr[path->pts+i*2]; - nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0); - } - // Close path - nsvg__addPathPoint(r, ptsSerializedPtr[path->pts]*scale, - ptsSerializedPtr[path->pts+1]*scale, 0); - // Build edges - for (i = 0, j = r->npoints-1; i < r->npoints; j = i++) - nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y); + if ((nsvgImage->width == 0.0) || (nsvgImage->height == 0.0)) { + width = height = 0; + scale = 1.0; + } else if (ropts->scaleToHeight > 0) { + /* + * Fixed height + */ + height = ropts->scaleToHeight; + scale = height / nsvgImage->height; + width = (int) ceil(nsvgImage->width * scale); + } else if (ropts->scaleToWidth > 0) { + /* + * Fixed width + */ + width = ropts->scaleToWidth; + scale = width / nsvgImage->width; + height = (int) ceil(nsvgImage->height * scale); + } else { + /* + * Scale factor + */ + scale = ropts->scale; + width = (int) ceil(nsvgImage->width * scale); + height = (int) ceil(nsvgImage->height * scale); } -} - -static void nsvg__initPaintSerialized(NSVGcachedPaint* cache, - NSVGpaintSerialized* paint, float opacity, - NSVGgradientSerialized *gradientSerializedPtr, - NSVGgradientStop *gradientStopPtr) -{ - int i, j; - NSVGgradientSerialized* grad; - cache->type = paint->type; - - if (paint->type == NSVG_PAINT_COLOR) { - cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); - return; - } + *heightPtr = height; + *widthPtr = width; + return scale; +} - grad = &(gradientSerializedPtr[paint->gradient]); +/* + *---------------------------------------------------------------------- + * + * GetCachePtr -- + * + * This function is called to get the per interpreter used + * svg image cache. + * + * Results: + * Return a pointer to the used cache. + * + * Side effects: + * Initialize the cache on the first call. + * + *---------------------------------------------------------------------- + */ - cache->spread = grad->spread; - memcpy(cache->xform, grad->xform, sizeof(float)*6); +static NSVGcache * +GetCachePtr( + Tcl_Interp *interp +) { + NSVGcache *cachePtr = (NSVGcache *)Tcl_GetAssocData(interp, "tksvgnano", NULL); + if (cachePtr == NULL) { + cachePtr = (NSVGcache *)ckalloc(sizeof(NSVGcache)); + cachePtr->dataOrChan = NULL; + Tcl_DStringInit(&cachePtr->formatString); + cachePtr->nsvgImage = NULL; + Tcl_SetAssocData(interp, "tksvgnano", FreeCache, cachePtr); + } + return cachePtr; +} - if (grad->nstops == 0) { - for (i = 0; i < 256; i++) - cache->colors[i] = 0; - } if (grad->nstops == 1) { - for (i = 0; i < 256; i++) - cache->colors[i] = nsvg__applyOpacity( - gradientStopPtr[grad->stops+i].color, opacity); - } else { - unsigned int ca, cb = 0; - float ua, ub, du, u; - int ia, ib, count; +/* + *---------------------------------------------------------------------- + * + * CacheSVG -- + * + * Add the given svg image informations to the cache for further usage. + * + * Results: + * Return 1 on success, and 0 otherwise. + * + * Side effects: + * + *---------------------------------------------------------------------- + */ - ca = nsvg__applyOpacity(gradientStopPtr[grad->stops].color, opacity); - ua = nsvg__clampf(gradientStopPtr[grad->stops].offset, 0, 1); - ub = nsvg__clampf(gradientStopPtr[grad->stops+grad->nstops-1].offset, - ua, 1); - ia = (int)(ua * 255.0f); - ib = (int)(ub * 255.0f); - for (i = 0; i < ia; i++) { - cache->colors[i] = ca; - } +static int +CacheSVG( + Tcl_Interp *interp, + ClientData dataOrChan, + Tcl_Obj *formatObj, + NSVGimage *nsvgImage, + RastOpts *ropts) +{ + TkSizeT length; + const char *data; + NSVGcache *cachePtr = GetCachePtr(interp); - for (i = 0; i < grad->nstops-1; i++) { - ca = nsvg__applyOpacity(gradientStopPtr[grad->stops+i].color, - opacity); - cb = nsvg__applyOpacity(gradientStopPtr[grad->stops+i+1].color, - opacity); - ua = nsvg__clampf(gradientStopPtr[grad->stops+i].offset, 0, 1); - ub = nsvg__clampf(gradientStopPtr[grad->stops+i+1].offset, 0, 1); - ia = (int)(ua * 255.0f); - ib = (int)(ub * 255.0f); - count = ib - ia; - if (count <= 0) continue; - u = 0; - du = 1.0f / (float)count; - for (j = 0; j < count; j++) { - cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); - u += du; - } + if (cachePtr != NULL) { + cachePtr->dataOrChan = dataOrChan; + if (formatObj != NULL) { + data = TkGetStringFromObj(formatObj, &length); + Tcl_DStringAppend(&cachePtr->formatString, data, length); } - - for (i = ib; i < 256; i++) - cache->colors[i] = cb; + cachePtr->nsvgImage = nsvgImage; + cachePtr->ropts = *ropts; + return 1; } - + return 0; } -static void nsvg__flattenShapeStrokeSerialized(NSVGrasterizer* r, - NSVGshapeSerialized* shape, NSVGpathSerialized *pathSerializedPtr, - float *ptsSerializedPtr, float scale) -{ - int i, j, closed, pathIndex; - NSVGpathSerialized* path; - NSVGpoint* p0, *p1; - float miterLimit = shape->miterLimit; - int lineJoin = shape->strokeLineJoin; - int lineCap = shape->strokeLineCap; - float lineWidth = shape->strokeWidth * scale; - - for (pathIndex = shape->paths; pathIndex != -1; - pathIndex = pathSerializedPtr[pathIndex].next) { - path = &(pathSerializedPtr[pathIndex]); - // Flatten path - r->npoints = 0; - nsvg__addPathPoint(r, ptsSerializedPtr[path->pts]*scale, - ptsSerializedPtr[path->pts+1]*scale, NSVG_PT_CORNER); - for (i = 0; i < path->npts-1; i += 3) { - float* p = &ptsSerializedPtr[path->pts+i*2]; - nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale, - p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, - NSVG_PT_CORNER); - } - if (r->npoints < 2) - continue; - - closed = path->closed; - - // If the first and last points are the same, remove the last, mark as closed path. - p0 = &r->points[r->npoints-1]; - p1 = &r->points[0]; - if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { - r->npoints--; - p0 = &r->points[r->npoints-1]; - closed = 1; - } - - if (shape->strokeDashCount > 0) { - int idash = 0, dashState = 1; - float totalDist = 0, dashLen, allDashLen, dashOffset; - NSVGpoint cur; - - if (closed) - nsvg__appendPathPoint(r, r->points[0]); - - // Duplicate points -> points2. - nsvg__duplicatePoints(r); - r->npoints = 0; - cur = r->points2[0]; - nsvg__appendPathPoint(r, cur); - - // Figure out dash offset. - allDashLen = 0; - for (j = 0; j < shape->strokeDashCount; j++) - allDashLen += shape->strokeDashArray[j]; - if (shape->strokeDashCount & 1) - allDashLen *= 2.0f; - // Find location inside pattern - dashOffset = fmodf(shape->strokeDashOffset, allDashLen); - if (dashOffset < 0.0f) - dashOffset += allDashLen; - - while (dashOffset > shape->strokeDashArray[idash]) { - dashOffset -= shape->strokeDashArray[idash]; - idash = (idash + 1) % shape->strokeDashCount; - } - dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale; - - for (j = 1; j < r->npoints2; ) { - float dx = r->points2[j].x - cur.x; - float dy = r->points2[j].y - cur.y; - float dist = sqrtf(dx*dx + dy*dy); - - if ((totalDist + dist) > dashLen) { - // Calculate intermediate point - float d = (dashLen - totalDist) / dist; - float x = cur.x + dx * d; - float y = cur.y + dy * d; - nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); +/* + *---------------------------------------------------------------------- + * + * GetCachedSVG -- + * + * Try to get the NSVGimage from the internal cache. + * + * Results: + * Return the found NSVGimage on success, and NULL otherwise. + * + * Side effects: + * Calls the CleanCache() function. + * + *---------------------------------------------------------------------- + */ - // Stroke - if (r->npoints > 1 && dashState) { - nsvg__prepareStroke(r, miterLimit, lineJoin); - nsvg__expandStroke(r, r->points, r->npoints, 0, - lineJoin, lineCap, lineWidth); - } - // Advance dash pattern - dashState = !dashState; - idash = (idash+1) % shape->strokeDashCount; - dashLen = shape->strokeDashArray[idash] * scale; - // Restart - cur.x = x; - cur.y = y; - cur.flags = NSVG_PT_CORNER; - totalDist = 0.0f; - r->npoints = 0; - nsvg__appendPathPoint(r, cur); - } else { - totalDist += dist; - cur = r->points2[j]; - nsvg__appendPathPoint(r, cur); - j++; - } +static NSVGimage * +GetCachedSVG( + Tcl_Interp *interp, + ClientData dataOrChan, + Tcl_Obj *formatObj, + RastOpts *ropts) +{ + TkSizeT length; + const char *data; + NSVGcache *cachePtr = GetCachePtr(interp); + NSVGimage *nsvgImage = NULL; + + if ((cachePtr != NULL) && (cachePtr->nsvgImage != NULL) && + (cachePtr->dataOrChan == dataOrChan)) { + if (formatObj != NULL) { + data = TkGetStringFromObj(formatObj, &length); + if (strcmp(data, Tcl_DStringValue(&cachePtr->formatString)) == 0) { + nsvgImage = cachePtr->nsvgImage; + *ropts = cachePtr->ropts; + cachePtr->nsvgImage = NULL; } - // Stroke any leftover path - if (r->npoints > 1 && dashState) - nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, - lineCap, lineWidth); - } else { - nsvg__prepareStroke(r, miterLimit, lineJoin); - nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, - lineCap, lineWidth); + } else if (Tcl_DStringLength(&cachePtr->formatString) == 0) { + nsvgImage = cachePtr->nsvgImage; + *ropts = cachePtr->ropts; + cachePtr->nsvgImage = NULL; } } + CleanCache(interp); + return nsvgImage; } - /* *---------------------------------------------------------------------- * - * RasterizeSVGSerialized -- + * CleanCache -- * - * This function is called to rasterize the given nsvgImage and - * fill the imageHandle with data. + * Reset the cache and delete the saved image in it. * * Results: - * A standard TCL completion code. If TCL_ERROR is returned then an error - * message is left in the interp's result. - * * * Side effects: - * On error the given nsvgImage will be deleted. * *---------------------------------------------------------------------- */ -static void nsvgRasterizeSerialized(NSVGrasterizer* r, - char *svgBlobPtr, float tx, float ty, float scale, - unsigned char* dst, int w, int h, int stride) +static void +CleanCache(Tcl_Interp *interp) { - NSVGshapeSerialized *shape = NULL; - NSVGedge *e = NULL; - NSVGcachedPaint cache; - int i, shapeIndex; - serializedHeader * serializedHeaderPtr; - NSVGshapeSerialized *shapeSerializedPtr; - NSVGpathSerialized *pathSerializedPtr; - float *ptsSerializedPtr; - NSVGgradientSerialized *gradientSerializedPtr; - NSVGgradientStop *gradientStopPtr; - - /* - * Prepare the array pointers of the data array placed after serializedHeader - */ - serializedHeaderPtr = (serializedHeader *) svgBlobPtr; - svgBlobPtr += sizeof (serializedHeader); - shapeSerializedPtr = (NSVGshapeSerialized *) svgBlobPtr; - svgBlobPtr += serializedHeaderPtr->shapeCount * sizeof(NSVGshapeSerialized); - pathSerializedPtr = (NSVGpathSerialized *) svgBlobPtr; - svgBlobPtr += serializedHeaderPtr->pathCount * sizeof(NSVGpathSerialized); - ptsSerializedPtr = (float *) svgBlobPtr; - svgBlobPtr += serializedHeaderPtr->ptsCount * sizeof(float); - gradientSerializedPtr = (NSVGgradientSerialized *) svgBlobPtr; - svgBlobPtr += serializedHeaderPtr->gradientCount * - sizeof(NSVGgradientSerialized); - gradientStopPtr = (NSVGgradientStop *) svgBlobPtr; - - r->bitmap = dst; - r->width = w; - r->height = h; - r->stride = stride; - - if (w > r->cscanline) { - r->cscanline = w; - r->scanline = (unsigned char*)NANOSVG_realloc(r->scanline, w); - if (r->scanline == NULL) return; - } - - for (i = 0; i < h; i++) - memset(&dst[i*stride], 0, w*4); - - for (shapeIndex = 0 ; shapeIndex < serializedHeaderPtr->shapeCount; - shapeIndex++) { - shape = &(shapeSerializedPtr[shapeIndex]); - if (!(shape->flags & NSVG_FLAGS_VISIBLE)) - continue; - - if (shape->fill.type != NSVG_PAINT_NONE) { - nsvg__resetPool(r); - r->freelist = NULL; - r->nedges = 0; - - nsvg__flattenShapeSerialized(r, shape->paths, pathSerializedPtr, ptsSerializedPtr, - scale); - - // Scale and translate edges - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - e->x0 = tx + e->x0; - e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; - e->x1 = tx + e->x1; - e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; - } - - // Rasterize edges - qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); - - // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule - nsvg__initPaintSerialized(&cache, &shape->fill, shape->opacity, - gradientSerializedPtr, gradientStopPtr); - - nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule); + NSVGcache *cachePtr = GetCachePtr(interp); + + if (cachePtr != NULL) { + cachePtr->dataOrChan = NULL; + Tcl_DStringSetLength(&cachePtr->formatString, 0); + if (cachePtr->nsvgImage != NULL) { + nsvgDelete(cachePtr->nsvgImage); + cachePtr->nsvgImage = NULL; } - if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { - nsvg__resetPool(r); - r->freelist = NULL; - r->nedges = 0; - - nsvg__flattenShapeStrokeSerialized(r, shape, pathSerializedPtr, - ptsSerializedPtr, scale); - - // dumpEdges(r, "edge.svg"); - - // Scale and translate edges - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - e->x0 = tx + e->x0; - e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; - e->x1 = tx + e->x1; - e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; - } + } +} - // Rasterize edges - qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); +/* + *---------------------------------------------------------------------- + * + * FreeCache -- + * + * This function is called to clean up the internal cache data. + * + * Results: + * + * Side effects: + * Existing image data in the cache and the cache will be deleted. + * + *---------------------------------------------------------------------- + */ - // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule - nsvg__initPaintSerialized(&cache, &shape->stroke, shape->opacity, - gradientSerializedPtr, gradientStopPtr); +static void +FreeCache(ClientData clientData, Tcl_Interp *interp) +{ + NSVGcache *cachePtr = (NSVGcache *)clientData; + (void)interp; - nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO); - } + Tcl_DStringFree(&cachePtr->formatString); + if (cachePtr->nsvgImage != NULL) { + nsvgDelete(cachePtr->nsvgImage); } - - nsvg__unpremultiplyAlpha(dst, w, h, stride); - - r->bitmap = NULL; - r->width = 0; - r->height = 0; - r->stride = 0; + ckfree(cachePtr); } diff --git a/generic/tkInt.h b/generic/tkInt.h index ae25937..c3fe72f 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -1061,7 +1061,7 @@ MODULE_SCOPE void (*tkHandleEventProc) (XEvent* eventPtr); MODULE_SCOPE Tk_PhotoImageFormat tkImgFmtDefault; MODULE_SCOPE Tk_PhotoImageFormatVersion3 tkImgFmtPNG; MODULE_SCOPE Tk_PhotoImageFormat tkImgFmtPPM; -MODULE_SCOPE Tk_PhotoImageFormatVersion3 tkImgFmtSVGnano; +MODULE_SCOPE Tk_PhotoImageFormat tkImgFmtSVGnano; MODULE_SCOPE TkMainInfo *tkMainWindowList; MODULE_SCOPE Tk_ImageType tkPhotoImageType; MODULE_SCOPE Tcl_HashTable tkPredefBitmapTable; diff --git a/generic/tkWindow.c b/generic/tkWindow.c index 2adb0fa..aaa4a06 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -340,7 +340,7 @@ CreateTopLevelWindow( Tk_CreatePhotoImageFormatVersion3(&tkImgFmtGIF); Tk_CreatePhotoImageFormatVersion3(&tkImgFmtPNG); Tk_CreatePhotoImageFormat(&tkImgFmtPPM); - Tk_CreatePhotoImageFormatVersion3(&tkImgFmtSVGnano); + Tk_CreatePhotoImageFormat(&tkImgFmtSVGnano); } if ((parent != NULL) && (screenName != NULL) && (screenName[0] == '\0')) { diff --git a/tests/imgPhoto.test b/tests/imgPhoto.test index 5f71b9a..f0cd730 100644 --- a/tests/imgPhoto.test +++ b/tests/imgPhoto.test @@ -2249,170 +2249,7 @@ test imgPhoto-23.6 {Two GIF comment blocks (-file)} -setup { file delete $path } -result {comment ABCD} -test imgPhoto-23.7 {XMP comment block before image (-data)} -setup { - set data $::gifstart - # Append an XMP comment extension block (including a Unicode codepoint 2022 - set xmpdata "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\ - xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\ - <rdf:Description rdf:about=\"1\u2022\">\ - <xmp:Rating>3</xmp:Rating></rdf:Description></rdf:RDF>" - append data "\x21\xff\x0B" "XMP DataXMP" [encoding convertto utf-8 $xmpdata] - # Special trailer of 1 ff fe ... 02 01 00 00 - append data "\x01" - for {set i 0xff} {$i != -1} {incr i -1} { - append data [binary format c $i] - } - append data "\x00" - - append data $::gifdata - # Trailer - append data $::gifend -} -body { - image create photo gif1 -data $data - set d [dict get [gif1 cget -metadata] XMP] - expr {$d eq $xmpdata} -} -cleanup { - catch {image delete gif1} -} -result {1} - -test imgPhoto-23.8 {XMP comment block before image (-file)} -setup { - set data $::gifstart - # Append an XMP comment extension block (including a Unicode codepoint 2022 - set xmpdata "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\ - xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\ - <rdf:Description rdf:about=\"1\u2022\">\ - <xmp:Rating>3</xmp:Rating></rdf:Description></rdf:RDF>" - append data "\x21\xff\x0B" "XMP DataXMP" [encoding convertto utf-8 $xmpdata] - # Special trailer of 1 ff fe ... 02 01 00 00 - append data "\x01" - for {set i 0xff} {$i != -1} {incr i -1} { - append data [binary format c $i] - } - append data "\x00" - - append data $::gifdata - # Trailer - append data $::gifend - set path [file join [configure -tmpdir] test.gif] - set h [open $path "WRONLY BINARY CREAT"] - puts $h $data - close $h -} -body { - image create photo gif1 -file $path - set d [dict get [gif1 cget -metadata] XMP] - expr {$d eq $xmpdata} -} -cleanup { - catch {image delete gif1} - file delete $path -} -result {1} - -test imgPhoto-23.9 {XMP comment block after image (-data)} -setup { - set data $::gifstart - append data $::gifdata - - # Append an XMP comment extension block (including a Unicode codepoint 2022 - set xmpdata "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\ - xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\ - <rdf:Description rdf:about=\"1\u2022\">\ - <xmp:Rating>3</xmp:Rating></rdf:Description></rdf:RDF>" - append data "\x21\xff\x0B" "XMP DataXMP" [encoding convertto utf-8 $xmpdata] - # Special trailer of 1 ff fe ... 02 01 00 00 - append data "\x01" - for {set i 0xff} {$i != -1} {incr i -1} { - append data [binary format c $i] - } - append data "\x00" - - # Trailer - append data $::gifend -} -body { - image create photo gif1 -data $data - set d [dict get [gif1 cget -metadata] XMP] - expr {$d eq $xmpdata} -} -cleanup { - catch {image delete gif1} -} -result {1} - -test imgPhoto-23.10 {XMP comment block after image (-file)} -setup { - set data $::gifstart - append data $::gifdata - - # Append an XMP comment extension block (including a Unicode codepoint 2022 - set xmpdata "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\ - xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\ - <rdf:Description rdf:about=\"1\u2022\">\ - <xmp:Rating>3</xmp:Rating></rdf:Description></rdf:RDF>" - append data "\x21\xff\x0B" "XMP DataXMP" [encoding convertto utf-8 $xmpdata] - # Special trailer of 1 ff fe ... 02 01 00 00 - append data "\x01" - for {set i 0xff} {$i != -1} {incr i -1} { - append data [binary format c $i] - } - append data "\x00" - - # Trailer - append data $::gifend - - set path [file join [configure -tmpdir] test.gif] - set h [open $path "WRONLY BINARY CREAT"] - puts $h $data - close $h -} -body { - image create photo gif1 -file $path - set d [dict get [gif1 cget -metadata] XMP] - expr {$d eq $xmpdata} -} -cleanup { - catch {image delete gif1} - file delete $path -} -result {1} - -test imgPhoto-23.11 {empty XMP comment block after image (-data)} -setup { - set data $::gifstart - append data $::gifdata - - append data "\x21\xff\x0B" "XMP DataXMP" - # Special trailer of 1 ff fe ... 02 01 00 00 - append data "\x01" - for {set i 0xff} {$i != -1} {incr i -1} { - append data [binary format c $i] - } - append data "\x00" - # Trailer - append data $::gifend -} -body { - image create photo gif1 -data $data - dict get [gif1 cget -metadata] XMP -} -cleanup { - catch {image delete gif1} -} -result {} - -test imgPhoto-23.12 {empty XMP comment block after image (-file)} -setup { - set data $::gifstart - append data $::gifdata - - append data "\x21\xff\x0B" "XMP DataXMP" - # Special trailer of 1 ff fe ... 02 01 00 00 - append data "\x01" - for {set i 0xff} {$i != -1} {incr i -1} { - append data [binary format c $i] - } - append data "\x00" - # Trailer - append data $::gifend - - set path [file join [configure -tmpdir] test.gif] - set h [open $path "WRONLY BINARY CREAT"] - puts $h $data - close $h -} -body { - image create photo gif1 -file $path - dict get [gif1 cget -metadata] XMP -} -cleanup { - catch {image delete gif1} - file delete $path -} -result {} - -test imgPhoto-23.13 {create: test if shared metadata object is not preserved\ +test imgPhoto-23.7 {create: test if shared metadata object is not preserved\ (-data)}\ -setup { set data $::gifstart @@ -2429,7 +2266,7 @@ test imgPhoto-23.13 {create: test if shared metadata object is not preserved\ catch {image delete gif1} } -result {{A 1 comment ABCD} {A 1} {A 1}} -test imgPhoto-23.14 {create: test if shared metadata object is not preserved\ +test imgPhoto-23.8 {create: test if shared metadata object is not preserved\ (-file)}\ -setup { set data $::gifstart @@ -2452,7 +2289,7 @@ test imgPhoto-23.14 {create: test if shared metadata object is not preserved\ file delete $path } -result {{A 1 comment ABCD} {A 1} {A 1}} -test imgPhoto-23.15 {configure: test if shared metadata object is not\ +test imgPhoto-23.9 {configure: test if shared metadata object is not\ preserved (empty image, -data)}\ -setup { set data $::gifstart @@ -2470,7 +2307,7 @@ test imgPhoto-23.15 {configure: test if shared metadata object is not\ catch {image delete gif1} } -result {{A 1 comment ABCD} {A 1} {A 1}} -test imgPhoto-23.16 {configure: test if shared metadata object is not preserved\ +test imgPhoto-23.10 {configure: test if shared metadata object is not preserved\ (empty image, -file)}\ -setup { set data $::gifstart @@ -2494,7 +2331,7 @@ test imgPhoto-23.16 {configure: test if shared metadata object is not preserved\ file delete $path } -result {{A 1 comment ABCD} {A 1} {A 1}} -test imgPhoto-23.17 {configure: test if shared metadata object is not preserved\ +test imgPhoto-23.11 {configure: test if shared metadata object is not preserved\ (metadata replace, -data}\ -setup { set data $::gifstart @@ -2512,7 +2349,7 @@ test imgPhoto-23.17 {configure: test if shared metadata object is not preserved\ catch {image delete gif1} } -result {{A 1 comment ABCD} {A 1} {A 1}} -test imgPhoto-23.18 {configure: test if shared metadata object is not preserved\ +test imgPhoto-23.12 {configure: test if shared metadata object is not preserved\ (metadata replace, -file}\ -setup { set data $::gifstart @@ -2536,7 +2373,7 @@ test imgPhoto-23.18 {configure: test if shared metadata object is not preserved\ file delete $path } -result {{A 1 comment ABCD} {A 1} {A 1}} -test imgPhoto-23.19 {configure: test if shared metadata object is not preserved\ +test imgPhoto-23.13 {configure: test if shared metadata object is not preserved\ (-data)}\ -setup { set data $::gifstart$::gifdata$::gifend @@ -2555,7 +2392,7 @@ test imgPhoto-23.19 {configure: test if shared metadata object is not preserved\ catch {image delete gif1} } -result {{A 1 comment ABCD} {A 1} {A 1}} -test imgPhoto-23.20 {configure: test if shared metadata object is not preserved\ +test imgPhoto-23.14 {configure: test if shared metadata object is not preserved\ (-file)}\ -setup { set data $::gifstart @@ -2579,7 +2416,7 @@ test imgPhoto-23.20 {configure: test if shared metadata object is not preserved\ file delete $path } -result {{A 1 comment ABCD} {A 1} {A 1}} -test imgPhoto-23.21 {output data with comment (from -metadata argument)}\ +test imgPhoto-23.15 {output data with comment (from -metadata argument)}\ -setup { set data $::gifstart$::gifdata$::gifend } -body { @@ -2604,7 +2441,7 @@ test imgPhoto-23.22 {output file with comment (from -metadata argument)}\ file delete $path } -result {ABCD} -test imgPhoto-23.23 {output data with comment (from -metadata property)}\ +test imgPhoto-23.16 {output data with comment (from -metadata property)}\ -setup { set data $::gifstart$::gifdata$::gifend } -body { @@ -2615,7 +2452,7 @@ test imgPhoto-23.23 {output data with comment (from -metadata property)}\ catch {image delete gif1} } -match glob -result {*ABCD*} -test imgPhoto-23.24 {output file with comment (from -metadata property)}\ +test imgPhoto-23.17 {output file with comment (from -metadata property)}\ -setup { set data $::gifstart$::gifdata$::gifend set path [file join [configure -tmpdir] test.gif] @@ -2631,44 +2468,7 @@ test imgPhoto-23.24 {output file with comment (from -metadata property)}\ file delete $path } -result {ABCD} -test imgPhoto-23.25 {output data with XMP (-data)} -setup { - set data $::gifstart$::gifdata$::gifend - set XMPData\ - "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\ - xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\ - <rdf:Description rdf:about=\"1\u2022\">\ - <xmp:Rating>3</xmp:Rating></rdf:Description></rdf:RDF>" -} -body { - image create photo gif1 -data $data - set gifData [gif1 data -format gif -metadata [dict create\ - XMP $XMPData]] - image delete gif1 - image create photo gif1 -data $gifData - expr {[dict get [gif1 cget -metadata] XMP] eq $XMPData} -} -cleanup { - catch {image delete gif1} -} -result {1} - -test imgPhoto-23.24 {output data with XMP (-file)} -setup { - set data $::gifstart$::gifdata$::gifend - set XMPData\ - "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\ - xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\ - <rdf:Description rdf:about=\"1\u2022\">\ - <xmp:Rating>3</xmp:Rating></rdf:Description></rdf:RDF>" - set path [file join [configure -tmpdir] test.gif] -} -body { - image create photo gif1 -data $data - set gifData [gif1 write $path -format gif -metadata [dict create\ - XMP $XMPData]] - image delete gif1 - image create photo gif1 -file $path - expr {[dict get [gif1 cget -metadata] XMP] eq $XMPData} -} -cleanup { - catch {image delete gif1} -} -result {1} - -test imgPhoto-23.25 {configure: empty metadata parameter overwrites image metadata} -setup { +test imgPhoto-23.18 {configure: empty metadata parameter overwrites image metadata} -setup { image create photo gif1 -data $::gifstart$::gifdata$::gifend\ -metadata {foo bar} set data $::gifstart @@ -2683,7 +2483,7 @@ test imgPhoto-23.25 {configure: empty metadata parameter overwrites image metada catch {image delete gif1} } -result {comment ABCD} -test imgPhoto-23.26 {write: empty metadata parameter overwrites image metadata} -setup { +test imgPhoto-23.19 {write: empty metadata parameter overwrites image metadata} -setup { image create photo gif1 -data $::gifstart$::gifdata$::gifend\ -metadata {comment bar} set path [file join [configure -tmpdir] test.gif] @@ -2697,7 +2497,7 @@ test imgPhoto-23.26 {write: empty metadata parameter overwrites image metadata} file delete $path } -result {0} -test imgPhoto-23.27 {data: empty metadata parameter overwrites image metadata} -setup { +test imgPhoto-23.20 {data: empty metadata parameter overwrites image metadata} -setup { image create photo gif1 -data $::gifstart$::gifdata$::gifend\ -metadata {comment bar} } -body { diff --git a/tests/imgSVGnano.test b/tests/imgSVGnano.test index f34316b..ff7046a 100644 --- a/tests/imgSVGnano.test +++ b/tests/imgSVGnano.test @@ -23,36 +23,10 @@ namespace eval svgnano { <polyline fill="red" stroke="purple" points="80,10 90,20 85,40"/> <polygon fill ="yellow" points="80,80 70,85 90,90"/> </svg>} - set data(bad) {<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0:w "> </svg>} - set data(gradient) {<svg - xmlns="http://www.w3.org/2000/svg" - height="10" - width="10"> - <defs - id="defs7"> - <linearGradient - gradientTransform="rotate(90)" - id="myGradient"> - <stop - id="stop2" - stop-color="gold" - offset="5%" /> - <stop - id="stop4" - stop-color="red" - offset="95%" /> - </linearGradient> - </defs> - <rect - id="" - style="fill:url(#myGradient)" - x="0" y="0" width="10" height="10" rx="0" ry="0" /> -</svg>} - tcltest::makeFile $data(plus) plus.svg set data(plusFilePath) [file join [tcltest::configure -tmpdir] plus.svg] @@ -233,96 +207,6 @@ test imgSVGnano-4.2 {error on file not accessible on reread due to configure} -s tcltest::removeFile tmpplus.svg } -returnCodes error -match glob -result {couldn't open "*/tmpplus.svg": no such file or directory} -# rendering tests - -test imgSVGnano-5.0 {data gradient rendering} -setup { - catch {rename foo ""} - catch {rename gif ""} - image create photo gif\ - -data "R0lGODlhCgAKALMAANnZ2f/XAP/LAP+yAP+zAP+bAP+DAP9rAP9TAP9UAP88AP8kAP8MAP///////////yH5BAEAAAAALAAAAAAKAAoAAAQhMMhJhb14DMI7L2AoGmRpHmiqImyLJLAiz/Ri3zij73wEADs="\ - -format gif -} -body { - image create photo foo -data $data(gradient) - set res "" - for {set y 0} {$y < [image height foo]} {incr y} { - for {set x 0} {$x < [image width foo]} {incr x} { - lassign [foo get $x $y] r1 g1 b1 - lassign [gif get $x $y] r2 g2 b2 - if {$r1 != $r2 || $g1 != $g2 || $b1 != $b2} { - append res "pixel $x,$y unequal: $r1 $g1 $b1 != $r2 $g2 $b2\n" - } - } - } - set res -} -cleanup { - rename foo "" - rename gif "" -} -result {} - -test imgSVGnano-5.1 {file gradient rendering} -setup { - catch {rename foo ""} - tcltest::makeFile $data(gradient) tmpgradient.svg - catch {rename gif ""} - image create photo gif\ - -data "R0lGODlhCgAKALMAANnZ2f/XAP/LAP+yAP+zAP+bAP+DAP9rAP9TAP9UAP88AP8kAP8MAP///////////yH5BAEAAAAALAAAAAAKAAoAAAQhMMhJhb14DMI7L2AoGmRpHmiqImyLJLAiz/Ri3zij73wEADs="\ - -format gif -} -body { - image create photo foo -file [file join [tcltest::configure -tmpdir] tmpgradient.svg] - set res "" - for {set y 0} {$y < [image height foo]} {incr y} { - for {set x 0} {$x < [image width foo]} {incr x} { - lassign [foo get $x $y] r1 g1 b1 - lassign [gif get $x $y] r2 g2 b2 - if {$r1 != $r2 || $g1 != $g2 || $b1 != $b2} { - append res "pixel $x,$y unequal: $r1 $g1 $b1 != $r2 $g2 $b2\n" - } - } - } - set res -} -cleanup { - rename foo "" - tcltest::removeFile tmpgradient.svg - rename gif "" -} -result {} - -# metadata tests - -test imgSVGnano-6.0 {svgblob metadata creation} -setup { - catch {rename foo ""} - tcltest::makeFile $data(plus) tmpplus.svg -} -body { - image create photo foo -file [file join [tcltest::configure -tmpdir] tmpplus.svg] - dict keys [foo cget -metadata] -} -cleanup { - rename foo "" - tcltest::removeFile tmpplus.svg -} -result {SVGBLOB} - -test imgSVGnano-6.1 {scale with svgblob metadata (data)} -setup { - catch {rename foo ""} -} -body { - image create photo foo -data $data(plus) - foo configure -data "<svg data=\"metadata\" />" -format "svg" - foo configure -format "svg -scale 2" - lappend res [image width foo] [image height foo] -} -cleanup { - rename foo "" - tcltest::removeFile tmpplus.svg -} -result {200 200} - -test imgSVGnano-6.2 {scale with svgblob metadata} -setup { - catch {rename foo ""} - tcltest::makeFile $data(plus) tmpplus.svg -} -body { - image create photo foo -file [file join [tcltest::configure -tmpdir] tmpplus.svg] - foo configure -file "" -data "<svg data=\"metadata\" />" - foo configure -format "svg -scale 2" - list [image width foo] [image height foo] -} -cleanup { - rename foo "" - tcltest::removeFile tmpplus.svg -} -result {200 200} - };# end of namespace svgnano namespace delete svgnano |