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