summaryrefslogtreecommitdiffstats
path: root/generic/tkImgSVGnano.c
diff options
context:
space:
mode:
authoroehhar <harald.oehlmann@elmicron.de>2019-06-07 18:26:29 (GMT)
committeroehhar <harald.oehlmann@elmicron.de>2019-06-07 18:26:29 (GMT)
commit8f9a049f972693966361e82b11cb4a85b9d52c56 (patch)
tree4866092a97e70be4cfeae7938be5880a9c91ba7a /generic/tkImgSVGnano.c
parent9eaf1ac422fac0fe4410f3f5576e76c353e7bd41 (diff)
downloadtk-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.c178
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