From 2127ede122ad920c22fecdc668e864c0210dd146 Mon Sep 17 00:00:00 2001
From: Stig Bakken <ssb@php.net>
Date: Fri, 16 Apr 1999 12:15:38 +0000
Subject: updated GD from php3 repository, using it as a test extension for the
 new directory structure

---
 ext/gd/gdttf.c | 862 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 862 insertions(+)
 create mode 100644 ext/gd/gdttf.c

(limited to 'ext/gd/gdttf.c')

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:
+ */
-- 
cgit v1.2.1