--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP HTML Embedded Scripting Language Version 3.0 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-1999 PHP Development Team (See Credits file) |
+ +----------------------------------------------------------------------+
+ | This program is free software; you can redistribute it and/or modify |
+ | it under the terms of one of the following licenses: |
+ | |
+ | A) 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. |
+ | |
+ | B) the PHP License as published by the PHP Development Team and |
+ | included in the distribution in the file: LICENSE |
+ | |
+ | 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 both licenses referred to here. |
+ | If you did not, or have any questions about PHP licensing, please |
+ | contact core@php.net. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
+ | Stig Bakken <ssb@guardian.no> |
+ | Jim Winstead <jimw@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+/* gd 1.2 is copyright 1994, 1995, Quest Protein Database Center,
+ Cold Spring Harbor Labs. */
+
+/* Note that there is no code from the gd package in this file */
+#ifdef THREAD_SAFE
+# include "tls.h"
+#endif
+#include "php.h"
+#include "head.h"
+#include <math.h>
+#include "php3_gd.h"
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#if WIN32|WINNT
+# include <io.h>
+# include <fcntl.h>
+#endif
+
+#if HAVE_LIBGD
+#include <gd.h>
+#include <gdfontt.h> /* 1 Tiny font */
+#include <gdfonts.h> /* 2 Small font */
+#include <gdfontmb.h> /* 3 Medium bold font */
+#include <gdfontl.h> /* 4 Large font */
+#include <gdfontg.h> /* 5 Giant font */
+#if HAVE_LIBTTF
+# if PHP_31
+# include "gdttf.h"
+# else
+# include "functions/gdttf.h"
+# endif
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#if HAVE_LIBTTF
+static void php3_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int);
+#endif
+
+#ifdef THREAD_SAFE
+DWORD GDlibTls;
+static int numthreads=0;
+void *gdlib_mutex=NULL;
+
+typedef struct gdlib_global_struct{
+ int le_gd;
+ int le_gd_font;
+} gdlib_global_struct;
+
+# define GD_GLOBAL(a) gdlib_globals->a
+# define GD_TLS_VARS gdlib_global_struct *gdlib_globals = TlsGetValue(GDlibTls);
+
+#else
+# define GD_GLOBAL(a) a
+# define GD_TLS_VARS
+int le_gd;
+int le_gd_font;
+#endif
+
+function_entry gd_functions[] = {
+ {"imagearc", php3_imagearc, NULL},
+ {"imagechar", php3_imagechar, NULL},
+ {"imagecharup", php3_imagecharup, NULL},
+ {"imagecolorallocate", php3_imagecolorallocate, NULL},
+ {"imagecolorat", php3_imagecolorat, NULL},
+ {"imagecolorclosest", php3_imagecolorclosest, NULL},
+ {"imagecolordeallocate", php3_imagecolordeallocate, NULL},
+ {"imagecolorresolve", php3_imagecolorresolve, NULL},
+ {"imagecolorexact", php3_imagecolorexact, NULL},
+ {"imagecolorset", php3_imagecolorset, NULL},
+ {"imagecolortransparent", php3_imagecolortransparent, NULL},
+ {"imagecolorstotal", php3_imagecolorstotal, NULL},
+ {"imagecolorsforindex", php3_imagecolorsforindex, NULL},
+ {"imagecopy", php3_imagecopy, NULL},
+ {"imagecopyresized", php3_imagecopyresized, NULL},
+ {"imagecreate", php3_imagecreate, NULL},
+ {"imagecreatefromgif", php3_imagecreatefromgif, NULL},
+ {"imagedestroy", php3_imagedestroy, NULL},
+ {"imagefill", php3_imagefill, NULL},
+ {"imagefilledpolygon", php3_imagefilledpolygon, NULL},
+ {"imagefilledrectangle", php3_imagefilledrectangle, NULL},
+ {"imagefilltoborder", php3_imagefilltoborder, NULL},
+ {"imagefontwidth", php3_imagefontwidth, NULL},
+ {"imagefontheight", php3_imagefontheight, NULL},
+ {"imagegif", php3_imagegif, NULL},
+ {"imageinterlace", php3_imageinterlace, NULL},
+ {"imageline", php3_imageline, NULL},
+ {"imageloadfont", php3_imageloadfont, NULL},
+ {"imagepolygon", php3_imagepolygon, NULL},
+ {"imagerectangle", php3_imagerectangle, NULL},
+ {"imagesetpixel", php3_imagesetpixel, NULL},
+ {"imagestring", php3_imagestring, NULL},
+ {"imagestringup", php3_imagestringup, NULL},
+ {"imagesx", php3_imagesxfn, NULL},
+ {"imagesy", php3_imagesyfn, NULL},
+ {"imagedashedline", php3_imagedashedline, NULL},
+#if HAVE_LIBTTF
+ {"imagettfbbox", php3_imagettfbbox, NULL},
+ {"imagettftext", php3_imagettftext, NULL},
+#endif
+ {NULL, NULL, NULL}
+};
+
+php3_module_entry gd_module_entry = {
+ "gd", gd_functions, php3_minit_gd, php3_mend_gd, NULL, NULL, php3_info_gd, STANDARD_MODULE_PROPERTIES
+};
+
+#if COMPILE_DL
+# if PHP_31
+# include "../phpdl.h"
+# else
+# include "dl/phpdl.h"
+# endif
+DLEXPORT php3_module_entry *get_module(void) { return &gd_module_entry; }
+#endif
+
+
+#define PolyMaxPoints 256
+
+
+int php3_minit_gd(INIT_FUNC_ARGS)
+{
+#if defined(THREAD_SAFE)
+ gdlib_global_struct *gdlib_globals;
+ PHP3_MUTEX_ALLOC(gdlib_mutex);
+ PHP3_MUTEX_LOCK(gdlib_mutex);
+ numthreads++;
+ if (numthreads==1){
+ if (!PHP3_TLS_PROC_STARTUP(GDlibTls)){
+ PHP3_MUTEX_UNLOCK(gdlib_mutex);
+ PHP3_MUTEX_FREE(gdlib_mutex);
+ return FAILURE;
+ }
+ }
+ PHP3_MUTEX_UNLOCK(gdlib_mutex);
+ if(!PHP3_TLS_THREAD_INIT(GDlibTls,gdlib_globals,gdlib_global_struct)){
+ PHP3_MUTEX_FREE(gdlib_mutex);
+ return FAILURE;
+ }
+#endif
+ GD_GLOBAL(le_gd) = register_list_destructors(gdImageDestroy, NULL);
+ GD_GLOBAL(le_gd_font) = register_list_destructors(php3_free_gd_font, NULL);
+ return SUCCESS;
+}
+
+void php3_info_gd(void) {
+ /* need to use a PHPAPI function here because it is external module in windows */
+#if HAVE_LIBGD13
+ php3_printf("Version 1.3");
+#else
+ php3_printf("Version 1.2");
+#endif
+#if HAVE_LIBTTF
+ php3_printf(" with FreeType support");
+#endif
+}
+
+int php3_mend_gd(void){
+ GD_TLS_VARS;
+#ifdef THREAD_SAFE
+ PHP3_TLS_THREAD_FREE(gdlib_globals);
+ PHP3_MUTEX_LOCK(gdlib_mutex);
+ numthreads--;
+ if (numthreads<1) {
+ PHP3_TLS_PROC_SHUTDOWN(GDlibTls);
+ PHP3_MUTEX_UNLOCK(gdlib_mutex);
+ PHP3_MUTEX_FREE(gdlib_mutex);
+ return SUCCESS;
+ }
+ PHP3_MUTEX_UNLOCK(gdlib_mutex);
+#endif
+ return SUCCESS;
+}
+
+/********************************************************************/
+/* gdImageColorResolve is a replacement for the old fragment: */
+/* */
+/* if ((color=gdImageColorExact(im,R,G,B)) < 0) */
+/* if ((color=gdImageColorAllocate(im,R,G,B)) < 0) */
+/* color=gdImageColorClosest(im,R,G,B); */
+/* */
+/* in a single function */
+
+int
+gdImageColorResolve(gdImagePtr im, int r, int g, int b)
+{
+ int c;
+ int ct = -1;
+ int op = -1;
+ long rd, gd, bd, dist;
+ long mindist = 3*255*255; /* init to max poss dist */
+
+ for (c = 0; c < im->colorsTotal; c++) {
+ if (im->open[c]) {
+ op = c; /* Save open slot */
+ continue; /* Color not in use */
+ }
+ rd = (long)(im->red [c] - r);
+ gd = (long)(im->green[c] - g);
+ bd = (long)(im->blue [c] - b);
+ dist = rd * rd + gd * gd + bd * bd;
+ if (dist < mindist) {
+ if (dist == 0) {
+ return c; /* Return exact match color */
+ }
+ mindist = dist;
+ ct = c;
+ }
+ }
+ /* no exact match. We now know closest, but first try to allocate exact */
+ if (op == -1) {
+ op = im->colorsTotal;
+ if (op == gdMaxColors) { /* No room for more colors */
+ return ct; /* Return closest available color */
+ }
+ im->colorsTotal++;
+ }
+ im->red [op] = r;
+ im->green[op] = g;
+ im->blue [op] = b;
+ im->open [op] = 0;
+ return op; /* Return newly allocated color */
+}
+
+void php3_free_gd_font(gdFontPtr fp)
+{
+ if (fp->data) {
+ efree(fp->data);
+ }
+ efree(fp);
+}
+
+/* {{{ proto int imageloadfont(string filename)
+Load a new font */
+void php3_imageloadfont(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *file;
+ int hdr_size = sizeof(gdFont) - sizeof(char *);
+ int ind, body_size, n=0, b;
+ gdFontPtr font;
+ FILE *fp;
+ int issock=0, socketd=0;
+ GD_TLS_VARS;
+
+
+ if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &file) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_string(file);
+
+#if WIN32|WINNT
+ fp = fopen(file->value.str.val, "rb");
+#else
+ fp = php3_fopen_wrapper(file->value.str.val, "r", IGNORE_PATH|IGNORE_URL_WIN, &issock, &socketd);
+#endif
+ if (fp == NULL) {
+ php3_error(E_WARNING, "ImageFontLoad: unable to open file");
+ RETURN_FALSE;
+ }
+
+ /* Only supports a architecture-dependent binary dump format
+ * at the moment.
+ * The file format is like this on machines with 32-byte integers:
+ *
+ * byte 0-3: (int) number of characters in the font
+ * byte 4-7: (int) value of first character in the font (often 32, space)
+ * byte 8-11: (int) pixel width of each character
+ * byte 12-15: (int) pixel height of each character
+ * bytes 16-: (char) array with character data, one byte per pixel
+ * in each character, for a total of
+ * (nchars*width*height) bytes.
+ */
+ font = (gdFontPtr)emalloc(sizeof(gdFont));
+ b = 0;
+ while (b < hdr_size && (n = fread(&font[b], 1, hdr_size - b, fp)))
+ b += n;
+ if (!n) {
+ fclose(fp);
+ efree(font);
+ if (feof(fp)) {
+ php3_error(E_WARNING, "ImageFontLoad: end of file while reading header");
+ } else {
+ php3_error(E_WARNING, "ImageFontLoad: error while reading header");
+ }
+ RETURN_FALSE;
+ }
+ body_size = font->w * font->h * font->nchars;
+ font->data = emalloc(body_size);
+ b = 0;
+ while (b < body_size && (n = fread(&font->data[b], 1, body_size - b, fp)))
+ b += n;
+ if (!n) {
+ fclose(fp);
+ efree(font->data);
+ efree(font);
+ if (feof(fp)) {
+ php3_error(E_WARNING, "ImageFontLoad: end of file while reading body");
+ } else {
+ php3_error(E_WARNING, "ImageFontLoad: error while reading body");
+ }
+ RETURN_FALSE;
+ }
+ fclose(fp);
+
+ /* Adding 5 to the font index so we will never have font indices
+ * that overlap with the old fonts (with indices 1-5). The first
+ * list index given out is always 1.
+ */
+ ind = 5 + php3_list_insert(font, GD_GLOBAL(le_gd_font));
+
+ RETURN_LONG(ind);
+}
+/* }}} */
+
+/* {{{ proto int imagecreate(int x_size, int y_size)
+Create a new image */
+void php3_imagecreate(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *x_size, *y_size;
+ int ind;
+ gdImagePtr im;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 2 || getParameters(ht, 2, &x_size, &y_size) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(x_size);
+ convert_to_long(y_size);
+
+ im = gdImageCreate(x_size->value.lval, y_size->value.lval);
+ ind = php3_list_insert(im, GD_GLOBAL(le_gd));
+
+ RETURN_LONG(ind);
+}
+/* }}} */
+
+/* {{{ proto int imagecreatefromgif(string filename)
+Create a new image from file or URL */
+void php3_imagecreatefromgif (INTERNAL_FUNCTION_PARAMETERS) {
+ pval *file;
+ int ind;
+ gdImagePtr im;
+ char *fn=NULL;
+ FILE *fp;
+ int issock=0, socketd=0;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &file) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_string(file);
+
+ fn = file->value.str.val;
+
+#if WIN32|WINNT
+ fp = fopen(file->value.str.val, "rb");
+#else
+ fp = php3_fopen_wrapper(file->value.str.val, "r", IGNORE_PATH|IGNORE_URL_WIN, &issock, &socketd);
+#endif
+ if (!fp) {
+ php3_strip_url_passwd(fn);
+ php3_error(E_WARNING,
+ "ImageCreateFromGif: Unable to open %s for reading", fn);
+ RETURN_FALSE;
+ }
+
+ im = gdImageCreateFromGif (fp);
+
+ fflush(fp);
+ fclose(fp);
+
+ ind = php3_list_insert(im, GD_GLOBAL(le_gd));
+
+ RETURN_LONG(ind);
+}
+/* }}} */
+
+/* {{{ proto int imagedestroy(int im)
+Destroy an image */
+void php3_imagedestroy(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *imgind;
+
+ if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &imgind) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(imgind);
+
+ php3_list_delete(imgind->value.lval);
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int imagecolorallocate(int im, int red, int green, int blue)
+Allocate a color for an image */
+void php3_imagecolorallocate(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *imgind, *red, *green, *blue;
+ int ind, ind_type;
+ int col;
+ int r, g, b;
+ gdImagePtr im;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 4 || getParameters(ht, 4, &imgind, &red,
+ &green, &blue) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(imgind);
+ convert_to_long(red);
+ convert_to_long(green);
+ convert_to_long(blue);
+
+ ind = imgind->value.lval;
+ r = red->value.lval;
+ g = green->value.lval;
+ b = blue->value.lval;
+
+ im = php3_list_find(ind, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "ImageColorAllocate: Unable to find image pointer");
+ RETURN_FALSE;
+ }
+ col = gdImageColorAllocate(im, r, g, b);
+ RETURN_LONG(col);
+}
+/* }}} */
+
+/* im, x, y */
+/* {{{ proto int imagecolorat(int im, int x, int y)
+Get the index of the color of a pixel */
+void php3_imagecolorat(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *imgind, *x, *y;
+ int ind, ind_type;
+ gdImagePtr im;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 3 || getParameters(ht, 3, &imgind, &x, &y) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(imgind);
+ convert_to_long(x);
+ convert_to_long(y);
+
+ ind = imgind->value.lval;
+
+ im = php3_list_find(ind, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "ImageColorAt: Unable to find image pointer");
+ RETURN_FALSE;
+ }
+ if (gdImageBoundsSafe(im, x->value.lval, y->value.lval)) {
+#if HAVE_LIBGD13
+ RETURN_LONG(im->pixels[y->value.lval][x->value.lval]);
+#else
+ RETURN_LONG(im->pixels[x->value.lval][y->value.lval]);
+#endif
+ }
+ else {
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ proto int imagecolorclosest(int im, int red, int green, int blue)
+Get the index of the closest color to the specified color */
+void php3_imagecolorclosest(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *imgind, *red, *green, *blue;
+ int ind, ind_type;
+ int col;
+ int r, g, b;
+ gdImagePtr im;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 4 || getParameters(ht, 4, &imgind, &red,
+ &green, &blue) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(imgind);
+ convert_to_long(red);
+ convert_to_long(green);
+ convert_to_long(blue);
+
+ ind = imgind->value.lval;
+ r = red->value.lval;
+ g = green->value.lval;
+ b = blue->value.lval;
+
+ im = php3_list_find(ind, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "ImageColorClosest: Unable to find image pointer");
+ RETURN_FALSE;
+ }
+ col = gdImageColorClosest(im, r, g, b);
+ RETURN_LONG(col);
+}
+/* }}} */
+
+/* {{{ proto int imagecolordeallocate(int im, int index)
+De-allocate a color for an image */
+void php3_imagecolordeallocate(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *imgind, *index;
+ int ind, ind_type, col;
+ gdImagePtr im;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 2 || getParameters(ht, 2, &imgind, &index) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(imgind);
+ convert_to_long(index);
+ ind = imgind->value.lval;
+ col = index->value.lval;
+
+ im = php3_list_find(ind, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "ImageColorDeallocate: Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ if (col >= 0 && col < gdImageColorsTotal(im)) {
+ gdImageColorDeallocate(im, col);
+ RETURN_TRUE;
+ }
+ else {
+ php3_error(E_WARNING, "Color index out of range");
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ proto int imagecolorresolve(int im, int red, int green, int blue)
+Get the index of the specified color or its closest possible alternative */
+void php3_imagecolorresolve(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *imgind, *red, *green, *blue;
+ int ind, ind_type;
+ int col;
+ int r, g, b;
+ gdImagePtr im;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 4 || getParameters(ht, 4, &imgind, &red,
+ &green, &blue) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(imgind);
+ convert_to_long(red);
+ convert_to_long(green);
+ convert_to_long(blue);
+
+ ind = imgind->value.lval;
+ r = red->value.lval;
+ g = green->value.lval;
+ b = blue->value.lval;
+
+ im = php3_list_find(ind, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "ImageColorResolve: Unable to find image pointer");
+ RETURN_FALSE;
+ }
+ col = gdImageColorResolve(im, r, g, b);
+ RETURN_LONG(col);
+}
+/* }}} */
+
+/* {{{ proto int imagecolorexact(int im, int red, int green, int blue)
+Get the index of the specified color */
+void php3_imagecolorexact(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *imgind, *red, *green, *blue;
+ int ind, ind_type;
+ int col;
+ int r, g, b;
+ gdImagePtr im;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 4 || getParameters(ht, 4, &imgind, &red,
+ &green, &blue) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(imgind);
+ convert_to_long(red);
+ convert_to_long(green);
+ convert_to_long(blue);
+
+ ind = imgind->value.lval;
+ r = red->value.lval;
+ g = green->value.lval;
+ b = blue->value.lval;
+
+ im = php3_list_find(ind, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "ImageColorExact: Unable to find image pointer");
+ RETURN_FALSE;
+ }
+ col = gdImageColorExact(im, r, g, b);
+ RETURN_LONG(col);
+}
+/* }}} */
+
+/* {{{ proto int imagecolorset(int im, int col, int red, int green, int blue)
+Set the color for the specified palette index */
+void php3_imagecolorset(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *imgind, *color, *red, *green, *blue;
+ int ind, ind_type;
+ int col;
+ int r, g, b;
+ gdImagePtr im;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 5 || getParameters(ht, 5, &imgind, &color, &red, &green, &blue) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(imgind);
+ convert_to_long(color);
+ convert_to_long(red);
+ convert_to_long(green);
+ convert_to_long(blue);
+
+ ind = imgind->value.lval;
+ col = color->value.lval;
+ r = red->value.lval;
+ g = green->value.lval;
+ b = blue->value.lval;
+
+ im = php3_list_find(ind, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "ImageColorSet: Unable to find image pointer");
+ RETURN_FALSE;
+ }
+ if (col >= 0 && col < gdImageColorsTotal(im)) {
+ im->red[col] = r;
+ im->green[col] = g;
+ im->blue[col] = b;
+ }
+ else {
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ proto array imagecolorsforindex(int im, int col)
+Get the colors for an index */
+void php3_imagecolorsforindex(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *imgind, *index;
+ int col, ind, ind_type;
+ gdImagePtr im;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 2 || getParameters(ht, 2, &imgind, &index) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(imgind);
+ convert_to_long(index);
+ ind = imgind->value.lval;
+ col = index->value.lval;
+
+ im = php3_list_find(ind, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "ImageColorsForIndex: Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ if (col >= 0 && col < gdImageColorsTotal(im)) {
+ if (array_init(return_value) == FAILURE) {
+ RETURN_FALSE;
+ }
+ add_assoc_long(return_value,"red",im->red[col]);
+ add_assoc_long(return_value,"green",im->green[col]);
+ add_assoc_long(return_value,"blue",im->blue[col]);
+ }
+ else {
+ php3_error(E_WARNING, "Color index out of range");
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ proto int imagegif(int im, string filename)
+Output image to browser or file */
+void php3_imagegif (INTERNAL_FUNCTION_PARAMETERS) {
+ pval *imgind, *file;
+ gdImagePtr im;
+ char *fn=NULL;
+ FILE *fp;
+ int argc;
+ int ind_type;
+ int output=1;
+ GD_TLS_VARS;
+
+ argc = ARG_COUNT(ht);
+ if (argc < 1 || argc > 2 || getParameters(ht, argc, &imgind, &file) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(imgind);
+
+ if (argc == 2) {
+ convert_to_string(file);
+ fn = file->value.str.val;
+ if (!fn || fn == empty_string || _php3_check_open_basedir(fn)) {
+ php3_error(E_WARNING, "ImageGif: Invalid filename");
+ RETURN_FALSE;
+ }
+ }
+
+ im = php3_list_find(imgind->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "ImageGif: unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ if (argc == 2) {
+ fp = fopen(fn, "wb");
+ if (!fp) {
+ php3_error(E_WARNING, "ImageGif: unable to open %s for writing", fn);
+ RETURN_FALSE;
+ }
+ gdImageGif (im,fp);
+ fflush(fp);
+ fclose(fp);
+ }
+ else {
+ int b;
+ FILE *tmp;
+ char buf[4096];
+
+ tmp = tmpfile();
+ if (tmp == NULL) {
+ php3_error(E_WARNING, "Unable to open temporary file");
+ RETURN_FALSE;
+ }
+
+ output = php3_header();
+
+ if (output) {
+ gdImageGif (im, tmp);
+ fseek(tmp, 0, SEEK_SET);
+#if APACHE && defined(CHARSET_EBCDIC)
+ /* This is a binary file already: avoid EBCDIC->ASCII conversion */
+ ap_bsetflag(php3_rqst->connection->client, B_EBCDIC2ASCII, 0);
+#endif
+ while ((b = fread(buf, 1, sizeof(buf), tmp)) > 0) {
+ php3_write(buf, b);
+ }
+ }
+
+ fclose(tmp);
+ /* the temporary file is automatically deleted */
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int imagesetpixel(int im, int x, int y, int col)
+Set a single pixel */
+void php3_imagesetpixel(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *imarg, *xarg, *yarg, *colarg;
+ gdImagePtr im;
+ int col, y, x;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 4 ||
+ getParameters(ht, 4, &imarg, &xarg, &yarg, &colarg) == FAILURE)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(imarg);
+ convert_to_long(xarg);
+ convert_to_long(yarg);
+ convert_to_long(colarg);
+
+ col = colarg->value.lval;
+ y = yarg->value.lval;
+ x = xarg->value.lval;
+
+ im = php3_list_find(imarg->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ gdImageSetPixel(im,x,y,col);
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* im, x1, y1, x2, y2, col */
+/* {{{ proto int imageline(int im, int x1, int y1, int x2, int y2, int col)
+Draw a line */
+void php3_imageline(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *IM, *COL, *X1, *Y1, *X2, *Y2;
+ gdImagePtr im;
+ int col, y2, x2, y1, x1;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 6 ||
+ getParameters(ht, 6, &IM, &X1, &Y1, &X2, &Y2, &COL) == FAILURE)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(IM);
+ convert_to_long(X1);
+ convert_to_long(Y1);
+ convert_to_long(X2);
+ convert_to_long(Y2);
+ convert_to_long(COL);
+
+ x1 = X1->value.lval;
+ y1 = Y1->value.lval;
+ x2 = X2->value.lval;
+ y2 = Y2->value.lval;
+ col = COL->value.lval;
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ gdImageLine(im,x1,y1,x2,y2,col);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int imagedashedline(int im, int x1, int y1, int x2, int y2, int col)
+Draw a dashed line */
+void php3_imagedashedline(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *IM, *COL, *X1, *Y1, *X2, *Y2;
+ gdImagePtr im;
+ int col, y2, x2, y1, x1;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 6 || getParameters(ht, 6, &IM, &X1, &Y1, &X2, &Y2, &COL) == FAILURE)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(IM);
+ convert_to_long(X1);
+ convert_to_long(Y1);
+ convert_to_long(X2);
+ convert_to_long(Y2);
+ convert_to_long(COL);
+
+ x1 = X1->value.lval;
+ y1 = Y1->value.lval;
+ x2 = X2->value.lval;
+ y2 = Y2->value.lval;
+ col = COL->value.lval;
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ gdImageDashedLine(im,x1,y1,x2,y2,col);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* im, x1, y1, x2, y2, col */
+/* {{{ proto int imagerectangle(int im, int x1, int y1, int x2, int y2, int col)
+Draw a rectangle */
+void php3_imagerectangle(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *IM, *COL, *X1, *Y1, *X2, *Y2;
+ gdImagePtr im;
+ int col, y2, x2, y1, x1;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 6 ||
+ getParameters(ht, 6, &IM, &X1, &Y1, &X2, &Y2, &COL) == FAILURE)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(IM);
+ convert_to_long(X1);
+ convert_to_long(Y1);
+ convert_to_long(X2);
+ convert_to_long(Y2);
+ convert_to_long(COL);
+
+ x1 = X1->value.lval;
+ y1 = Y1->value.lval;
+ x2 = X2->value.lval;
+ y2 = Y2->value.lval;
+ col = COL->value.lval;
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ gdImageRectangle(im,x1,y1,x2,y2,col);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* im, x1, y1, x2, y2, col */
+/* {{{ proto int imagefilledrectangle(int im, int x1, int y1, int x2, int y2, int col)
+Draw a filled rectangle */
+void php3_imagefilledrectangle(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *IM, *COL, *X1, *Y1, *X2, *Y2;
+ gdImagePtr im;
+ int col, y2, x2, y1, x1;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 6 ||
+ getParameters(ht, 6, &IM, &X1, &Y1, &X2, &Y2, &COL) == FAILURE)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(IM);
+ convert_to_long(X1);
+ convert_to_long(Y1);
+ convert_to_long(X2);
+ convert_to_long(Y2);
+ convert_to_long(COL);
+
+ x1 = X1->value.lval;
+ y1 = Y1->value.lval;
+ x2 = X2->value.lval;
+ y2 = Y2->value.lval;
+ col = COL->value.lval;
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ gdImageFilledRectangle(im,x1,y1,x2,y2,col);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int imagearc(int im, int cx, int cy, int w, int h, int s, int e, int col)
+Draw a partial ellipse */
+void php3_imagearc(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *COL, *E, *ST, *H, *W, *CY, *CX, *IM;
+ gdImagePtr im;
+ int col, e, st, h, w, cy, cx;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 8 ||
+ getParameters(ht, 8, &IM, &CX, &CY, &W, &H, &ST, &E, &COL) == FAILURE)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(IM);
+ convert_to_long(CX);
+ convert_to_long(CY);
+ convert_to_long(W);
+ convert_to_long(H);
+ convert_to_long(ST);
+ convert_to_long(E);
+ convert_to_long(COL);
+
+ col = COL->value.lval;
+ e = E->value.lval;
+ st = ST->value.lval;
+ h = H->value.lval;
+ w = W->value.lval;
+ cy = CY->value.lval;
+ cx = CX->value.lval;
+
+ if (e < 0) {
+ e %= 360;
+ }
+ if (st < 0) {
+ st %= 360;
+ }
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ gdImageArc(im,cx,cy,w,h,st,e,col);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* im, x, y, border, col */
+/* {{{ proto int imagefilltoborder(int im, int x, int y, int border, int col)
+Flood fill to specific color */
+void php3_imagefilltoborder(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *IM, *X, *Y, *BORDER, *COL;
+ gdImagePtr im;
+ int col, border, y, x;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 5 ||
+ getParameters(ht, 5, &IM, &X, &Y, &BORDER, &COL) == FAILURE)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(IM);
+ convert_to_long(X);
+ convert_to_long(Y);
+ convert_to_long(BORDER);
+ convert_to_long(COL);
+
+ col = COL->value.lval;
+ border = BORDER->value.lval;
+ y = Y->value.lval;
+ x = X->value.lval;
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ gdImageFillToBorder(im,x,y,border,col);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* im, x, y, col */
+/* {{{ proto int imagefill(int im, int x, int y, int col)
+Flood fill */
+void php3_imagefill(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *IM, *X, *Y, *COL;
+ gdImagePtr im;
+ int col, y, x;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 4 ||
+ getParameters(ht, 4, &IM, &X, &Y, &COL) == FAILURE)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(IM);
+ convert_to_long(X);
+ convert_to_long(Y);
+ convert_to_long(COL);
+
+ col = COL->value.lval;
+ y = Y->value.lval;
+ x = X->value.lval;
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ gdImageFill(im,x,y,col);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int imagecolorstotal(int im)
+Find out the number of colors in an image's palette */
+void php3_imagecolorstotal(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *IM;
+ gdImagePtr im;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &IM) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+ convert_to_long(IM);
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ RETURN_LONG(gdImageColorsTotal(im));
+}
+/* }}} */
+
+/* im, col */
+/* {{{ proto int imagecolortransparent(int im [, int col])
+Define a color as transparent */
+void php3_imagecolortransparent(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *IM, *COL = NULL;
+ gdImagePtr im;
+ int col;
+ int ind_type;
+ GD_TLS_VARS;
+
+ switch(ARG_COUNT(ht)) {
+ case 1:
+ if (getParameters(ht, 1, &IM) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+ break;
+ case 2:
+ if (getParameters(ht, 2, &IM, &COL) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+ convert_to_long(COL);
+ break;
+ default:
+ WRONG_PARAM_COUNT;
+ }
+ convert_to_long(IM);
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ if (COL != NULL) {
+ col = COL->value.lval;
+ gdImageColorTransparent(im,col);
+ }
+ col = gdImageGetTransparent(im);
+ RETURN_LONG(col);
+}
+/* }}} */
+
+/* im, interlace */
+/* {{{ proto int imageinterlace(int im [, int interlace])
+Enable or disable interlace */
+void php3_imageinterlace(INTERNAL_FUNCTION_PARAMETERS) {
+ pval *IM, *INT = NULL;
+ gdImagePtr im;
+ int interlace;
+ int ind_type;
+ GD_TLS_VARS;
+
+ switch(ARG_COUNT(ht)) {
+ case 1:
+ if (getParameters(ht, 1, &IM) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+ break;
+ case 2:
+ if (getParameters(ht, 2, &IM, &INT) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+ convert_to_long(INT);
+ break;
+ default:
+ WRONG_PARAM_COUNT;
+ }
+ convert_to_long(IM);
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ if (INT != NULL) {
+ interlace = INT->value.lval;
+ gdImageInterlace(im,interlace);
+ }
+ interlace = gdImageGetInterlaced(im);
+ RETURN_LONG(interlace);
+}
+/* }}} */
+
+/* arg = 0 normal polygon
+ arg = 1 filled polygon */
+/* im, points, num_points, col */
+static void _php3_imagepolygon(INTERNAL_FUNCTION_PARAMETERS, int filled) {
+ pval *IM, *POINTS, *NPOINTS, *COL, *var;
+ gdImagePtr im;
+ gdPoint points[PolyMaxPoints];
+ int npoints, col, nelem, i;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 4 ||
+ getParameters(ht, 4, &IM, &POINTS, &NPOINTS, &COL) == FAILURE)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(IM);
+ convert_to_long(NPOINTS);
+ convert_to_long(COL);
+
+ npoints = NPOINTS->value.lval;
+ col = COL->value.lval;
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ if (POINTS->type != IS_ARRAY) {
+ php3_error(E_WARNING, "2nd argument to imagepolygon not an array");
+ RETURN_FALSE;
+ }
+
+/*
+ ** we shouldn't need this check, should we? **
+
+ if (!ParameterPassedByReference(ht, 2)) {
+ php3_error(E_WARNING, "2nd argument to imagepolygon not passed by reference");
+ RETURN_FALSE;
+ }
+*/
+
+ nelem = _php3_hash_num_elements(POINTS->value.ht);
+ if (nelem < 6) {
+ php3_error(E_WARNING,
+ "you must have at least 3 points in your array");
+ RETURN_FALSE;
+ }
+
+ if (nelem < npoints * 2) {
+ php3_error(E_WARNING,
+ "trying to use %d points in array with only %d points",
+ npoints, nelem/2);
+ RETURN_FALSE;
+ }
+
+ if (npoints > PolyMaxPoints) {
+ php3_error(E_WARNING, "maximum %d points", PolyMaxPoints);
+ RETURN_FALSE;
+ }
+
+ for (i = 0; i < npoints; i++) {
+ if (_php3_hash_index_find(POINTS->value.ht, (i * 2), (void **)&var) == SUCCESS) {
+ convert_to_long(var);
+ points[i].x = var->value.lval;
+ }
+ if (_php3_hash_index_find(POINTS->value.ht, (i * 2) + 1, (void **)&var) == SUCCESS) {
+ convert_to_long(var);
+ points[i].y = var->value.lval;
+ }
+ }
+
+ if (filled) {
+ gdImageFilledPolygon(im, points, npoints, col);
+ }
+ else {
+ gdImagePolygon(im, points, npoints, col);
+ }
+
+ RETURN_TRUE;
+}
+
+
+/* {{{ proto int imagepolygon(int im, array point, int num_points, int col)
+Draw a polygon */
+void php3_imagepolygon(INTERNAL_FUNCTION_PARAMETERS)
+{
+ _php3_imagepolygon(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+}
+/* }}} */
+
+/* {{{ proto int imagefilledpolygon(int im, array point, int num_points, int col)
+Draw a filled polygon */
+void php3_imagefilledpolygon(INTERNAL_FUNCTION_PARAMETERS)
+{
+ _php3_imagepolygon(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
+}
+/* }}} */
+
+
+static gdFontPtr _php3_find_gd_font(int size)
+{
+ gdFontPtr font;
+ int ind_type;
+ GD_TLS_VARS;
+
+ switch (size) {
+ case 1:
+ font = gdFontTiny;
+ break;
+ case 2:
+ font = gdFontSmall;
+ break;
+ case 3:
+ font = gdFontMediumBold;
+ break;
+ case 4:
+ font = gdFontLarge;
+ break;
+ case 5:
+ font = gdFontGiant;
+ break;
+ default:
+ font = php3_list_find(size - 5, &ind_type);
+ if (!font || ind_type != GD_GLOBAL(le_gd_font)) {
+ if (size < 1) {
+ font = gdFontTiny;
+ } else {
+ font = gdFontGiant;
+ }
+ }
+ break;
+ }
+
+ return font;
+}
+
+
+/*
+ * arg = 0 ImageFontWidth
+ * arg = 1 ImageFontHeight
+ */
+static void _php3_imagefontsize(INTERNAL_FUNCTION_PARAMETERS, int arg)
+{
+ pval *SIZE;
+ gdFontPtr font;
+
+ if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &SIZE) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+ convert_to_long(SIZE);
+ font = _php3_find_gd_font(SIZE->value.lval);
+ RETURN_LONG(arg ? font->h : font->w);
+}
+
+
+/* {{{ proto int imagefontwidth(int font)
+Get font width */
+void php3_imagefontwidth(INTERNAL_FUNCTION_PARAMETERS)
+{
+ _php3_imagefontsize(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+}
+/* }}} */
+
+/* {{{ proto int imagefontheight(int font)
+Get font height */
+void php3_imagefontheight(INTERNAL_FUNCTION_PARAMETERS)
+{
+ _php3_imagefontsize(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
+}
+/* }}} */
+
+
+/* workaround for a bug in gd 1.2 */
+void _php3_gdimagecharup(gdImagePtr im, gdFontPtr f, int x, int y, int c,
+ int color)
+{
+ int cx, cy, px, py, fline;
+ cx = 0;
+ cy = 0;
+ if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
+ return;
+ }
+ fline = (c - f->offset) * f->h * f->w;
+ for (py = y; (py > (y - f->w)); py--) {
+ for (px = x; (px < (x + f->h)); px++) {
+ if (f->data[fline + cy * f->w + cx]) {
+ gdImageSetPixel(im, px, py, color);
+ }
+ cy++;
+ }
+ cy = 0;
+ cx++;
+ }
+}
+
+/*
+ * arg = 0 ImageChar
+ * arg = 1 ImageCharUp
+ * arg = 2 ImageString
+ * arg = 3 ImageStringUp
+ */
+static void _php3_imagechar(INTERNAL_FUNCTION_PARAMETERS, int mode) {
+ pval *IM, *SIZE, *X, *Y, *C, *COL;
+ gdImagePtr im;
+ int ch = 0, col, x, y, size, i, l = 0;
+ unsigned char *string = NULL;
+ int ind_type;
+ gdFontPtr font;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 6 ||
+ getParameters(ht, 6, &IM, &SIZE, &X, &Y, &C, &COL) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(IM);
+ convert_to_long(SIZE);
+ convert_to_long(X);
+ convert_to_long(Y);
+ convert_to_string(C);
+ convert_to_long(COL);
+
+ col = COL->value.lval;
+
+ if (mode < 2) {
+ ch = (int)((unsigned char)*(C->value.str.val));
+ } else {
+ string = (unsigned char *) estrndup(C->value.str.val,C->value.str.len);
+ l = strlen(string);
+ }
+
+ y = Y->value.lval;
+ x = X->value.lval;
+ size = SIZE->value.lval;
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ if (string) {
+ efree(string);
+ }
+ RETURN_FALSE;
+ }
+
+ font = _php3_find_gd_font(size);
+
+ switch(mode) {
+ case 0:
+ gdImageChar(im, font, x, y, ch, col);
+ break;
+ case 1:
+ _php3_gdimagecharup(im, font, x, y, ch, col);
+ break;
+ case 2:
+ for (i = 0; (i < l); i++) {
+ gdImageChar(im, font, x, y, (int)((unsigned char)string[i]),
+ col);
+ x += font->w;
+ }
+ break;
+ case 3: {
+ for (i = 0; (i < l); i++) {
+ /* _php3_gdimagecharup(im, font, x, y, (int)string[i], col); */
+ gdImageCharUp(im, font, x, y, (int)string[i], col);
+ y -= font->w;
+ }
+ break;
+ }
+ }
+ if (string) {
+ efree(string);
+ }
+ RETURN_TRUE;
+}
+
+/* {{{ proto int imagechar(int im, int font, int x, int y, string c, int col)
+Draw a character */
+void php3_imagechar(INTERNAL_FUNCTION_PARAMETERS) {
+ _php3_imagechar(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+}
+/* }}} */
+
+/* {{{ proto int imagecharup(int im, int font, int x, int y, string c, int col)
+Draw a character rotated 90 degrees counter-clockwise */
+void php3_imagecharup(INTERNAL_FUNCTION_PARAMETERS) {
+ _php3_imagechar(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
+}
+/* }}} */
+
+/* {{{ proto int imagestring(int im, int font, int x, int y, string str, int col)
+Draw a string horizontally */
+void php3_imagestring(INTERNAL_FUNCTION_PARAMETERS) {
+ _php3_imagechar(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
+}
+/* }}} */
+
+/* {{{ proto int imagestringup(int im, int font, int x, int y, string str, int col)
+Draw a string vertically - rotated 90 degrees counter-clockwise */
+void php3_imagestringup(INTERNAL_FUNCTION_PARAMETERS) {
+ _php3_imagechar(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
+}
+/* }}} */
+
+/* {{{ proto int imagecopy(int dst_im, int src_im, int dstX, int dstY, int srcX, int srcY, int srcW, int srcH)
+Copy part of an image */
+void php3_imagecopy(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pval *SIM, *DIM, *SX, *SY, *SW, *SH, *DX, *DY;
+ gdImagePtr im_dst;
+ gdImagePtr im_src;
+ int srcH, srcW, srcY, srcX, dstY, dstX;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 8 ||
+ getParameters(ht, 8, &DIM, &SIM, &DX, &DY, &SX, &SY, &SW, &SH)
+ == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(SIM);
+ convert_to_long(DIM);
+ convert_to_long(SX);
+ convert_to_long(SY);
+ convert_to_long(SW);
+ convert_to_long(SH);
+ convert_to_long(DX);
+ convert_to_long(DY);
+
+ srcX = SX->value.lval;
+ srcY = SY->value.lval;
+ srcH = SH->value.lval;
+ srcW = SW->value.lval;
+ dstX = DX->value.lval;
+ dstY = DY->value.lval;
+
+ im_src = php3_list_find(SIM->value.lval, &ind_type);
+ if (!im_src || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ im_dst = php3_list_find(DIM->value.lval, &ind_type);
+ if (!im_dst || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ gdImageCopy(im_dst, im_src, dstX, dstY, srcX, srcY, srcW, srcH);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int imagecopyresized(int dst_im, int src_im, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH);
+Copy and resize part of an image */
+void php3_imagecopyresized(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pval *SIM, *DIM, *SX, *SY, *SW, *SH, *DX, *DY, *DW, *DH;
+ gdImagePtr im_dst;
+ gdImagePtr im_src;
+ int srcH, srcW, dstH, dstW, srcY, srcX, dstY, dstX;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 10 ||
+ getParameters(ht, 10, &DIM, &SIM, &DX, &DY, &SX, &SY, &DW, &DH,
+ &SW, &SH) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long(SIM);
+ convert_to_long(DIM);
+ convert_to_long(SX);
+ convert_to_long(SY);
+ convert_to_long(SW);
+ convert_to_long(SH);
+ convert_to_long(DX);
+ convert_to_long(DY);
+ convert_to_long(DW);
+ convert_to_long(DH);
+
+ srcX = SX->value.lval;
+ srcY = SY->value.lval;
+ srcH = SH->value.lval;
+ srcW = SW->value.lval;
+ dstX = DX->value.lval;
+ dstY = DY->value.lval;
+ dstH = DH->value.lval;
+ dstW = DW->value.lval;
+
+ im_src = php3_list_find(SIM->value.lval, &ind_type);
+ if (!im_src || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ im_dst = php3_list_find(DIM->value.lval, &ind_type);
+ if (!im_dst || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ gdImageCopyResized(im_dst, im_src, dstX, dstY, srcX, srcY, dstW, dstH,
+ srcW, srcH);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int imagesx(int im)
+Get image width */
+void php3_imagesxfn(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pval *IM;
+ gdImagePtr im;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &IM) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ RETURN_LONG(gdImageSX(im));
+}
+/* }}} */
+
+/* {{{ proto int imagesy(int im)
+Get image height */
+void php3_imagesyfn(INTERNAL_FUNCTION_PARAMETERS)
+{
+ pval *IM;
+ gdImagePtr im;
+ int ind_type;
+ GD_TLS_VARS;
+
+ if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &IM) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+
+ RETURN_LONG(gdImageSY(im));
+}
+/* }}} */
+
+#if HAVE_LIBTTF
+
+#define TTFTEXT_DRAW 0
+#define TTFTEXT_BBOX 1
+
+/* {{{ proto array imagettfbbox(int size, int angle, string font_file, string text)
+Give the bounding box of a text using TrueType fonts */
+void php3_imagettfbbox(INTERNAL_FUNCTION_PARAMETERS)
+{
+ php3_imagettftext_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, TTFTEXT_BBOX);
+}
+/* }}} */
+
+/* {{{ proto array imagettftext(int im, int size, int angle, int x, int y, int col, string font_file, string text)
+Write text to the image using a TrueType font */
+void php3_imagettftext(INTERNAL_FUNCTION_PARAMETERS)
+{
+ php3_imagettftext_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, TTFTEXT_DRAW);
+}
+/* }}} */
+
+static
+void php3_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int mode)
+{
+ pval *IM, *PTSIZE, *ANGLE, *X, *Y, *C, *FONTNAME, *COL;
+ gdImagePtr im;
+ int col, x, y, l=0, i;
+ int brect[8];
+ double ptsize, angle;
+ unsigned char *string = NULL, *fontname = NULL;
+ int ind_type;
+ char *error;
+
+ GD_TLS_VARS;
+
+ if (mode == TTFTEXT_BBOX) {
+ if (ARG_COUNT(ht) != 4 || getParameters(ht, 4, &PTSIZE, &ANGLE, &FONTNAME, &C) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+ } else {
+ if (ARG_COUNT(ht) != 8 || getParameters(ht, 8, &IM, &PTSIZE, &ANGLE, &X, &Y, &COL, &FONTNAME, &C) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+ }
+
+ convert_to_double(PTSIZE);
+ convert_to_double(ANGLE);
+ convert_to_string(FONTNAME);
+ convert_to_string(C);
+ if (mode == TTFTEXT_BBOX) {
+ im = NULL;
+ col = x = y = -1;
+ } else {
+ convert_to_long(X);
+ convert_to_long(Y);
+ convert_to_long(IM);
+ convert_to_long(COL);
+ col = COL->value.lval;
+ y = Y->value.lval;
+ x = X->value.lval;
+ im = php3_list_find(IM->value.lval, &ind_type);
+ if (!im || ind_type != GD_GLOBAL(le_gd)) {
+ php3_error(E_WARNING, "Unable to find image pointer");
+ RETURN_FALSE;
+ }
+ }
+
+ ptsize = PTSIZE->value.dval;
+ angle = ANGLE->value.dval * (M_PI/180); /* convert to radians */
+
+ string = (unsigned char *) C->value.str.val;
+ l = strlen(string);
+ fontname = (unsigned char *) FONTNAME->value.str.val;
+
+ error = gdttf(im, brect, col, fontname, ptsize, angle, x, y, string);
+
+ if (error) {
+ php3_error(E_WARNING, error);
+ RETURN_FALSE;
+ }
+
+
+ if (array_init(return_value) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ /* return array with the text's bounding box */
+ for (i = 0; i < 8; i++) {
+ add_next_index_long(return_value, brect[i]);
+ }
+}
+#endif
+#endif
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
--- /dev/null
+/* gd interface to freetype library */
+/* */
+/* John Ellson ellson@lucent.com */
+
+/* $Id$ */
+
+#if WIN32|WINNT
+#include "config.w32.h"
+#else
+#include "config.h"
+#endif
+#if HAVE_LIBTTF
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <gd.h>
+#include "gdttf.h"
+#include "gdcache.h"
+#include <freetype.h>
+
+/* number of fonts cached before least recently used is replaced */
+#define FONTCACHESIZE 6
+
+/* number of character glyphs cached per font before
+ least-recently-used is replaced */
+#define GLYPHCACHESIZE 120
+
+/* number of bitmaps cached per glyph before
+ least-recently-used is replaced */
+#define BITMAPCACHESIZE 8
+
+/* number of antialias color lookups cached */
+#define TWEENCOLORCACHESIZE 32
+
+/* ptsize below which anti-aliasing is ineffective */
+#define MINANTIALIASPTSIZE 0
+
+/* display resolution - (Not really. This has to be 72 or hinting is wrong) */
+#define RESOLUTION 72
+
+/* Number of colors used for anti-aliasing */
+#define NUMCOLORS 4
+
+/* Line separation as a factor of font height.
+ No space between if LINESPACE = 1.00
+ Line separation will be rounded up to next pixel row*/
+#define LINESPACE 1.05
+
+#ifndef TRUE
+#define FALSE 0
+#define TRUE !FALSE
+#endif
+
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+typedef struct {
+ char *fontname; /* key */
+ double ptsize; /* key */
+ double angle; /* key */
+ double sin_a, cos_a;
+ TT_Engine *engine;
+ TT_Face face;
+ TT_Face_Properties properties;
+ TT_Instance instance;
+ TT_CharMap char_map;
+ TT_Matrix matrix;
+ TT_Instance_Metrics imetrics;
+ gdCache_head_t *glyphCache;
+} font_t;
+
+typedef struct {
+ char *fontname; /* key */
+ double ptsize; /* key */
+ double angle; /* key */
+ TT_Engine *engine;
+} fontkey_t;
+
+typedef struct {
+ int character; /* key */
+ int hinting; /* key */
+ TT_Glyph glyph;
+ TT_Glyph_Metrics metrics;
+ TT_Outline outline;
+ TT_Pos oldx, oldy;
+ TT_Raster_Map Bit;
+ int gray_render;
+ int xmin, xmax, ymin, ymax;
+ gdCache_head_t *bitmapCache;
+} glyph_t;
+
+typedef struct {
+ int character; /* key */
+ int hinting; /* key */
+ int gray_render;
+ font_t *font;
+} glyphkey_t;
+
+typedef struct {
+ int xoffset; /* key */
+ int yoffset; /* key */
+ char *bitmap;
+} bitmap_t;
+
+typedef struct {
+ int xoffset; /* key */
+ int yoffset; /* key */
+ glyph_t *glyph;
+} bitmapkey_t;
+
+typedef struct {
+ unsigned char pixel; /* key */
+ unsigned char bgcolor; /* key */
+ int fgcolor; /* key */ /* -ve means no antialias */
+ gdImagePtr im; /* key */
+ unsigned char tweencolor;
+} tweencolor_t;
+
+typedef struct {
+ unsigned char pixel; /* key */
+ unsigned char bgcolor; /* key */
+ int fgcolor; /* key */ /* -ve means no antialias */
+ gdImagePtr im; /* key */
+} tweencolorkey_t;
+
+/* forward declarations so that glyphCache can be initialized by font code */
+static int glyphTest ( void *element, void *key );
+static void *glyphFetch ( char **error, void *key );
+static void glyphRelease( void *element );
+
+/* forward declarations so that bitmapCache can be initialized by glyph code */
+static int bitmapTest ( void *element, void *key );
+static void *bitmapFetch ( char **error, void *key );
+static void bitmapRelease( void *element );
+
+/* local prototype */
+char *gdttfchar(gdImage *im, int fg, font_t *font, int x, int y, TT_F26Dot6 x1, TT_F26Dot6 y1, TT_F26Dot6 *advance, TT_BBox **bbox, char **next);
+
+/* This prototype is missing from gd.h */
+/* FIXME Since when does GD have this function??? My copy of 1.3 doesnt
+int gdImageColorResolve(gdImagePtr im, int r, int g, int b);
+*/
+
+/********************************************************************/
+/* gdImageColorResolve is a replacement for the old fragment: */
+/* */
+/* if ((color=gdImageColorExact(im,R,G,B)) < 0) */
+/* if ((color=gdImageColorAllocate(im,R,G,B)) < 0) */
+/* color=gdImageColorClosest(im,R,G,B); */
+/* */
+/* in a single function */
+
+static int
+gdImageColorResolve(gdImagePtr im, int r, int g, int b)
+{
+ int c;
+ int ct = -1;
+ int op = -1;
+ long rd, gd, bd, dist;
+ long mindist = 3*255*255; /* init to max poss dist */
+
+ for (c = 0; c < im->colorsTotal; c++) {
+ if (im->open[c]) {
+ op = c; /* Save open slot */
+ continue; /* Color not in use */
+ }
+ rd = (long)(im->red [c] - r);
+ gd = (long)(im->green[c] - g);
+ bd = (long)(im->blue [c] - b);
+ dist = rd * rd + gd * gd + bd * bd;
+ if (dist < mindist) {
+ if (dist == 0) {
+ return c; /* Return exact match color */
+ }
+ mindist = dist;
+ ct = c;
+ }
+ }
+ /* no exact match. We now know closest, but first try to allocate exact */
+ if (op == -1) {
+ op = im->colorsTotal;
+ if (op == gdMaxColors) { /* No room for more colors */
+ return ct; /* Return closest available color */
+ }
+ im->colorsTotal++;
+ }
+ im->red [op] = r;
+ im->green[op] = g;
+ im->blue [op] = b;
+ im->open [op] = 0;
+ return op; /* Return newly allocated color */
+}
+
+/********************************************************************
+ * gdTcl_UtfToUniChar is borrowed from ...
+ */
+/*
+ * tclUtf.c --
+ *
+ * Routines for manipulating UTF-8 strings.
+ *
+ * Copyright (c) 1997-1998 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tclUtf.c 1.25 98/01/28 18:02:43
+ */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * gdTcl_UtfToUniChar --
+ *
+ * Extract the Tcl_UniChar represented by the UTF-8 string. Bad
+ * UTF-8 sequences are converted to valid Tcl_UniChars and processing
+ * continues. Equivalent to Plan 9 chartorune().
+ *
+ * The caller must ensure that the source buffer is long enough that
+ * this routine does not run off the end and dereference non-existent
+ * memory looking for trail bytes. If the source buffer is known to
+ * be '\0' terminated, this cannot happen. Otherwise, the caller
+ * should call Tcl_UtfCharComplete() before calling this routine to
+ * ensure that enough bytes remain in the string.
+ *
+ * Results:
+ * *chPtr is filled with the Tcl_UniChar, and the return value is the
+ * number of bytes from the UTF-8 string that were consumed.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifndef CHARSET_EBCDIC
+#define ASC(ch) (ch)
+#else /*CHARSET_EBCDIC*/
+#define ASC(ch) os_toascii[(unsigned char) (ch)]
+#endif /*CHARSET_EBCDIC*/
+
+#define Tcl_UniChar int
+#define TCL_UTF_MAX 3
+static int
+gdTcl_UtfToUniChar(char *str, Tcl_UniChar *chPtr)
+/* str is the UTF8 next character pointer */
+/* chPtr is the int for the result */
+{
+ int byte;
+
+ /* HTML4.0 entities in decimal form, e.g. Å */
+ byte = *((unsigned char *) str);
+ if (byte == '&') {
+ int i, n=0;
+
+ byte = *((unsigned char *) (str+1));
+ if (byte == '#') {
+ for (i = 2; i < 8; i++) {
+ byte = *((unsigned char *) (str+i));
+ if (byte >= '0' && byte <= '9') {
+ n = (n * 10) + (byte - '0');
+ }
+ else
+ break;
+ }
+ if (byte == ';') {
+ *chPtr = (Tcl_UniChar) n;
+ return ++i;
+ }
+ }
+ }
+
+ /*
+ * Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones.
+ */
+
+ byte = ASC(*((unsigned char *) str));
+ if (byte < 0xC0) {
+ /*
+ * Handles properly formed UTF-8 characters between 0x01 and 0x7F.
+ * Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
+ * characters representing themselves.
+ */
+
+ *chPtr = (Tcl_UniChar) byte;
+ return 1;
+ } else if (byte < 0xE0) {
+ if ((ASC(str[1]) & 0xC0) == 0x80) {
+ /*
+ * Two-byte-character lead-byte followed by a trail-byte.
+ */
+
+ *chPtr = (Tcl_UniChar) (((byte & 0x1F) << 6) | (ASC(str[1]) & 0x3F));
+ return 2;
+ }
+ /*
+ * A two-byte-character lead-byte not followed by trail-byte
+ * represents itself.
+ */
+
+ *chPtr = (Tcl_UniChar) byte;
+ return 1;
+ } else if (byte < 0xF0) {
+ if (((ASC(str[1]) & 0xC0) == 0x80) && ((ASC(str[2]) & 0xC0) == 0x80)) {
+ /*
+ * Three-byte-character lead byte followed by two trail bytes.
+ */
+
+ *chPtr = (Tcl_UniChar) (((byte & 0x0F) << 12)
+ | ((ASC(str[1]) & 0x3F) << 6) | (ASC(str[2]) & 0x3F));
+ return 3;
+ }
+ /*
+ * A three-byte-character lead-byte not followed by two trail-bytes
+ * represents itself.
+ */
+
+ *chPtr = (Tcl_UniChar) byte;
+ return 1;
+ }
+#if TCL_UTF_MAX > 3
+ else {
+ int ch, total, trail;
+
+ total = totalBytes[byte];
+ trail = total - 1;
+ if (trail > 0) {
+ ch = byte & (0x3F >> trail);
+ do {
+ str++;
+ if ((ASC(*str) & 0xC0) != 0x80) {
+ *chPtr = byte;
+ return 1;
+ }
+ ch <<= 6;
+ ch |= (ASC(*str) & 0x3F);
+ trail--;
+ } while (trail > 0);
+ *chPtr = ch;
+ return total;
+ }
+ }
+#endif
+
+ *chPtr = (Tcl_UniChar) byte;
+ return 1;
+}
+
+/********************************************************************/
+/* font cache functions */
+
+static int
+fontTest ( void *element, void *key )
+{
+ font_t *a=(font_t *)element;
+ fontkey_t *b=(fontkey_t *)key;
+
+ return ( strcmp(a->fontname, b->fontname) == 0
+ && a->ptsize == b->ptsize
+ && a->angle == b->angle);
+}
+
+static void *
+fontFetch ( char **error, void *key )
+{
+ TT_Error err;
+ font_t *a;
+ fontkey_t *b=(fontkey_t *)key;
+ int i, n;
+ short platform, encoding;
+
+ a = (font_t *)malloc(sizeof(font_t));
+ a->fontname = (char *)malloc(strlen(b->fontname) + 1);
+ strcpy(a->fontname,b->fontname);
+ a->ptsize = b->ptsize;
+ a->angle = b->angle;
+ a->sin_a = sin(a->angle);
+ a->cos_a = cos(a->angle);
+ a->engine = b->engine;
+ if ((err = TT_Open_Face(*b->engine, a->fontname, &a->face))) {
+ if (err == TT_Err_Could_Not_Open_File) {
+ *error = "Could not find/open font";
+ }
+ else {
+ *error = "Could not read font";
+ }
+ return NULL;
+ }
+ /* get face properties and allocate preload arrays */
+ TT_Get_Face_Properties(a->face, &a->properties);
+
+ /* create instance */
+ if (TT_New_Instance(a->face, &a->instance)) {
+ *error = "Could not create face instance";
+ return NULL;
+ }
+
+ if (TT_Set_Instance_Resolutions(a->instance, RESOLUTION, RESOLUTION)) {
+ *error = "Could not set device resolutions";
+ return NULL;
+ }
+
+ if (TT_Set_Instance_CharSize(a->instance, (TT_F26Dot6)(a->ptsize*64))) {
+ *error = "Could not set character size";
+ return NULL;
+ }
+
+ TT_Get_Instance_Metrics(a->instance, &a->imetrics);
+
+ /* First, look for a Unicode charmap */
+ n = TT_Get_CharMap_Count(a->face);
+
+ for (i = 0; i < n; i++) {
+ TT_Get_CharMap_ID(a->face, i, &platform, &encoding);
+ if ((platform == 3 && encoding == 1) ||
+ (platform == 2 && encoding == 1) ||
+ (platform == 0)) {
+ TT_Get_CharMap(a->face, i, &a->char_map);
+ i = n+1;
+ }
+ }
+
+ if (i == n) {
+ *error = "Sorry, but this font doesn't contain any Unicode mapping table";
+ return NULL;
+ }
+
+ a->matrix.xx = (TT_Fixed) (a->cos_a * (1<<16));
+ a->matrix.yx = (TT_Fixed) (a->sin_a * (1<<16));
+ a->matrix.xy = - a->matrix.yx;
+ a->matrix.yy = a->matrix.xx;
+
+ a->glyphCache = gdCacheCreate( GLYPHCACHESIZE,
+ glyphTest, glyphFetch, glyphRelease);
+
+ return (void *)a;
+}
+
+static void
+fontRelease( void *element )
+{
+ font_t *a=(font_t *)element;
+
+ gdCacheDelete(a->glyphCache);
+ TT_Done_Instance(a->instance);
+ TT_Close_Face(a->face);
+ free(a->fontname);
+ free( (char *)element );
+}
+
+/********************************************************************/
+/* glyph cache functions */
+
+static int
+glyphTest ( void *element, void *key )
+{
+ glyph_t *a=(glyph_t *)element;
+ glyphkey_t *b=(glyphkey_t *)key;
+
+ return (a->character == b->character
+ && a->hinting == b->hinting
+ && a->gray_render == b->gray_render);
+}
+
+static void *
+glyphFetch ( char **error, void *key )
+{
+ glyph_t *a;
+ glyphkey_t *b=(glyphkey_t *)key;
+ short glyph_code;
+ int flags, err;
+ int crect[8], xmin, xmax, ymin, ymax;
+ double cos_a, sin_a;
+
+ a = (glyph_t *)malloc(sizeof(glyph_t));
+ a->character = b->character;
+ a->hinting = b->hinting;
+ a->gray_render = b->gray_render;
+ a->oldx = a->oldy = 0;
+
+ /* create glyph container */
+ if ((TT_New_Glyph(b->font->face, &a->glyph))) {
+ *error = "Could not create glyph container";
+ return NULL;
+ }
+
+ flags = TTLOAD_SCALE_GLYPH;
+ if (a->hinting && b->font->angle == 0.0) {
+ flags |= TTLOAD_HINT_GLYPH;
+ }
+ glyph_code = TT_Char_Index(b->font->char_map, a->character);
+ if ((err=TT_Load_Glyph(b->font->instance, a->glyph, glyph_code, flags))) {
+ *error = "TT_Load_Glyph problem";
+ return NULL;
+ }
+
+ TT_Get_Glyph_Metrics(a->glyph, &a->metrics);
+ if (b->font->angle != 0.0) {
+ TT_Get_Glyph_Outline(a->glyph, &a->outline);
+ TT_Transform_Outline(&a->outline, &b->font->matrix);
+ }
+
+ /* calculate bitmap size */
+ xmin = a->metrics.bbox.xMin -64;
+ ymin = a->metrics.bbox.yMin -64;
+ xmax = a->metrics.bbox.xMax +64;
+ ymax = a->metrics.bbox.yMax +64;
+
+ cos_a = b->font->cos_a;
+ sin_a = b->font->sin_a;
+ crect[0] = (int)(xmin * cos_a - ymin * sin_a);
+ crect[1] = (int)(xmin * sin_a + ymin * cos_a);
+ crect[2] = (int)(xmax * cos_a - ymin * sin_a);
+ crect[3] = (int)(xmax * sin_a + ymin * cos_a);
+ crect[4] = (int)(xmax * cos_a - ymax * sin_a);
+ crect[5] = (int)(xmax * sin_a + ymax * cos_a);
+ crect[6] = (int)(xmin * cos_a - ymax * sin_a);
+ crect[7] = (int)(xmin * sin_a + ymax * cos_a);
+ a->xmin = MIN(MIN(crect[0],crect[2]),MIN(crect[4],crect[6]));
+ a->xmax = MAX(MAX(crect[0],crect[2]),MAX(crect[4],crect[6]));
+ a->ymin = MIN(MIN(crect[1],crect[3]),MIN(crect[5],crect[7]));
+ a->ymax = MAX(MAX(crect[1],crect[3]),MAX(crect[5],crect[7]));
+
+ /* allocate bitmap large enough for character */
+ a->Bit.rows = (a->ymax - a->ymin + 32 + 64) / 64;
+ a->Bit.width = (a->xmax - a->xmin + 32 + 64) / 64;
+ a->Bit.flow = TT_Flow_Up;
+ if (a->gray_render) {
+ a->Bit.cols = a->Bit.width; /* 1 byte per pixel */
+ }
+ else {
+ a->Bit.cols = (a->Bit.width + 7) / 8; /* 1 bit per pixel */
+ }
+ a->Bit.cols = (a->Bit.cols + 3) & ~3; /* pad to 32 bits */
+ a->Bit.size = a->Bit.rows * a->Bit.cols; /* # of bytes in buffer */
+ a->Bit.bitmap = NULL;
+
+ a->bitmapCache = gdCacheCreate( BITMAPCACHESIZE,
+ bitmapTest, bitmapFetch, bitmapRelease);
+
+ return (void *)a;
+}
+
+static void
+glyphRelease( void *element )
+{
+ glyph_t *a=(glyph_t *)element;
+
+ gdCacheDelete(a->bitmapCache);
+ TT_Done_Glyph( a->glyph );
+ free( (char *)element );
+}
+
+/********************************************************************/
+/* bitmap cache functions */
+
+static int
+bitmapTest ( void *element, void *key )
+{
+ bitmap_t *a=(bitmap_t *)element;
+ bitmapkey_t *b=(bitmapkey_t *)key;
+
+ if (a->xoffset == b->xoffset && a->yoffset == b->yoffset) {
+ b->glyph->Bit.bitmap = a->bitmap;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void *
+bitmapFetch ( char **error, void *key )
+{
+ bitmap_t *a;
+ bitmapkey_t *b=(bitmapkey_t *)key;
+
+ a = (bitmap_t *)malloc(sizeof(bitmap_t));
+ a->xoffset = b->xoffset;
+ a->yoffset = b->yoffset;
+
+ b->glyph->Bit.bitmap = a->bitmap = (char *)malloc(b->glyph->Bit.size);
+ memset(a->bitmap, 0, b->glyph->Bit.size);
+ /* render glyph */
+ if (b->glyph->gray_render) {
+ TT_Get_Glyph_Pixmap(b->glyph->glyph, &b->glyph->Bit,
+ a->xoffset, a->yoffset);
+ }
+ else {
+ TT_Get_Glyph_Bitmap(b->glyph->glyph, &b->glyph->Bit,
+ a->xoffset, a->yoffset);
+ }
+ return (void *)a;
+}
+
+static void
+bitmapRelease( void *element )
+{
+ bitmap_t *a=(bitmap_t *)element;
+
+ free( a->bitmap );
+ free( (char *)element );
+}
+
+/********************************************************************/
+/* tweencolor cache functions */
+
+static int
+tweenColorTest (void *element, void *key)
+{
+ tweencolor_t *a=(tweencolor_t *)element;
+ tweencolorkey_t *b=(tweencolorkey_t *)key;
+
+ return (a->pixel == b->pixel
+ && a->bgcolor == b->bgcolor
+ && a->fgcolor == b->fgcolor
+ && a->im == b->im);
+}
+
+static void *
+tweenColorFetch (char **error, void *key)
+{
+ tweencolor_t *a;
+ tweencolorkey_t *b=(tweencolorkey_t *)key;
+ int pixel, npixel, bg, fg;
+ gdImagePtr im;
+
+ a = (tweencolor_t *)malloc(sizeof(tweencolor_t));
+ pixel = a->pixel = b->pixel;
+ bg = a->bgcolor = b->bgcolor;
+ fg = a->fgcolor = b->fgcolor;
+ im = b->im;
+
+ /* if fg is specified by a negative color idx, then don't antialias */
+ if (fg <0) {
+ a->tweencolor = -fg;
+ } else {
+ npixel = NUMCOLORS - pixel;
+ a->tweencolor = gdImageColorResolve(im,
+ (pixel * im->red [fg] + npixel * im->red [bg]) / NUMCOLORS,
+ (pixel * im->green[fg] + npixel * im->green[bg]) / NUMCOLORS,
+ (pixel * im->blue [fg] + npixel * im->blue [bg]) / NUMCOLORS);
+ }
+ *error = NULL;
+ return (void *)a;
+}
+
+static void
+tweenColorRelease(void *element)
+{
+ free((char *)element);
+}
+
+/********************************************************************/
+/* gdttfchar - render one character onto a gd image */
+
+static int OneTime=0;
+static gdCache_head_t *tweenColorCache;
+
+char *
+gdttfchar(gdImage *im, int fg, font_t *font,
+ int x, int y, /* string start pos in pixels */
+ TT_F26Dot6 x1, TT_F26Dot6 y1, /* char start offset (*64) from x,y */
+ TT_F26Dot6 *advance,
+ TT_BBox **bbox,
+ char **next)
+{
+ int pc, ch, len;
+ int row, col;
+ int x2, y2; /* char start pos in pixels */
+ int x3, y3; /* current pixel pos */
+ unsigned char *pixel;
+
+ glyph_t *glyph;
+ glyphkey_t glyphkey;
+ bitmapkey_t bitmapkey;
+ tweencolor_t *tweencolor;
+ tweencolorkey_t tweencolorkey;
+
+ /****** set up tweenColorCache on first call ************/
+ if (! OneTime) {
+ tweenColorCache = gdCacheCreate(TWEENCOLORCACHESIZE,
+ tweenColorTest, tweenColorFetch, tweenColorRelease);
+ OneTime++;
+ }
+ /**************/
+
+ len = gdTcl_UtfToUniChar(*next, &ch);
+ *next += len;
+
+ glyphkey.character = ch;
+ glyphkey.hinting = 1;
+ /* if fg is specified by a negative color idx, then don't antialias */
+ glyphkey.gray_render = ((font->ptsize < MINANTIALIASPTSIZE) || (fg <0))?FALSE:TRUE;
+ glyphkey.font = font;
+ glyph = (glyph_t *)gdCacheGet(font->glyphCache, &glyphkey);
+ if (! glyph)
+ return font->glyphCache->error;
+
+ *bbox = &glyph->metrics.bbox;
+ *advance = glyph->metrics.advance;
+
+ /* if null *im, or invalid color, then assume user just wants brect */
+ if (!im || fg > 255 || fg < -255)
+ return (char *)NULL;
+
+ /* render (via cache) a bitmap for the current fractional offset */
+ bitmapkey.xoffset = ((x1+32) & 63) - 32 - ((glyph->xmin+32) & -64);
+ bitmapkey.yoffset = ((y1+32) & 63) - 32 - ((glyph->ymin+32) & -64);
+ bitmapkey.glyph = glyph;
+ gdCacheGet(glyph->bitmapCache, &bitmapkey);
+
+ /* copy to gif, mapping colors */
+ x2 = x + (((glyph->xmin+32) & -64) + ((x1+32) & -64)) / 64;
+ y2 = y - (((glyph->ymin+32) & -64) + ((y1+32) & -64)) / 64;
+ tweencolorkey.fgcolor = fg;
+ tweencolorkey.im = im;
+ for (row = 0; row < glyph->Bit.rows; row++) {
+ if (glyph->gray_render)
+ pc = row * glyph->Bit.cols;
+ else
+ pc = row * glyph->Bit.cols * 8;
+ y3 = y2 - row;
+ if (y3 >= im->sy || y3 < 0) continue;
+ for (col = 0; col < glyph->Bit.width; col++, pc++) {
+ if (glyph->gray_render) {
+ tweencolorkey.pixel =
+ *((unsigned char *)(glyph->Bit.bitmap) + pc);
+ } else {
+ tweencolorkey.pixel =
+ (((*((unsigned char *)(glyph->Bit.bitmap) + pc/8))
+ <<(pc%8))&128)?4:0;
+ }
+ /* if not background */
+ if (tweencolorkey.pixel > 0) {
+ x3 = x2 + col;
+ if (x3 >= im->sx || x3 < 0) continue;
+#if HAVE_LIBGD13
+ pixel = &im->pixels[y3][x3];
+#else
+ pixel = &im->pixels[x3][y3];
+#endif
+ tweencolorkey.bgcolor = *pixel;
+ tweencolor = (tweencolor_t *)gdCacheGet(
+ tweenColorCache, &tweencolorkey);
+ *pixel = tweencolor->tweencolor;
+ }
+ }
+ }
+ return (char *)NULL;
+}
+
+/********************************************************************/
+/* gdttf - render a utf8 string onto a gd image */
+
+char *
+gdttf(gdImage *im, int *brect, int fg, char *fontname,
+ double ptsize, double angle, int x, int y, char *string)
+{
+ TT_F26Dot6 ur_x=0, ur_y=0, ll_x=0, ll_y=0;
+ TT_F26Dot6 advance_x, advance_y, advance, x1, y1;
+ TT_BBox *bbox;
+ double sin_a, cos_a;
+ int i=0, ch;
+ font_t *font;
+ fontkey_t fontkey;
+ char *error, *next;
+
+ /****** initialize font engine on first call ************/
+ static gdCache_head_t *fontCache;
+ static TT_Engine engine;
+
+ if (! fontCache) {
+ if (TT_Init_FreeType(&engine)) {
+ return "Failure to initialize font engine";
+ }
+ fontCache = gdCacheCreate( FONTCACHESIZE,
+ fontTest, fontFetch, fontRelease);
+ }
+ /**************/
+
+ /* get the font (via font cache) */
+ fontkey.fontname = fontname;
+ fontkey.ptsize = ptsize;
+ fontkey.angle = angle;
+ fontkey.engine = &engine;
+ font = (font_t *)gdCacheGet(fontCache, &fontkey);
+ if (! font) {
+ return fontCache->error;
+ }
+ sin_a = font->sin_a;
+ cos_a = font->cos_a;
+ advance_x = advance_y = 0;
+
+ next=string;
+ while (*next) {
+ ch = *next;
+
+ /* carriage returns */
+ if (ch == '\r') {
+ advance_x = 0;
+ next++;
+ continue;
+ }
+ /* newlines */
+ if (ch == '\n') {
+ advance_y -= (TT_F26Dot6)(font->imetrics.y_ppem * LINESPACE * 64);
+ advance_y = (advance_y-32) & -64; /* round to next pixel row */
+ next++;
+ continue;
+ }
+
+ x1 = (TT_F26Dot6)(advance_x * cos_a - advance_y * sin_a);
+ y1 = (TT_F26Dot6)(advance_x * sin_a + advance_y * cos_a);
+
+ if ((error=gdttfchar(im, fg, font, x, y, x1, y1, &advance, &bbox, &next)))
+ return error;
+
+ if (! i++) { /* if first character, init BB corner values */
+ ll_x = bbox->xMin;
+ ll_y = bbox->yMin;
+ ur_x = bbox->xMax;
+ ur_y = bbox->yMax;
+ }
+ else {
+ if (! advance_x) ll_x = MIN(bbox->xMin, ll_x);
+ ll_y = MIN(advance_y + bbox->yMin, ll_y);
+ ur_x = MAX(advance_x + bbox->xMax, ur_x);
+ if (! advance_y) ur_y = MAX(bbox->yMax, ur_y);
+ }
+ advance_x += advance;
+ }
+
+ /* rotate bounding rectangle */
+ brect[0] = (int)(ll_x * cos_a - ll_y * sin_a);
+ brect[1] = (int)(ll_x * sin_a + ll_y * cos_a);
+ brect[2] = (int)(ur_x * cos_a - ll_y * sin_a);
+ brect[3] = (int)(ur_x * sin_a + ll_y * cos_a);
+ brect[4] = (int)(ur_x * cos_a - ur_y * sin_a);
+ brect[5] = (int)(ur_x * sin_a + ur_y * cos_a);
+ brect[6] = (int)(ll_x * cos_a - ur_y * sin_a);
+ brect[7] = (int)(ll_x * sin_a + ur_y * cos_a);
+
+ /* scale, round and offset brect */
+ i = 0;
+ while (i<8) {
+ brect[i] = x + (brect[i] + 32) / 64;
+ i++;
+ brect[i] = y - (brect[i] + 32) / 64;
+ i++;
+ }
+
+ return (char *)NULL;
+}
+
+#endif /* HAVE_LIBTTF */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */