summaryrefslogtreecommitdiff
path: root/ext/gd/gdttf.c
diff options
context:
space:
mode:
authorStig Bakken <ssb@php.net>1999-04-16 12:15:38 +0000
committerStig Bakken <ssb@php.net>1999-04-16 12:15:38 +0000
commit2127ede122ad920c22fecdc668e864c0210dd146 (patch)
treef33a7d7dd7769fdeb2336a903f23072190ed3857 /ext/gd/gdttf.c
parent8cf2d74b76add639c3b4cfed3656e664010c164b (diff)
downloadphp-git-2127ede122ad920c22fecdc668e864c0210dd146.tar.gz
updated GD from php3 repository, using it as a test extension for the new
directory structure
Diffstat (limited to 'ext/gd/gdttf.c')
-rw-r--r--ext/gd/gdttf.c862
1 files changed, 862 insertions, 0 deletions
diff --git a/ext/gd/gdttf.c b/ext/gd/gdttf.c
new file mode 100644
index 0000000000..39e93ed339
--- /dev/null
+++ b/ext/gd/gdttf.c
@@ -0,0 +1,862 @@
+/* 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. &#197; */
+ 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:
+ */