summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
authoroehhar <harald.oehlmann@elmicron.de>2020-06-21 12:06:34 (GMT)
committeroehhar <harald.oehlmann@elmicron.de>2020-06-21 12:06:34 (GMT)
commit2db28ea679f6ec225e01b37471beb98385f2741a (patch)
treeda98804d049bd9c6f6a9c0aa5ad846ec99d13aef /generic
parentfab62ba87797a77dd9eac2c168580ea41829711f (diff)
downloadtk-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.c6
-rw-r--r--generic/tkImgSVGnano.c1232
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;
}