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