Annotation of embedaddon/php/ext/gd/libgd/gdft.c, revision 1.1
1.1 ! misho 1:
! 2: /********************************************/
! 3: /* gd interface to freetype library */
! 4: /* */
! 5: /* John Ellson ellson@graphviz.org */
! 6: /********************************************/
! 7:
! 8: #include <stdio.h>
! 9: #include <stdlib.h>
! 10: #include <string.h>
! 11: #include <math.h>
! 12: #include "gd.h"
! 13: #include "gdhelpers.h"
! 14:
! 15: #ifndef MSWIN32
! 16: #include <unistd.h>
! 17: #else
! 18: #include <io.h>
! 19: #ifndef R_OK
! 20: # define R_OK 04 /* Needed in Windows */
! 21: #endif
! 22: #endif
! 23:
! 24: #ifdef WIN32
! 25: #define access _access
! 26: #ifndef R_OK
! 27: #define R_OK 2
! 28: #endif
! 29: #endif
! 30:
! 31: /* number of antialised colors for indexed bitmaps */
! 32: /* overwrite Windows GDI define in case of windows build */
! 33: #ifdef NUMCOLORS
! 34: #undef NUMCOLORS
! 35: #endif
! 36: #define NUMCOLORS 8
! 37:
! 38: char *
! 39: gdImageStringTTF (gdImage * im, int *brect, int fg, char *fontlist,
! 40: double ptsize, double angle, int x, int y, char *string)
! 41: {
! 42: /* 2.0.6: valid return */
! 43: return gdImageStringFT (im, brect, fg, fontlist, ptsize, angle, x, y, string);
! 44: }
! 45:
! 46: #ifndef HAVE_LIBFREETYPE
! 47: char *
! 48: gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist,
! 49: double ptsize, double angle, int x, int y, char *string,
! 50: gdFTStringExtraPtr strex)
! 51: {
! 52: return "libgd was not built with FreeType font support\n";
! 53: }
! 54:
! 55: char *
! 56: gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
! 57: double ptsize, double angle, int x, int y, char *string)
! 58: {
! 59: return "libgd was not built with FreeType font support\n";
! 60: }
! 61: #else
! 62:
! 63: #include "gdcache.h"
! 64: #include <ft2build.h>
! 65: #include FT_FREETYPE_H
! 66: #include FT_GLYPH_H
! 67:
! 68: /* number of fonts cached before least recently used is replaced */
! 69: #define FONTCACHESIZE 6
! 70:
! 71: /* number of antialias color lookups cached */
! 72: #define TWEENCOLORCACHESIZE 32
! 73:
! 74: /*
! 75: * Line separation as a factor of font height.
! 76: * No space between if LINESPACE = 1.00
! 77: * Line separation will be rounded up to next pixel row.
! 78: */
! 79: #define LINESPACE 1.05
! 80:
! 81: /*
! 82: * The character (space) used to separate alternate fonts in the
! 83: * fontlist parameter to gdImageStringFT. 2.0.18: space was a oor choice for this.
! 84: */
! 85: #define LISTSEPARATOR ";"
! 86:
! 87: /*
! 88: * DEFAULT_FONTPATH and PATHSEPARATOR are host type dependent and
! 89: * are normally set by configure in config.h. These are just
! 90: * some last resort values that might match some Un*x system
! 91: * if building this version of gd separate from graphviz.
! 92: */
! 93: #ifndef DEFAULT_FONTPATH
! 94: #if defined(__APPLE__) || (defined(__MWERKS__) && defined(macintosh))
! 95: #define DEFAULT_FONTPATH "/usr/share/fonts/truetype:/System/Library/Fonts:/Library/Fonts"
! 96: #else
! 97: #define DEFAULT_FONTPATH "/usr/share/fonts/truetype"
! 98: #endif
! 99: #endif
! 100: #ifndef PATHSEPARATOR
! 101: #define PATHSEPARATOR ":"
! 102: #endif
! 103:
! 104: #ifndef TRUE
! 105: #define FALSE 0
! 106: #define TRUE !FALSE
! 107: #endif
! 108:
! 109: #ifndef MAX
! 110: #define MAX(a,b) ((a)>(b)?(a):(b))
! 111: #endif
! 112:
! 113: #ifndef MIN
! 114: #define MIN(a,b) ((a)<(b)?(a):(b))
! 115: #endif
! 116:
! 117: typedef struct
! 118: {
! 119: char *fontlist; /* key */
! 120: FT_Library *library;
! 121: FT_Face face;
! 122: FT_Bool have_char_map_unicode, have_char_map_big5, have_char_map_sjis, have_char_map_apple_roman;
! 123: gdCache_head_t *glyphCache;
! 124: } font_t;
! 125:
! 126: typedef struct
! 127: {
! 128: char *fontlist; /* key */
! 129: FT_Library *library;
! 130: } fontkey_t;
! 131:
! 132: typedef struct
! 133: {
! 134: int pixel; /* key */
! 135: int bgcolor; /* key */
! 136: int fgcolor; /* key *//* -ve means no antialias */
! 137: gdImagePtr im; /* key */
! 138: int tweencolor;
! 139: } tweencolor_t;
! 140:
! 141: typedef struct
! 142: {
! 143: int pixel; /* key */
! 144: int bgcolor; /* key */
! 145: int fgcolor; /* key *//* -ve means no antialias */
! 146: gdImagePtr im; /* key */
! 147: } tweencolorkey_t;
! 148:
! 149: /********************************************************************
! 150: * gdTcl_UtfToUniChar is borrowed from Tcl ...
! 151: */
! 152: /*
! 153: * tclUtf.c --
! 154: *
! 155: * Routines for manipulating UTF-8 strings.
! 156: *
! 157: * Copyright (c) 1997-1998 Sun Microsystems, Inc.
! 158: *
! 159: * See the file "license.terms" for information on usage and redistribution
! 160: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
! 161: *
! 162: * SCCS: @(#) tclUtf.c 1.25 98/01/28 18:02:43
! 163: */
! 164:
! 165: /*
! 166: *---------------------------------------------------------------------------
! 167: *
! 168: * gdTcl_UtfToUniChar --
! 169: *
! 170: * Extract the Tcl_UniChar represented by the UTF-8 string. Bad
! 171: * UTF-8 sequences are converted to valid Tcl_UniChars and processing
! 172: * continues. Equivalent to Plan 9 chartorune().
! 173: *
! 174: * The caller must ensure that the source buffer is long enough that
! 175: * this routine does not run off the end and dereference non-existent
! 176: * memory looking for trail bytes. If the source buffer is known to
! 177: * be '\0' terminated, this cannot happen. Otherwise, the caller
! 178: * should call Tcl_UtfCharComplete() before calling this routine to
! 179: * ensure that enough bytes remain in the string.
! 180: *
! 181: * Results:
! 182: * *chPtr is filled with the Tcl_UniChar, and the return value is the
! 183: * number of bytes from the UTF-8 string that were consumed.
! 184: *
! 185: * Side effects:
! 186: * None.
! 187: *
! 188: *---------------------------------------------------------------------------
! 189: */
! 190:
! 191: #ifdef JISX0208
! 192: #include "jisx0208.h"
! 193: #endif
! 194:
! 195: extern int any2eucjp (char *, char *, unsigned int);
! 196:
! 197: /* Persistent font cache until explicitly cleared */
! 198: /* Fonts can be used across multiple images */
! 199:
! 200: /* 2.0.16: thread safety (the font cache is shared) */
! 201: gdMutexDeclare(gdFontCacheMutex);
! 202: static gdCache_head_t *fontCache = NULL;
! 203: static FT_Library library;
! 204:
! 205: #define Tcl_UniChar int
! 206: #define TCL_UTF_MAX 3
! 207: static int gdTcl_UtfToUniChar (char *str, Tcl_UniChar * chPtr)
! 208: /* str is the UTF8 next character pointer */
! 209: /* chPtr is the int for the result */
! 210: {
! 211: int byte;
! 212:
! 213: /* HTML4.0 entities in decimal form, e.g. Å */
! 214: byte = *((unsigned char *) str);
! 215: if (byte == '&') {
! 216: int i, n = 0;
! 217:
! 218: byte = *((unsigned char *) (str + 1));
! 219: if (byte == '#') {
! 220: byte = *((unsigned char *) (str + 2));
! 221: if (byte == 'x' || byte == 'X') {
! 222: for (i = 3; i < 8; i++) {
! 223: byte = *((unsigned char *) (str + i));
! 224: if (byte >= 'A' && byte <= 'F')
! 225: byte = byte - 'A' + 10;
! 226: else if (byte >= 'a' && byte <= 'f')
! 227: byte = byte - 'a' + 10;
! 228: else if (byte >= '0' && byte <= '9')
! 229: byte = byte - '0';
! 230: else
! 231: break;
! 232: n = (n * 16) + byte;
! 233: }
! 234: } else {
! 235: for (i = 2; i < 8; i++) {
! 236: byte = *((unsigned char *) (str + i));
! 237: if (byte >= '0' && byte <= '9') {
! 238: n = (n * 10) + (byte - '0');
! 239: } else {
! 240: break;
! 241: }
! 242: }
! 243: }
! 244: if (byte == ';') {
! 245: *chPtr = (Tcl_UniChar) n;
! 246: return ++i;
! 247: }
! 248: }
! 249: }
! 250:
! 251: /* Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones. */
! 252:
! 253: byte = *((unsigned char *) str);
! 254: #ifdef JISX0208
! 255: if (0xA1 <= byte && byte <= 0xFE) {
! 256: int ku, ten;
! 257:
! 258: ku = (byte & 0x7F) - 0x20;
! 259: ten = (str[1] & 0x7F) - 0x20;
! 260: if ((ku < 1 || ku > 92) || (ten < 1 || ten > 94)) {
! 261: *chPtr = (Tcl_UniChar) byte;
! 262: return 1;
! 263: }
! 264:
! 265: *chPtr = (Tcl_UniChar) UnicodeTbl[ku - 1][ten - 1];
! 266: return 2;
! 267: } else
! 268: #endif /* JISX0208 */
! 269: if (byte < 0xC0) {
! 270: /* Handles properly formed UTF-8 characters between
! 271: * 0x01 and 0x7F. Also treats \0 and naked trail
! 272: * bytes 0x80 to 0xBF as valid characters representing
! 273: * themselves.
! 274: */
! 275:
! 276: *chPtr = (Tcl_UniChar) byte;
! 277: return 1;
! 278: } else if (byte < 0xE0) {
! 279: if ((str[1] & 0xC0) == 0x80) {
! 280: /* Two-byte-character lead-byte followed by a trail-byte. */
! 281:
! 282: *chPtr = (Tcl_UniChar) (((byte & 0x1F) << 6) | (str[1] & 0x3F));
! 283: return 2;
! 284: }
! 285: /*
! 286: * A two-byte-character lead-byte not followed by trail-byte
! 287: * represents itself.
! 288: */
! 289:
! 290: *chPtr = (Tcl_UniChar) byte;
! 291: return 1;
! 292: } else if (byte < 0xF0) {
! 293: if (((str[1] & 0xC0) == 0x80) && ((str[2] & 0xC0) == 0x80)) {
! 294: /* Three-byte-character lead byte followed by two trail bytes. */
! 295:
! 296: *chPtr = (Tcl_UniChar) (((byte & 0x0F) << 12) | ((str[1] & 0x3F) << 6) | (str[2] & 0x3F));
! 297: return 3;
! 298: }
! 299: /* A three-byte-character lead-byte not followed by two trail-bytes represents itself. */
! 300:
! 301: *chPtr = (Tcl_UniChar) byte;
! 302: return 1;
! 303: }
! 304: #if TCL_UTF_MAX > 3
! 305: else {
! 306: int ch, total, trail;
! 307:
! 308: total = totalBytes[byte];
! 309: trail = total - 1;
! 310:
! 311: if (trail > 0) {
! 312: ch = byte & (0x3F >> trail);
! 313: do {
! 314: str++;
! 315: if ((*str & 0xC0) != 0x80) {
! 316: *chPtr = byte;
! 317: return 1;
! 318: }
! 319: ch <<= 6;
! 320: ch |= (*str & 0x3F);
! 321: trail--;
! 322: } while (trail > 0);
! 323: *chPtr = ch;
! 324: return total;
! 325: }
! 326: }
! 327: #endif
! 328:
! 329: *chPtr = (Tcl_UniChar) byte;
! 330: return 1;
! 331: }
! 332:
! 333: /********************************************************************/
! 334: /* font cache functions */
! 335:
! 336: static int fontTest (void *element, void *key)
! 337: {
! 338: font_t *a = (font_t *) element;
! 339: fontkey_t *b = (fontkey_t *) key;
! 340:
! 341: return (strcmp (a->fontlist, b->fontlist) == 0);
! 342: }
! 343:
! 344: static void *fontFetch (char **error, void *key)
! 345: {
! 346: font_t *a;
! 347: fontkey_t *b = (fontkey_t *) key;
! 348: int n;
! 349: int font_found = 0;
! 350: unsigned short platform, encoding;
! 351: char *fontsearchpath, *fontlist;
! 352: char fullname[MAXPATHLEN], cur_dir[MAXPATHLEN];
! 353: char *name, *path=NULL, *dir;
! 354: char *strtok_ptr;
! 355: FT_Error err;
! 356: FT_CharMap found = 0;
! 357: FT_CharMap charmap;
! 358:
! 359: a = (font_t *) gdPMalloc(sizeof(font_t));
! 360: a->fontlist = gdPEstrdup(b->fontlist);
! 361: a->library = b->library;
! 362:
! 363: /*
! 364: * Search the pathlist for any of a list of font names.
! 365: */
! 366: fontsearchpath = getenv ("GDFONTPATH");
! 367: if (!fontsearchpath) {
! 368: fontsearchpath = DEFAULT_FONTPATH;
! 369: }
! 370: fontlist = gdEstrdup(a->fontlist);
! 371:
! 372: /*
! 373: * Must use gd_strtok_r else pointer corrupted by strtok in nested loop.
! 374: */
! 375: for (name = gd_strtok_r (fontlist, LISTSEPARATOR, &strtok_ptr); name; name = gd_strtok_r (0, LISTSEPARATOR, &strtok_ptr)) {
! 376: /* make a fresh copy each time - strtok corrupts it. */
! 377: path = gdEstrdup (fontsearchpath);
! 378:
! 379: /* if name is an absolute filename then test directly */
! 380: #ifdef NETWARE
! 381: if (*name == '/' || (name[0] != 0 && strstr(name, ":/"))) {
! 382: #else
! 383: if (*name == '/' || (name[0] != 0 && name[1] == ':' && (name[2] == '/' || name[2] == '\\'))) {
! 384: #endif
! 385: snprintf(fullname, sizeof(fullname) - 1, "%s", name);
! 386: if (access(fullname, R_OK) == 0) {
! 387: font_found++;
! 388: break;
! 389: }
! 390: }
! 391: for (dir = strtok (path, PATHSEPARATOR); dir; dir = strtok (0, PATHSEPARATOR)) {
! 392: if (!strcmp(dir, ".")) {
! 393: TSRMLS_FETCH();
! 394: #if HAVE_GETCWD
! 395: dir = VCWD_GETCWD(cur_dir, MAXPATHLEN);
! 396: #elif HAVE_GETWD
! 397: dir = VCWD_GETWD(cur_dir);
! 398: #endif
! 399: if (!dir) {
! 400: continue;
! 401: }
! 402: }
! 403:
! 404: #define GD_CHECK_FONT_PATH(ext) \
! 405: snprintf(fullname, sizeof(fullname) - 1, "%s/%s%s", dir, name, ext); \
! 406: if (access(fullname, R_OK) == 0) { \
! 407: font_found++; \
! 408: break; \
! 409: } \
! 410:
! 411: GD_CHECK_FONT_PATH("");
! 412: GD_CHECK_FONT_PATH(".ttf");
! 413: GD_CHECK_FONT_PATH(".pfa");
! 414: GD_CHECK_FONT_PATH(".pfb");
! 415: GD_CHECK_FONT_PATH(".dfont");
! 416: }
! 417: gdFree(path);
! 418: path = NULL;
! 419: if (font_found) {
! 420: break;
! 421: }
! 422: }
! 423:
! 424: if (path) {
! 425: gdFree(path);
! 426: }
! 427:
! 428: gdFree(fontlist);
! 429:
! 430: if (!font_found) {
! 431: gdPFree(a->fontlist);
! 432: gdPFree(a);
! 433: *error = "Could not find/open font";
! 434: return NULL;
! 435: }
! 436:
! 437: err = FT_New_Face (*b->library, fullname, 0, &a->face);
! 438: if (err) {
! 439: gdPFree(a->fontlist);
! 440: gdPFree(a);
! 441: *error = "Could not read font";
! 442: return NULL;
! 443: }
! 444:
! 445: /* FIXME - This mapping stuff is imcomplete - where is the spec? */
! 446: /* EAM - It's worse than that. It's pointless to match character encodings here.
! 447: * As currently written, the stored a->face->charmap only matches one of
! 448: * the actual charmaps and we cannot know at this stage if it is the right
! 449: * one. We should just skip all this stuff, and check in gdImageStringFTEx
! 450: * if some particular charmap is preferred and if so whether it is held in
! 451: * one of the a->face->charmaps[0..num_charmaps].
! 452: * And why is it so bad not to find any recognized charmap? The user may
! 453: * still know what mapping to use, even if we do not. In that case we can
! 454: * just use the map in a->face->charmaps[num_charmaps] and be done with it.
! 455: */
! 456:
! 457: a->have_char_map_unicode = 0;
! 458: a->have_char_map_big5 = 0;
! 459: a->have_char_map_sjis = 0;
! 460: a->have_char_map_apple_roman = 0;
! 461: for (n = 0; n < a->face->num_charmaps; n++) {
! 462: charmap = a->face->charmaps[n];
! 463: platform = charmap->platform_id;
! 464: encoding = charmap->encoding_id;
! 465:
! 466: /* EAM DEBUG - Newer versions of libfree2 make it easier by defining encodings */
! 467: #if (defined(FREETYPE_MAJOR) && ((FREETYPE_MAJOR == 2 && ((FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 3) || FREETYPE_MINOR > 1) || FREETYPE_MAJOR > 2)))
! 468: if (charmap->encoding == FT_ENCODING_MS_SYMBOL
! 469: || charmap->encoding == FT_ENCODING_ADOBE_CUSTOM
! 470: || charmap->encoding == FT_ENCODING_ADOBE_STANDARD) {
! 471: a->have_char_map_unicode = 1;
! 472: found = charmap;
! 473: a->face->charmap = charmap;
! 474: return (void *)a;
! 475: }
! 476: #endif /* Freetype 2.1.3 or better */
! 477: /* EAM DEBUG */
! 478:
! 479: if ((platform == 3 && encoding == 1) /* Windows Unicode */
! 480: || (platform == 3 && encoding == 0) /* Windows Symbol */
! 481: || (platform == 2 && encoding == 1) /* ISO Unicode */
! 482: || (platform == 0))
! 483: { /* Apple Unicode */
! 484: a->have_char_map_unicode = 1;
! 485: found = charmap;
! 486: } else if (platform == 3 && encoding == 4) { /* Windows Big5 */
! 487: a->have_char_map_big5 = 1;
! 488: found = charmap;
! 489: } else if (platform == 3 && encoding == 2) { /* Windows Sjis */
! 490: a->have_char_map_sjis = 1;
! 491: found = charmap;
! 492: } else if ((platform == 1 && encoding == 0) /* Apple Roman */
! 493: || (platform == 2 && encoding == 0))
! 494: { /* ISO ASCII */
! 495: a->have_char_map_apple_roman = 1;
! 496: found = charmap;
! 497: }
! 498: }
! 499: if (!found) {
! 500: gdPFree(a->fontlist);
! 501: gdPFree(a);
! 502: *error = "Unable to find a CharMap that I can handle";
! 503: return NULL;
! 504: }
! 505:
! 506: /* 2.0.5: we should actually return this */
! 507: a->face->charmap = found;
! 508: return (void *) a;
! 509: }
! 510:
! 511: static void fontRelease (void *element)
! 512: {
! 513: font_t *a = (font_t *) element;
! 514:
! 515: FT_Done_Face (a->face);
! 516: gdPFree(a->fontlist);
! 517: gdPFree((char *) element);
! 518: }
! 519:
! 520: /********************************************************************/
! 521: /* tweencolor cache functions */
! 522:
! 523: static int tweenColorTest (void *element, void *key)
! 524: {
! 525: tweencolor_t *a = (tweencolor_t *) element;
! 526: tweencolorkey_t *b = (tweencolorkey_t *) key;
! 527:
! 528: return (a->pixel == b->pixel && a->bgcolor == b->bgcolor && a->fgcolor == b->fgcolor && a->im == b->im);
! 529: }
! 530:
! 531: /*
! 532: * Computes a color in im's color table that is part way between
! 533: * the background and foreground colors proportional to the gray
! 534: * pixel value in the range 0-NUMCOLORS. The fg and bg colors must already
! 535: * be in the color table for palette images. For truecolor images the
! 536: * returned value simply has an alpha component and gdImageAlphaBlend
! 537: * does the work so that text can be alpha blended across a complex
! 538: * background (TBB; and for real in 2.0.2).
! 539: */
! 540: static void * tweenColorFetch (char **error, void *key)
! 541: {
! 542: tweencolor_t *a;
! 543: tweencolorkey_t *b = (tweencolorkey_t *) key;
! 544: int pixel, npixel, bg, fg;
! 545: gdImagePtr im;
! 546:
! 547: a = (tweencolor_t *) gdMalloc (sizeof (tweencolor_t));
! 548: pixel = a->pixel = b->pixel;
! 549: bg = a->bgcolor = b->bgcolor;
! 550: fg = a->fgcolor = b->fgcolor;
! 551: im = a->im = b->im;
! 552:
! 553: /* if fg is specified by a negative color idx, then don't antialias */
! 554: if (fg < 0) {
! 555: if ((pixel + pixel) >= NUMCOLORS) {
! 556: a->tweencolor = -fg;
! 557: } else {
! 558: a->tweencolor = bg;
! 559: }
! 560: } else {
! 561: npixel = NUMCOLORS - pixel;
! 562: if (im->trueColor) {
! 563: /* 2.0.1: use gdImageSetPixel to do the alpha blending work,
! 564: * or to just store the alpha level. All we have to do here
! 565: * is incorporate our knowledge of the percentage of this
! 566: * pixel that is really "lit" by pushing the alpha value
! 567: * up toward transparency in edge regions.
! 568: */
! 569: a->tweencolor = gdTrueColorAlpha(
! 570: gdTrueColorGetRed(fg),
! 571: gdTrueColorGetGreen(fg),
! 572: gdTrueColorGetBlue(fg),
! 573: gdAlphaMax - (gdTrueColorGetAlpha (fg) * pixel / NUMCOLORS));
! 574: } else {
! 575: a->tweencolor = gdImageColorResolve(im,
! 576: (pixel * im->red[fg] + npixel * im->red[bg]) / NUMCOLORS,
! 577: (pixel * im->green[fg] + npixel * im->green[bg]) / NUMCOLORS,
! 578: (pixel * im->blue[fg] + npixel * im->blue[bg]) / NUMCOLORS);
! 579: }
! 580: }
! 581: return (void *) a;
! 582: }
! 583:
! 584: static void tweenColorRelease (void *element)
! 585: {
! 586: gdFree((char *) element);
! 587: }
! 588:
! 589: /* draw_bitmap - transfers glyph bitmap to GD image */
! 590: static char * gdft_draw_bitmap (gdCache_head_t *tc_cache, gdImage * im, int fg, FT_Bitmap bitmap, int pen_x, int pen_y)
! 591: {
! 592: unsigned char *pixel = NULL;
! 593: int *tpixel = NULL;
! 594: int x, y, row, col, pc, pcr;
! 595:
! 596: tweencolor_t *tc_elem;
! 597: tweencolorkey_t tc_key;
! 598:
! 599: /* copy to image, mapping colors */
! 600: tc_key.fgcolor = fg;
! 601: tc_key.im = im;
! 602: /* Truecolor version; does not require the cache */
! 603: if (im->trueColor) {
! 604: for (row = 0; row < bitmap.rows; row++) {
! 605: pc = row * bitmap.pitch;
! 606: pcr = pc;
! 607: y = pen_y + row;
! 608: /* clip if out of bounds */
! 609: /* 2.0.16: clipping rectangle, not image bounds */
! 610: if ((y > im->cy2) || (y < im->cy1)) {
! 611: continue;
! 612: }
! 613: for (col = 0; col < bitmap.width; col++, pc++) {
! 614: int level;
! 615: if (bitmap.pixel_mode == ft_pixel_mode_grays) {
! 616: /* Scale to 128 levels of alpha for gd use.
! 617: * alpha 0 is opacity, so be sure to invert at the end
! 618: */
! 619: level = (bitmap.buffer[pc] * gdAlphaMax / (bitmap.num_grays - 1));
! 620: } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
! 621: /* 2.0.5: mode_mono fix from Giuliano Pochini */
! 622: level = ((bitmap.buffer[(col>>3)+pcr]) & (1<<(~col&0x07))) ? gdAlphaTransparent : gdAlphaOpaque;
! 623: } else {
! 624: return "Unsupported ft_pixel_mode";
! 625: }
! 626: if ((fg >= 0) && (im->trueColor)) {
! 627: /* Consider alpha in the foreground color itself to be an
! 628: * upper bound on how opaque things get, when truecolor is
! 629: * available. Without truecolor this results in far too many
! 630: * color indexes.
! 631: */
! 632: level = level * (gdAlphaMax - gdTrueColorGetAlpha(fg)) / gdAlphaMax;
! 633: }
! 634: level = gdAlphaMax - level;
! 635: x = pen_x + col;
! 636: /* clip if out of bounds */
! 637: /* 2.0.16: clip to clipping rectangle, Matt McNabb */
! 638: if ((x > im->cx2) || (x < im->cx1)) {
! 639: continue;
! 640: }
! 641: /* get pixel location in gd buffer */
! 642: tpixel = &im->tpixels[y][x];
! 643: if (fg < 0) {
! 644: if (level < (gdAlphaMax / 2)) {
! 645: *tpixel = -fg;
! 646: }
! 647: } else {
! 648: if (im->alphaBlendingFlag) {
! 649: *tpixel = gdAlphaBlend(*tpixel, (level << 24) + (fg & 0xFFFFFF));
! 650: } else {
! 651: *tpixel = (level << 24) + (fg & 0xFFFFFF);
! 652: }
! 653: }
! 654: }
! 655: }
! 656: return (char *) NULL;
! 657: }
! 658: /* Non-truecolor case, restored to its more or less original form */
! 659: for (row = 0; row < bitmap.rows; row++) {
! 660: int pcr;
! 661: pc = row * bitmap.pitch;
! 662: pcr = pc;
! 663: if (bitmap.pixel_mode==ft_pixel_mode_mono) {
! 664: pc *= 8; /* pc is measured in bits for monochrome images */
! 665: }
! 666: y = pen_y + row;
! 667:
! 668: /* clip if out of bounds */
! 669: if (y >= im->sy || y < 0) {
! 670: continue;
! 671: }
! 672:
! 673: for (col = 0; col < bitmap.width; col++, pc++) {
! 674: if (bitmap.pixel_mode == ft_pixel_mode_grays) {
! 675: /*
! 676: * Round to NUMCOLORS levels of antialiasing for
! 677: * index color images since only 256 colors are
! 678: * available.
! 679: */
! 680: tc_key.pixel = ((bitmap.buffer[pc] * NUMCOLORS) + bitmap.num_grays / 2) / (bitmap.num_grays - 1);
! 681: } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
! 682: tc_key.pixel = ((bitmap.buffer[pc / 8] << (pc % 8)) & 128) ? NUMCOLORS : 0;
! 683: /* 2.0.5: mode_mono fix from Giuliano Pochini */
! 684: tc_key.pixel = ((bitmap.buffer[(col>>3)+pcr]) & (1<<(~col&0x07))) ? NUMCOLORS : 0;
! 685: } else {
! 686: return "Unsupported ft_pixel_mode";
! 687: }
! 688: if (tc_key.pixel > 0) { /* if not background */
! 689: x = pen_x + col;
! 690:
! 691: /* clip if out of bounds */
! 692: if (x >= im->sx || x < 0) {
! 693: continue;
! 694: }
! 695: /* get pixel location in gd buffer */
! 696: pixel = &im->pixels[y][x];
! 697: if (tc_key.pixel == NUMCOLORS) {
! 698: /* use fg color directly. gd 2.0.2: watch out for
! 699: * negative indexes (thanks to David Marwood).
! 700: */
! 701: *pixel = (fg < 0) ? -fg : fg;
! 702: } else {
! 703: /* find antialised color */
! 704: tc_key.bgcolor = *pixel;
! 705: tc_elem = (tweencolor_t *) gdCacheGet(tc_cache, &tc_key);
! 706: *pixel = tc_elem->tweencolor;
! 707: }
! 708: }
! 709: }
! 710: }
! 711: return (char *) NULL;
! 712: }
! 713:
! 714: static int
! 715: gdroundupdown (FT_F26Dot6 v1, int updown)
! 716: {
! 717: return (!updown) ? (v1 < 0 ? ((v1 - 63) >> 6) : v1 >> 6) : (v1 > 0 ? ((v1 + 63) >> 6) : v1 >> 6);
! 718: }
! 719:
! 720: void gdFontCacheShutdown()
! 721: {
! 722: gdMutexLock(gdFontCacheMutex);
! 723:
! 724: if (fontCache) {
! 725: gdCacheDelete(fontCache);
! 726: fontCache = NULL;
! 727: FT_Done_FreeType(library);
! 728: }
! 729:
! 730: gdMutexUnlock(gdFontCacheMutex);
! 731: }
! 732:
! 733: void gdFreeFontCache()
! 734: {
! 735: gdFontCacheShutdown();
! 736: }
! 737:
! 738: void gdFontCacheMutexSetup()
! 739: {
! 740: gdMutexSetup(gdFontCacheMutex);
! 741: }
! 742:
! 743: void gdFontCacheMutexShutdown()
! 744: {
! 745: gdMutexShutdown(gdFontCacheMutex);
! 746: }
! 747:
! 748: int gdFontCacheSetup(void)
! 749: {
! 750: if (fontCache) {
! 751: /* Already set up */
! 752: return 0;
! 753: }
! 754: if (FT_Init_FreeType(&library)) {
! 755: return -1;
! 756: }
! 757: fontCache = gdCacheCreate (FONTCACHESIZE, fontTest, fontFetch, fontRelease);
! 758: return 0;
! 759: }
! 760:
! 761:
! 762: /********************************************************************/
! 763: /* gdImageStringFT - render a utf8 string onto a gd image */
! 764:
! 765: char *
! 766: gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
! 767: double ptsize, double angle, int x, int y, char *string)
! 768: {
! 769: return gdImageStringFTEx(im, brect, fg, fontlist, ptsize, angle, x, y, string, 0);
! 770: }
! 771:
! 772: char *
! 773: gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsize, double angle, int x, int y, char *string, gdFTStringExtraPtr strex)
! 774: {
! 775: FT_BBox bbox, glyph_bbox;
! 776: FT_Matrix matrix;
! 777: FT_Vector pen, delta, penf;
! 778: FT_Face face;
! 779: FT_Glyph image;
! 780: FT_GlyphSlot slot;
! 781: FT_Bool use_kerning;
! 782: FT_UInt glyph_index, previous;
! 783: double sin_a = sin (angle);
! 784: double cos_a = cos (angle);
! 785: int len, i = 0, ch;
! 786: int x1 = 0, y1 = 0;
! 787: int xb = x, yb = y;
! 788: int yd = 0;
! 789: font_t *font;
! 790: fontkey_t fontkey;
! 791: char *next;
! 792: char *tmpstr = NULL;
! 793: int render = (im && (im->trueColor || (fg <= 255 && fg >= -255)));
! 794: FT_BitmapGlyph bm;
! 795: /* 2.0.13: Bob Ostermann: don't force autohint, that's just for testing freetype and doesn't look as good */
! 796: int render_mode = FT_LOAD_DEFAULT;
! 797: int m, mfound;
! 798: /* Now tuneable thanks to Wez Furlong */
! 799: double linespace = LINESPACE;
! 800: /* 2.0.6: put this declaration with the other declarations! */
! 801: /*
! 802: * make a new tweenColorCache on every call
! 803: * because caching colormappings between calls
! 804: * is not safe. If the im-pointer points to a
! 805: * brand new image, the cache gives out bogus
! 806: * colorindexes. -- 27.06.2001 <krisku@arrak.fi>
! 807: */
! 808: gdCache_head_t *tc_cache;
! 809: /* Tuneable horizontal and vertical resolution in dots per inch */
! 810: int hdpi, vdpi;
! 811:
! 812: if (strex && ((strex->flags & gdFTEX_LINESPACE) == gdFTEX_LINESPACE)) {
! 813: linespace = strex->linespacing;
! 814: }
! 815: tc_cache = gdCacheCreate(TWEENCOLORCACHESIZE, tweenColorTest, tweenColorFetch, tweenColorRelease);
! 816:
! 817: /***** initialize font library and font cache on first call ******/
! 818:
! 819: gdMutexLock(gdFontCacheMutex);
! 820: if (!fontCache) {
! 821: if (gdFontCacheSetup() != 0) {
! 822: gdCacheDelete(tc_cache);
! 823: gdMutexUnlock(gdFontCacheMutex);
! 824: return "Failure to initialize font library";
! 825: }
! 826: }
! 827: /*****/
! 828:
! 829: /* get the font (via font cache) */
! 830: fontkey.fontlist = fontlist;
! 831: fontkey.library = &library;
! 832: font = (font_t *) gdCacheGet (fontCache, &fontkey);
! 833: if (!font) {
! 834: gdCacheDelete(tc_cache);
! 835: gdMutexUnlock(gdFontCacheMutex);
! 836: return fontCache->error;
! 837: }
! 838: face = font->face; /* shortcut */
! 839: slot = face->glyph; /* shortcut */
! 840:
! 841: /*
! 842: * Added hdpi and vdpi to support images at non-screen resolutions, i.e. 300 dpi TIFF,
! 843: * or 100h x 50v dpi FAX format. 2.0.23.
! 844: * 2004/02/27 Mark Shackelford, mark.shackelford@acs-inc.com
! 845: */
! 846: hdpi = GD_RESOLUTION;
! 847: vdpi = GD_RESOLUTION;
! 848: if (strex && (strex->flags & gdFTEX_RESOLUTION)) {
! 849: hdpi = strex->hdpi;
! 850: vdpi = strex->vdpi;
! 851: }
! 852:
! 853: if (FT_Set_Char_Size(face, 0, (FT_F26Dot6) (ptsize * 64), hdpi, vdpi)) {
! 854: gdCacheDelete(tc_cache);
! 855: gdMutexUnlock(gdFontCacheMutex);
! 856: return "Could not set character size";
! 857: }
! 858:
! 859: matrix.xx = (FT_Fixed) (cos_a * (1 << 16));
! 860: matrix.yx = (FT_Fixed) (sin_a * (1 << 16));
! 861: matrix.xy = -matrix.yx;
! 862: matrix.yy = matrix.xx;
! 863:
! 864: penf.x = penf.y = 0; /* running position of non-rotated string */
! 865: pen.x = pen.y = 0; /* running position of rotated string */
! 866: bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0;
! 867:
! 868: use_kerning = FT_HAS_KERNING (face);
! 869: previous = 0;
! 870: if (fg < 0) {
! 871: render_mode |= FT_LOAD_MONOCHROME;
! 872: }
! 873: /* 2.0.12: allow explicit specification of the preferred map;
! 874: * but we still fall back if it is not available.
! 875: */
! 876: m = gdFTEX_Unicode;
! 877: if (strex && (strex->flags & gdFTEX_CHARMAP)) {
! 878: m = strex->charmap;
! 879: }
! 880: /* Try all three types of maps, but start with the specified one */
! 881: mfound = 0;
! 882: for (i = 0; i < 3; i++) {
! 883: switch (m) {
! 884: case gdFTEX_Unicode:
! 885: if (font->have_char_map_unicode) {
! 886: mfound = 1;
! 887: }
! 888: break;
! 889: case gdFTEX_Shift_JIS:
! 890: if (font->have_char_map_sjis) {
! 891: mfound = 1;
! 892: }
! 893: break;
! 894: case gdFTEX_Big5:
! 895: /* This was the 'else' case, we can't really 'detect' it */
! 896: mfound = 1;
! 897: break;
! 898: }
! 899: if (mfound) {
! 900: break;
! 901: }
! 902: m++;
! 903: m %= 3;
! 904: }
! 905: if (!mfound) {
! 906: /* No character set found! */
! 907: gdMutexUnlock(gdFontCacheMutex);
! 908: return "No character set found";
! 909: }
! 910:
! 911: #ifndef JISX0208
! 912: if (font->have_char_map_sjis) {
! 913: #endif
! 914: tmpstr = (char *) gdMalloc(BUFSIZ);
! 915: any2eucjp(tmpstr, string, BUFSIZ);
! 916: next = tmpstr;
! 917: #ifndef JISX0208
! 918: } else {
! 919: next = string;
! 920: }
! 921: #endif
! 922:
! 923: i = 0;
! 924: while (*next) {
! 925: ch = *next;
! 926:
! 927: /* carriage returns */
! 928: if (ch == '\r') {
! 929: penf.x = 0;
! 930: x1 = (int)(- penf.y * sin_a + 32) / 64;
! 931: y1 = (int)(- penf.y * cos_a + 32) / 64;
! 932: pen.x = pen.y = 0;
! 933: previous = 0; /* clear kerning flag */
! 934: next++;
! 935: continue;
! 936: }
! 937: /* newlines */
! 938: if (ch == '\n') {
! 939: if (!*(++next)) break;
! 940: /* 2.0.13: reset penf.x. Christopher J. Grayce */
! 941: penf.x = 0;
! 942: penf.y -= (long)(face->size->metrics.height * linespace);
! 943: penf.y = (penf.y - 32) & -64; /* round to next pixel row */
! 944: x1 = (int)(- penf.y * sin_a + 32) / 64;
! 945: y1 = (int)(- penf.y * cos_a + 32) / 64;
! 946: xb = x + x1;
! 947: yb = y + y1;
! 948: yd = 0;
! 949: pen.x = pen.y = 0;
! 950: previous = 0; /* clear kerning flag */
! 951: continue;
! 952: }
! 953:
! 954: /* EAM DEBUG */
! 955: #if (defined(FREETYPE_MAJOR) && ((FREETYPE_MAJOR == 2 && ((FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 3) || FREETYPE_MINOR > 1) || FREETYPE_MAJOR > 2)))
! 956: if (font->face->family_name && font->face->charmap->encoding &&
! 957: font->face->charmap->encoding == FT_ENCODING_MS_SYMBOL && strcmp(font->face->family_name, "Symbol") == 0) {
! 958: /* I do not know the significance of the constant 0xf000.
! 959: * It was determined by inspection of the character codes
! 960: * stored in Microsoft font symbol.
! 961: * Added by Pierre (pajoye@php.net):
! 962: * Convert to the Symbol glyph range only for a Symbol family member
! 963: */
! 964: len = gdTcl_UtfToUniChar (next, &ch);
! 965: ch |= 0xf000;
! 966: next += len;
! 967: } else
! 968: #endif /* Freetype 2.1 or better */
! 969: /* EAM DEBUG */
! 970:
! 971: switch (m) {
! 972: case gdFTEX_Unicode:
! 973: if (font->have_char_map_unicode) {
! 974: /* use UTF-8 mapping from ASCII */
! 975: len = gdTcl_UtfToUniChar(next, &ch);
! 976: next += len;
! 977: }
! 978: break;
! 979: case gdFTEX_Shift_JIS:
! 980: if (font->have_char_map_sjis) {
! 981: unsigned char c;
! 982: int jiscode;
! 983: c = *next;
! 984: if (0xA1 <= c && c <= 0xFE) {
! 985: next++;
! 986: jiscode = 0x100 * (c & 0x7F) + ((*next) & 0x7F);
! 987:
! 988: ch = (jiscode >> 8) & 0xFF;
! 989: jiscode &= 0xFF;
! 990:
! 991: if (ch & 1) {
! 992: jiscode += 0x40 - 0x21;
! 993: } else {
! 994: jiscode += 0x9E - 0x21;
! 995: }
! 996:
! 997: if (jiscode >= 0x7F) {
! 998: jiscode++;
! 999: }
! 1000: ch = (ch - 0x21) / 2 + 0x81;
! 1001: if (ch >= 0xA0) {
! 1002: ch += 0x40;
! 1003: }
! 1004:
! 1005: ch = (ch << 8) + jiscode;
! 1006: } else {
! 1007: ch = c & 0xFF; /* don't extend sign */
! 1008: }
! 1009: if (*next) next++;
! 1010: }
! 1011: break;
! 1012: case gdFTEX_Big5: {
! 1013: /*
! 1014: * Big 5 mapping:
! 1015: * use "JIS-8 half-width katakana" coding from 8-bit characters. Ref:
! 1016: * ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs
! 1017: */
! 1018: ch = (*next) & 0xFF; /* don't extend sign */
! 1019: next++;
! 1020: if (ch >= 161 /* first code of JIS-8 pair */
! 1021: && *next) { /* don't advance past '\0' */
! 1022: /* TBB: Fix from Kwok Wah On: & 255 needed */
! 1023: ch = (ch * 256) + ((*next) & 255);
! 1024: next++;
! 1025: }
! 1026: }
! 1027: break;
! 1028: }
! 1029:
! 1030: /* set rotation transform */
! 1031: FT_Set_Transform(face, &matrix, NULL);
! 1032: /* Convert character code to glyph index */
! 1033: glyph_index = FT_Get_Char_Index(face, ch);
! 1034:
! 1035: /* retrieve kerning distance and move pen position */
! 1036: if (use_kerning && previous && glyph_index) {
! 1037: FT_Get_Kerning(face, previous, glyph_index, ft_kerning_default, &delta);
! 1038: pen.x += delta.x;
! 1039: penf.x += delta.x;
! 1040: }
! 1041:
! 1042: /* load glyph image into the slot (erase previous one) */
! 1043: if (FT_Load_Glyph(face, glyph_index, render_mode)) {
! 1044: if (tmpstr) {
! 1045: gdFree(tmpstr);
! 1046: }
! 1047: gdCacheDelete(tc_cache);
! 1048: gdMutexUnlock(gdFontCacheMutex);
! 1049: return "Problem loading glyph";
! 1050: }
! 1051:
! 1052: /* transform glyph image */
! 1053: FT_Get_Glyph(slot, &image);
! 1054: if (brect) { /* only if need brect */
! 1055: FT_Glyph_Get_CBox(image, ft_glyph_bbox_gridfit, &glyph_bbox);
! 1056: glyph_bbox.xMin += penf.x;
! 1057: glyph_bbox.yMin += penf.y;
! 1058: glyph_bbox.xMax += penf.x;
! 1059: glyph_bbox.yMax += penf.y;
! 1060: if (ch == ' ') { /* special case for trailing space */
! 1061: glyph_bbox.xMax += slot->metrics.horiAdvance;
! 1062: }
! 1063: if (!i) { /* if first character, init BB corner values */
! 1064: yd = slot->metrics.height - slot->metrics.horiBearingY;
! 1065: bbox.xMin = glyph_bbox.xMin;
! 1066: bbox.yMin = glyph_bbox.yMin;
! 1067: bbox.xMax = glyph_bbox.xMax;
! 1068: bbox.yMax = glyph_bbox.yMax;
! 1069: } else {
! 1070: FT_Pos desc;
! 1071:
! 1072: if ( (desc = (slot->metrics.height - slot->metrics.horiBearingY)) > yd) {
! 1073: yd = desc;
! 1074: }
! 1075: if (bbox.xMin > glyph_bbox.xMin) {
! 1076: bbox.xMin = glyph_bbox.xMin;
! 1077: }
! 1078: if (bbox.yMin > glyph_bbox.yMin) {
! 1079: bbox.yMin = glyph_bbox.yMin;
! 1080: }
! 1081: if (bbox.xMax < glyph_bbox.xMax) {
! 1082: bbox.xMax = glyph_bbox.xMax;
! 1083: }
! 1084: if (bbox.yMax < glyph_bbox.yMax) {
! 1085: bbox.yMax = glyph_bbox.yMax;
! 1086: }
! 1087: }
! 1088: i++;
! 1089: }
! 1090:
! 1091: if (render) {
! 1092: if (image->format != ft_glyph_format_bitmap && FT_Glyph_To_Bitmap(&image, ft_render_mode_normal, 0, 1)) {
! 1093: FT_Done_Glyph(image);
! 1094: if (tmpstr) {
! 1095: gdFree(tmpstr);
! 1096: }
! 1097: gdCacheDelete(tc_cache);
! 1098: gdMutexUnlock(gdFontCacheMutex);
! 1099: return "Problem rendering glyph";
! 1100: }
! 1101:
! 1102: /* now, draw to our target surface */
! 1103: bm = (FT_BitmapGlyph) image;
! 1104: gdft_draw_bitmap(tc_cache, im, fg, bm->bitmap, x + x1 + ((pen.x + 31) >> 6) + bm->left, y + y1 + ((pen.y + 31) >> 6) - bm->top);
! 1105: }
! 1106:
! 1107: /* record current glyph index for kerning */
! 1108: previous = glyph_index;
! 1109:
! 1110: /* increment pen position */
! 1111: pen.x += image->advance.x >> 10;
! 1112: pen.y -= image->advance.y >> 10;
! 1113:
! 1114: penf.x += slot->metrics.horiAdvance;
! 1115:
! 1116: FT_Done_Glyph(image);
! 1117: }
! 1118:
! 1119: if (brect) { /* only if need brect */
! 1120: /* For perfect rounding, must get sin(a + pi/4) and sin(a - pi/4). */
! 1121: double d1 = sin (angle + 0.78539816339744830962);
! 1122: double d2 = sin (angle - 0.78539816339744830962);
! 1123:
! 1124: /* make the center of rotation at (0, 0) */
! 1125: FT_BBox normbox;
! 1126:
! 1127: normbox.xMin = 0;
! 1128: normbox.yMin = 0;
! 1129: normbox.xMax = bbox.xMax - bbox.xMin;
! 1130: normbox.yMax = bbox.yMax - bbox.yMin;
! 1131:
! 1132: brect[0] = brect[2] = brect[4] = brect[6] = (int) (yd * sin_a);
! 1133: brect[1] = brect[3] = brect[5] = brect[7] = (int)(- yd * cos_a);
! 1134:
! 1135: /* rotate bounding rectangle */
! 1136: brect[0] += (int) (normbox.xMin * cos_a - normbox.yMin * sin_a);
! 1137: brect[1] += (int) (normbox.xMin * sin_a + normbox.yMin * cos_a);
! 1138: brect[2] += (int) (normbox.xMax * cos_a - normbox.yMin * sin_a);
! 1139: brect[3] += (int) (normbox.xMax * sin_a + normbox.yMin * cos_a);
! 1140: brect[4] += (int) (normbox.xMax * cos_a - normbox.yMax * sin_a);
! 1141: brect[5] += (int) (normbox.xMax * sin_a + normbox.yMax * cos_a);
! 1142: brect[6] += (int) (normbox.xMin * cos_a - normbox.yMax * sin_a);
! 1143: brect[7] += (int) (normbox.xMin * sin_a + normbox.yMax * cos_a);
! 1144:
! 1145: /* scale, round and offset brect */
! 1146: brect[0] = xb + gdroundupdown(brect[0], d2 > 0);
! 1147: brect[1] = yb - gdroundupdown(brect[1], d1 < 0);
! 1148: brect[2] = xb + gdroundupdown(brect[2], d1 > 0);
! 1149: brect[3] = yb - gdroundupdown(brect[3], d2 > 0);
! 1150: brect[4] = xb + gdroundupdown(brect[4], d2 < 0);
! 1151: brect[5] = yb - gdroundupdown(brect[5], d1 > 0);
! 1152: brect[6] = xb + gdroundupdown(brect[6], d1 < 0);
! 1153: brect[7] = yb - gdroundupdown(brect[7], d2 < 0);
! 1154: }
! 1155:
! 1156: if (tmpstr) {
! 1157: gdFree(tmpstr);
! 1158: }
! 1159: gdCacheDelete(tc_cache);
! 1160: gdMutexUnlock(gdFontCacheMutex);
! 1161: return (char *) NULL;
! 1162: }
! 1163:
! 1164: #endif /* HAVE_LIBFREETYPE */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>