diff options
author | oehhar <harald.oehlmann@elmicron.de> | 2020-06-21 12:06:34 (GMT) |
---|---|---|
committer | oehhar <harald.oehlmann@elmicron.de> | 2020-06-21 12:06:34 (GMT) |
commit | 2db28ea679f6ec225e01b37471beb98385f2741a (patch) | |
tree | da98804d049bd9c6f6a9c0aa5ad846ec99d13aef /generic | |
parent | fab62ba87797a77dd9eac2c168580ea41829711f (diff) | |
download | tk-2db28ea679f6ec225e01b37471beb98385f2741a.zip tk-2db28ea679f6ec225e01b37471beb98385f2741a.tar.gz tk-2db28ea679f6ec225e01b37471beb98385f2741a.tar.bz2 |
TIP529 image metadata: implement svg intermediate metadata memory
Diffstat (limited to 'generic')
-rw-r--r-- | generic/tkImgGIF.c | 6 | ||||
-rw-r--r-- | generic/tkImgSVGnano.c | 1232 |
2 files changed, 961 insertions, 277 deletions
diff --git a/generic/tkImgGIF.c b/generic/tkImgGIF.c index 02ff197..06a44ac 100644 --- a/generic/tkImgGIF.c +++ b/generic/tkImgGIF.c @@ -1060,7 +1060,7 @@ ReadColorMap( * - Application extension * - XMP data is stored in key "XMP" * - any other under the key Application_<name><code> -* - Comment extension in key "Comment" +* - Comment extension in key "comment" * Plain text extensions are currently ignored. * *---------------------------------------------------------------------- @@ -1096,7 +1096,7 @@ DoExtension( } break; case 0xfe: /* Comment Extension */ - strcpy(extensionStreamName,"Comment"); + strcpy(extensionStreamName,"comment"); /* copy the extension data below */ break; case 0xff: /* Application Extension */ @@ -2099,7 +2099,7 @@ CommonWriteGIF( */ if (TCL_ERROR == Tcl_DictObjGet(interp, metadataInObj, - Tcl_NewStringObj("Comment",-1), + Tcl_NewStringObj("comment",-1), &itemData)) { return TCL_ERROR; } diff --git a/generic/tkImgSVGnano.c b/generic/tkImgSVGnano.c index bed4776..e15de54 100644 --- a/generic/tkImgSVGnano.c +++ b/generic/tkImgSVGnano.c @@ -27,13 +27,84 @@ #define NANOSVGRAST_IMPLEMENTATION #include "nanosvgrast.h" -/* Additional parameters to nsvgRasterize() */ +/* + * 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; double scale; int scaleToHeight; int scaleToWidth; -} RastOpts; + float width; // Width of the image. + float height; // Height of the image. + int shapeCount; + int pathCount; + int ptsCount; + int gradientCount; + int gradientStopCount; +} serializedHeader; + +/* + * 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; + /* * Per interp cache of last NSVGimage which was matched to @@ -49,7 +120,6 @@ typedef struct { ClientData dataOrChan; Tcl_DString formatString; NSVGimage *nsvgImage; - RastOpts ropts; } NSVGcache; static int FileMatchSVG(Tcl_Interp *interp, Tcl_Channel chan, @@ -76,21 +146,33 @@ static int StringReadSVG(Tcl_Interp *interp, Tcl_Obj *dataObj, Tcl_Obj *metadataOut, Tcl_DString *driverInternal); static NSVGimage * ParseSVGWithOptions(Tcl_Interp *interp, const char *input, TkSizeT length, Tcl_Obj *format, - RastOpts *ropts); + serializedHeader *serializedHeaderPtr); static int RasterizeSVG(Tcl_Interp *interp, - Tk_PhotoHandle imageHandle, NSVGimage *nsvgImage, + Tk_PhotoHandle imageHandle, + char *svgBlob, + TkSizeT serializeDataLength, int destX, int destY, int width, int height, - 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); + int srcX, int srcY); +static double GetScaleFromParameters( + serializedHeader *serializedHeaderPtr, + int *widthPtr, int *heightPtr); +static void SerializeNSVG(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, 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); /* * The format record for the SVG nano file format: @@ -143,28 +225,36 @@ FileMatchSVG( TkSizeT length; Tcl_Obj *dataObj = Tcl_NewObj(); const char *data; - RastOpts ropts; NSVGimage *nsvgImage; (void)fileName; + serializedHeader *serializedHeaderPtr; - 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; } + Tcl_DStringSetLength(driverInternalPtr, sizeof(serializedHeader)); + serializedHeaderPtr = (serializedHeader *)Tcl_DStringValue(driverInternalPtr); data = TkGetStringFromObj(dataObj, &length); - nsvgImage = ParseSVGWithOptions(interp, data, length, formatObj, &ropts); + nsvgImage = ParseSVGWithOptions(interp, data, length, formatObj, + serializedHeaderPtr); Tcl_DecrRefCount(dataObj); if (nsvgImage != NULL) { - GetScaleFromParameters(nsvgImage, &ropts, widthPtr, heightPtr); + serializedHeaderPtr->width = nsvgImage->width; + serializedHeaderPtr->height = nsvgImage->height; + GetScaleFromParameters(serializedHeaderPtr, widthPtr, heightPtr); if ((*widthPtr <= 0.0) || (*heightPtr <= 0.0)) { nsvgDelete(nsvgImage); return 0; } - if (!CacheSVG(interp, chan, formatObj, nsvgImage, &ropts)) { - nsvgDelete(nsvgImage); - } + + /* + * Serialize the NSVGImage structure + * As the DString is resized, serializedHeaderPtr may get invalid + */ + SerializeNSVG(driverInternalPtr, nsvgImage); + nsvgDelete(nsvgImage); return 1; } return 0; @@ -173,6 +263,284 @@ FileMatchSVG( /* *---------------------------------------------------------------------- * + * SerializeNSVG -- + * + * 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 SerializeNSVG(Tcl_DString *driverInternalPtr, NSVGimage *nsvgImage) { + serializedHeader *serializedHeaderPtr; + Tcl_DString shapeDString, pathDString, ptsDString, gradientDString, + gradientStopDString; + NSVGshape *shapePtr; + + serializedHeaderPtr = (serializedHeader *)Tcl_DStringValue(driverInternalPtr); + 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; + } + } 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 @@ -207,32 +575,19 @@ FileReadSVG( Tcl_DString *driverInternalPtr) /* memory passed from FileMatchGIF */ { - TkSizeT length; - const char *data; - RastOpts ropts; - NSVGimage *nsvgImage = GetCachedSVG(interp, chan, formatObj, &ropts); + int result; (void)fileName; - if (nsvgImage == NULL) { - Tcl_Obj *dataObj = Tcl_NewObj(); - - 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; - } + result = RasterizeSVG(interp, imageHandle, + Tcl_DStringValue(driverInternalPtr), + Tcl_DStringLength(driverInternalPtr), destX, destY, width, height, + srcX, srcY); + if (result == TCL_OK) { + result = SaveSVGBLOBToMetadata(interp, metadataOutObj, + driverInternalPtr); } - return RasterizeSVG(interp, imageHandle, nsvgImage, destX, destY, - width, height, srcX, srcY, &ropts); + + return result; } /* @@ -267,21 +622,52 @@ StringMatchSVG( { TkSizeT length; const char *data; - RastOpts ropts; NSVGimage *nsvgImage; + serializedHeader *serializedHeaderPtr; + char * svgBlobPtr; - CleanCache(interp); data = TkGetStringFromObj(dataObj, &length); - nsvgImage = ParseSVGWithOptions(interp, data, length, formatObj, &ropts); + + + /* + * 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, &length); + + if (NULL != svgBlobPtr) { + GetScaleFromParameters((serializedHeader *) svgBlobPtr, widthPtr, + heightPtr); + return 1; + } + + /* + * Check the passed data object and serialize it on success. + */ + + Tcl_DStringSetLength(driverInternalPtr, sizeof(serializedHeader)); + serializedHeaderPtr = (serializedHeader *)Tcl_DStringValue(driverInternalPtr); + nsvgImage = ParseSVGWithOptions(interp, data, length, formatObj, + serializedHeaderPtr); + if (nsvgImage != NULL) { - GetScaleFromParameters(nsvgImage, &ropts, widthPtr, heightPtr); + serializedHeaderPtr->width = nsvgImage->width; + serializedHeaderPtr->height = nsvgImage->height; + GetScaleFromParameters(serializedHeaderPtr, widthPtr, heightPtr); if ((*widthPtr <= 0.0) || (*heightPtr <= 0.0)) { nsvgDelete(nsvgImage); return 0; } - if (!CacheSVG(interp, dataObj, formatObj, nsvgImage, &ropts)) { - nsvgDelete(nsvgImage); - } + + /* + * Serialize the NSVGImage structure + * As the DString is resized, serializedHeaderPtr may get invalid + */ + + SerializeNSVG(driverInternalPtr, nsvgImage); + + nsvgDelete(nsvgImage); return 1; } return 0; @@ -290,6 +676,65 @@ 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 */ + 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 ) { + return NULL; + } + *lengthOutPtr = length; + return svgBlobPtr; +} + +/* + *---------------------------------------------------------------------- + * * StringReadSVG -- * * This function is called by the photo image type to read SVG format @@ -310,30 +755,83 @@ 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 *metadataIn, /* metadata input, may be 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 *metadataOut, /* metadata return dict, may be NULL */ + Tcl_Obj *metadataOutObj, /* metadata return dict, may be NULL */ Tcl_DString *driverInternalPtr) - /* memory passed from StringReadGIF */ + /* memory passed from StringReadSVG */ { + int result; + TkSizeT serializedDataLength; + char * data; TkSizeT length; - const char *data; - RastOpts ropts; - NSVGimage *nsvgImage = GetCachedSVG(interp, dataObj, formatObj, &ropts); + char * svgBlobPtr; - if (nsvgImage == NULL) { - data = TkGetStringFromObj(dataObj, &length); - nsvgImage = ParseSVGWithOptions(interp, data, length, formatObj, - &ropts); + data = TkGetStringFromObj(dataObj, &length); + + + /* + * Check for the special data to indicate that the metadata should be used. + * On special data, get the serialized header structure and use it. + */ + + svgBlobPtr = StringCheckMetadata( dataObj, metadataInObj, + &serializedDataLength); + + if (NULL != svgBlobPtr) { + return RasterizeSVG(interp, imageHandle, svgBlobPtr, + serializedDataLength, destX, destY, width, height, srcX, srcY); } - if (nsvgImage == NULL) { - return TCL_ERROR; + + /* + * Otherwise, the SVGBlob data is contained in the driver internal + * DString. Use that memory. + */ + + result = RasterizeSVG(interp, imageHandle, + Tcl_DStringValue(driverInternalPtr), + Tcl_DStringLength(driverInternalPtr), destX, destY, width, height, + srcX, srcY); + if (result != TCL_OK) { + return result; } - return RasterizeSVG(interp, imageHandle, nsvgImage, destX, destY, - width, height, srcX, srcY, &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; + } + return Tcl_DictObjPut(interp, metadataOutObj, + Tcl_NewStringObj("SVGBLOB",-1), + Tcl_NewByteArrayObj(Tcl_DStringValue(driverInternalPtr), + Tcl_DStringLength(driverInternalPtr))); } /* @@ -357,7 +855,7 @@ ParseSVGWithOptions( const char *input, TkSizeT length, Tcl_Obj *formatObj, - RastOpts *ropts) + serializedHeader *serializedHeaderPtr) { Tcl_Obj **objv = NULL; int objc = 0; @@ -390,9 +888,9 @@ ParseSVGWithOptions( * Process elements of format specification as a list. */ - ropts->scale = 1.0; - ropts->scaleToHeight = 0; - ropts->scaleToWidth = 0; + serializedHeaderPtr->scale = 1.0; + serializedHeaderPtr->scaleToHeight = 0; + serializedHeaderPtr->scaleToWidth = 0; if ((formatObj != NULL) && Tcl_ListObjGetElements(interp, formatObj, &objc, &objv) != TCL_OK) { goto error; @@ -460,11 +958,12 @@ ParseSVGWithOptions( } break; case OPT_SCALE: - if (Tcl_GetDoubleFromObj(interp, objv[0], &ropts->scale) == + if (Tcl_GetDoubleFromObj(interp, objv[0], + &(serializedHeaderPtr->scale)) == TCL_ERROR) { goto error; } - if (ropts->scale <= 0.0) { + if (serializedHeaderPtr->scale <= 0.0) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "-scale value must be positive", -1)); Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "BAD_SCALE", @@ -473,11 +972,11 @@ ParseSVGWithOptions( } break; case OPT_SCALE_TO_HEIGHT: - if (Tcl_GetIntFromObj(interp, objv[0], &ropts->scaleToHeight) == - TCL_ERROR) { + if (Tcl_GetIntFromObj(interp, objv[0], + &(serializedHeaderPtr->scaleToHeight)) == TCL_ERROR) { goto error; } - if (ropts->scaleToHeight <= 0) { + if (serializedHeaderPtr->scaleToHeight <= 0) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "-scaletoheight value must be positive", -1)); Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "BAD_SCALE", @@ -486,11 +985,11 @@ ParseSVGWithOptions( } break; case OPT_SCALE_TO_WIDTH: - if (Tcl_GetIntFromObj(interp, objv[0], &ropts->scaleToWidth) == - TCL_ERROR) { + if (Tcl_GetIntFromObj(interp, objv[0], + &(serializedHeaderPtr->scaleToWidth)) == TCL_ERROR) { goto error; } - if (ropts->scaleToWidth <= 0) { + if (serializedHeaderPtr->scaleToWidth <= 0) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "-scaletowidth value must be positive", -1)); Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "BAD_SCALE", @@ -520,6 +1019,65 @@ error: /* *---------------------------------------------------------------------- * + * 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, + 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 (serializedHeaderPtr->scaleToHeight > 0) { + /* + * Fixed height + */ + height = serializedHeaderPtr->scaleToHeight; + scale = height / serializedHeaderPtr->height; + width = (int) ceil(serializedHeaderPtr->width * scale); + } else if (serializedHeaderPtr->scaleToWidth > 0) { + /* + * Fixed width + */ + width = serializedHeaderPtr->scaleToWidth; + scale = width / serializedHeaderPtr->width; + height = (int) ceil(serializedHeaderPtr->height * scale); + } else { + /* + * Scale factor + */ + scale = serializedHeaderPtr->scale; + width = (int) ceil(serializedHeaderPtr->width * scale); + height = (int) ceil(serializedHeaderPtr->height * scale); + } + + *heightPtr = height; + *widthPtr = width; + return scale; +} + +/* + *---------------------------------------------------------------------- + * * RasterizeSVG -- * * This function is called to rasterize the given nsvgImage and @@ -540,11 +1098,11 @@ static int RasterizeSVG( Tcl_Interp *interp, Tk_PhotoHandle imageHandle, - NSVGimage *nsvgImage, + char *svgBlobPtr, + TkSizeT serializedDataLength, int destX, int destY, int width, int height, - int srcX, int srcY, - RastOpts *ropts) + int srcX, int srcY) { int w, h, c; NSVGrasterizer *rast; @@ -553,15 +1111,16 @@ RasterizeSVG( double scale; (void)srcX; (void)srcY; - - scale = GetScaleFromParameters(nsvgImage, ropts, &w, &h); + + scale = GetScaleFromParameters((serializedHeader *) svgBlobPtr, + &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); - goto cleanAST; + return TCL_ERROR; } imgData = (unsigned char *)attemptckalloc(w * h *4); if (imgData == NULL) { @@ -569,7 +1128,8 @@ RasterizeSVG( Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "OUT_OF_MEMORY", NULL); goto cleanRAST; } - nsvgRasterize(rast, nsvgImage, 0, 0, + + nsvgRasterizeSerialized(rast, svgBlobPtr, 0, 0, (float) scale, imgData, w, h, w * 4); /* transfer the data to a photo block */ svgblock.pixelPtr = imgData; @@ -590,7 +1150,6 @@ RasterizeSVG( } ckfree(imgData); nsvgDeleteRasterizer(rast); - nsvgDelete(nsvgImage); return TCL_OK; cleanimg: @@ -599,244 +1158,369 @@ cleanimg: cleanRAST: nsvgDeleteRasterizer(rast); -cleanAST: - nsvgDelete(nsvgImage); return TCL_ERROR; } /* *---------------------------------------------------------------------- * - * GetScaleFromParameters -- + * Rasterize Serialized -- * - * Get the scale value from the already parsed parameters -scale, - * -scaletoheight and -scaletowidth. - * - * The image width and height is also returned. + * Fiunctions of svgnrast.h which requires modification due to the + * serialized data structure. * * 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( - NSVGimage *nsvgImage, - RastOpts *ropts, - int *widthPtr, - int *heightPtr) +static void nsvg__flattenShapeSerialized(NSVGrasterizer* r, int pathIndex, + NSVGpathSerialized *pathSerializedPtr, float *ptsSerializedPtr, + float scale) { - double scale; - int width, height; + int i, j; + NSVGpathSerialized* path; - 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); + 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); } - - *heightPtr = height; - *widthPtr = width; - return scale; } -/* - *---------------------------------------------------------------------- - * - * 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. - * - *---------------------------------------------------------------------- - */ +static void nsvg__initPaintSerialized(NSVGcachedPaint* cache, + NSVGpaintSerialized* paint, float opacity, + NSVGgradientSerialized *gradientSerializedPtr, + NSVGgradientStop *gradientStopPtr) +{ + int i, j; + NSVGgradientSerialized* grad; -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; -} + cache->type = paint->type; -/* - *---------------------------------------------------------------------- - * - * CacheSVG -- - * - * Add the given svg image informations to the cache for further usage. - * - * Results: - * Return 1 on success, and 0 otherwise. - * - * Side effects: - * - *---------------------------------------------------------------------- - */ + if (paint->type == NSVG_PAINT_COLOR) { + cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); + return; + } -static int -CacheSVG( - Tcl_Interp *interp, - ClientData dataOrChan, - Tcl_Obj *formatObj, - NSVGimage *nsvgImage, - RastOpts *ropts) -{ - TkSizeT length; - const char *data; - NSVGcache *cachePtr = GetCachePtr(interp); + grad = &(gradientSerializedPtr[paint->gradient]); + + cache->spread = grad->spread; + memcpy(cache->xform, grad->xform, sizeof(float)*6); - if (cachePtr != NULL) { - cachePtr->dataOrChan = dataOrChan; - if (formatObj != NULL) { - data = TkGetStringFromObj(formatObj, &length); - Tcl_DStringAppend(&cachePtr->formatString, data, length); + 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; + + 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; } - cachePtr->nsvgImage = nsvgImage; - cachePtr->ropts = *ropts; - return 1; + + 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; + } + } + + for (i = ib; i < 256; i++) + cache->colors[i] = cb; } - return 0; -} -/* - *---------------------------------------------------------------------- - * - * 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. - * - *---------------------------------------------------------------------- - */ +} -static NSVGimage * -GetCachedSVG( - Tcl_Interp *interp, - ClientData dataOrChan, - Tcl_Obj *formatObj, - RastOpts *ropts) +static void nsvg__flattenShapeStrokeSerialized(NSVGrasterizer* r, + NSVGshapeSerialized* shape, NSVGpathSerialized *pathSerializedPtr, + float *ptsSerializedPtr, float scale) { - 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; + 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; } - } else if (Tcl_DStringLength(&cachePtr->formatString) == 0) { - nsvgImage = cachePtr->nsvgImage; - *ropts = cachePtr->ropts; - cachePtr->nsvgImage = NULL; + 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); + + // 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++; + } + } + // 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); } } - CleanCache(interp); - return nsvgImage; } + /* *---------------------------------------------------------------------- * - * CleanCache -- + * RasterizeSVGSerialized -- * - * Reset the cache and delete the saved image in it. + * This function is called to rasterize the given nsvgImage and + * fill the imageHandle with data. * * 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 -CleanCache(Tcl_Interp *interp) +static void nsvgRasterizeSerialized(NSVGrasterizer* r, + char *svgBlobPtr, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride) { - 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; - } + 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; } -} -/* - *---------------------------------------------------------------------- - * - * 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. - * - *---------------------------------------------------------------------- - */ + for (i = 0; i < h; i++) + memset(&dst[i*stride], 0, w*4); -static void -FreeCache(ClientData clientData, Tcl_Interp *interp) -{ - NSVGcache *cachePtr = (NSVGcache *)clientData; - (void)interp; + 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); - Tcl_DStringFree(&cachePtr->formatString); - if (cachePtr->nsvgImage != NULL) { - nsvgDelete(cachePtr->nsvgImage); + // 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); + } + 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); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaintSerialized(&cache, &shape->stroke, shape->opacity, + gradientSerializedPtr, gradientStopPtr); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO); + } } - ckfree(cachePtr); + + nsvg__unpremultiplyAlpha(dst, w, h, stride); + + r->bitmap = NULL; + r->width = 0; + r->height = 0; + r->stride = 0; } |