Graphviz 14.1.2~dev.20251230.1146
Loading...
Searching...
No Matches
gdtclft.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13#include "../tcl-compat.h"
14#include "gd.h"
15#include <assert.h>
16#include <errno.h>
17#include <limits.h>
18#include <math.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <tcl.h>
23#include <util/agxbuf.h>
24#include <util/startswith.h>
25#include <util/streq.h>
26
27#ifdef _WIN32
28#include <windows.h>
29#endif
30
31static Tcl_UpdateStringProc GdPtrTypeUpdate;
32static Tcl_SetFromAnyProc GdPtrTypeSet;
33static Tcl_ObjType GdPtrType = {.name = "gd",
34 .updateStringProc = GdPtrTypeUpdate,
35 .setFromAnyProc = GdPtrTypeSet};
36#define IMGPTR(O) (O->internalRep.otherValuePtr)
37
38/* The only two symbols exported */
39#ifdef GVDLL
40__declspec(dllexport)
41#endif
42Tcl_AppInitProc Gdtclft_Init;
43#ifdef GVDLL
44__declspec(dllexport)
45#endif
46Tcl_AppInitProc Gdtclft_SafeInit;
47
48typedef int(GdDataFunction)(Tcl_Interp *interp, int argc,
49 Tcl_Obj *const objv[]);
50typedef int(GdImgFunction)(Tcl_Interp *interp, gdImagePtr gdImg, int argc,
51 const int args[]);
52
58
62
63typedef struct {
64 const char *cmd;
66 unsigned int minargs, maxargs;
67 unsigned int subcmds;
68 unsigned int ishandle;
69 unsigned int unsafearg;
70 const char *usage;
72
73typedef struct {
74 const char *cmd;
76 unsigned int minargs, maxargs;
77 const char *usage;
79
81 {"create", tclGdCreateCmd, 2, 3, 0, 0, 0, "width heighti ?true?"},
82 {"createTrueColor", tclGdCreateCmd, 2, 2, 0, 0, 2, "width height"},
83 {"createFromGD", tclGdCreateCmd, 1, 1, 0, 0, 2, "filehandle"},
84#ifdef HAVE_LIBZ
85 {"createFromGD2", tclGdCreateCmd, 1, 1, 0, 0, 2, "filehandle"},
86#endif
87 {"createFromGIF", tclGdCreateCmd, 1, 1, 0, 0, 2, "filehandle"},
88#ifdef HAVE_GD_JPEG
89 {"createFromJPEG", tclGdCreateCmd, 1, 1, 0, 0, 2, "filehandle"},
90#endif
91#ifdef HAVE_GD_PNG
92 {"createFromPNG", tclGdCreateCmd, 1, 1, 0, 0, 2, "filehandle"},
93#endif
94 {"createFromWBMP", tclGdCreateCmd, 1, 1, 0, 0, 2, "filehandle"},
95#ifdef HAVE_GD_XPM
96 {"createFromXBM", tclGdCreateCmd, 1, 1, 0, 0, 2, "filehandle"},
97#endif
98
99 {"destroy", tclGdDestroyCmd, 1, 1, 0, 1, 0, "gdhandle"},
100 {"writeGD", tclGdWriteCmd, 2, 2, 0, 1, 3, "gdhandle filehandle"},
101#ifdef HAVE_LIBZ
102 {"writeGD2", tclGdWriteCmd, 2, 2, 0, 1, 3, "gdhandle filehandle"},
103#endif
104 {"writeGIF", tclGdWriteCmd, 2, 2, 0, 1, 3, "gdhandle filehandle"},
105#ifdef HAVE_GD_JPEG
106 {"writeJPEG", tclGdWriteCmd, 2, 2, 0, 1, 3, "gdhandle filehandle"},
107#endif
108#ifdef HAVE_GD_PNG
109 {"writePNG", tclGdWriteCmd, 2, 2, 0, 1, 3, "gdhandle filehandle"},
110#endif
111 {"writeWBMP", tclGdWriteCmd, 2, 2, 0, 1, 3, "gdhandle filehandle"},
112#ifdef HAVE_GD_XPM
113 {"writeXBM", tclGdWriteCmd, 2, 2, 0, 1, 3, "gdhandle filehandle"},
114#endif
115#ifdef HAVE_GD_PNG
116 {"writePNGvar", tclGdWriteBufCmd, 2, 2, 0, 1, 0, "gdhandle var"},
117#endif
118 {"interlace", tclGdInterlaceCmd, 1, 2, 0, 1, 0, "gdhandle ?on-off?"},
119 {"color", tclGdColorCmd, 2, 5, 1, 1, 0, "option values..."},
120 {"brush", tclGdBrushCmd, 2, 2, 0, 2, 0, "gdhandle brushhandle"},
121 {"style", tclGdStyleCmd, 2, 999, 0, 1, 0, "gdhandle color..."},
122 {"tile", tclGdTileCmd, 2, 2, 0, 2, 0, "gdhandle tilehandle"},
123 {"set", tclGdSetCmd, 4, 4, 0, 1, 0, "gdhandle color x y"},
124 {"line", tclGdLineCmd, 6, 6, 0, 1, 0, "gdhandle color x1 y1 x2 y2"},
125 {"rectangle", tclGdRectCmd, 6, 6, 0, 1, 0, "gdhandle color x1 y1 x2 y2"},
126 {"fillrectangle", tclGdRectCmd, 6, 6, 0, 1, 0,
127 "gdhandle color x1 y1 x2 y2"},
128 {"arc", tclGdArcCmd, 8, 8, 0, 1, 0,
129 "gdhandle color cx cy width height start end"},
130 {"fillarc", tclGdArcCmd, 8, 8, 0, 1, 0,
131 "gdhandle color cx cy width height start end"},
132 {"openarc", tclGdArcCmd, 8, 8, 0, 1, 0,
133 "gdhandle color cx cy width height start end"},
134 {"chord", tclGdArcCmd, 8, 8, 0, 1, 0,
135 "gdhandle color cx cy width height start end"},
136 {"fillchord", tclGdArcCmd, 8, 8, 0, 1, 0,
137 "gdhandle color cx cy width height start end"},
138 {"openchord", tclGdArcCmd, 8, 8, 0, 1, 0,
139 "gdhandle color cx cy width height start end"},
140 {"pie", tclGdArcCmd, 8, 8, 0, 1, 0,
141 "gdhandle color cx cy width height start end"},
142 {"fillpie", tclGdArcCmd, 8, 8, 0, 1, 0,
143 "gdhandle color cx cy width height start end"},
144 {"openpie", tclGdArcCmd, 8, 8, 0, 1, 0,
145 "gdhandle color cx cy width height start end"},
146 {"polygon", tclGdPolygonCmd, 2, 999, 0, 1, 0,
147 "gdhandle color x1 y1 x2 y2 x3 y3 ..."},
148 {"fillpolygon", tclGdPolygonCmd, 3, 999, 0, 1, 0,
149 "gdhandle color x1 y1 x2 y2 x3 y3 ..."},
150 {"fill", tclGdFillCmd, 4, 5, 0, 1, 0, "gdhandle color x y ?bordercolor?"},
151 /*
152 * we allow null gd handles to the text command to allow program to get size
153 * of text string, so the text command provides its own handle processing
154 * and checking
155 */
156 {"text", tclGdTextCmd, 8, 8, 0, 0, 4,
157 "gdhandle color fontname size angle x y string"},
158 {"copy", tclGdCopyCmd, 8, 10, 0, 2, 0,
159 "desthandle srchandle destx desty srcx srcy destw desth ?srcw srch?"},
160 {"get", tclGdGetCmd, 3, 3, 0, 1, 0, "gdhandle x y"},
161 {"size", tclGdSizeCmd, 1, 1, 0, 1, 0, "gdhandle"},
162};
163
165 {"new", tclGdColorNewCmd, 5, 5, "red green blue"},
166 {"exact", tclGdColorExactCmd, 5, 5, "red green blue"},
167 {"closest", tclGdColorClosestCmd, 5, 5, "red green blue"},
168 {"resolve", tclGdColorResolveCmd, 5, 5, "red green blue"},
169 {"free", tclGdColorFreeCmd, 3, 3, "color"},
170 {"transparent", tclGdColorTranspCmd, 2, 3, "?color?"},
171 {"get", tclGdColorGetCmd, 2, 3, "?color?"}};
172
173/*
174 * Helper function to interpret color_idx values.
175 */
176static int tclGd_GetColor(Tcl_Interp *interp, Tcl_Obj *obj, int *color) {
177 int retval = TCL_OK;
178 Tcl_Obj **theList;
179
180 /* Assume it's an integer, check other cases on failure. */
181 if (Tcl_GetIntFromObj(interp, obj, color) == TCL_OK)
182 return TCL_OK;
183 else {
184 Tcl_ResetResult(interp);
185 Tcl_Size nlist;
186 if (Tcl_ListObjGetElements(interp, obj, &nlist, &theList) != TCL_OK)
187 return TCL_ERROR;
188 if (nlist < 1 || nlist > 2)
189 retval = TCL_ERROR;
190 else {
191 char *firsttag = Tcl_GetString(theList[0]);
192 switch (firsttag[0]) {
193 case 'b':
194 *color = gdBrushed;
195 if (nlist == 2) {
196 char *secondtag = Tcl_GetString(theList[1]);
197 if (secondtag[0] == 's') {
198 *color = gdStyledBrushed;
199 } else {
200 retval = TCL_ERROR;
201 }
202 }
203 break;
204
205 case 's':
206 *color = gdStyled;
207 if (nlist == 2) {
208 char *secondtag = Tcl_GetString(theList[1]);
209 if (secondtag[0] == 'b') {
210 *color = gdStyledBrushed;
211 } else {
212 retval = TCL_ERROR;
213 }
214 }
215 break;
216
217 case 't':
218 *color = gdTiled;
219 break;
220
221 default:
222 retval = TCL_ERROR;
223 }
224 }
225 }
226 if (retval == TCL_ERROR)
227 Tcl_SetResult(interp, "Malformed special color value", TCL_STATIC);
228
229 return retval;
230}
231
232/*
233 * GD composite command:
234 *
235 * gd create <width> <height>
236 * Return a handle to a new gdImage that is width X height.
237 * gd createTrueColor <width> <height>
238 * Return a handle to a new trueColor gdImage that is width X
239 * height. gd createFromGD <filehandle> gd createFromGD2 <filehandle> gd
240 * createFromGIF <filehandle> gd createFromJPEG <filehandle> gd createFromPNG
241 * <filehandle> gd createFromWBMP <filehandle> gd createFromXBM <filehandle>
242 * Return a handle to a new gdImage created by reading an
243 * image from the file of the indicated format
244 * open on filehandle.
245 *
246 * gd destroy <gdhandle>
247 * Destroy the gdImage referred to by gdhandle.
248 *
249 * gd writeGD <gdhandle> <filehandle>
250 * gd writeGD2 <gdhandle> <filehandle>
251 * gd writeGIF <gdhandle> <filehandle>
252 * gd writeJPEG <gdhandle> <filehandle>
253 * gd writePNG <gdhandle> <filehandle>
254 * gd writeWBMP <gdhandle> <filehandle>
255 * gd writeXBM <gdhandle> <filehandle>
256 * Write the image in gdhandle to filehandle in the
257 * format indicated.
258 *
259 * gd color new <gdhandle> <red> <green> <blue>
260 * Allocate a new color with the given RGB values. Returns the
261 * color_idx, or -1 on failure (256 colors already allocated).
262 * gd color exact <gdhandle> <red> <green> <blue>
263 * Find a color_idx in the image that exactly matches the given RGB
264 * color. Returns the color_idx, or -1 if no exact match. gd color closest
265 * <gdhandle> <red> <green> <blue> Find a color in the image that is closest to
266 * the given RGB color. Guaranteed to return a color idx. gd color resolve
267 * <gdhandle> <red> <green> <blue> Return the index of the best possible effort
268 * to get a color. Guaranteed to return a color idx. Equivalent to: if {[set
269 * idx [gd color exact $gd $r $g $b]] == -1} { if {[set idx [gd color neW $Gd $r
270 * $g $b]] == -1} { set idx [gd color closest $gd $r $g $b]
271 * }
272 * }
273 * gd color free <gdhandle> <color_idx>
274 * Free the color at the given color_idx for reuse.
275 * gd color transparent <gdhandle> <color_idx>
276 * Mark the color_idx as the transparent background color.
277 * gd color get <gdhandle> [<color_idx>]
278 * Return the RGB value at <color_idx>, or {} if it is not
279 * allocated. If <color_idx> is not specified, return a list of {color_idx R G
280 * B} values for all allocated colors. gd color gettransparent <gdhandle> Return
281 * the color_idx of the transparent color.
282 *
283 * gd brush <gdhandle> <brushhandle>
284 * Set the brush image to be used for brushed lines. Transparent
285 * pixels in the brush will not change the image when the brush
286 * is applied.
287 * gd style <gdhandle> <color_idx> ...
288 * Set the line style to the list of color indices. This is
289 * interpreted in one of two ways. For a simple styled line, each color is
290 * applied to points along the line in turn. The transparent color
291 * value may be used to leave gaps in the line. For a styled,
292 * brushed line, a 0 (or the transparent color_idx) means not to fill the pixel,
293 * and a non-zero value means to apply the brush.
294 * gd tile <gdhandle> <tilehandle>
295 * Set the tile image to be used for tiled fills. Transparent
296 * pixels in the tile will not change the underlying image during tiling.
297 *
298 * In all drawing functions, the color_idx is a number, or may be one of the
299 * strings styled, brushed, tiled, "styled brushed" or "brushed styled". The
300 * style, brush, or tile currently in effect will be used. Brushing and
301 * styling apply to lines, tiling to filled areas.
302 *
303 * gd set <gdhandle> <color_idx> <x> <y>
304 * Set the pixel at (x,y) to color <color_idx>.
305 * gd line <gdhandle> <color_idx> <x1> <y1> <x2> <y2>
306 * Draw a line in color <color_idx> from (x1,y1) to (x2,y2).
307 * gd rectangle <gdhandle> <color_idx> <x1> <y1> <x2> <y2>
308 * gd fillrectangle <gdhandle> <color_idx> <x1> <y1> <x2> <y2>
309 * Draw the outline of (resp. fill) a rectangle in color
310 * <color_idx> with corners at (x1,y1) and (x2,y2). gd arc <gdhandle>
311 * <color_idx> <cx> <cy> <width> <height> <start> <end> gd fillarc <gdhandle>
312 * <color_idx> <cx> <cy> <width> <height> <start> <end> Draw an arc, or filled
313 * segment, in color <color_idx>, centered at (cx,cy) in a rectangle width x
314 * height, starting at start degrees and ending at end degrees.
315 * Start must be > end. gd polygon <gdhandle> <color_idx> <x1> <y1> ... gd
316 * fillpolygon <gdhandle> <color_idx> <x1> <y1> ... Draw the outline of, or
317 * fill, a polygon specified by the x, y coordinate list.
318 *
319 * gd fill <gdhandle> <color_idx> <x> <y>
320 * gd fill <gdhandle> <color_idx> <x> <y> <borderindex>
321 * Fill with color <color_idx>, starting from (x,y) within a region
322 * of pixels all the color of the pixel at (x,y) (resp., within a
323 * border colored borderindex).
324 *
325 * gd size <gdhandle>
326 * Returns a list {width height} of the image.
327 *
328 * gd text <gdhandle> <color_idx> <fontname> <size> <angle> <x> <y> <string>
329 * Draw text using <fontname> in color <color_idx>,
330 * with pointsize <size>, rotation in radians <angle>, with lower left
331 * corner at (x,y). String may contain UTF8 sequences like: "&#192;"
332 * Returns 4 corner coords of bounding rectangle.
333 * Use gdhandle = {} to get boundary without rendering.
334 * Use negative of color_idx to disable antialiasing.
335 *
336 * The file <fontname>.ttf must be found in the builtin DEFAULT_FONTPATH
337 * or in the fontpath specified in a GDFONTPATH environment variable.
338 *
339 * gd copy <desthandle> <srchandle> <destx> <desty> <srcx> <srcy> <w> <h>
340 * gd copy <desthandle> <srchandle> <destx> <desty> <srcx> <srcy> \
341 * <destw> <desth> <srcw> <srch>
342 * Copy a subimage from srchandle(srcx, srcy) to
343 * desthandle(destx, desty), size w x h. Or, resize the subimage
344 * in copying from srcw x srch to destw x desth.
345 *
346 */
347static int gdCmd(ClientData clientData, Tcl_Interp *interp, int argc,
348 Tcl_Obj *const objv[]) {
349 /* Check for subcommand. */
350 if (argc < 2) {
351 Tcl_SetResult(interp, "wrong # args: should be \"gd option ...\"",
352 TCL_STATIC);
353 return TCL_ERROR;
354 }
355
356 /* Find the subcommand. */
357 for (size_t subi = 0; subi < sizeof(subcmdVec) / sizeof(subcmdVec[0]);
358 subi++) {
359 if (streq(subcmdVec[subi].cmd, Tcl_GetString(objv[1]))) {
360
361 /* Check arg count. */
362 if ((unsigned)argc - 2 < subcmdVec[subi].minargs ||
363 (unsigned)argc - 2 > subcmdVec[subi].maxargs) {
364 Tcl_WrongNumArgs(interp, 2, objv, subcmdVec[subi].usage);
365 return TCL_ERROR;
366 }
367
368 /* Check for valid handle(s). */
369 if (subcmdVec[subi].ishandle > 0) {
370 /* Check each handle to see if it's a valid handle. */
371 if (2 + subcmdVec[subi].subcmds + subcmdVec[subi].ishandle >
372 (unsigned)argc) {
373 Tcl_SetResult(interp, "GD handle(s) not specified", TCL_STATIC);
374 return TCL_ERROR;
375 }
376 for (unsigned argi = 2 + subcmdVec[subi].subcmds;
377 argi < 2 + subcmdVec[subi].subcmds + subcmdVec[subi].ishandle;
378 argi++) {
379 if (objv[argi]->typePtr != &GdPtrType &&
380 GdPtrTypeSet(interp, objv[argi]) != TCL_OK)
381 return TCL_ERROR;
382 }
383 }
384 /*
385 * If we are operating in a safe interpreter, check,
386 * if this command is suspect -- and only let existing
387 * filehandles through, if so.
388 */
389 if (clientData != NULL && subcmdVec[subi].unsafearg != 0) {
390 const char *fname = Tcl_GetString(objv[subcmdVec[subi].unsafearg]);
391 if (!Tcl_IsChannelExisting(fname)) {
392 Tcl_AppendResult(interp, "Access to ", fname,
393 " not allowed in safe interpreter", NULL);
394 return TCL_ERROR;
395 }
396 }
397 /* Call the subcommand function. */
398 return subcmdVec[subi].f(interp, argc, objv);
399 }
400 }
401
402 /* If we get here, the option doesn't match. */
403 Tcl_AppendResult(interp, "bad option \"", Tcl_GetString(objv[1]),
404 "\": should be ", 0);
405 for (size_t subi = 0; subi < sizeof(subcmdVec) / sizeof(subcmdVec[0]); subi++)
406 Tcl_AppendResult(interp, (subi > 0 ? ", " : ""), subcmdVec[subi].cmd, 0);
407 return TCL_ERROR;
408}
409
410static int tclGdCreateCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
411 int w, h;
412 gdImagePtr im = NULL;
413 int fileByName;
414
415 char *cmd = Tcl_GetString(objv[1]);
416 if (streq(cmd, "create")) {
417 int trueColor = 0;
418 if (Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK)
419 return TCL_ERROR;
420 if (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)
421 return TCL_ERROR;
422 /* An optional argument may specify true for "TrueColor" */
423 if (argc == 5 &&
424 Tcl_GetBooleanFromObj(interp, objv[4], &trueColor) == TCL_ERROR)
425 return TCL_ERROR;
426 if (trueColor)
427 im = gdImageCreateTrueColor(w, h);
428 else
429 im = gdImageCreate(w, h);
430 if (im == NULL) {
431 char buf[255];
432 snprintf(buf, sizeof(buf), "GD unable to allocate %d X %d image", w, h);
433 Tcl_SetResult(interp, buf, TCL_VOLATILE);
434 return TCL_ERROR;
435 }
436 } else if (streq(cmd, "createTrueColor")) {
437 if (Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK)
438 return TCL_ERROR;
439 if (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)
440 return TCL_ERROR;
441 im = gdImageCreateTrueColor(w, h);
442 if (im == NULL) {
443 char buf[255];
444 snprintf(buf, sizeof(buf), "GD unable to allocate %d X %d image", w, h);
445 Tcl_SetResult(interp, buf, TCL_VOLATILE);
446 return TCL_ERROR;
447 }
448 } else {
449 char *arg2 = Tcl_GetString(objv[2]);
450 fileByName = 0; /* first try to get file from open channel */
451 FILE *filePtr = NULL;
452#if !defined(_WIN32)
453 ClientData clientdata;
454 if (Tcl_GetOpenFile(interp, arg2, 0, 1, &clientdata) == TCL_OK) {
455 filePtr = (FILE *)clientdata;
456 }
457#endif
458 if (filePtr == NULL) {
459 /* Not a channel, or Tcl_GetOpenFile() not supported.
460 * See if we can open directly.
461 */
462 if ((filePtr = fopen(arg2, "rb")) == NULL) {
463 return TCL_ERROR;
464 }
465 fileByName++;
466 Tcl_ResetResult(interp);
467 }
468
469 /* Read file */
470 if (streq(&cmd[10], "GD")) {
471 im = gdImageCreateFromGd(filePtr);
472#ifdef HAVE_LIBZ
473 } else if (streq(&cmd[10], "GD2")) {
474 im = gdImageCreateFromGd2(filePtr);
475#endif
476 } else if (streq(&cmd[10], "GIF")) {
477 im = gdImageCreateFromGif(filePtr);
478#ifdef HAVE_GD_JPEG
479 } else if (streq(&cmd[10], "JPEG")) {
480 im = gdImageCreateFromJpeg(filePtr);
481#endif
482#ifdef HAVE_GD_PNG
483 } else if (streq(&cmd[10], "PNG")) {
484 im = gdImageCreateFromPng(filePtr);
485#endif
486 } else if (streq(&cmd[10], "WBMP")) {
487 im = gdImageCreateFromWBMP(filePtr);
488#ifdef HAVE_GD_XPM
489 } else if (streq(&cmd[10], "XBM")) {
490 im = gdImageCreateFromXbm(filePtr);
491#endif
492 } else {
493 Tcl_AppendResult(interp, cmd + 10, "unrecognizable format requested",
494 NULL);
495 if (fileByName) {
496 fclose(filePtr);
497 }
498 return TCL_ERROR;
499 }
500 if (fileByName) {
501 fclose(filePtr);
502 }
503 if (im == NULL) {
504 Tcl_AppendResult(interp, "GD unable to read image file '", arg2, "` as ",
505 cmd + 10, NULL);
506 return TCL_ERROR;
507 }
508 }
509
510 Tcl_Obj *result = Tcl_NewObj();
511 IMGPTR(result) = im;
512 result->typePtr = &GdPtrType;
513 result->bytes = NULL;
514 Tcl_SetObjResult(interp, result);
515 return TCL_OK;
516}
517
518static int tclGdDestroyCmd(Tcl_Interp *interp, int argc,
519 Tcl_Obj *const objv[]) {
520 (void)interp;
521 (void)argc;
522
523 /* Get the image pointer and destroy it */
524 gdImagePtr im = IMGPTR(objv[2]);
525 gdImageDestroy(im);
526
527 return TCL_OK;
528}
529
530static int tclGdWriteCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
531 int arg4;
532
533 const char *cmd = Tcl_GetString(objv[1]);
534 if (cmd[5] == 'J' || cmd[5] == 'W') {
535 /* JPEG and WBMP expect an extra (integer) argument */
536 if (argc < 5) {
537 if (cmd[5] == 'J')
538 arg4 = -1; /* default quality-level */
539 else {
540 Tcl_SetResult(interp, "WBMP saving requires the foreground pixel value",
541 TCL_STATIC);
542 return TCL_ERROR;
543 }
544 } else if (Tcl_GetIntFromObj(interp, objv[4], &arg4) != TCL_OK)
545 return TCL_ERROR;
546
547 if (cmd[5] == 'J' && argc > 4 && (arg4 > 100 || arg4 < 1)) {
548 Tcl_SetObjResult(interp, objv[4]);
549 Tcl_AppendResult(interp,
550 ": JPEG image quality, if specified, must be an integer "
551 "from 1 to 100, or -1 for default",
552 NULL);
553 return TCL_ERROR;
554 }
555 /* XXX no error-checking for the WBMP case here */
556 }
557 /* Get the image pointer. */
558 gdImagePtr im = IMGPTR(objv[2]);
559 const char *fname = Tcl_GetString(objv[3]);
560
561 /* Get the file reference. */
562 int fileByName = 0; // first try to get file from open channel
563 FILE *filePtr = NULL;
564#if !defined(_WIN32)
565 ClientData clientdata;
566 if (Tcl_GetOpenFile(interp, fname, 1, 1, &clientdata) == TCL_OK) {
567 filePtr = (FILE *)clientdata;
568 }
569#endif
570 if (filePtr == NULL) {
571 /* Not a channel, or Tcl_GetOpenFile() not supported.
572 * See if we can open directly.
573 */
574 fileByName++;
575 if ((filePtr = fopen(fname, "wb")) == NULL) {
576 Tcl_AppendResult(interp, "could not open :", fname,
577 "': ", strerror(errno), NULL);
578 return TCL_ERROR;
579 }
580 Tcl_ResetResult(interp);
581 }
582
583 /*
584 * Write IM to OUTFILE as a JFIF-formatted JPEG image, using quality
585 * JPEG_QUALITY. If JPEG_QUALITY is in the range 0-100, increasing values
586 * represent higher quality but also larger image size. If JPEG_QUALITY is
587 * negative, the IJG JPEG library's default quality is used (which
588 * should be near optimal for many applications). See the IJG JPEG
589 * library documentation for more details. */
590
591 /* Do it. */
592 if (streq(&cmd[5], "GD")) {
593 gdImageGd(im, filePtr);
594 } else if (streq(&cmd[5], "GD2")) {
595#ifdef HAVE_LIBZ
596#define GD2_CHUNKSIZE 128
597#define GD2_COMPRESSED 2
598 gdImageGd2(im, filePtr, GD2_CHUNKSIZE, GD2_COMPRESSED);
599#endif
600 } else if (streq(&cmd[5], "GIF")) {
601 gdImageGif(im, filePtr);
602#ifdef HAVE_GD_JPEG
603 } else if (streq(&cmd[5], "JPEG")) {
604#define JPEG_QUALITY -1
605 gdImageJpeg(im, filePtr, JPEG_QUALITY);
606#endif
607#ifdef HAVE_GD_PNG
608 } else if (streq(&cmd[5], "PNG")) {
609 gdImagePng(im, filePtr);
610#endif
611 } else if (streq(&cmd[5], "WBMP")) {
612 /* Assume the color closest to black is the foreground
613 color for the B&W wbmp image. */
614 int foreground = gdImageColorClosest(im, 0, 0, 0);
615 gdImageWBMP(im, foreground, filePtr);
616 } else {
617 /* cannot happen - but would result in an empty output file */
618 }
619 if (fileByName) {
620 fclose(filePtr);
621 } else {
622 fflush(filePtr);
623 }
624 return TCL_OK;
625}
626
627static int tclGdInterlaceCmd(Tcl_Interp *interp, int argc,
628 Tcl_Obj *const objv[]) {
629 int on_off;
630
631 /* Get the image pointer. */
632 gdImagePtr im = IMGPTR(objv[2]);
633
634 if (argc == 4) {
635 /* Get the on_off values. */
636 if (Tcl_GetBooleanFromObj(interp, objv[3], &on_off) != TCL_OK)
637 return TCL_ERROR;
638
639 /* Do it. */
640 gdImageInterlace(im, on_off);
641 } else {
642 /* Get the current state. */
643 on_off = gdImageGetInterlaced(im);
644 }
645 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(on_off));
646 return TCL_OK;
647}
648
649static int tclGdColorCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
650 int args[3];
651
652 int nsub = sizeof(colorCmdVec) / sizeof(colorCmdVec[0]);
653 if (argc >= 3) {
654 /* Find the subcommand. */
655 for (int subi = 0; subi < nsub; subi++) {
656 if (streq(colorCmdVec[subi].cmd, Tcl_GetString(objv[2]))) {
657 /* Check arg count. */
658 if ((unsigned)argc - 2 < colorCmdVec[subi].minargs ||
659 (unsigned)argc - 2 > colorCmdVec[subi].maxargs) {
660 Tcl_WrongNumArgs(interp, 3, objv, colorCmdVec[subi].usage);
661 return TCL_ERROR;
662 }
663
664 /* Get the image pointer. */
665 gdImagePtr im = IMGPTR(objv[3]);
666
667 /* Parse off integer arguments.
668 * 1st 4 are gd color <opt> <handle>
669 */
670 for (int i = 0; i < argc - 4; i++) {
671 if (Tcl_GetIntFromObj(interp, objv[i + 4], &args[i]) != TCL_OK) {
672
673 /* gd text uses -ve colors to turn off anti-aliasing */
674 if (args[i] < -255 || args[i] > 255) {
675 Tcl_SetResult(interp, "argument out of range 0-255", TCL_STATIC);
676 return TCL_ERROR;
677 }
678 }
679 }
680
681 /* Call the subcommand function. */
682 return colorCmdVec[subi].f(interp, im, argc - 4, args);
683 }
684 }
685 }
686
687 /* If we get here, the option doesn't match. */
688 if (argc > 2) {
689 Tcl_AppendResult(interp, "bad option \"", Tcl_GetString(objv[2]),
690 "\": ", 0);
691 } else {
692 Tcl_AppendResult(interp, "wrong # args: ", 0);
693 }
694 Tcl_AppendResult(interp, "should be ", 0);
695 for (int subi = 0; subi < nsub; subi++)
696 Tcl_AppendResult(interp, subi > 0 ? ", " : "", colorCmdVec[subi].cmd, 0);
697
698 return TCL_ERROR;
699}
700
701static int tclGdColorNewCmd(Tcl_Interp *interp, gdImagePtr im, int argc,
702 const int args[]) {
703 (void)argc;
704
705 int color = gdImageColorAllocate(im, args[0], args[1], args[2]);
706 Tcl_SetObjResult(interp, Tcl_NewIntObj(color));
707 return TCL_OK;
708}
709
710static int tclGdColorExactCmd(Tcl_Interp *interp, gdImagePtr im, int argc,
711 const int args[]) {
712 (void)argc;
713
714 int color = gdImageColorExact(im, args[0], args[1], args[2]);
715 Tcl_SetObjResult(interp, Tcl_NewIntObj(color));
716 return TCL_OK;
717}
718
719static int tclGdColorClosestCmd(Tcl_Interp *interp, gdImagePtr im, int argc,
720 const int args[]) {
721 (void)argc;
722
723 int color = gdImageColorClosest(im, args[0], args[1], args[2]);
724 Tcl_SetObjResult(interp, Tcl_NewIntObj(color));
725 return TCL_OK;
726}
727
728static int tclGdColorResolveCmd(Tcl_Interp *interp, gdImagePtr im, int argc,
729 const int args[]) {
730 (void)argc;
731
732 int color = gdImageColorResolve(im, args[0], args[1], args[2]);
733 Tcl_SetObjResult(interp, Tcl_NewIntObj(color));
734 return TCL_OK;
735}
736
737static int tclGdColorFreeCmd(Tcl_Interp *interp, gdImagePtr im, int argc,
738 const int args[]) {
739 (void)interp;
740 (void)argc;
741
742 gdImageColorDeallocate(im, args[0]);
743 return TCL_OK;
744}
745
746static int tclGdColorTranspCmd(Tcl_Interp *interp, gdImagePtr im, int argc,
747 const int args[]) {
748 int color;
749
750 if (argc > 0) {
751 color = args[0];
752 gdImageColorTransparent(im, color);
753 } else {
754 color = gdImageGetTransparent(im);
755 }
756 Tcl_SetObjResult(interp, Tcl_NewIntObj(color));
757 return TCL_OK;
758}
759
760static int tclGdColorGetCmd(Tcl_Interp *interp, gdImagePtr im, int argc,
761 const int args[]) {
762 Tcl_Obj *result;
763
764 int ncolors = gdImageColorsTotal(im);
765 /* IF one arg, return the single color, else return list of all colors. */
766 if (argc == 1) {
767 int i = args[0];
768 if (i >= ncolors || im->open[i]) {
769 Tcl_SetResult(interp, "No such color", TCL_STATIC);
770 return TCL_ERROR;
771 }
772 Tcl_Obj *tuple[] = {Tcl_NewIntObj(i), Tcl_NewIntObj(gdImageRed(im, i)),
773 Tcl_NewIntObj(gdImageGreen(im, i)),
774 Tcl_NewIntObj(gdImageBlue(im, i))};
775 const Tcl_Size tuple_size = sizeof(tuple) / sizeof(tuple[0]);
776 Tcl_SetObjResult(interp, Tcl_NewListObj(tuple_size, tuple));
777 } else {
778 result = Tcl_NewListObj(0, NULL);
779 for (int i = 0; i < ncolors; i++) {
780 if (im->open[i])
781 continue;
782 Tcl_Obj *tuple[] = {Tcl_NewIntObj(i), Tcl_NewIntObj(gdImageRed(im, i)),
783 Tcl_NewIntObj(gdImageGreen(im, i)),
784 Tcl_NewIntObj(gdImageBlue(im, i))};
785 const Tcl_Size tuple_size = sizeof(tuple) / sizeof(tuple[0]);
786 Tcl_ListObjAppendElement(NULL, result, Tcl_NewListObj(tuple_size, tuple));
787 }
788 Tcl_SetObjResult(interp, result);
789 }
790
791 return TCL_OK;
792}
793
794static int tclGdBrushCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
795 (void)interp;
796 (void)argc;
797
798 /* Get the image pointers. */
799 gdImagePtr im = IMGPTR(objv[2]);
800 gdImagePtr imbrush = IMGPTR(objv[3]);
801
802 /* Do it. */
803 gdImageSetBrush(im, imbrush);
804
805 return TCL_OK;
806}
807
808static int tclGdTileCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
809 (void)interp;
810 (void)argc;
811
812 /* Get the image pointers. */
813 gdImagePtr im = IMGPTR(objv[2]);
814 gdImagePtr tile = IMGPTR(objv[3]);
815
816 /* Do it. */
817 gdImageSetTile(im, tile);
818
819 return TCL_OK;
820}
821
822static int tclGdStyleCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
823 Tcl_Obj *const *colorObjv = &objv[3]; // by default, colors are listed in objv
824 int retval = TCL_OK;
825
826 /* Get the image pointer. */
827 gdImagePtr im = IMGPTR(objv[2]);
828
829 /* Figure out how many colors in the style list and allocate memory. */
830 Tcl_Size ncolor = (Tcl_Size)argc - 3;
831 /* If only one argument, treat it as a list. */
832 if (ncolor == 1) {
833 Tcl_Obj **colorObjp;
834 if (Tcl_ListObjGetElements(interp, objv[3], &ncolor, &colorObjp) != TCL_OK)
835 return TCL_ERROR;
836 colorObjv = colorObjp;
837 }
838
839 int *colors = (int *)Tcl_Alloc((size_t)ncolor * sizeof(int));
840 /* Get the color values. */
841 for (Tcl_Size i = 0; i < ncolor; i++)
842 if (Tcl_GetIntFromObj(interp, colorObjv[i], &colors[i]) != TCL_OK) {
843 retval = TCL_ERROR;
844 break;
845 }
846
847 /* Call the Style function if no error. */
848 if (retval == TCL_OK)
849 gdImageSetStyle(im, colors, (int)ncolor);
850
851 /* Free the colors. */
852 if (colors != NULL)
853 Tcl_Free((char *)colors);
854
855 return retval;
856}
857
858static int tclGdSetCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
859 (void)argc;
860
861 gdImagePtr im;
862 int color, x, y;
863
864 /* Get the image pointer. */
865 im = IMGPTR(objv[2]);
866
867 /* Get the color, x, y values. */
868 if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK)
869 return TCL_ERROR;
870 if (Tcl_GetIntFromObj(interp, objv[4], &x) != TCL_OK)
871 return TCL_ERROR;
872 if (Tcl_GetIntFromObj(interp, objv[5], &y) != TCL_OK)
873 return TCL_ERROR;
874
875 /* Call the Set function. */
876 gdImageSetPixel(im, x, y, color);
877
878 return TCL_OK;
879}
880
881static int tclGdLineCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
882 (void)argc;
883
884 gdImagePtr im;
885 int color, x1, y1, x2, y2;
886
887 /* Get the image pointer. */
888 im = IMGPTR(objv[2]);
889
890 /* Get the color, x, y values. */
891 if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK)
892 return TCL_ERROR;
893 if (Tcl_GetIntFromObj(interp, objv[4], &x1) != TCL_OK)
894 return TCL_ERROR;
895 if (Tcl_GetIntFromObj(interp, objv[5], &y1) != TCL_OK)
896 return TCL_ERROR;
897 if (Tcl_GetIntFromObj(interp, objv[6], &x2) != TCL_OK)
898 return TCL_ERROR;
899 if (Tcl_GetIntFromObj(interp, objv[7], &y2) != TCL_OK)
900 return TCL_ERROR;
901
902 /* Call the appropriate Line function. */
903 gdImageLine(im, x1, y1, x2, y2, color);
904
905 return TCL_OK;
906}
907
908static int tclGdRectCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
909 (void)argc;
910
911 gdImagePtr im;
912 int color, x1, y1, x2, y2;
913 const char *cmd;
914
915 /* Get the image pointer. */
916 im = IMGPTR(objv[2]);
917
918 /* Get the color, x, y values. */
919 if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK)
920 return TCL_ERROR;
921 if (Tcl_GetIntFromObj(interp, objv[4], &x1) != TCL_OK)
922 return TCL_ERROR;
923 if (Tcl_GetIntFromObj(interp, objv[5], &y1) != TCL_OK)
924 return TCL_ERROR;
925 if (Tcl_GetIntFromObj(interp, objv[6], &x2) != TCL_OK)
926 return TCL_ERROR;
927 if (Tcl_GetIntFromObj(interp, objv[7], &y2) != TCL_OK)
928 return TCL_ERROR;
929
930 /* Call the appropriate rectangle function. */
931 cmd = Tcl_GetString(objv[1]);
932 if (cmd[0] == 'r')
933 gdImageRectangle(im, x1, y1, x2, y2, color);
934 else
935 gdImageFilledRectangle(im, x1, y1, x2, y2, color);
936
937 return TCL_OK;
938}
939
940static int tclGdArcCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
941 (void)argc;
942
943 gdImagePtr im;
944 int color, cx, cy, width, height, start, end;
945 const char *cmd;
946
947 /* Get the image pointer. */
948 im = IMGPTR(objv[2]);
949
950 /* Get the color, x, y values. */
951 if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK)
952 return TCL_ERROR;
953 if (Tcl_GetIntFromObj(interp, objv[4], &cx) != TCL_OK)
954 return TCL_ERROR;
955 if (Tcl_GetIntFromObj(interp, objv[5], &cy) != TCL_OK)
956 return TCL_ERROR;
957 if (Tcl_GetIntFromObj(interp, objv[6], &width) != TCL_OK)
958 return TCL_ERROR;
959 if (Tcl_GetIntFromObj(interp, objv[7], &height) != TCL_OK)
960 return TCL_ERROR;
961 if (Tcl_GetIntFromObj(interp, objv[8], &start) != TCL_OK)
962 return TCL_ERROR;
963 if (Tcl_GetIntFromObj(interp, objv[9], &end) != TCL_OK)
964 return TCL_ERROR;
965
966 /* Call the appropriate arc function. */
967 cmd = Tcl_GetString(objv[1]);
968 if (cmd[0] == 'a') /* arc */
969 gdImageArc(im, cx, cy, width, height, start, end, color);
970 /* This one is not really useful as gd renderers it the same as fillpie */
971 /* It would be more useful if gd provided fill between arc and chord */
972 else if (cmd[0] == 'f' && cmd[4] == 'a') /* fill arc */
973 gdImageFilledArc(im, cx, cy, width, height, start, end, color, gdArc);
974 /* this one is a kludge */
975 else if (cmd[0] == 'o' && cmd[4] == 'a') { /* open arc */
976 gdImageArc(im, cx, cy, width, height, start, end, color);
977 gdImageFilledArc(im, cx, cy, width, height, start, end, color,
978 gdChord | gdNoFill);
979 } else if (cmd[0] == 'c') /* chord */
980 gdImageFilledArc(im, cx, cy, width, height, start, end, color,
981 gdChord | gdNoFill);
982 else if (cmd[0] == 'f' && cmd[4] == 'c') /* fill chord */
983 gdImageFilledArc(im, cx, cy, width, height, start, end, color, gdChord);
984 else if (cmd[0] == 'o' && cmd[4] == 'c') /* open chord */
985 gdImageFilledArc(im, cx, cy, width, height, start, end, color,
986 gdChord | gdEdged | gdNoFill);
987 else if (cmd[0] == 'p' ||
988 (cmd[0] == 'f' && cmd[4] == 'p')) /* pie or fill pie */
989 gdImageFilledArc(im, cx, cy, width, height, start, end, color, gdPie);
990 else if (cmd[0] == 'o' && cmd[4] == 'p') /* open pie */
991 gdImageFilledArc(im, cx, cy, width, height, start, end, color,
992 gdPie | gdEdged | gdNoFill);
993
994 return TCL_OK;
995}
996
997static int tclGdPolygonCmd(Tcl_Interp *interp, int argc,
998 Tcl_Obj *const objv[]) {
999 gdImagePtr im;
1000 int color;
1001 Tcl_Obj *const *pointObjv = &objv[4];
1002 gdPointPtr points = NULL;
1003 int retval = TCL_OK;
1004 char *cmd;
1005
1006 /* Get the image pointer. */
1007 im = IMGPTR(objv[2]);
1008
1009 /* Get the color, x, y values. */
1010 if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK)
1011 return TCL_ERROR;
1012
1013 /* Figure out how many points in the list and allocate memory. */
1014 Tcl_Size npoints = (Tcl_Size)argc - 4;
1015 /* If only one argument, treat it as a list. */
1016 if (npoints == 1) {
1017 Tcl_Obj **pointObjp;
1018 if (Tcl_ListObjGetElements(interp, objv[4], &npoints, &pointObjp) != TCL_OK)
1019 return TCL_ERROR;
1020 pointObjv = pointObjp;
1021 }
1022
1023 /* Error check size of point list. */
1024 if (npoints % 2 != 0) {
1025 Tcl_SetResult(interp, "Number of coordinates must be even", TCL_STATIC);
1026 retval = TCL_ERROR;
1027 goto out;
1028 }
1029
1030 /* Divide by 2 to get number of points, and final error check. */
1031 npoints /= 2;
1032 if (npoints < 3) {
1033 Tcl_SetResult(interp, "Must specify at least 3 points.", TCL_STATIC);
1034 retval = TCL_ERROR;
1035 goto out;
1036 }
1037
1038 points = (gdPointPtr)Tcl_Alloc((size_t)npoints * sizeof(gdPoint));
1039
1040 /* Get the point values. */
1041 for (Tcl_Size i = 0; i < npoints; i++)
1042 if (Tcl_GetIntFromObj(interp, pointObjv[i * 2], &points[i].x) != TCL_OK ||
1043 Tcl_GetIntFromObj(interp, pointObjv[i * 2 + 1], &points[i].y) !=
1044 TCL_OK) {
1045 retval = TCL_ERROR;
1046 goto out;
1047 }
1048
1049 /* Call the appropriate polygon function. */
1050 cmd = Tcl_GetString(objv[1]);
1051 if (cmd[0] == 'p')
1052 gdImagePolygon(im, points, (int)npoints, color);
1053 else
1054 gdImageFilledPolygon(im, points, (int)npoints, color);
1055
1056out:
1057 /* Free the points. */
1058 if (points != NULL)
1059 Tcl_Free((char *)points);
1060
1061 /* return TCL_OK; */
1062 return retval;
1063}
1064
1065static int tclGdFillCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
1066 gdImagePtr im;
1067 int color, x, y, border;
1068
1069 /* Get the image pointer. */
1070 im = IMGPTR(objv[2]);
1071
1072 /* Get the color, x, y and possibly bordercolor values. */
1073 if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK)
1074 return TCL_ERROR;
1075 if (Tcl_GetIntFromObj(interp, objv[4], &x) != TCL_OK)
1076 return TCL_ERROR;
1077 if (Tcl_GetIntFromObj(interp, objv[5], &y) != TCL_OK)
1078 return TCL_ERROR;
1079
1080 /* Call the appropriate fill function. */
1081 if (argc - 2 == 5) {
1082 if (Tcl_GetIntFromObj(interp, objv[6], &border) != TCL_OK)
1083 return TCL_ERROR;
1084 gdImageFillToBorder(im, x, y, border, color);
1085 } else {
1086 gdImageFill(im, x, y, color);
1087 }
1088
1089 return TCL_OK;
1090}
1091
1092static int tclGdCopyCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
1093 gdImagePtr imdest, imsrc;
1094 int destx, desty, srcx, srcy, destw, desth, srcw, srch;
1095
1096 /* Get the image pointer. */
1097 imdest = IMGPTR(objv[2]);
1098 imsrc = IMGPTR(objv[3]);
1099
1100 /* Get the x, y, etc. values. */
1101 if (Tcl_GetIntFromObj(interp, objv[4], &destx) != TCL_OK)
1102 return TCL_ERROR;
1103 if (Tcl_GetIntFromObj(interp, objv[5], &desty) != TCL_OK)
1104 return TCL_ERROR;
1105 if (Tcl_GetIntFromObj(interp, objv[6], &srcx) != TCL_OK)
1106 return TCL_ERROR;
1107 if (Tcl_GetIntFromObj(interp, objv[7], &srcy) != TCL_OK)
1108 return TCL_ERROR;
1109 if (Tcl_GetIntFromObj(interp, objv[8], &destw) != TCL_OK)
1110 return TCL_ERROR;
1111 if (Tcl_GetIntFromObj(interp, objv[9], &desth) != TCL_OK)
1112 return TCL_ERROR;
1113
1114 /* Call the appropriate copy function. */
1115 if (argc - 2 == 10) {
1116 if (Tcl_GetIntFromObj(interp, objv[10], &srcw) != TCL_OK)
1117 return TCL_ERROR;
1118 if (Tcl_GetIntFromObj(interp, objv[11], &srch) != TCL_OK)
1119 return TCL_ERROR;
1120
1121 gdImageCopyResized(imdest, imsrc, destx, desty, srcx, srcy, destw, desth,
1122 srcw, srch);
1123 } else
1124 gdImageCopy(imdest, imsrc, destx, desty, srcx, srcy, destw, desth);
1125
1126 return TCL_OK;
1127}
1128
1129static int tclGdGetCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
1130 (void)argc;
1131
1132 gdImagePtr im;
1133 int color, x, y;
1134
1135 /* Get the image pointer. */
1136 im = IMGPTR(objv[2]);
1137
1138 /* Get the x, y values. */
1139 if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK)
1140 return TCL_ERROR;
1141 if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK)
1142 return TCL_ERROR;
1143
1144 /* Call the Get function. */
1145 color = gdImageGetPixel(im, x, y);
1146 Tcl_SetObjResult(interp, Tcl_NewIntObj(color));
1147 return TCL_OK;
1148}
1149
1150static int tclGdSizeCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
1151 (void)argc;
1152
1153 gdImagePtr im;
1154 Tcl_Obj *answers[2];
1155
1156 /* Get the image pointer. */
1157 im = IMGPTR(objv[2]);
1158
1159 answers[0] = Tcl_NewIntObj(gdImageSX(im));
1160 answers[1] = Tcl_NewIntObj(gdImageSY(im));
1161 Tcl_SetObjResult(interp, Tcl_NewListObj(2, answers));
1162 return TCL_OK;
1163}
1164
1165static int tclGdTextCmd(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[]) {
1166 /* gd gdhandle color fontname size angle x y string */
1167 gdImagePtr im;
1168 int color, x, y;
1169 double ptsize, angle;
1170 char *error, *fontname;
1171 int i, brect[8];
1172 char *str;
1173 Tcl_Obj *orect[8];
1174
1175 /* Get the image pointer. (an invalid or null arg[2] will result in string
1176 size calculation but no rendering */
1177 if (argc == 2 || (objv[2]->typePtr != &GdPtrType &&
1178 GdPtrTypeSet(NULL, objv[2]) != TCL_OK)) {
1179 im = NULL;
1180 } else {
1181 im = IMGPTR(objv[2]);
1182 }
1183
1184 /* Get the color, values. */
1185 if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK) {
1186 return TCL_ERROR;
1187 }
1188
1189 /* Get point size */
1190 if (Tcl_GetDoubleFromObj(interp, objv[5], &ptsize) != TCL_OK) {
1191 return TCL_ERROR;
1192 }
1193
1194 /* Get rotation (radians) */
1195 if (Tcl_GetDoubleFromObj(interp, objv[6], &angle) != TCL_OK) {
1196 return TCL_ERROR;
1197 }
1198
1199 /* get x, y position */
1200 if (Tcl_GetIntFromObj(interp, objv[7], &x) != TCL_OK) {
1201 return TCL_ERROR;
1202 }
1203 if (Tcl_GetIntFromObj(interp, objv[8], &y) != TCL_OK) {
1204 return TCL_ERROR;
1205 }
1206
1207 str = Tcl_GetStringFromObj(objv[9], NULL);
1208 fontname = Tcl_GetString(objv[4]);
1209
1210 gdFTUseFontConfig(1);
1211 error = gdImageStringFT(im, brect, color, fontname, ptsize, angle, x, y, str);
1212
1213 if (error) {
1214 Tcl_SetResult(interp, error, TCL_VOLATILE);
1215 return TCL_ERROR;
1216 }
1217 for (i = 0; i < 8; i++) {
1218 orect[i] = Tcl_NewIntObj(brect[i]);
1219 }
1220 Tcl_SetObjResult(interp, Tcl_NewListObj(8, orect));
1221 return TCL_OK;
1222}
1223
1224/*
1225 * Initialize the package.
1226 */
1227int Gdtclft_Init(Tcl_Interp *interp) {
1228#ifdef USE_TCL_STUBS
1229 if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
1230 return TCL_ERROR;
1231 }
1232#else
1233 if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 0) == NULL) {
1234 return TCL_ERROR;
1235 }
1236#endif
1237 // inter-release Graphviz versions have a number including '~dev.' that does
1238 // not comply with TCL version number rules, so replace this with 'b'
1239 char adjusted_version[sizeof(PACKAGE_VERSION)] = PACKAGE_VERSION;
1240 char *tilde_dev = strstr(adjusted_version, "~dev.");
1241 if (tilde_dev != NULL) {
1242 *tilde_dev = 'b';
1243 memmove(tilde_dev + 1, tilde_dev + strlen("~dev."),
1244 strlen(tilde_dev + strlen("~dev.")) + 1);
1245 }
1246 if (Tcl_PkgProvide(interp, "Gdtclft", adjusted_version) != TCL_OK) {
1247 return TCL_ERROR;
1248 }
1249 Tcl_CreateObjCommand(interp, "gd", gdCmd, NULL, (Tcl_CmdDeleteProc *)NULL);
1250 return TCL_OK;
1251}
1252
1253int Gdtclft_SafeInit(Tcl_Interp *interp) {
1254 Tcl_CmdInfo info;
1255 if (Gdtclft_Init(interp) != TCL_OK ||
1256 Tcl_GetCommandInfo(interp, "gd", &info) != 1)
1257 return TCL_ERROR;
1258 info.objClientData = (char *)info.objClientData + 1; /* Non-NULL */
1259 if (Tcl_SetCommandInfo(interp, "gd", &info) != 1)
1260 return TCL_ERROR;
1261 return TCL_OK;
1262}
1263
1264#ifndef __CYGWIN__
1265#ifdef __WIN32__
1266/* Define DLL entry point, standard macro */
1267
1268/*
1269 *----------------------------------------------------------------------
1270 *
1271 * DllEntryPoint --
1272 *
1273 * This wrapper function is used by Windows to invoke the
1274 * initialization code for the DLL. If we are compiling
1275 * with Visual C++, this routine will be renamed to DllMain.
1276 * routine.
1277 *
1278 * Results:
1279 * Returns TRUE;
1280 *
1281 * Side effects:
1282 * None.
1283 *
1284 *----------------------------------------------------------------------
1285 *
1286 * @param hInst Library instance handle
1287 * @param reason Reason this function is being called
1288 * @param reserved Not used
1289 */
1290BOOL APIENTRY DllEntryPoint(HINSTANCE hInst, DWORD reason, LPVOID reserved);
1291BOOL APIENTRY DllEntryPoint(HINSTANCE hInst, DWORD reason, LPVOID reserved) {
1292 (void)hInst;
1293 (void)reason;
1294 (void)reserved;
1295
1296 return TRUE;
1297}
1298#endif
1299#endif
1300
1301#ifdef HAVE_GD_PNG
1302static int BufferSinkFunc(void *context, const char *buffer, int len) {
1303 agxbuf *p = context;
1304 if (len > 0) {
1305 agxbput_n(p, buffer, (size_t)len);
1306 }
1307 return len;
1308}
1309
1310static int tclGdWriteBufCmd(Tcl_Interp *interp, int argc,
1311 Tcl_Obj *const objv[]) {
1312 (void)argc;
1313
1314 agxbuf buffer = {0};
1315 gdSink buffsink = {.sink = BufferSinkFunc, .context = &buffer};
1316 /* Get the image pointer. */
1317 gdImagePtr im = IMGPTR(objv[2]);
1318
1319 gdImagePngToSink(im, &buffsink);
1320
1321 const size_t buffer_length = agxblen(&buffer);
1322 void *const result = agxbuse(&buffer);
1323
1324 assert(buffer_length <= INT_MAX);
1325 Tcl_Obj *output = Tcl_NewByteArrayObj(result, (Tcl_Size)buffer_length);
1326 agxbfree(&buffer);
1327 if (output == NULL)
1328 return TCL_ERROR;
1329 else
1330 Tcl_IncrRefCount(output);
1331
1332 if (Tcl_ObjSetVar2(interp, objv[3], NULL, output, 0) == NULL)
1333 return TCL_ERROR;
1334 else
1335 return TCL_OK;
1336}
1337
1338static void GdPtrTypeUpdate(struct Tcl_Obj *O) {
1339 size_t len = strlen(GdPtrType.name) + (sizeof(void *) + 1) * 2 + 1;
1340 O->bytes = Tcl_Alloc(len);
1341 O->length = snprintf(O->bytes, len, "%s%p", GdPtrType.name, IMGPTR(O));
1342}
1343
1344static int GdPtrTypeSet(Tcl_Interp *I, struct Tcl_Obj *O) {
1345 if (O->bytes == NULL || O->bytes[0] == '\0' ||
1346 !startswith(O->bytes, GdPtrType.name) ||
1347 sscanf(O->bytes + strlen(GdPtrType.name), "%p", &IMGPTR(O)) != 1) {
1348 if (I != NULL)
1349 Tcl_AppendResult(I, O->bytes, " is not a ", GdPtrType.name, "-handle",
1350 NULL);
1351 return TCL_ERROR;
1352 }
1353 O->typePtr = &GdPtrType;
1354 return TCL_OK;
1355}
1356#endif
static void out(agerrlevel_t level, const char *fmt, va_list args)
Report messages using a user-supplied or default write function.
Definition agerror.c:84
Dynamically expanding string buffers.
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:97
static size_t agxbput_n(agxbuf *xb, const char *s, size_t ssz)
append string s of length ssz into xb
Definition agxbuf.h:268
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:325
static size_t agxblen(const agxbuf *xb)
return number of characters currently stored
Definition agxbuf.h:108
static char * cmd
Definition acyclic.c:40
static char * fname
#define I
Definition expr.h:71
#define O
Definition gdefs.h:8
static GdDataFunction tclGdCopyCmd
Definition gdtclft.c:55
static Tcl_ObjType GdPtrType
Definition gdtclft.c:33
static GdImgFunction tclGdColorGetCmd
Definition gdtclft.c:61
static GdDataFunction tclGdInterlaceCmd
Definition gdtclft.c:54
static cmdDataOptions subcmdVec[]
Definition gdtclft.c:80
static GdImgFunction tclGdColorTranspCmd
Definition gdtclft.c:60
static GdDataFunction tclGdSetCmd
Definition gdtclft.c:54
Tcl_AppInitProc Gdtclft_SafeInit
Definition gdtclft.c:46
static GdDataFunction tclGdRectCmd
Definition gdtclft.c:54
static GdDataFunction tclGdLineCmd
Definition gdtclft.c:54
static Tcl_UpdateStringProc GdPtrTypeUpdate
Definition gdtclft.c:31
static GdDataFunction tclGdStyleCmd
Definition gdtclft.c:56
static GdDataFunction tclGdBrushCmd
Definition gdtclft.c:56
static GdImgFunction tclGdColorResolveCmd
Definition gdtclft.c:60
static GdImgFunction tclGdColorNewCmd
Definition gdtclft.c:59
static GdDataFunction tclGdCreateCmd
Definition gdtclft.c:53
static GdImgFunction tclGdColorFreeCmd
Definition gdtclft.c:60
static GdImgFunction tclGdColorExactCmd
Definition gdtclft.c:59
static GdDataFunction tclGdDestroyCmd
Definition gdtclft.c:53
int() GdImgFunction(Tcl_Interp *interp, gdImagePtr gdImg, int argc, const int args[])
Definition gdtclft.c:50
static GdDataFunction tclGdArcCmd
Definition gdtclft.c:55
static int gdCmd(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const objv[])
Definition gdtclft.c:347
static GdDataFunction tclGdPolygonCmd
Definition gdtclft.c:57
#define IMGPTR(O)
Definition gdtclft.c:36
static GdDataFunction tclGdSizeCmd
Definition gdtclft.c:55
static GdDataFunction tclGdWriteBufCmd
Definition gdtclft.c:56
Tcl_AppInitProc Gdtclft_Init
Definition gdtclft.c:42
static GdDataFunction tclGdWriteCmd
Definition gdtclft.c:53
static GdDataFunction tclGdGetCmd
Definition gdtclft.c:56
static GdDataFunction tclGdTextCmd
Definition gdtclft.c:55
static int tclGd_GetColor(Tcl_Interp *interp, Tcl_Obj *obj, int *color)
Definition gdtclft.c:176
static GdImgFunction tclGdColorClosestCmd
Definition gdtclft.c:59
static GdDataFunction tclGdTileCmd
Definition gdtclft.c:56
static cmdImgOptions colorCmdVec[]
Definition gdtclft.c:164
static Tcl_SetFromAnyProc GdPtrTypeSet
Definition gdtclft.c:32
static GdDataFunction tclGdFillCmd
Definition gdtclft.c:55
int() GdDataFunction(Tcl_Interp *interp, int argc, Tcl_Obj *const objv[])
Definition gdtclft.c:48
static GdDataFunction tclGdColorCmd
Definition gdtclft.c:54
static double len(glCompPoint p)
Definition glutils.c:136
node NULL
Definition grammar.y:181
static void color(Agraph_t *g)
Definition gvcolor.c:129
static const char * usage
Definition gvpr.c:52
static gdPoint * points
textitem scanner parser str
Definition htmlparse.y:218
table Syntax error
Definition htmlparse.y:288
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
const char * cmd
Definition gdtclft.c:64
const char * usage
Definition gdtclft.c:70
unsigned int unsafearg
Definition gdtclft.c:69
unsigned int minargs
Definition gdtclft.c:66
unsigned int maxargs
Definition gdtclft.c:66
unsigned int ishandle
Definition gdtclft.c:68
unsigned int subcmds
Definition gdtclft.c:67
GdDataFunction * f
Definition gdtclft.c:65
unsigned int minargs
Definition gdtclft.c:76
const char * cmd
Definition gdtclft.c:74
const char * usage
Definition gdtclft.c:77
unsigned int maxargs
Definition gdtclft.c:76
GdImgFunction * f
Definition gdtclft.c:75
#define Tcl_Size
Definition tcl-compat.h:33