Browse Source
updated GD from php3 repository, using it as a test extension for the new
updated GD from php3 repository, using it as a test extension for the new
directory structuremigration/INITIAL
5 changed files with 2952 additions and 0 deletions
-
1786ext/gd/gd.c
-
201ext/gd/gdcache.c
-
87ext/gd/gdcache.h
-
862ext/gd/gdttf.c
-
16ext/gd/gdttf.h
1786
ext/gd/gd.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,201 @@ |
|||
/* |
|||
* $Id$ |
|||
* |
|||
* Caches of pointers to user structs in which the least-recently-used |
|||
* element is replaced in the event of a cache miss after the cache has |
|||
* reached a given size. |
|||
* |
|||
* John Ellson (ellson@lucent.com) Oct 31, 1997 |
|||
* |
|||
* Test this with: |
|||
* gcc -o gdcache -g -Wall -DTEST gdcache.c |
|||
* |
|||
* The cache is implemented by a singly-linked list of elements |
|||
* each containing a pointer to a user struct that is being managed by |
|||
* the cache. |
|||
* |
|||
* The head structure has a pointer to the most-recently-used |
|||
* element, and elements are moved to this position in the list each |
|||
* time they are used. The head also contains pointers to three |
|||
* user defined functions: |
|||
* - a function to test if a cached userdata matches some keydata |
|||
* - a function to provide a new userdata struct to the cache |
|||
* if there has been a cache miss. |
|||
* - a function to release a userdata struct when it is |
|||
* no longer being managed by the cache |
|||
* |
|||
* In the event of a cache miss the cache is allowed to grow up to |
|||
* a specified maximum size. After the maximum size is reached then |
|||
* the least-recently-used element is discarded to make room for the |
|||
* new. The most-recently-returned value is always left at the |
|||
* beginning of the list after retrieval. |
|||
* |
|||
* In the current implementation the cache is traversed by a linear |
|||
* search from most-recent to least-recent. This linear search |
|||
* probably limits the usefulness of this implementation to cache |
|||
* sizes of a few tens of elements. |
|||
*/ |
|||
|
|||
/* This just seems unessacary */ |
|||
#if (WIN32|WINNT) |
|||
#define HAVE_LIBTTF 1 |
|||
#else |
|||
#include "config.h" |
|||
#endif |
|||
#if HAVE_LIBTTF |
|||
|
|||
#include "gdcache.h" |
|||
|
|||
/*********************************************************/ |
|||
/* implementation */ |
|||
/*********************************************************/ |
|||
|
|||
|
|||
/* create a new cache */ |
|||
gdCache_head_t * |
|||
gdCacheCreate( |
|||
int size, |
|||
gdCacheTestFn_t gdCacheTest, |
|||
gdCacheFetchFn_t gdCacheFetch, |
|||
gdCacheReleaseFn_t gdCacheRelease ) |
|||
{ |
|||
gdCache_head_t *head; |
|||
|
|||
head = (gdCache_head_t *)malloc(sizeof(gdCache_head_t)); |
|||
head->mru = NULL; |
|||
head->size = size; |
|||
head->gdCacheTest = gdCacheTest; |
|||
head->gdCacheFetch = gdCacheFetch; |
|||
head->gdCacheRelease = gdCacheRelease; |
|||
return head; |
|||
} |
|||
|
|||
void |
|||
gdCacheDelete( gdCache_head_t *head ) |
|||
{ |
|||
gdCache_element_t *elem, *prev; |
|||
|
|||
elem = head->mru; |
|||
while(elem) { |
|||
(*(head->gdCacheRelease))(elem->userdata); |
|||
prev = elem; |
|||
elem = elem->next; |
|||
free((char *)prev); |
|||
} |
|||
free((char *)head); |
|||
} |
|||
|
|||
void * |
|||
gdCacheGet( gdCache_head_t *head, void *keydata ) |
|||
{ |
|||
int i=0; |
|||
gdCache_element_t *elem, *prev = NULL, *prevprev = NULL; |
|||
void *userdata; |
|||
|
|||
elem = head->mru; |
|||
while(elem) { |
|||
if ((*(head->gdCacheTest))(elem->userdata, keydata)) { |
|||
if (i) { /* if not already most-recently-used */ |
|||
/* relink to top of list */ |
|||
prev->next = elem->next; |
|||
elem->next = head->mru; |
|||
head->mru = elem; |
|||
} |
|||
return elem->userdata; |
|||
} |
|||
prevprev = prev; |
|||
prev = elem; |
|||
elem = elem->next; |
|||
i++; |
|||
} |
|||
userdata = (*(head->gdCacheFetch))(&(head->error), keydata); |
|||
if (! userdata) { |
|||
/* if there was an error in the fetch then don't cache */ |
|||
return NULL; |
|||
} |
|||
if (i < head->size) { /* cache still growing - add new elem */ |
|||
elem = (gdCache_element_t *)malloc(sizeof(gdCache_element_t)); |
|||
} |
|||
else { /* cache full - replace least-recently-used */ |
|||
/* preveprev becomes new end of list */ |
|||
prevprev->next = NULL; |
|||
elem = prev; |
|||
(*(head->gdCacheRelease))(elem->userdata); |
|||
} |
|||
/* relink to top of list */ |
|||
elem->next = head->mru; |
|||
head->mru = elem; |
|||
elem->userdata = userdata; |
|||
return userdata; |
|||
} |
|||
|
|||
|
|||
|
|||
/*********************************************************/ |
|||
/* test stub */ |
|||
/*********************************************************/ |
|||
|
|||
|
|||
#ifdef GDCACHE_TEST |
|||
|
|||
#include <stdio.h> |
|||
|
|||
typedef struct { |
|||
int key; |
|||
int value; |
|||
} key_value_t; |
|||
|
|||
static int |
|||
cacheTest( void *map, void *key ) |
|||
{ |
|||
return (((key_value_t *)map)->key == *(int *)key); |
|||
} |
|||
|
|||
static void * |
|||
cacheFetch( char **error, void *key ) |
|||
{ |
|||
key_value_t *map; |
|||
|
|||
map = (key_value_t *)malloc(sizeof(key_value_t)); |
|||
map->key = *(int *)key; |
|||
map->value = 3; |
|||
|
|||
*error = NULL; |
|||
return (void *)map; |
|||
} |
|||
|
|||
static void |
|||
cacheRelease( void *map) |
|||
{ |
|||
free( (char *)map ); |
|||
} |
|||
|
|||
int |
|||
main(char *argv[], int argc) |
|||
{ |
|||
gdCache_head_t *cacheTable; |
|||
int elem, key; |
|||
|
|||
cacheTable = gdCacheCreate(3, cacheTest, cacheFetch, cacheRelease); |
|||
|
|||
key = 20; |
|||
elem = *(int *)gdCacheGet(cacheTable, &key); |
|||
key = 30; |
|||
elem = *(int *)gdCacheGet(cacheTable, &key); |
|||
key = 40; |
|||
elem = *(int *)gdCacheGet(cacheTable, &key); |
|||
key = 50; |
|||
elem = *(int *)gdCacheGet(cacheTable, &key); |
|||
key = 30; |
|||
elem = *(int *)gdCacheGet(cacheTable, &key); |
|||
key = 30; |
|||
elem = *(int *)gdCacheGet(cacheTable, &key); |
|||
|
|||
gdCacheDelete(cacheTable); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
#endif |
|||
|
|||
#endif /* HAVE_LIBTTF */ |
|||
@ -0,0 +1,87 @@ |
|||
/* |
|||
* $Id$ |
|||
* |
|||
* Caches of pointers to user structs in which the least-recently-used |
|||
* element is replaced in the event of a cache miss after the cache has |
|||
* reached a given size. |
|||
* |
|||
* John Ellson (ellson@lucent.com) Oct 31, 1997 |
|||
* |
|||
* Test this with: |
|||
* gcc -o gdcache -g -Wall -DTEST gdcache.c |
|||
* |
|||
* The cache is implemented by a singly-linked list of elements |
|||
* each containing a pointer to a user struct that is being managed by |
|||
* the cache. |
|||
* |
|||
* The head structure has a pointer to the most-recently-used |
|||
* element, and elements are moved to this position in the list each |
|||
* time they are used. The head also contains pointers to three |
|||
* user defined functions: |
|||
* - a function to test if a cached userdata matches some keydata |
|||
* - a function to provide a new userdata struct to the cache |
|||
* if there has been a cache miss. |
|||
* - a function to release a userdata struct when it is |
|||
* no longer being managed by the cache |
|||
* |
|||
* In the event of a cache miss the cache is allowed to grow up to |
|||
* a specified maximum size. After the maximum size is reached then |
|||
* the least-recently-used element is discarded to make room for the |
|||
* new. The most-recently-returned value is always left at the |
|||
* beginning of the list after retrieval. |
|||
* |
|||
* In the current implementation the cache is traversed by a linear |
|||
* search from most-recent to least-recent. This linear search |
|||
* probably limits the usefulness of this implementation to cache |
|||
* sizes of a few tens of elements. |
|||
*/ |
|||
|
|||
/*********************************************************/ |
|||
/* header */ |
|||
/*********************************************************/ |
|||
|
|||
#ifndef _OSD_POSIX |
|||
#include <malloc.h> |
|||
#else |
|||
#include <stdlib.h> /* BS2000/OSD defines malloc() & friends in stdlib.h */ |
|||
#endif |
|||
#ifndef NULL |
|||
#define NULL (void *)0 |
|||
#endif |
|||
|
|||
/* user defined function templates */ |
|||
typedef int (*gdCacheTestFn_t)(void *userdata, void *keydata); |
|||
typedef void *(*gdCacheFetchFn_t)(char **error, void *keydata); |
|||
typedef void (*gdCacheReleaseFn_t)(void *userdata); |
|||
|
|||
/* element structure */ |
|||
typedef struct gdCache_element_s gdCache_element_t; |
|||
struct gdCache_element_s { |
|||
gdCache_element_t *next; |
|||
void *userdata; |
|||
}; |
|||
|
|||
/* head structure */ |
|||
typedef struct gdCache_head_s gdCache_head_t; |
|||
struct gdCache_head_s { |
|||
gdCache_element_t *mru; |
|||
int size; |
|||
char *error; |
|||
gdCacheTestFn_t gdCacheTest; |
|||
gdCacheFetchFn_t gdCacheFetch; |
|||
gdCacheReleaseFn_t gdCacheRelease; |
|||
}; |
|||
|
|||
/* function templates */ |
|||
gdCache_head_t * |
|||
gdCacheCreate( |
|||
int size, |
|||
gdCacheTestFn_t gdCacheTest, |
|||
gdCacheFetchFn_t gdCacheFetch, |
|||
gdCacheReleaseFn_t gdCacheRelease ); |
|||
|
|||
void |
|||
gdCacheDelete( gdCache_head_t *head ); |
|||
|
|||
void * |
|||
gdCacheGet( gdCache_head_t *head, void *keydata ); |
|||
@ -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. Å */ |
|||
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: |
|||
*/ |
|||
@ -0,0 +1,16 @@ |
|||
/* $Id$ */ |
|||
|
|||
#ifdef _OSD_POSIX |
|||
#ifndef APACHE |
|||
#error On this EBCDIC platform, PHP3 is only supported as an Apache module. |
|||
#else /*APACHE*/ |
|||
#ifndef CHARSET_EBCDIC |
|||
#define CHARSET_EBCDIC /* this machine uses EBCDIC, not ASCII! */ |
|||
#endif |
|||
#include "ebcdic.h" |
|||
#endif /*APACHE*/ |
|||
#endif /*_OSD_POSIX*/ |
|||
|
|||
char * gdttf(gdImage *im, int *brect, int fg, char *fontname, |
|||
double ptsize, double angle, int x, int y, char *string); |
|||
|
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue