root/ext/gd/libgd/gdft.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gdImageStringTTF
  2. gdImageStringFTEx
  3. gdImageStringFT
  4. gdTcl_UtfToUniChar
  5. fontTest
  6. fontFetch
  7. fontRelease
  8. tweenColorTest
  9. tweenColorFetch
  10. tweenColorRelease
  11. gdft_draw_bitmap
  12. gdroundupdown
  13. gdFontCacheShutdown
  14. gdFreeFontCache
  15. gdFontCacheMutexSetup
  16. gdFontCacheMutexShutdown
  17. gdFontCacheSetup
  18. gdImageStringFT
  19. gdImageStringFTEx

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

/* [<][>][^][v][top][bottom][index][help] */