/* * nsgd2.c - New interface gd to AOLserver/Tcl. * * Updates by Richard Kotal : * - support for aolserver 4.0.x * - fix memory leak in tclGdWriteCmd function * - support for gif,wbmp,jpeg,gd formats * - fix mime-type bug in tclGdReturnCmd function * - remove global variable GdData * - support for true color image * * Converted to an AOLserver module by Matthew Burke * Portions Copyright (C) 2000 ArsDigita Corporation * Original author information as follows: * * Author: Spencer W. Thomas * Information Technology and Networking * University of Michigan * Date: Fri Aug 19 1994 * Copyright (c) 1994, University of Michigan * * Updates by John Ellson (ellson@graphviz.org): * - conversion to freetype text rendering * - support for tcl8.0 Tcl_Objects * - support for tcl8.1 Unicode * - remove support for tcl8.0 and earlier * * Windows port by Bill Schongar (bills@lcdmultimedia.com) * * writePNGvar by David N. Welton (davidw@prosa.it) * * Except where it conflicts with the original authors' intent (outlined * in the accompanying file license terms, * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/nsgd2/nsgd2.c,v 2.0 2004/11/20 07:27:32 developer Exp $, compiled: " __DATE__ " " __TIME__; #define USE_TCL8X #include "config.h" #include #include #include #include "ns.h" #include "gd.h" #include "gdfontt.h" #include "gdfonts.h" #include "gdfontmb.h" #include "gdfontl.h" #include "gdfontg.h" ////////////////////////////////////////////////////////////////////////////////////////////// static Ns_TclInterpInitProc GdInterpInit; static int gdCmd(ClientData clientData, Tcl_Interp * interp, int argc, Tcl_Obj * CONST objv[]); ////////////////////////////////////////////////////////////////////////////////////////////// Tcl_Obj * Tcl_NewGDImageObj(gdImagePtr im); void Tcl_DestroyGDImageObj(Tcl_Obj *obj); gdImagePtr Tcl_GetGDImage(Tcl_Obj *obj); ////////////////////////////////////////////////////////////////////////////////////////////// int Ns_ModuleVersion = 1; typedef struct { char *cmd; int (*f) (); int minargs, maxargs; int subcmds; int ishandle; char *usage; } cmdOptions; typedef struct { char *buf; int buflen; } BuffSinkContext; static cmdOptions subcmdVec[] = { {"create", tclGdCreateCmd, 2, 2, 0, 0, "width height"}, #ifdef HAVE_TRUECOLOR {"createtruecolor", tclGdCreateCmd, 2, 2, 0, 0, "width height"}, #endif {"createFromGD", tclGdCreateCmd, 1, 1, 0, 0, "filehandle"}, #ifdef HAVE_LIBZ {"createFromGD2", tclGdCreateCmd, 1, 1, 0, 0, "filehandle"}, #endif #ifdef HAVE_GD_GIF {"createFromGIF", tclGdCreateCmd, 1, 1, 0, 0, "filehandle"}, #endif #ifdef HAVE_GD_JPEG {"createFromJPEG", tclGdCreateCmd, 1, 1, 0, 0, "filehandle"}, #endif #ifdef HAVE_GD_PNG {"createFromPNG", tclGdCreateCmd, 1, 1, 0, 0, "filehandle"}, #endif {"createFromWBMP", tclGdCreateCmd, 1, 1, 0, 0, "filehandle"}, #ifdef HAVE_GD_XPM {"createFromXBM", tclGdCreateCmd, 1, 1, 0, 0, "filehandle"}, #endif {"destroy", tclGdDestroyCmd, 1, 1, 0, 1, "gdhandle"}, {"writeGD", tclGdWriteCmd, 2, 2, 0, 1, "gdhandle filehandle"}, #ifdef HAVE_LIBZ {"writeGD2", tclGdWriteCmd, 2, 2, 0, 1, "gdhandle filehandle"}, #endif #ifdef HAVE_GD_GIF {"writeGIF", tclGdWriteCmd, 2, 2, 0, 1, "gdhandle filehandle"}, #endif #ifdef HAVE_GD_JPEG {"writeJPEG", tclGdWriteCmd, 2, 2, 0, 1, "gdhandle filehandle"}, #endif #ifdef HAVE_GD_PNG {"writePNG", tclGdWriteCmd, 2, 2, 0, 1, "gdhandle filehandle"}, #endif {"writeWBMP", tclGdWriteCmd, 2, 2, 0, 1, "gdhandle filehandle"}, #ifdef HAVE_GD_XBM {"writeXBM", tclGdWriteCmd, 2, 2, 0, 1, "gdhandle filehandle"}, #endif #ifdef HAVE_GD_PNG {"writePNGvar", tclGdWriteBufCmd, 2, 2, 0, 1, "gdhandle var"}, #endif #ifdef HAVE_GD_PNG {"returnPNG", tclGdReturnCmd, 1, 1, 0, 1, "gdhandle"}, #endif {"returnGD", tclGdReturnCmd, 2, 2, 0, 1, "gdhandle"}, #ifdef HAVE_LIBZ {"returnGD2", tclGdReturnCmd, 2, 2, 0, 1, "gdhandle"}, #endif #ifdef HAVE_GD_GIF {"returnGIF", tclGdReturnCmd, 2, 2, 0, 1, "gdhandle"}, #endif #ifdef HAVE_GD_JPEG {"returnJPEG", tclGdReturnCmd, 2, 2, 0, 1, "gdhandle"}, #endif #ifdef HAVE_GD_PNG {"returnPNG", tclGdReturnCmd, 2, 2, 0, 1, "gdhandle"}, #endif {"returnWBMP", tclGdReturnCmd, 2, 2, 0, 1, "gdhandle"}, #ifdef HAVE_GD_XBM {"returnXBM", tclGdReturnCmd, 2, 2, 0, 1, "gdhandle"}, #endif {"interlace", tclGdInterlaceCmd, 1, 2, 0, 1, "gdhandle ?on-off?"}, {"color", tclGdColorCmd, 2, 5, 1, 1, "option values..."}, {"brush", tclGdBrushCmd, 2, 2, 0, 2, "gdhandle brushhandle"}, {"style", tclGdStyleCmd, 2, 999, 0, 1, "gdhandle color..."}, {"tile", tclGdTileCmd, 2, 2, 0, 2, "gdhandle tilehandle"}, {"set", tclGdSetCmd, 4, 4, 0, 1, "gdhandle color x y"}, {"line", tclGdLineCmd, 6, 6, 0, 1, "gdhandle color x1 y1 x2 y2"}, {"rectangle", tclGdRectCmd, 6, 6, 0, 1, "gdhandle color x1 y1 x2 y2"}, {"fillrectangle", tclGdRectCmd, 6, 6, 0, 1, "gdhandle color x1 y1 x2 y2"}, {"arc", tclGdArcCmd, 8, 8, 0, 1, "gdhandle color cx cy width height start end"}, {"fillarc", tclGdArcCmd, 8, 8, 0, 1, "gdhandle color cx cy width height start end"}, {"polygon", tclGdPolygonCmd, 2, 999, 0, 1, "gdhandle color x1 y1 x2 y2 x3 y3 ..."}, {"fillpolygon", tclGdPolygonCmd, 3, 999, 0, 1, "gdhandle color x1 y1 x2 y2 x3 y3 ..."}, {"fill", tclGdFillCmd, 4, 5, 0, 1, "gdhandle color x y ?bordercolor?"}, /* * we allow null gd handles to the text command to allow program to get size * of text string, so the text command provides its own handle processing and checking */ {"textft", tclGdTextFTCmd, 8, 8, 0, 0, "gdhandle color fontpathname size angle x y string"}, {"text", tclGdTextCmd, 6, 7, 0, 1, "gdhandle color fontpathname x y string ?up?"}, {"copy", tclGdCopyCmd, 8, 10, 0, 2, "desthandle srchandle destx desty srcx srcy destw desth ?srcw srch?"}, {"get", tclGdGetCmd, 3, 3, 0, 1, "gdhandle x y"}, {"size", tclGdSizeCmd, 1, 1, 0, 1, "gdhandle"}, }; static cmdOptions colorCmdVec[] = { {"new", tclGdColorNewCmd, 5, 5, 1, 1, "gdhandle red green blue"}, {"exact", tclGdColorExactCmd, 5, 5, 1, 1, "gdhandle red green blue"}, {"closest", tclGdColorClosestCmd, 5, 5, 1, 1, "gdhandle red green blue"}, {"resolve", tclGdColorResolveCmd, 5, 5, 1, 1, "gdhandle red green blue"}, {"free", tclGdColorFreeCmd, 3, 3, 1, 1, "gdhandle color"}, {"transparent", tclGdColorTranspCmd, 2, 3, 1, 1, "gdhandle ?color?"}, {"get", tclGdColorGetCmd, 2, 3, 1, 1, "gdhandle ?color?"} }; ////////////////////////////////////////////////////////////////////////////////////////////// int Ns_ModuleInit(char *hServer, char *hModule) { return Ns_TclInitInterps(hServer, GdInterpInit, NULL); } ////////////////////////////////////////////////////////////////////////////////////////////// static int GdInterpInit(Tcl_Interp *interp, void *context) { Tcl_CreateObjCommand(interp, "gd", gdCmd, context, (Tcl_CmdDeleteProc *)NULL); return TCL_OK; } /* * GD composite command: * * gd create * Return a handle to a new gdImage that is width X height. * gd createFromGD * gd createFromGD2 * gd createFromGIF * gd createFromJPEG * gd createFromPNG * gd createFromWBMP * gd createFromXBM * Return a handle to a new gdImage created by reading an * image from the file of the indicated format * open on filehandle. * * gd destroy * Destroy the gdImage referred to by gdhandle. * * gd writeGD * gd writeGD2 * gd writeGIF * gd writeJPEG * gd writePNG * gd writeWBMP * gd writeXBM * Write the image in gdhandle to filehandle in the * format indicated. * * gd returnPNG * Returns the image as a PNG to the connection (a la ns_return). * * gd color new * Allocate a new color with the given RGB values. Returns the * color_idx, or -1 on failure (256 colors already allocated). * gd color exact * Find a color_idx in the image that exactly matches the given RGB color. * Returns the color_idx, or -1 if no exact match. * gd color closest * Find a color in the image that is closest to the given RGB color. * Guaranteed to return a color idx. * gd color resolve * Return the index of the best possible effort to get a color. Guaranteed * to return a color idx. Equivalent to: * if {[set idx [gd color exact $gd $r $g $b]] == -1} { * if {[set idx [gd color neW $Gd $r $g $b]] == -1} { * set idx [gd color closest $gd $r $g $b] * } * } * gd color free * Free the color at the given color_idx for reuse. * gd color transparent * Mark the color_idx as the transparent background color. * gd color get [] * Return the RGB value at , or {} if it is not allocated. * If is not specified, return a list of {color_idx R G B} values * for all allocated colors. * gd color gettransparent * Return the color_idx of the transparent color. * * gd brush * Set the brush image to be used for brushed lines. Transparent * pixels in the brush will not change the image when the brush * is applied. * gd style ... * Set the line style to the list of color indices. This is interpreted * in one of two ways. For a simple styled line, each color is * applied to points along the line in turn. The transparent color * value may be used to leave gaps in the line. For a styled, brushed * line, a 0 (or the transparent color_idx) means not to fill the pixel, * and a non-zero value means to apply the brush. * gd tile * Set the tile image to be used for tiled fills. Transparent pixels in * the tile will not change the underlying image during tiling. * * In all drawing functions, the color_idx is a number, or may be one of the * strings styled, brushed, tiled, "styled brushed" or "brushed styled". The * style, brush, or tile currently in effect will be used. Brushing and * styling apply to lines, tiling to filled areas. * * gd set * Set the pixel at (x,y) to color . * gd line * Draw a line in color from (x1,y1) to (x2,y2). * gd rectangle * gd fillrectangle * Draw the outline of (resp. fill) a rectangle in color * with corners at (x1,y1) and (x2,y2). * gd arc * gd fillarc * Draw an arc, or filled segment, in color , centered at (cx,cy) * in a rectangle width x height, starting at start degrees and ending * at end degrees. Start must be > end. * gd polygon ... * gd fillpolygon ... * Draw the outline of, or fill, a polygon specified by the x, y * coordinate list. * * gd fill * gd fill * Fill with color , starting from (x,y) within a region * of pixels all the color of the pixel at (x,y) (resp., within a * border colored borderindex). * * gd size * Returns a list {width height} of the image. * * gd text * Draw text using in color , * with pointsize , rotation in radians , with lower left * corner at (x,y). String may contain UTF8 sequences like: "À" * Returns 4 corner coords of bounding rectangle. * Use gdhandle = {} to get boundary without rendering. * Use negative of color_idx to disable antialiasing. * * The file .ttf must be found in the builtin DEFAULT_FONTPATH * or in the fontpath specified in a GDFONTPATH environment variable. * * gd copy * gd copy \ * * Copy a subimage from srchandle(srcx, srcy) to * desthandle(destx, desty), size w x h. Or, resize the subimage * in copying from srcw x srch to destw x desth. * */ static int gdCmd(ClientData clientData, Tcl_Interp * interp, int argc, Tcl_Obj * CONST objv[]) { int argi, subi; char buf[100]; /* Check for subcommand. */ if (argc < 2) { Tcl_SetResult(interp, "wrong # args: should be \"gd option ...\"", TCL_STATIC); return TCL_ERROR; } /* Find the subcommand. */ for (subi = 0; subi < (sizeof subcmdVec) / (sizeof subcmdVec[0]); subi++) { if (Tcl_GetString(objv[1])!=NULL && strcmp(subcmdVec[subi].cmd, Tcl_GetString(objv[1])) == 0) { /* Check arg count. */ if (argc - 2 < subcmdVec[subi].minargs || argc - 2 > subcmdVec[subi].maxargs) { sprintf(buf, "wrong # args: should be \"gd %s %s\"",subcmdVec[subi].cmd, subcmdVec[subi].usage); Tcl_SetResult(interp, buf, TCL_VOLATILE); return TCL_ERROR; } /* Check for valid handle(s). */ if (subcmdVec[subi].ishandle > 0) { /* Check each handle to see if it's a valid handle. */ if (2 + subcmdVec[subi].subcmds + subcmdVec[subi].ishandle > argc) { Tcl_SetResult(interp, "GD handle(s) not specified", TCL_STATIC); return TCL_ERROR; } for (argi = 2 + subcmdVec[subi].subcmds; argi < (2 + subcmdVec[subi].subcmds + subcmdVec[subi].ishandle); argi++) { if (objv[argi]==NULL) { Tcl_SetResult(interp, "no such GD handle(s)", TCL_STATIC); return TCL_ERROR; } } } /* Call the subcommand function. */ return (*subcmdVec[subi].f) (interp, clientData, argc, objv); } } /* If we get here, the option doesn't match. */ Tcl_AppendResult(interp, "bad option \"", Tcl_GetString(objv[1]), "\": should be ", 0); for (subi = 0; subi < (sizeof subcmdVec) / (sizeof subcmdVec[0]); subi++) Tcl_AppendResult(interp, (subi > 0 ? ", " : ""), subcmdVec[subi].cmd, 0); return TCL_ERROR; } /* * Helper function to interpret color_idx values. */ static int tclGd_GetColor(Tcl_Interp * interp, Tcl_Obj * obj, int *color) { int nlist, retval = TCL_OK; Tcl_Obj **theList; char *firsttag, *secondtag; /* Assume it's an integer, check other cases on failure. */ if (Tcl_GetIntFromObj(interp, obj, color) == TCL_OK) return TCL_OK; else { Tcl_ResetResult(interp); if (Tcl_ListObjGetElements(interp, obj, &nlist, &theList) != TCL_OK) return TCL_ERROR; if (nlist < 1 || nlist > 2) retval = TCL_ERROR; else { firsttag = Tcl_GetString(theList[0]); switch (firsttag[0]) { case 'b': *color = gdBrushed; if (nlist == 2) { secondtag = Tcl_GetString(theList[1]); if (secondtag[0] == 's') { *color = gdStyledBrushed; } else { retval = TCL_ERROR; } } break; case 's': *color = gdStyled; if (nlist == 2) { secondtag = Tcl_GetString(theList[1]); if (secondtag[0] == 'b') { *color = gdStyledBrushed; } else { retval = TCL_ERROR; } } break; case 't': *color = gdTiled; break; default: retval = TCL_ERROR; } } } if (retval == TCL_ERROR) Tcl_SetResult(interp, "Malformed special color value", TCL_STATIC); return retval; } void Tcl_DestroyGDImageObj(Tcl_Obj *obj) { gdImagePtr im = NULL; char *name = NULL; if (obj == NULL) return; im = (gdImagePtr)obj->internalRep.otherValuePtr; if(im != NULL) { gdImageDestroy(im); obj->internalRep.otherValuePtr = NULL; } name = obj->bytes; if (name != NULL) { free(name); obj->bytes = NULL; obj->length = 0; } Tcl_DecrRefCount(obj); return; } gdImagePtr Tcl_GetGDImage(Tcl_Obj *obj) { gdImagePtr im = NULL; if (obj == NULL) return im; im = (gdImagePtr)obj->internalRep.otherValuePtr; return im; } Tcl_Obj * Tcl_NewGDImageObj(gdImagePtr im) { Tcl_Obj *out = NULL; char *name = NULL; if (im == NULL) return out; asprintf(&name,"%p",im); out=Tcl_NewObj(); Tcl_IncrRefCount(out); out->internalRep.otherValuePtr=(void *)im; out->typePtr=NULL; if (name!=NULL) { out->bytes=name; out->length=strlen(name); } else { out->bytes=NULL; out->length=0; } return out; } static int tclGdCreateCmd(Tcl_Interp * interp,ClientData clientData , int argc, Tcl_Obj * CONST objv[]) { int w, h; gdImagePtr im = NULL; FILE *filePtr; ClientData clientdata; char *cmd, buf[50]; int fileByName; cmd = Tcl_GetString(objv[1]); if (cmd!=NULL && strcmp(cmd, "create") == 0) { if (Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK) return TCL_ERROR; im = gdImageCreate(w, h); if (im == NULL) { sprintf(buf, "GD unable to allocate %d X %d image", w, h); Tcl_SetResult(interp, buf, TCL_VOLATILE); return TCL_ERROR; } #ifdef HAVE_TRUECOLOR } else if (cmd!=NULL && strcmp(cmd, "createtruecolor") == 0) { if (Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK) return TCL_ERROR; im = gdImageCreateTrueColor(w, h); if (im == NULL) { sprintf(buf, "GD unable to allocate %d X %d truecolorimage", w, h); Tcl_SetResult(interp, buf, TCL_VOLATILE); return TCL_ERROR; } #endif } else { fileByName = 0; /* first try to get file from open channel */ if (Tcl_GetOpenFile(interp, Tcl_GetString(objv[2]), 0, 1, &clientdata) == TCL_OK) { filePtr = (FILE *) clientdata; } else { /* Not a channel, or Tcl_GetOpenFile() not supported. * See if we can open directly. */ fileByName++; if ((filePtr = fopen(Tcl_GetString(objv[2]), "rb")) == NULL) { return TCL_ERROR; } Tcl_ResetResult(interp); } /* Read file */ if (strcmp(&cmd[10], "GD") == 0) { im = gdImageCreateFromGd(filePtr); #ifdef HAVE_LIBZ } else if (strcmp(&cmd[10], "GD2") == 0) { im = gdImageCreateFromGd2(filePtr); #endif #ifdef HAVE_GD_GIF } else if (strcmp(&cmd[10], "GIF") == 0) { im = gdImageCreateFromGif(filePtr); #endif #ifdef HAVE_GD_JPEG } else if (strcmp(&cmd[10], "JPEG") == 0) { im = gdImageCreateFromJpeg(filePtr); #endif #ifdef HAVE_GD_PNG } else if (strcmp(&cmd[10], "PNG") == 0) { im = gdImageCreateFromPng(filePtr); #endif } else if (strcmp(&cmd[10], "WBMP") == 0) { im = gdImageCreateFromWBMP(filePtr); #ifdef HAVE_GD_XPM } else if (strcmp(&cmd[10], "XBM") == 0) { /* FIXME - also "XPM" ? */ im = gdImageCreateFromXbm(filePtr); #endif } else { /* no im struct - will result in error */ } if (fileByName) { fclose(filePtr); } if (im == NULL) { Tcl_SetResult(interp, "GD unable to read image file", TCL_STATIC); return TCL_ERROR; } } Tcl_SetObjResult(interp, Tcl_NewGDImageObj(im)); return TCL_OK; } static int tclGdDestroyCmd(Tcl_Interp * interp,ClientData clientData , int argc,Tcl_Obj * CONST objv[]) { if (objv[2] == NULL) return TCL_OK; Tcl_DestroyGDImageObj(objv[2]); return TCL_OK; } static int tclGdWriteCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; Tcl_Channel clientdata; char *cmd; int fileByName,imSize; void *buf = NULL; cmd = Tcl_GetString(objv[1]); /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; /* Get the file reference. */ fileByName = 0; /* first try to get file from open channel */ if ((clientdata=Tcl_GetChannel(interp, Tcl_GetString(objv[3]), NULL)) == NULL) { fileByName++; if ((clientdata = Tcl_OpenFileChannel(interp,Tcl_GetString(objv[3]),"wb",0664)) == NULL) { return TCL_ERROR; } Tcl_ResetResult(interp); } /* * Write IM to OUTFILE as a JFIF-formatted JPEG image, using quality * JPEG_QUALITY. If JPEG_QUALITY is in the range 0-100, increasing values * represent higher quality but also larger image size. If JPEG_QUALITY is * negative, the IJG JPEG library's default quality is used (which * should be near optimal for many applications). See the IJG JPEG * library documentation for more details. */ /* Do it. */ if (strcmp(&cmd[5], "GD") == 0) { buf = gdImageGdPtr(im, &imSize); } else if (strcmp(&cmd[5], "GD2") == 0) { #ifdef HAVE_LIBZ #define GD2_CHUNKSIZE 128 #define GD2_COMPRESSED 2 buf = gdImageGd2Ptr(im, GD2_CHUNKSIZE, GD2_COMPRESSED,&imSize); #endif #ifdef HAVE_GD_GIF } else if (strcmp(&cmd[5], "GIF") == 0) { buf = gdImageGifPtr(im, &imSize); #endif #ifdef HAVE_GD_JPEG } else if (strcmp(&cmd[5], "JPEG") == 0) { #define JPEG_QUALITY -1 buf = gdImageJpegPtr(im, &imSize, JPEG_QUALITY); #endif #ifdef HAVE_GD_PNG } else if (strcmp(&cmd[5], "PNG") == 0) { buf = gdImagePngPtr(im, &imSize); #endif } else if (strcmp(&cmd[5], "WBMP") == 0) { /* Assume the color closest to black is the foreground color for the B&W wbmp image. */ int foreground = gdImageColorClosest(im, 0, 0, 0); buf = gdImageWBMPPtr(im, &imSize, foreground); #ifdef HAVE_GD_XBM } else if (strcmp(&cmd[5], "XBM") == 0) { buf = gdImageXbmPtr(im, &imSize); #endif } else { /* cannot happen - but would result in an empty output file */ } Tcl_Write(clientdata,buf,imSize); if(buf!=NULL) gdFree(buf); if (fileByName) { Tcl_Close(interp,clientdata); } else { Tcl_Flush(clientdata); } return TCL_OK; } static int tclGdReturnCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; char *cmd; int imSize; void *buf = NULL; int status; Ns_Conn *conn; char *mimeStr; conn = Ns_TclGetConn(interp); if (conn == NULL) { Tcl_AppendResult(interp, "no connection", NULL); return TCL_ERROR; } cmd = Tcl_GetString(objv[1]); /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; /* * Write IM to OUTFILE as a JFIF-formatted JPEG image, using quality * JPEG_QUALITY. If JPEG_QUALITY is in the range 0-100, increasing values * represent higher quality but also larger image size. If JPEG_QUALITY is * negative, the IJG JPEG library's default quality is used (which * should be near optimal for many applications). See the IJG JPEG * library documentation for more details. */ /* Do it. */ if (strcmp(&cmd[6], "GD") == 0) { buf = gdImageGdPtr(im, &imSize); mimeStr = "image/gd"; } else if (strcmp(&cmd[6], "GD2") == 0) { #ifdef HAVE_LIBZ #define GD2_CHUNKSIZE 128 #define GD2_COMPRESSED 2 buf = gdImageGd2Ptr(im, GD2_CHUNKSIZE, GD2_COMPRESSED,&imSize); mimeStr = "image/gd2"; #endif #ifdef HAVE_GD_GIF } else if (strcmp(&cmd[6], "GIF") == 0) { buf = gdImageGifPtr(im, &imSize); mimeStr = "image/gif"; #endif #ifdef HAVE_GD_JPEG } else if (strcmp(&cmd[6], "JPEG") == 0) { #define JPEG_QUALITY -1 buf = gdImageJpegPtr(im, &imSize, JPEG_QUALITY); mimeStr = "image/jpeg"; #endif #ifdef HAVE_GD_PNG } else if (strcmp(&cmd[6], "PNG") == 0) { buf = gdImagePngPtr(im, &imSize); mimeStr = "image/png"; #endif } else if (strcmp(&cmd[6], "WBMP") == 0) { /* Assume the color closest to black is the foreground color for the B&W wbmp image. */ int foreground = gdImageColorClosest(im, 0, 0, 0); buf = gdImageWBMPPtr(im, &imSize, foreground); mimeStr = "image/vnd.wap.wbmp"; #ifdef HAVE_GD_XBM } else if (strcmp(&cmd[6], "XBM") == 0) { buf = gdImageXbmPtr(im, &imSize); mimeStr = "image/x-xbitmap"; #endif } else { /* cannot happen - but would result in an empty output file */ Tcl_AppendResult(interp, "0", NULL); return TCL_OK; } status = Ns_ConnReturnData(conn, 200, buf, imSize, mimeStr); if(buf!=NULL) gdFree(buf); if (status == NS_OK) { Tcl_AppendResult(interp, "1", NULL); } else { Tcl_AppendResult(interp, "0", NULL); } return TCL_OK; } static int tclGdInterlaceCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; int on_off; /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; if (argc == 4) { /* Get the on_off values. */ if (Tcl_GetBooleanFromObj(interp, objv[3], &on_off) != TCL_OK) return TCL_ERROR; /* Do it. */ gdImageInterlace(im, on_off); } else { /* Get the current state. */ on_off = gdImageGetInterlaced(im); } Tcl_SetObjResult(interp, Tcl_NewBooleanObj(on_off)); return TCL_OK; } static int tclGdColorCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; int subi, nsub, i, args[3]; nsub = (sizeof colorCmdVec) / (sizeof colorCmdVec[0]); if (argc >= 3) { /* Find the subcommand. */ for (subi = 0; subi < nsub; subi++) { if (strcmp(colorCmdVec[subi].cmd, Tcl_GetString(objv[2])) == 0) { /* Check arg count. */ if (argc - 2 < colorCmdVec[subi].minargs || argc - 2 > colorCmdVec[subi].maxargs) { Tcl_AppendResult(interp, "wrong # args: should be \"gd color ", colorCmdVec[subi].cmd, " ", colorCmdVec[subi].usage, "\"", 0); return TCL_ERROR; } /* Get the image pointer. */ im = Tcl_GetGDImage(objv[3]); if (im == NULL) return TCL_ERROR; /* Parse off integer arguments. * 1st 4 are gd color */ for (i = 0; i < argc - 4; i++) { if (Tcl_GetIntFromObj(interp, objv[i + 4], &args[i]) != TCL_OK) { #if 0 if (args[i] < 0 || args[i] > 255) { #else /* gd text uses -ve colors to turn off anti-aliasing */ if (args[i] < -255 || args[i] > 255) { #endif Tcl_SetResult(interp, "argument out of range 0-255", TCL_STATIC); return TCL_ERROR; } } } /* Call the subcommand function. */ return (*colorCmdVec[subi].f) (interp, im, argc - 4, args); } } } /* If we get here, the option doesn't match. */ if (argc > 2) { Tcl_AppendResult(interp, "bad option \"", Tcl_GetString(objv[2]), "\": ", 0); } else { Tcl_AppendResult(interp, "wrong # args: ", 0); } Tcl_AppendResult(interp, "should be ", 0); for (subi = 0; subi < nsub; subi++) Tcl_AppendResult(interp, (subi > 0 ? ", " : ""), colorCmdVec[subi].cmd, 0); return TCL_ERROR; } static int tclGdColorNewCmd(Tcl_Interp * interp, gdImagePtr im, int argc, int args[]) { int color; color = gdImageColorAllocate(im, args[0], args[1], args[2]); Tcl_SetObjResult(interp, Tcl_NewIntObj(color)); return TCL_OK; } static int tclGdColorExactCmd(Tcl_Interp * interp, gdImagePtr im, int argc, int args[]) { int color; color = gdImageColorExact(im, args[0], args[1], args[2]); Tcl_SetObjResult(interp, Tcl_NewIntObj(color)); return TCL_OK; } static int tclGdColorClosestCmd(Tcl_Interp * interp, gdImagePtr im, int argc, int args[]) { int color; color = gdImageColorClosest(im, args[0], args[1], args[2]); Tcl_SetObjResult(interp, Tcl_NewIntObj(color)); return TCL_OK; } static int tclGdColorResolveCmd(Tcl_Interp * interp, gdImagePtr im, int argc, int args[]) { int color; color = gdImageColorResolve(im, args[0], args[1], args[2]); Tcl_SetObjResult(interp, Tcl_NewIntObj(color)); return TCL_OK; } static int tclGdColorFreeCmd(Tcl_Interp * interp, gdImagePtr im, int argc, int args[]) { gdImageColorDeallocate(im, args[0]); return TCL_OK; } static int tclGdColorTranspCmd(Tcl_Interp * interp, gdImagePtr im, int argc, int args[]) { int color; if (argc > 0) { color = args[0]; gdImageColorTransparent(im, color); } else { color = gdImageGetTransparent(im); } Tcl_SetObjResult(interp, Tcl_NewIntObj(color)); return TCL_OK; } static int tclGdColorGetCmd(Tcl_Interp * interp, gdImagePtr im, int argc, int args[]) { char buf[30]; int i; /* IF one arg, return the single color, else return list of all colors. */ if (argc == 1) { i = args[0]; if (i >= gdImageColorsTotal(im) || im->open[i]) { Tcl_SetResult(interp, "No such color", TCL_STATIC); return TCL_ERROR; } sprintf(buf, "%d %d %d %d", i, gdImageRed(im, i), gdImageGreen(im, i), gdImageBlue(im, i)); Tcl_SetResult(interp, buf, TCL_VOLATILE); } else { for (i = 0; i < gdImageColorsTotal(im); i++) { if (im->open[i]) continue; sprintf(buf, "%d %d %d %d", i, gdImageRed(im, i), gdImageGreen(im, i), gdImageBlue(im, i)); Tcl_AppendElement(interp, buf); } } return TCL_OK; } static int tclGdBrushCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im, imbrush; /* Get the image pointers. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; imbrush = Tcl_GetGDImage(objv[3]); if (imbrush == NULL) return TCL_ERROR; /* Do it. */ gdImageSetBrush(im, imbrush); return TCL_OK; } static int tclGdTileCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im, tile; /* Get the image pointers. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; tile = Tcl_GetGDImage(objv[3]); if (tile == NULL) return TCL_ERROR; /* Do it. */ gdImageSetTile(im, tile); return TCL_OK; } static int tclGdStyleCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; int ncolor, *colors = NULL, i; Tcl_Obj **colorObjv = (Tcl_Obj **) (&objv[3]); /* By default, colors are listed in objv. */ int retval = TCL_OK; /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; /* Figure out how many colors in the style list and allocate memory. */ ncolor = argc - 3; /* If only one argument, treat it as a list. */ if (ncolor == 1) if (Tcl_ListObjGetElements(interp, objv[3], &ncolor, &colorObjv) != TCL_OK) return TCL_ERROR; colors = (int *) Tcl_Alloc(ncolor * sizeof(int)); if (colors == NULL) { Tcl_SetResult(interp, "Memory allocation failed", TCL_STATIC); retval = TCL_ERROR; goto out; } /* Get the color values. */ for (i = 0; i < ncolor; i++) if (Tcl_GetIntFromObj(interp, colorObjv[i], &colors[i]) != TCL_OK) { retval = TCL_ERROR; break; } /* Call the Style function if no error. */ if (retval == TCL_OK) gdImageSetStyle(im, colors, ncolor); out: /* Free the colors. */ if (colors != NULL) Tcl_Free((char *) colors); return retval; } static int tclGdSetCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; int color, x, y; /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; /* Get the color, x, y values. */ if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[4], &x) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[5], &y) != TCL_OK) return TCL_ERROR; /* Call the Set function. */ gdImageSetPixel(im, x, y, color); return TCL_OK; } static int tclGdLineCmd(Tcl_Interp * interp,ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; int color, x1, y1, x2, y2; /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; /* Get the color, x, y values. */ if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[4], &x1) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[5], &y1) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[6], &x2) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[7], &y2) != TCL_OK) return TCL_ERROR; /* Call the appropriate Line function. */ gdImageLine(im, x1, y1, x2, y2, color); return TCL_OK; } static int tclGdRectCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; int color, x1, y1, x2, y2; char *cmd; /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; /* Get the color, x, y values. */ if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[4], &x1) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[5], &y1) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[6], &x2) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[7], &y2) != TCL_OK) return TCL_ERROR; /* Call the appropriate rectangle function. */ cmd = Tcl_GetString(objv[1]); if (cmd[0] == 'r') gdImageRectangle(im, x1, y1, x2, y2, color); else gdImageFilledRectangle(im, x1, y1, x2, y2, color); return TCL_OK; } static int tclGdArcCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; int color, cx, cy, width, height, start, end; char *cmd; /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; /* Get the color, x, y values. */ if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[4], &cx) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[5], &cy) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[6], &width) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[7], &height) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[8], &start) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[9], &end) != TCL_OK) return TCL_ERROR; /* Call the appropriate arc function. */ cmd = Tcl_GetString(objv[1]); if (cmd[0] == 'a') gdImageArc(im, cx, cy, width, height, start, end, color); else { /* gdImageFilledArc(im, cx, cy, width, height, start, end, color); */ Tcl_SetResult(interp, "gdImageFilledArc not supported in gd1.2", TCL_STATIC); return TCL_ERROR; } return TCL_OK; } static int tclGdPolygonCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; int color, npoints, i; Tcl_Obj **pointObjv = (Tcl_Obj **) (&objv[4]); gdPointPtr points = NULL; int retval = TCL_OK; char *cmd; /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; /* Get the color, x, y values. */ if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK) return TCL_ERROR; /* Figure out how many points in the list and allocate memory. */ npoints = argc - 4; /* If only one argument, treat it as a list. */ if (npoints == 1) if (Tcl_ListObjGetElements(interp, objv[4], &npoints, &pointObjv) != TCL_OK) return TCL_ERROR; /* Error check size of point list. */ if (npoints % 2 != 0) { Tcl_SetResult(interp, "Number of coordinates must be even", TCL_STATIC); retval = TCL_ERROR; goto out; } /* Divide by 2 to get number of points, and final error check. */ npoints /= 2; if (npoints < 3) { Tcl_SetResult(interp, "Must specify at least 3 points.", TCL_STATIC); retval = TCL_ERROR; goto out; } points = (gdPointPtr) Tcl_Alloc(npoints * sizeof(gdPoint)); if (points == NULL) { Tcl_SetResult(interp, "Memory allocation failed", TCL_STATIC); retval = TCL_ERROR; goto out; } /* Get the point values. */ for (i = 0; i < npoints; i++) if (Tcl_GetIntFromObj(interp, pointObjv[i * 2], &points[i].x) != TCL_OK || Tcl_GetIntFromObj(interp, pointObjv[i * 2 + 1], &points[i].y) != TCL_OK) { retval = TCL_ERROR; break; } /* Call the appropriate polygon function. */ cmd = Tcl_GetString(objv[1]); if (cmd[0] == 'p') gdImagePolygon(im, points, npoints, color); else gdImageFilledPolygon(im, points, npoints, color); out: /* Free the points. */ if (points != NULL) Tcl_Free((char *) points); /* return TCL_OK; */ return retval; } static int tclGdFillCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; int color, x, y, border; /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; /* Get the color, x, y and possibly bordercolor values. */ if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[4], &x) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[5], &y) != TCL_OK) return TCL_ERROR; if (x==im->sx || y==im->sy) { #define NB 500 char buf[NB]; snprintf(buf, NB, "wrong # One of fill coordinate (%d,%d) is equal to image size (%d,%d).\nThis can crash aolserver.",x,y,im->sx,im->sy); Tcl_SetResult(interp, buf, TCL_VOLATILE); return TCL_ERROR; } /* Call the appropriate fill function. */ if (argc - 2 == 5) { if (Tcl_GetIntFromObj(interp, objv[6], &border) != TCL_OK) return TCL_ERROR; gdImageFillToBorder(im, x, y, border, color); } else { gdImageFill(im, x, y, color); } return TCL_OK; } static int tclGdCopyCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr imdest, imsrc; int destx, desty, srcx, srcy, destw, desth, srcw, srch; /* Get the image pointer. */ imdest = Tcl_GetGDImage(objv[2]); if (imdest == NULL) return TCL_ERROR; imsrc = Tcl_GetGDImage(objv[3]); if (imsrc == NULL) return TCL_ERROR; /* Get the x, y, etc. values. */ if (Tcl_GetIntFromObj(interp, objv[4], &destx) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[5], &desty) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[6], &srcx) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[7], &srcy) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[8], &destw) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[9], &desth) != TCL_OK) return TCL_ERROR; /* Call the appropriate copy function. */ if (argc - 2 == 10) { if (Tcl_GetIntFromObj(interp, objv[10], &srcw) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[11], &srch) != TCL_OK) return TCL_ERROR; #ifdef HAVE_TRUECOLOR gdImageCopyResampled(imdest, imsrc, destx, desty, srcx, srcy, destw, desth, srcw, srch); #else gdImageCopyResized(imdest, imsrc, destx, desty, srcx, srcy, destw, desth, srcw, srch); #endif } else gdImageCopy(imdest, imsrc, destx, desty, srcx, srcy, destw, desth); return TCL_OK; } static int tclGdGetCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; int color, x, y; /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; /* Get the x, y values. */ if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) return TCL_ERROR; if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) return TCL_ERROR; /* Call the Get function. */ color = gdImageGetPixel(im, x, y); Tcl_SetObjResult(interp, Tcl_NewIntObj(color)); return TCL_OK; } static int tclGdSizeCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { gdImagePtr im; char buf[30]; /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; sprintf(buf, "%d %d", gdImageSX(im), gdImageSY(im)); Tcl_SetResult(interp, buf, TCL_VOLATILE); return TCL_OK; } static int tclGdTextCmd(Tcl_Interp *interp, ClientData clientData , int argc, Tcl_Obj *CONST objv[]) { /* gd gdhandle color fontpathname x y string */ gdImagePtr im; int color, x, y; char *fontStr, *upStr; gdFontPtr font = NULL; int len, up_p = 0; unsigned char *str; /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; /* Get the color, values. */ if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK) { return TCL_ERROR; } /* get x, y position */ if (Tcl_GetIntFromObj(interp, objv[5], &x) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[6], &y) != TCL_OK) { return TCL_ERROR; } str = Tcl_GetStringFromObj(objv[7], &len); fontStr = Tcl_GetString(objv[4]); if (!strcmp(fontStr, "tiny")) { font = gdFontTiny; } else if (!strcmp(fontStr, "small")) { font = gdFontSmall; } else if (!strcmp(fontStr, "medium")) { font = gdFontMediumBold; } else if (!strcmp(fontStr, "large")) { font = gdFontLarge; } else if (!strcmp(fontStr, "giant")) { font = gdFontGiant; } else { font = NULL; } if (font == NULL) { return TCL_ERROR; } if (argc == 9) { upStr = Tcl_GetString(objv[8]); if (!strcmp(upStr, "up")) { up_p = 1; } else { return TCL_ERROR; } } if (up_p == 1) { gdImageStringUp(im, font, x, y, str, color); } else { gdImageString(im, font, x, y, str, color); }; return TCL_OK; } static int tclGdTextFTCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { /* gd gdhandle color fontpathname size angle x y string */ gdImagePtr im = NULL; int color, x, y; double ptsize, angle; char *error, buf[32], *font; int i, brect[8], len; char *str; /* Get the image pointer. (an invalid or null arg[2] will result in string size calculation but no rendering */ if (objv[2] != NULL) { im = Tcl_GetGDImage(objv[2]); } /* Get the color, values. */ if (tclGd_GetColor(interp, objv[3], &color) != TCL_OK) { return TCL_ERROR; } /* Get point size */ if (Tcl_GetDoubleFromObj(interp, objv[5], &ptsize) != TCL_OK) { return TCL_ERROR; } /* Get rotation (radians) */ if (Tcl_GetDoubleFromObj(interp, objv[6], &angle) != TCL_OK) { return TCL_ERROR; } /* get x, y position */ if (Tcl_GetIntFromObj(interp, objv[7], &x) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[8], &y) != TCL_OK) { return TCL_ERROR; } str = Tcl_GetStringFromObj(objv[9], &len); font = Tcl_GetString(objv[4]); error = gdImageStringFT(im, brect, color, font, ptsize, angle, x, y, str); if (error) { Tcl_SetResult(interp, error, TCL_VOLATILE); return TCL_ERROR; } for (i = 0; i < 8; i++) { sprintf(buf, "%d", brect[i]); Tcl_AppendElement(interp, buf); } return TCL_OK; } static int BufferSinkFunc(void *context, const char *buffer, int len) { BuffSinkContext *p = context; char *bufend; if (p->buflen == 0) { p->buf = Tcl_Alloc((unsigned int)(len + 1)); memcpy(p->buf, buffer, (unsigned int)len); p->buf[len] = '\0'; p->buflen = len; } else { p->buf = Tcl_Realloc(p->buf, (unsigned int)(len + p->buflen + 1)); bufend = p->buf + p->buflen; memmove(bufend, buffer, (unsigned int)len); p->buf[len + p->buflen] = '\0'; p->buflen += len; } return len; } static int tclGdWriteBufCmd(Tcl_Interp * interp, ClientData clientData, int argc, Tcl_Obj * CONST objv[]) { #ifdef HAVE_GD_PNG gdImagePtr im; Tcl_Obj *output; /* char *cmd; */ char *result = NULL; BuffSinkContext bsc = { NULL, 0 }; BuffSinkContext *res; gdSink buffsink; buffsink.sink = BufferSinkFunc; buffsink.context = (void *) &bsc; /* cmd = */ Tcl_GetString(objv[1]); /* Get the image pointer. */ im = Tcl_GetGDImage(objv[2]); if (im == NULL) return TCL_ERROR; gdImagePngToSink(im, &buffsink); /* I'm not so hot with lots of pointer indirection, so this makes things easier. - davidw */ res = buffsink.context; result = res->buf; output = Tcl_NewByteArrayObj((unsigned char *) result, res->buflen); if (output == NULL) return TCL_ERROR; else Tcl_IncrRefCount(output); if (Tcl_ObjSetVar2(interp, objv[3], NULL, output, 0) == NULL) return TCL_ERROR; else #endif return TCL_OK; }