mirror of
https://github.com/php/php-src.git
synced 2024-11-23 18:04:36 +08:00
updated GD from php3 repository, using it as a test extension for the new
directory structure
This commit is contained in:
parent
8cf2d74b76
commit
2127ede122
1786
ext/gd/gd.c
Normal file
1786
ext/gd/gd.c
Normal file
File diff suppressed because it is too large
Load Diff
201
ext/gd/gdcache.c
Normal file
201
ext/gd/gdcache.c
Normal 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 */
|
87
ext/gd/gdcache.h
Normal file
87
ext/gd/gdcache.h
Normal file
@ -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 );
|
862
ext/gd/gdttf.c
Normal file
862
ext/gd/gdttf.c
Normal file
@ -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:
|
||||
*/
|
16
ext/gd/gdttf.h
Normal file
16
ext/gd/gdttf.h
Normal file
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user