|
|
|
@ -28,11 +28,11 @@ extern int gdImageColorResolve(gdImagePtr, int, int, int); |
|
|
|
/* number of fonts cached before least recently used is replaced */ |
|
|
|
#define FONTCACHESIZE 6 |
|
|
|
|
|
|
|
/* number of character glyphs cached per font before |
|
|
|
/* number of character glyphs cached per font before |
|
|
|
least-recently-used is replaced */ |
|
|
|
#define GLYPHCACHESIZE 120 |
|
|
|
|
|
|
|
/* number of bitmaps cached per glyph before |
|
|
|
/* number of bitmaps cached per glyph before |
|
|
|
least-recently-used is replaced */ |
|
|
|
#define BITMAPCACHESIZE 8 |
|
|
|
|
|
|
|
@ -42,15 +42,15 @@ extern int gdImageColorResolve(gdImagePtr, int, int, int); |
|
|
|
/* ptsize below which anti-aliasing is ineffective */ |
|
|
|
#define MINANTIALIASPTSIZE 0 |
|
|
|
|
|
|
|
/* display resolution - (Not really. This has to be 72 or hinting is wrong) */ |
|
|
|
/* display resolution - (Not really. This has to be 72 or hinting is wrong) */ |
|
|
|
#define RESOLUTION 72 |
|
|
|
|
|
|
|
/* Number of colors used for anti-aliasing */ |
|
|
|
#undef NUMCOLORS |
|
|
|
#define NUMCOLORS 4 |
|
|
|
|
|
|
|
/* Line separation as a factor of font height. |
|
|
|
No space between if LINESPACE = 1.00 |
|
|
|
/* 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 |
|
|
|
|
|
|
|
@ -125,7 +125,7 @@ typedef struct { |
|
|
|
glyph_t *glyph; |
|
|
|
} bitmapkey_t; |
|
|
|
|
|
|
|
typedef struct { |
|
|
|
typedef struct { |
|
|
|
unsigned char pixel; /* key */ |
|
|
|
unsigned char bgcolor; /* key */ |
|
|
|
int fgcolor; /* key */ /* -ve means no antialias */ |
|
|
|
@ -138,7 +138,7 @@ typedef struct { |
|
|
|
unsigned char bgcolor; /* key */ |
|
|
|
int fgcolor; /* key */ /* -ve means no antialias */ |
|
|
|
gdImagePtr im; /* key */ |
|
|
|
} tweencolorkey_t; |
|
|
|
} tweencolorkey_t; |
|
|
|
|
|
|
|
/* forward declarations so that glyphCache can be initialized by font code */ |
|
|
|
static int glyphTest ( void *element, void *key ); |
|
|
|
@ -196,7 +196,7 @@ char *gdttfchar(gdImage *im, int fg, font_t *font, int x, int y, TT_F26Dot6 x1, |
|
|
|
* |
|
|
|
*--------------------------------------------------------------------------- |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
#ifndef CHARSET_EBCDIC |
|
|
|
#define ASC(ch) (ch) |
|
|
|
#else /*CHARSET_EBCDIC*/ |
|
|
|
@ -205,17 +205,16 @@ char *gdttfchar(gdImage *im, int fg, font_t *font, int x, int y, TT_F26Dot6 x1, |
|
|
|
|
|
|
|
#define Tcl_UniChar int |
|
|
|
#define TCL_UTF_MAX 3 |
|
|
|
static int |
|
|
|
gdTcl_UtfToUniChar(char *str, Tcl_UniChar *chPtr) |
|
|
|
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; |
|
|
|
|
|
|
|
int byte; |
|
|
|
|
|
|
|
/* HTML4.0 entities in decimal form, e.g. Å */ |
|
|
|
byte = *((unsigned char *) str); |
|
|
|
byte = *((unsigned char *) str); |
|
|
|
if (byte == '&') { |
|
|
|
int i, n=0; |
|
|
|
int i, n = 0; |
|
|
|
|
|
|
|
byte = *((unsigned char *) (str+1)); |
|
|
|
if (byte == '#') { |
|
|
|
@ -223,9 +222,9 @@ gdTcl_UtfToUniChar(char *str, Tcl_UniChar *chPtr) |
|
|
|
byte = *((unsigned char *) (str+i)); |
|
|
|
if (byte >= '0' && byte <= '9') { |
|
|
|
n = (n * 10) + (byte - '0'); |
|
|
|
} |
|
|
|
else |
|
|
|
} else { |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
if (byte == ';') { |
|
|
|
*chPtr = (Tcl_UniChar) n; |
|
|
|
@ -233,105 +232,91 @@ gdTcl_UtfToUniChar(char *str, Tcl_UniChar *chPtr) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* 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. |
|
|
|
*/ |
|
|
|
/* Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones. */ |
|
|
|
|
|
|
|
*chPtr = (Tcl_UniChar) byte; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
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; |
|
|
|
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; |
|
|
|
} |
|
|
|
ch <<= 6; |
|
|
|
ch |= (ASC(*str) & 0x3F); |
|
|
|
trail--; |
|
|
|
} while (trail > 0); |
|
|
|
*chPtr = ch; |
|
|
|
return total; |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
*chPtr = (Tcl_UniChar) byte; |
|
|
|
return 1; |
|
|
|
*chPtr = (Tcl_UniChar) byte; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
/********************************************************************/ |
|
|
|
/* font cache functions */ |
|
|
|
|
|
|
|
static int |
|
|
|
fontTest ( void *element, void *key ) |
|
|
|
static int fontTest ( void *element, void *key ) |
|
|
|
{ |
|
|
|
font_t *a=(font_t *)element; |
|
|
|
fontkey_t *b=(fontkey_t *)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); |
|
|
|
return (strcmp(a->fontname, b->fontname) == 0 && a->ptsize == b->ptsize && a->angle == b->angle); |
|
|
|
} |
|
|
|
|
|
|
|
static void * |
|
|
|
fontFetch ( char **error, void *key ) |
|
|
|
static void * fontFetch ( char **error, void *key ) |
|
|
|
{ |
|
|
|
TT_Error err; |
|
|
|
font_t *a; |
|
|
|
fontkey_t *b=(fontkey_t *)key; |
|
|
|
int i, n, map_found; |
|
|
|
short platform, encoding; |
|
|
|
TT_Error err; |
|
|
|
font_t *a; |
|
|
|
fontkey_t *b = (fontkey_t *)key; |
|
|
|
int i, n, map_found; |
|
|
|
short platform, encoding; |
|
|
|
TSRMLS_FETCH(); |
|
|
|
|
|
|
|
a = (font_t *)pemalloc(sizeof(font_t), 1); |
|
|
|
@ -354,8 +339,7 @@ fontFetch ( char **error, void *key ) |
|
|
|
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 { |
|
|
|
} else { |
|
|
|
*error = "Could not read font"; |
|
|
|
} |
|
|
|
pefree(a, 1); |
|
|
|
@ -370,7 +354,7 @@ fontFetch ( char **error, void *key ) |
|
|
|
pefree(a, 1); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (TT_Set_Instance_Resolutions(a->instance, RESOLUTION, RESOLUTION)) { |
|
|
|
*error = "Could not set device resolutions"; |
|
|
|
pefree(a, 1); |
|
|
|
@ -384,12 +368,12 @@ fontFetch ( char **error, void *key ) |
|
|
|
} |
|
|
|
|
|
|
|
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); |
|
|
|
TT_Get_CharMap_ID(a->face, i, &platform, &encoding); |
|
|
|
if ((platform == 3 && encoding == 1) /* Windows Unicode */ |
|
|
|
|| (platform == 2 && encoding == 1) |
|
|
|
|| (platform == 0)) { /* ?? Unicode */ |
|
|
|
@ -407,7 +391,7 @@ fontFetch ( char **error, void *key ) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (! map_found) { |
|
|
|
if (!map_found) { |
|
|
|
*error = "Unable to find a CharMap that I can handle"; |
|
|
|
pefree(a, 1); |
|
|
|
return NULL; |
|
|
|
@ -418,16 +402,14 @@ fontFetch ( char **error, void *key ) |
|
|
|
a->matrix.xy = - a->matrix.yx; |
|
|
|
a->matrix.yy = a->matrix.xx; |
|
|
|
|
|
|
|
a->glyphCache = gdCacheCreate( GLYPHCACHESIZE, |
|
|
|
glyphTest, glyphFetch, glyphRelease); |
|
|
|
a->glyphCache = gdCacheCreate(GLYPHCACHESIZE, glyphTest, glyphFetch, glyphRelease); |
|
|
|
|
|
|
|
return (void *)a; |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
fontRelease( void *element ) |
|
|
|
static void fontRelease( void *element ) |
|
|
|
{ |
|
|
|
font_t *a=(font_t *)element; |
|
|
|
font_t *a = (font_t *)element; |
|
|
|
|
|
|
|
gdCacheDelete(a->glyphCache); |
|
|
|
TT_Done_Instance(a->instance); |
|
|
|
@ -439,26 +421,22 @@ fontRelease( void *element ) |
|
|
|
/********************************************************************/ |
|
|
|
/* glyph cache functions */ |
|
|
|
|
|
|
|
static int |
|
|
|
glyphTest ( void *element, void *key ) |
|
|
|
static int glyphTest ( void *element, void *key ) |
|
|
|
{ |
|
|
|
glyph_t *a=(glyph_t *)element; |
|
|
|
glyphkey_t *b=(glyphkey_t *)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); |
|
|
|
return (a->character == b->character && a->hinting == b->hinting && a->gray_render == b->gray_render); |
|
|
|
} |
|
|
|
|
|
|
|
static void * |
|
|
|
glyphFetch ( char **error, void *key ) |
|
|
|
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; |
|
|
|
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 *)pemalloc(sizeof(glyph_t), 1); |
|
|
|
a->character = b->character; |
|
|
|
@ -523,38 +501,34 @@ glyphFetch ( char **error, void *key ) |
|
|
|
a->Bit.flow = TT_Flow_Up; |
|
|
|
if (a->gray_render) { |
|
|
|
a->Bit.cols = a->Bit.width; /* 1 byte per pixel */ |
|
|
|
} |
|
|
|
else { |
|
|
|
} 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); |
|
|
|
a->bitmapCache = gdCacheCreate(BITMAPCACHESIZE, bitmapTest, bitmapFetch, bitmapRelease); |
|
|
|
|
|
|
|
return (void *)a; |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
glyphRelease( void *element ) |
|
|
|
static void glyphRelease( void *element ) |
|
|
|
{ |
|
|
|
glyph_t *a=(glyph_t *)element; |
|
|
|
glyph_t *a = (glyph_t *)element; |
|
|
|
|
|
|
|
gdCacheDelete(a->bitmapCache); |
|
|
|
TT_Done_Glyph( a->glyph ); |
|
|
|
pefree ((char *)element, 1); |
|
|
|
TT_Done_Glyph(a->glyph); |
|
|
|
pefree((char *)element, 1); |
|
|
|
} |
|
|
|
|
|
|
|
/********************************************************************/ |
|
|
|
/* bitmap cache functions */ |
|
|
|
|
|
|
|
static int |
|
|
|
bitmapTest ( void *element, void *key ) |
|
|
|
static int bitmapTest ( void *element, void *key ) |
|
|
|
{ |
|
|
|
bitmap_t *a=(bitmap_t *)element; |
|
|
|
bitmapkey_t *b=(bitmapkey_t *)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; |
|
|
|
@ -563,11 +537,10 @@ bitmapTest ( void *element, void *key ) |
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
|
|
|
|
static void * |
|
|
|
bitmapFetch ( char **error, void *key ) |
|
|
|
static void * bitmapFetch ( char **error, void *key ) |
|
|
|
{ |
|
|
|
bitmap_t *a; |
|
|
|
bitmapkey_t *b=(bitmapkey_t *)key; |
|
|
|
bitmap_t *a; |
|
|
|
bitmapkey_t *b = (bitmapkey_t *)key; |
|
|
|
|
|
|
|
a = (bitmap_t *)pemalloc(sizeof(bitmap_t), 1); |
|
|
|
a->xoffset = b->xoffset; |
|
|
|
@ -577,56 +550,47 @@ bitmapFetch ( char **error, void *key ) |
|
|
|
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); |
|
|
|
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 ) |
|
|
|
static void bitmapRelease( void *element ) |
|
|
|
{ |
|
|
|
bitmap_t *a=(bitmap_t *)element; |
|
|
|
bitmap_t *a = (bitmap_t *)element; |
|
|
|
|
|
|
|
pefree (a->bitmap, 1); |
|
|
|
pefree ((char *)element, 1); |
|
|
|
pefree(a->bitmap, 1); |
|
|
|
pefree((char *)element, 1); |
|
|
|
} |
|
|
|
|
|
|
|
/********************************************************************/ |
|
|
|
/* 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) |
|
|
|
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; |
|
|
|
tweencolor_t *a; |
|
|
|
tweencolorkey_t *b = (tweencolorkey_t *)key; |
|
|
|
int pixel, npixel, bg, fg; |
|
|
|
gdImagePtr im; |
|
|
|
|
|
|
|
a = (tweencolor_t *)pemalloc(sizeof(tweencolor_t), 1); |
|
|
|
|
|
|
|
a = (tweencolor_t *)pemalloc(sizeof(tweencolor_t), 1); |
|
|
|
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) { |
|
|
|
if (fg < 0) { |
|
|
|
a->tweencolor = -fg; |
|
|
|
} else { |
|
|
|
npixel = NUMCOLORS - pixel; |
|
|
|
@ -635,20 +599,19 @@ tweenColorFetch (char **error, void *key) |
|
|
|
(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) |
|
|
|
{ |
|
|
|
pefree((char *)element, 1); |
|
|
|
} |
|
|
|
*error = NULL; |
|
|
|
return (void *)a; |
|
|
|
} |
|
|
|
|
|
|
|
static void tweenColorRelease(void *element) |
|
|
|
{ |
|
|
|
pefree((char *)element, 1); |
|
|
|
} |
|
|
|
|
|
|
|
/********************************************************************/ |
|
|
|
/* gdttfchar - render one character onto a gd image */ |
|
|
|
|
|
|
|
static int OneTime=0; |
|
|
|
static int OneTime = 0; |
|
|
|
static gdCache_head_t *tweenColorCache; |
|
|
|
|
|
|
|
char * |
|
|
|
@ -656,38 +619,37 @@ 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, |
|
|
|
TT_BBox **bbox, |
|
|
|
char **next) |
|
|
|
{ |
|
|
|
int pc, ch, len; |
|
|
|
int pc, ch, len; |
|
|
|
int row, col; |
|
|
|
int x2, y2; /* char start pos in pixels */ |
|
|
|
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; |
|
|
|
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); |
|
|
|
if (!OneTime) { |
|
|
|
tweenColorCache = gdCacheCreate(TWEENCOLORCACHESIZE, tweenColorTest, tweenColorFetch, tweenColorRelease); |
|
|
|
OneTime++; |
|
|
|
} |
|
|
|
/**************/ |
|
|
|
|
|
|
|
if (font->have_char_map_Unicode) { /* use UTF-8 mapping from ASCII */ |
|
|
|
len = gdTcl_UtfToUniChar(*next, &ch); |
|
|
|
*next += len; |
|
|
|
len = gdTcl_UtfToUniChar(*next, &ch); |
|
|
|
*next += len; |
|
|
|
} else { |
|
|
|
/* |
|
|
|
* Big 5 mapping: |
|
|
|
* use "JIS-8 half-width katakana" coding from 8-bit characters. Ref: |
|
|
|
* ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs |
|
|
|
*/ |
|
|
|
/* |
|
|
|
* Big 5 mapping: |
|
|
|
* use "JIS-8 half-width katakana" coding from 8-bit characters. Ref: |
|
|
|
* ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs |
|
|
|
*/ |
|
|
|
ch = (**next) & 255; /* don't extend sign */ |
|
|
|
(*next)++; |
|
|
|
if (ch >= 161 /* first code of JIS-8 pair */ |
|
|
|
@ -700,18 +662,20 @@ gdttfchar(gdImage *im, int fg, font_t *font, |
|
|
|
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) |
|
|
|
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) |
|
|
|
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); |
|
|
|
@ -720,30 +684,32 @@ gdttfchar(gdImage *im, int fg, font_t *font, |
|
|
|
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; |
|
|
|
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) |
|
|
|
if (glyph->gray_render) { |
|
|
|
pc = row * glyph->Bit.cols; |
|
|
|
else |
|
|
|
} else { |
|
|
|
pc = row * glyph->Bit.cols * 8; |
|
|
|
} |
|
|
|
y3 = y2 - row; |
|
|
|
if (y3 >= im->sy || y3 < 0) continue; |
|
|
|
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); |
|
|
|
tweencolorkey.pixel = *((unsigned char *)(glyph->Bit.bitmap) + pc); |
|
|
|
} else { |
|
|
|
tweencolorkey.pixel = |
|
|
|
(((*((unsigned char *)(glyph->Bit.bitmap) + pc/8)) |
|
|
|
<<(pc%8))&128)?4:0; |
|
|
|
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 (x3 >= im->sx || x3 < 0) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
#if HAVE_LIBGD20 |
|
|
|
if (im->trueColor) { |
|
|
|
pixel = &im->tpixels[y3][x3]; |
|
|
|
@ -757,8 +723,7 @@ gdttfchar(gdImage *im, int fg, font_t *font, |
|
|
|
#endif |
|
|
|
} |
|
|
|
tweencolorkey.bgcolor = *pixel; |
|
|
|
tweencolor = (tweencolor_t *)gdCacheGet( |
|
|
|
tweenColorCache, &tweencolorkey); |
|
|
|
tweencolor = (tweencolor_t *)gdCacheGet(tweenColorCache, &tweencolorkey); |
|
|
|
*pixel = tweencolor->tweencolor; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -769,29 +734,26 @@ gdttfchar(gdImage *im, int fg, font_t *font, |
|
|
|
/********************************************************************/ |
|
|
|
/* 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 *str) |
|
|
|
char * gdttf(gdImage *im, int *brect, int fg, char *fontname, double ptsize, double angle, int x, int y, char *str) |
|
|
|
{ |
|
|
|
TT_F26Dot6 ur_x=0, ur_y=0, ll_x=0, ll_y=0; |
|
|
|
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; |
|
|
|
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 gdCache_head_t *fontCache; |
|
|
|
static TT_Engine engine; |
|
|
|
|
|
|
|
if (! fontCache) { |
|
|
|
if (!fontCache) { |
|
|
|
if (TT_Init_FreeType(&engine)) { |
|
|
|
return "Failure to initialize font engine"; |
|
|
|
} |
|
|
|
fontCache = gdCacheCreate( FONTCACHESIZE, |
|
|
|
fontTest, fontFetch, fontRelease); |
|
|
|
fontCache = gdCacheCreate(FONTCACHESIZE, fontTest, fontFetch, fontRelease); |
|
|
|
} |
|
|
|
/**************/ |
|
|
|
|
|
|
|
@ -801,15 +763,15 @@ gdttf(gdImage *im, int *brect, int fg, char *fontname, |
|
|
|
fontkey.angle = angle; |
|
|
|
fontkey.engine = &engine; |
|
|
|
font = (font_t *)gdCacheGet(fontCache, &fontkey); |
|
|
|
if (! font) { |
|
|
|
if (!font) { |
|
|
|
return fontCache->error; |
|
|
|
} |
|
|
|
sin_a = font->sin_a; |
|
|
|
cos_a = font->cos_a; |
|
|
|
advance_x = advance_y = 0; |
|
|
|
|
|
|
|
next=str; |
|
|
|
while (*next) { |
|
|
|
next = str; |
|
|
|
while (*next) { |
|
|
|
ch = *next; |
|
|
|
|
|
|
|
/* carriage returns */ |
|
|
|
@ -820,8 +782,8 @@ gdttf(gdImage *im, int *brect, int fg, char *fontname, |
|
|
|
} |
|
|
|
/* 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 */ |
|
|
|
advance_y -= (TT_F26Dot6)(font->imetrics.y_ppem * LINESPACE * 64); |
|
|
|
advance_y = (advance_y-32) & -64; /* round to next pixel row */ |
|
|
|
next++; |
|
|
|
continue; |
|
|
|
} |
|
|
|
@ -829,20 +791,24 @@ gdttf(gdImage *im, int *brect, int fg, char *fontname, |
|
|
|
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))) |
|
|
|
if ((error = gdttfchar(im, fg, font, x, y, x1, y1, &advance, &bbox, &next))) { |
|
|
|
return error; |
|
|
|
} |
|
|
|
|
|
|
|
if (! i++) { /* if first character, init BB corner values */ |
|
|
|
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); |
|
|
|
} 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); |
|
|
|
if (!advance_y) { |
|
|
|
ur_y = MAX(bbox->yMax, ur_y); |
|
|
|
} |
|
|
|
} |
|
|
|
advance_x += advance; |
|
|
|
} |
|
|
|
@ -859,7 +825,7 @@ gdttf(gdImage *im, int *brect, int fg, char *fontname, |
|
|
|
|
|
|
|
/* scale, round and offset brect */ |
|
|
|
i = 0; |
|
|
|
while (i<8) { |
|
|
|
while (i < 8) { |
|
|
|
brect[i] = x + (brect[i] + 32) / 64; |
|
|
|
i++; |
|
|
|
brect[i] = y - (brect[i] + 32) / 64; |
|
|
|
@ -868,7 +834,7 @@ gdttf(gdImage *im, int *brect, int fg, char *fontname, |
|
|
|
|
|
|
|
return (char *)NULL; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_LIBTTF */ |
|
|
|
|
|
|
|
/* |
|
|
|
|