diff options
author | oehhar <harald.oehlmann@elmicron.de> | 2019-06-07 18:26:29 (GMT) |
---|---|---|
committer | oehhar <harald.oehlmann@elmicron.de> | 2019-06-07 18:26:29 (GMT) |
commit | 8f9a049f972693966361e82b11cb4a85b9d52c56 (patch) | |
tree | 4866092a97e70be4cfeae7938be5880a9c91ba7a /generic/tkImgSVGnano.c | |
parent | 9eaf1ac422fac0fe4410f3f5576e76c353e7bd41 (diff) | |
download | tk-8f9a049f972693966361e82b11cb4a85b9d52c56.zip tk-8f9a049f972693966361e82b11cb4a85b9d52c56.tar.gz tk-8f9a049f972693966361e82b11cb4a85b9d52c56.tar.bz2 |
Implement -scaletowidth/height, output very small scale images as 1x1 images.
Diffstat (limited to 'generic/tkImgSVGnano.c')
-rw-r--r-- | generic/tkImgSVGnano.c | 178 |
1 files changed, 161 insertions, 17 deletions
diff --git a/generic/tkImgSVGnano.c b/generic/tkImgSVGnano.c index da28d9b..3f1f7e4 100644 --- a/generic/tkImgSVGnano.c +++ b/generic/tkImgSVGnano.c @@ -31,6 +31,8 @@ typedef struct { double scale; + int scaleToHeight; + int scaleToWidth; } RastOpts; /* @@ -40,6 +42,10 @@ typedef struct { */ typedef struct { + /* A poiner to remember if it is the same svn image (data) + * It is a Tcl_Channel if image created by -file option + * or a Tcl_Obj, if image is created with the -data option + */ ClientData dataOrChan; Tcl_DString formatString; NSVGimage *nsvgImage; @@ -66,6 +72,8 @@ static int RasterizeSVG(Tcl_Interp *interp, Tk_PhotoHandle imageHandle, NSVGimage *nsvgImage, 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, @@ -132,12 +140,11 @@ FileMatchSVG( nsvgImage = ParseSVGWithOptions(interp, data, length, formatObj, &ropts); Tcl_DecrRefCount(dataObj); if (nsvgImage != NULL) { - *widthPtr = (int) ceil(nsvgImage->width * ropts.scale); - *heightPtr = (int) ceil(nsvgImage->height * ropts.scale); - if ((*widthPtr <= 0) || (*heightPtr <= 0)) { - nsvgDelete(nsvgImage); - return 0; - } + GetScaleFromParameters( + nsvgImage, + &ropts, + widthPtr, + heightPtr); if (!CacheSVG(interp, chan, formatObj, nsvgImage, &ropts)) { nsvgDelete(nsvgImage); } @@ -237,12 +244,11 @@ StringMatchSVG( data = Tcl_GetStringFromObj(dataObj, &length); nsvgImage = ParseSVGWithOptions(interp, data, length, formatObj, &ropts); if (nsvgImage != NULL) { - *widthPtr = (int) ceil(nsvgImage->width * ropts.scale); - *heightPtr = (int) ceil(nsvgImage->height * ropts.scale); - if ((*widthPtr <= 0) || (*heightPtr <= 0)) { - nsvgDelete(nsvgImage); - return 0; - } + GetScaleFromParameters( + nsvgImage, + &ropts, + widthPtr, + heightPtr); if (!CacheSVG(interp, dataObj, formatObj, nsvgImage, &ropts)) { nsvgDelete(nsvgImage); } @@ -324,11 +330,12 @@ ParseSVGWithOptions( double dpi = 96.0; char *inputCopy = NULL; NSVGimage *nsvgImage; + int parameterScaleSeen = 0; static const char *const fmtOptions[] = { - "-dpi", "-scale", NULL + "-dpi", "-scale", "-scaletoheight", "-scaletowidth", NULL }; enum fmtOptions { - OPT_DPI, OPT_SCALE + OPT_DPI, OPT_SCALE, OPT_SCALE_TO_HEIGHT, OPT_SCALE_TO_WIDTH }; /* @@ -350,6 +357,8 @@ ParseSVGWithOptions( */ ropts->scale = 1.0; + ropts->scaleToHeight = 0; + ropts->scaleToWidth = 0; if ((formatObj != NULL) && Tcl_ListObjGetElements(interp, formatObj, &objc, &objv) != TCL_OK) { goto error; @@ -380,6 +389,26 @@ ParseSVGWithOptions( objc--; objv++; + /* + * check that only one scale option is given + */ + switch ((enum fmtOptions) optIndex) { + case OPT_SCALE: + case OPT_SCALE_TO_HEIGHT: + case OPT_SCALE_TO_WIDTH: + if ( parameterScaleSeen ) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "only one of -scale, -scaletoheight, -scaletowidth may be given", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "BAD_SCALE", + NULL); + goto error; + } + parameterScaleSeen = 1; + } + + /* + * Decode parameters + */ switch ((enum fmtOptions) optIndex) { case OPT_DPI: if (Tcl_GetDoubleFromObj(interp, objv[0], &dpi) == TCL_ERROR) { @@ -406,6 +435,32 @@ ParseSVGWithOptions( goto error; } break; + case OPT_SCALE_TO_HEIGHT: + if (Tcl_GetIntFromObj(interp, objv[0], &ropts->scaleToHeight) == + TCL_ERROR) { + goto error; + } + if (ropts->scaleToHeight <= 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "-scaletoheight value must be positive", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "BAD_SCALE", + NULL); + goto error; + } + break; + case OPT_SCALE_TO_WIDTH: + if (Tcl_GetIntFromObj(interp, objv[0], &ropts->scaleToWidth) == + TCL_ERROR) { + goto error; + } + if (ropts->scaleToWidth <= 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "-scaletowidth value must be positive", -1)); + Tcl_SetErrorCode(interp, "TK", "IMAGE", "SVG", "BAD_SCALE", + NULL); + goto error; + } + break; } } @@ -458,9 +513,14 @@ RasterizeSVG( NSVGrasterizer *rast; unsigned char *imgData; Tk_PhotoImageBlock svgblock; + double scale; + + scale = GetScaleFromParameters( + nsvgImage, + ropts, + &w, + &h); - w = (int) ceil(nsvgImage->width * ropts->scale); - h = (int) ceil(nsvgImage->height * ropts->scale); rast = nsvgCreateRasterizer(); if (rast == NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj("cannot initialize rasterizer", -1)); @@ -475,7 +535,7 @@ RasterizeSVG( goto cleanRAST; } nsvgRasterize(rast, nsvgImage, 0, 0, - (float) ropts->scale, imgData, w, h, w * 4); + (float) scale, imgData, w, h, w * 4); /* transfer the data to a photo block */ svgblock.pixelPtr = imgData; svgblock.width = w; @@ -512,6 +572,90 @@ cleanAST: /* *---------------------------------------------------------------------- * + * GetScaleFromParameters -- + * + * Get the scale value from the already parsed parameters -scale, + * -scaletoheight and -scaletowidth. + * + * The image width and height is also returned. + * Both are greater or equal to 1. + * + * Results: + * The evaluated or configured scale value or 0.0 on failure + * + * Side effects: + * height and width is set to heightPtr and widthPtr. + * + *---------------------------------------------------------------------- + */ + +static double +GetScaleFromParameters( + NSVGimage *nsvgImage, + RastOpts *ropts, + int *widthPtr, + int *heightPtr) +{ + double scale; + int width, height; + /* + * To avoid division by 0, check for positive image size + */ + if ((nsvgImage->width == 0.0) || (nsvgImage->height == 0.0)) { + /* + * Image width or height is zero. + * This might be due to a small image with a very high dpi value. + * Set image size to 1x1 pixel, which is the minimum + * This is more sensible than throwing an error. + */ + width = 1; + height = 1; + scale = 1.0; + } else if (ropts->scaleToHeight > 0) { + /* + * Fix height + */ + height = ropts->scaleToHeight; + scale = height / nsvgImage->height; + width = (int) ceil(nsvgImage->width * scale); + } else if (ropts->scaleToWidth > 0) { + /* + * Fix 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); + } + /* + * Set width or height to minimum 1 pixel. + * This is the minimum. + * It is more sensible to scale to the minimum than output an + * error on very small scales. + */ + if (width <= 0) { + width = 1; + } + if (height <= 0) { + height = 1; + } + /* + * Output the found values + */ + *heightPtr = height; + *widthPtr = width; + return scale; +} + +/* + *---------------------------------------------------------------------- + * * GetCachePtr -- * * This function is called to get the per interpreter used |