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