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                         #if HAVE_GETCWD
 416                                 dir = VCWD_GETCWD(cur_dir, MAXPATHLEN);
 417 #elif HAVE_GETWD
 418                                 dir = VCWD_GETWD(cur_dir);
 419 #endif
 420                                 if (!dir) {
 421                                         continue;
 422                                 }
 423                         }
 424 
 425 #define GD_CHECK_FONT_PATH(ext) \
 426         snprintf(fullname, sizeof(fullname) - 1, "%s/%s%s", dir, name, ext);    \
 427         if (access(fullname, R_OK) == 0) {      \
 428                 font_found++;   \
 429                 break;  \
 430         }       \
 431 
 432                         GD_CHECK_FONT_PATH("");
 433                         GD_CHECK_FONT_PATH(".ttf");
 434                         GD_CHECK_FONT_PATH(".pfa");
 435                         GD_CHECK_FONT_PATH(".pfb");
 436                         GD_CHECK_FONT_PATH(".dfont");
 437                 }
 438                 gdFree(path);
 439                 path = NULL;
 440                 if (font_found) {
 441                         break;
 442                 }
 443         }
 444 
 445         if (path) {
 446                 gdFree(path);
 447         }
 448 
 449         gdFree(fontlist);
 450 
 451         if (!font_found) {
 452                 gdPFree(a->fontlist);
 453                 gdPFree(a);
 454                 *error = "Could not find/open font";
 455                 return NULL;
 456         }
 457 
 458         err = FT_New_Face (*b->library, fullname, 0, &a->face);
 459         if (err) {
 460                 gdPFree(a->fontlist);
 461                 gdPFree(a);
 462                 *error = "Could not read font";
 463                 return NULL;
 464         }
 465 
 466         /* FIXME - This mapping stuff is incomplete - where is the spec? */
 467         /* EAM   - It's worse than that. It's pointless to match character encodings here.
 468          *         As currently written, the stored a->face->charmap only matches one of
 469          *         the actual charmaps and we cannot know at this stage if it is the right
 470          *         one. We should just skip all this stuff, and check in gdImageStringFTEx
 471          *         if some particular charmap is preferred and if so whether it is held in
 472          *         one of the a->face->charmaps[0..num_charmaps].
 473          *         And why is it so bad not to find any recognized charmap?  The user may
 474          *         still know what mapping to use, even if we do not.  In that case we can
 475          *         just use the map in a->face->charmaps[num_charmaps] and be done with it.
 476          */
 477 
 478         for (n = 0; n < a->face->num_charmaps; n++) {
 479                 charmap = a->face->charmaps[n];
 480                 platform = charmap->platform_id;
 481                 encoding = charmap->encoding_id;
 482 
 483                 /* Whatever is the last value is what should be set */
 484                 a->have_char_map_unicode = 0;
 485                 a->have_char_map_big5 = 0;
 486                 a->have_char_map_sjis = 0;
 487                 a->have_char_map_apple_roman = 0;
 488 
 489 /* EAM DEBUG - Newer versions of libfree2 make it easier by defining encodings */
 490 #if (defined(FREETYPE_MAJOR) && ((FREETYPE_MAJOR == 2 && ((FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 3) || FREETYPE_MINOR > 1) || FREETYPE_MAJOR > 2)))
 491         if (charmap->encoding == FT_ENCODING_MS_SYMBOL
 492                 || charmap->encoding == FT_ENCODING_ADOBE_CUSTOM
 493                 || charmap->encoding == FT_ENCODING_ADOBE_STANDARD) {
 494                 a->have_char_map_unicode = 1;
 495                 found = charmap;
 496                 a->face->charmap = charmap;
 497                 return (void *)a;
 498         }
 499 #endif /* Freetype 2.1.3 or better */
 500 /* EAM DEBUG */
 501 
 502                 if ((platform == 3 && encoding == 1)            /* Windows Unicode */
 503                         || (platform == 3 && encoding == 0)     /* Windows Symbol */
 504                         || (platform == 2 && encoding == 1)     /* ISO Unicode */
 505                         || (platform == 0))
 506                 {                                               /* Apple Unicode */
 507                         a->have_char_map_unicode = 1;
 508                         found = charmap;
 509                         if (b->preferred_map == gdFTEX_Unicode) {
 510                                 break;
 511                         }
 512                 } else if (platform == 3 && encoding == 4) {    /* Windows Big5 */
 513                         a->have_char_map_big5 = 1;
 514                         found = charmap;
 515                         if (b->preferred_map == gdFTEX_Big5) {
 516                                 break;
 517                         }
 518                 } else if (platform == 3 && encoding == 2) {    /* Windows Sjis */
 519                         a->have_char_map_sjis = 1;
 520                         found = charmap;
 521                         if (b->preferred_map == gdFTEX_Shift_JIS) {
 522                                 break;
 523                         }
 524                 } else if ((platform == 1 && encoding == 0)     /* Apple Roman */
 525                         || (platform == 2 && encoding == 0))
 526                 {                                               /* ISO ASCII */
 527                         a->have_char_map_apple_roman = 1;
 528                         found = charmap;
 529                         if (b->preferred_map == gdFTEX_MacRoman) {
 530                                 break;
 531                         }
 532                 }
 533         }
 534         if (!found) {
 535                 gdPFree(a->fontlist);
 536                 gdPFree(a);
 537                 *error = "Unable to find a CharMap that I can handle";
 538                 return NULL;
 539         }
 540 
 541         /* 2.0.5: we should actually return this */
 542         a->face->charmap = found;
 543         return (void *) a;
 544 }
 545 
 546 static void fontRelease (void *element)
 547 {
 548         font_t *a = (font_t *) element;
 549 
 550         FT_Done_Face (a->face);
 551         gdPFree(a->fontlist);
 552         gdPFree((char *) element);
 553 }
 554 
 555 /********************************************************************/
 556 /* tweencolor cache functions                                            */
 557 
 558 static int tweenColorTest (void *element, void *key)
 559 {
 560         tweencolor_t *a = (tweencolor_t *) element;
 561         tweencolorkey_t *b = (tweencolorkey_t *) key;
 562 
 563         return (a->pixel == b->pixel && a->bgcolor == b->bgcolor && a->fgcolor == b->fgcolor && a->im == b->im);
 564 }
 565 
 566 /*
 567  * Computes a color in im's color table that is part way between
 568  * the background and foreground colors proportional to the gray
 569  * pixel value in the range 0-NUMCOLORS. The fg and bg colors must already
 570  * be in the color table for palette images. For truecolor images the
 571  * returned value simply has an alpha component and gdImageAlphaBlend
 572  * does the work so that text can be alpha blended across a complex
 573  * background (TBB; and for real in 2.0.2).
 574  */
 575 static void * tweenColorFetch (char **error, void *key)
 576 {
 577         tweencolor_t *a;
 578         tweencolorkey_t *b = (tweencolorkey_t *) key;
 579         int pixel, npixel, bg, fg;
 580         gdImagePtr im;
 581 
 582         a = (tweencolor_t *) gdMalloc (sizeof (tweencolor_t));
 583         pixel = a->pixel = b->pixel;
 584         bg = a->bgcolor = b->bgcolor;
 585         fg = a->fgcolor = b->fgcolor;
 586         im = a->im = b->im;
 587 
 588         /* if fg is specified by a negative color idx, then don't antialias */
 589         if (fg < 0) {
 590                 if ((pixel + pixel) >= NUMCOLORS) {
 591                         a->tweencolor = -fg;
 592                 } else {
 593                         a->tweencolor = bg;
 594                 }
 595         } else {
 596                 npixel = NUMCOLORS - pixel;
 597                 if (im->trueColor) {
 598                         /* 2.0.1: use gdImageSetPixel to do the alpha blending work,
 599                          * or to just store the alpha level. All we have to do here
 600                          * is incorporate our knowledge of the percentage of this
 601                          * pixel that is really "lit" by pushing the alpha value
 602                          * up toward transparency in edge regions.
 603                          */
 604                         a->tweencolor = gdTrueColorAlpha(
 605                                                 gdTrueColorGetRed(fg),
 606                                                 gdTrueColorGetGreen(fg),
 607                                                 gdTrueColorGetBlue(fg),
 608                                                 gdAlphaMax - (gdTrueColorGetAlpha (fg) * pixel / NUMCOLORS));
 609                 } else {
 610                         a->tweencolor = gdImageColorResolve(im,
 611                                                 (pixel * im->red[fg] + npixel * im->red[bg]) / NUMCOLORS,
 612                                                 (pixel * im->green[fg] + npixel * im->green[bg]) / NUMCOLORS,
 613                                                 (pixel * im->blue[fg] + npixel * im->blue[bg]) / NUMCOLORS);
 614                 }
 615         }
 616         return (void *) a;
 617 }
 618 
 619 static void tweenColorRelease (void *element)
 620 {
 621         gdFree((char *) element);
 622 }
 623 
 624 /* draw_bitmap - transfers glyph bitmap to GD image */
 625 static char * gdft_draw_bitmap (gdCache_head_t *tc_cache, gdImage * im, int fg, FT_Bitmap bitmap, int pen_x, int pen_y)
 626 {
 627         unsigned char *pixel = NULL;
 628         int *tpixel = NULL;
 629         int x, y, row, col, pc, pcr;
 630 
 631         tweencolor_t *tc_elem;
 632         tweencolorkey_t tc_key;
 633 
 634         /* copy to image, mapping colors */
 635         tc_key.fgcolor = fg;
 636         tc_key.im = im;
 637         /* Truecolor version; does not require the cache */
 638         if (im->trueColor) {
 639                 for (row = 0; row < bitmap.rows; row++) {
 640                         pc = row * bitmap.pitch;
 641                         pcr = pc;
 642                         y = pen_y + row;
 643                         /* clip if out of bounds */
 644                         /* 2.0.16: clipping rectangle, not image bounds */
 645                         if ((y > im->cy2) || (y < im->cy1)) {
 646                                 continue;
 647                         }
 648                         for (col = 0; col < bitmap.width; col++, pc++) {
 649                                 int level;
 650                                 if (bitmap.pixel_mode == ft_pixel_mode_grays) {
 651                                         /* Scale to 128 levels of alpha for gd use.
 652                                          * alpha 0 is opacity, so be sure to invert at the end
 653                                          */
 654                                         level = (bitmap.buffer[pc] * gdAlphaMax / (bitmap.num_grays - 1));
 655                                 } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
 656                                         /* 2.0.5: mode_mono fix from Giuliano Pochini */
 657                                         level = ((bitmap.buffer[(col>>3)+pcr]) & (1<<(~col&0x07))) ? gdAlphaTransparent : gdAlphaOpaque;
 658                                 } else {
 659                                         return "Unsupported ft_pixel_mode";
 660                                 }
 661                                 if ((fg >= 0) && (im->trueColor)) {
 662                                         /* Consider alpha in the foreground color itself to be an
 663                                          * upper bound on how opaque things get, when truecolor is
 664                                          * available. Without truecolor this results in far too many
 665                                          * color indexes.
 666                                          */
 667                                         level = level * (gdAlphaMax - gdTrueColorGetAlpha(fg)) / gdAlphaMax;
 668                                 }
 669                                 level = gdAlphaMax - level;
 670                                 x = pen_x + col;
 671                                 /* clip if out of bounds */
 672                                 /* 2.0.16: clip to clipping rectangle, Matt McNabb */
 673                                 if ((x > im->cx2) || (x < im->cx1)) {
 674                                         continue;
 675                                 }
 676                                 /* get pixel location in gd buffer */
 677                                 tpixel = &im->tpixels[y][x];
 678                                 if (fg < 0) {
 679                                         if (level < (gdAlphaMax / 2)) {
 680                                                 *tpixel = -fg;
 681                                         }
 682                                 } else {
 683                                         if (im->alphaBlendingFlag) {
 684                                                 *tpixel = gdAlphaBlend(*tpixel, (level << 24) + (fg & 0xFFFFFF));
 685                                         } else {
 686                                                 *tpixel = (level << 24) + (fg & 0xFFFFFF);
 687                                         }
 688                                 }
 689                         }
 690                 }
 691                 return (char *) NULL;
 692         }
 693         /* Non-truecolor case, restored to its more or less original form */
 694         for (row = 0; row < bitmap.rows; row++) {
 695                 int pcr;
 696                 pc = row * bitmap.pitch;
 697                 pcr = pc;
 698                 if (bitmap.pixel_mode==ft_pixel_mode_mono) {
 699                         pc *= 8;    /* pc is measured in bits for monochrome images */
 700                 }
 701                 y = pen_y + row;
 702 
 703                 /* clip if out of bounds */
 704                 if (y >= im->sy || y < 0) {
 705                         continue;
 706                 }
 707 
 708                 for (col = 0; col < bitmap.width; col++, pc++) {
 709                         if (bitmap.pixel_mode == ft_pixel_mode_grays) {
 710                                 /*
 711                                  * Round to NUMCOLORS levels of antialiasing for
 712                                  * index color images since only 256 colors are
 713                                  * available.
 714                                  */
 715                                 tc_key.pixel = ((bitmap.buffer[pc] * NUMCOLORS) + bitmap.num_grays / 2) / (bitmap.num_grays - 1);
 716                         } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
 717                                 tc_key.pixel = ((bitmap.buffer[pc / 8] << (pc % 8)) & 128) ? NUMCOLORS : 0;
 718                                 /* 2.0.5: mode_mono fix from Giuliano Pochini */
 719                                 tc_key.pixel = ((bitmap.buffer[(col>>3)+pcr]) & (1<<(~col&0x07))) ? NUMCOLORS : 0;
 720                         } else {
 721                                 return "Unsupported ft_pixel_mode";
 722                         }
 723                         if (tc_key.pixel > 0) { /* if not background */
 724                                 x = pen_x + col;
 725 
 726                                 /* clip if out of bounds */
 727                                 if (x >= im->sx || x < 0) {
 728                                         continue;
 729                                 }
 730                                 /* get pixel location in gd buffer */
 731                                 pixel = &im->pixels[y][x];
 732                                 if (tc_key.pixel == NUMCOLORS) {
 733                                         /* use fg color directly. gd 2.0.2: watch out for
 734                                          * negative indexes (thanks to David Marwood).
 735                                          */
 736                                         *pixel = (fg < 0) ? -fg : fg;
 737                                 } else {
 738                                         /* find antialised color */
 739                                         tc_key.bgcolor = *pixel;
 740                                         tc_elem = (tweencolor_t *) gdCacheGet(tc_cache, &tc_key);
 741                                         *pixel = tc_elem->tweencolor;
 742                                 }
 743                         }
 744                 }
 745         }
 746         return (char *) NULL;
 747 }
 748 
 749 static int
 750 gdroundupdown (FT_F26Dot6 v1, int updown)
 751 {
 752         return (!updown) ? (v1 < 0 ? ((v1 - 63) >> 6) : v1 >> 6) : (v1 > 0 ? ((v1 + 63) >> 6) : v1 >> 6);
 753 }
 754 
 755 void gdFontCacheShutdown()
 756 {
 757         gdMutexLock(gdFontCacheMutex);
 758 
 759         if (fontCache) {
 760                 gdCacheDelete(fontCache);
 761                 fontCache = NULL;
 762                 FT_Done_FreeType(library);
 763         }
 764 
 765         gdMutexUnlock(gdFontCacheMutex);
 766 }
 767 
 768 void gdFreeFontCache()
 769 {
 770         gdFontCacheShutdown();
 771 }
 772 
 773 void gdFontCacheMutexSetup()
 774 {
 775         gdMutexSetup(gdFontCacheMutex);
 776 }
 777 
 778 void gdFontCacheMutexShutdown()
 779 {
 780         gdMutexShutdown(gdFontCacheMutex);
 781 }
 782 
 783 int gdFontCacheSetup(void)
 784 {
 785         if (fontCache) {
 786                 /* Already set up */
 787                 return 0;
 788         }
 789         if (FT_Init_FreeType(&library)) {
 790                 return -1;
 791         }
 792         fontCache = gdCacheCreate (FONTCACHESIZE, fontTest, fontFetch, fontRelease);
 793         return 0;
 794 }
 795 
 796 
 797 /********************************************************************/
 798 /* gdImageStringFT -  render a utf8 string onto a gd image          */
 799 
 800 char *
 801 gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
 802                  double ptsize, double angle, int x, int y, char *string)
 803 {
 804         return gdImageStringFTEx(im, brect, fg, fontlist, ptsize, angle, x, y, string, 0);
 805 }
 806 
 807 char *
 808 gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsize, double angle, int x, int y, char *string, gdFTStringExtraPtr strex)
 809 {
 810         FT_BBox bbox, glyph_bbox;
 811         FT_Matrix matrix;
 812         FT_Vector pen, delta, penf;
 813         FT_Face face;
 814         FT_Glyph image;
 815         FT_GlyphSlot slot;
 816         FT_Bool use_kerning;
 817         FT_UInt glyph_index, previous;
 818         double sin_a = sin (angle);
 819         double cos_a = cos (angle);
 820         int len, i = 0, ch;
 821         int x1 = 0, y1 = 0;
 822         int xb = x, yb = y;
 823         int yd = 0;
 824         font_t *font;
 825         fontkey_t fontkey;
 826         char *next;
 827         char *tmpstr = NULL;
 828         int render = (im && (im->trueColor || (fg <= 255 && fg >= -255)));
 829         FT_BitmapGlyph bm;
 830         /* 2.0.13: Bob Ostermann: don't force autohint, that's just for testing freetype and doesn't look as good */
 831         int render_mode = FT_LOAD_DEFAULT;
 832         int m, mfound;
 833         /* Now tuneable thanks to Wez Furlong */
 834         double linespace = LINESPACE;
 835         /* 2.0.6: put this declaration with the other declarations! */
 836         /*
 837         *   make a new tweenColorCache on every call
 838         *   because caching colormappings between calls
 839         *   is not safe. If the im-pointer points to a
 840         *   brand new image, the cache gives out bogus
 841         *   colorindexes.          -- 27.06.2001 <krisku@arrak.fi>
 842         */
 843         gdCache_head_t  *tc_cache;
 844         /* Tuneable horizontal and vertical resolution in dots per inch */
 845         int hdpi, vdpi;
 846 
 847         if (strex && ((strex->flags & gdFTEX_LINESPACE) == gdFTEX_LINESPACE)) {
 848                 linespace = strex->linespacing;
 849         }
 850         tc_cache = gdCacheCreate(TWEENCOLORCACHESIZE, tweenColorTest, tweenColorFetch, tweenColorRelease);
 851 
 852         /***** initialize font library and font cache on first call ******/
 853 
 854         gdMutexLock(gdFontCacheMutex);
 855         if (!fontCache) {
 856                 if (gdFontCacheSetup() != 0) {
 857                         gdCacheDelete(tc_cache);
 858                         gdMutexUnlock(gdFontCacheMutex);
 859                         return "Failure to initialize font library";
 860                 }
 861         }
 862         /*****/
 863 
 864         /* 2.0.12: allow explicit specification of the preferred map;
 865          * but we still fall back if it is not available.
 866          */
 867         m = gdFTEX_Unicode;
 868         if (strex && (strex->flags & gdFTEX_CHARMAP)) {
 869                 m = strex->charmap;
 870         }
 871 
 872         /* get the font (via font cache) */
 873         fontkey.fontlist = fontlist;
 874         fontkey.library = &library;
 875         fontkey.preferred_map = m;
 876         font = (font_t *) gdCacheGet (fontCache, &fontkey);
 877         if (!font) {
 878                 gdCacheDelete(tc_cache);
 879                 gdMutexUnlock(gdFontCacheMutex);
 880                 return fontCache->error;
 881         }
 882         face = font->face;              /* shortcut */
 883         slot = face->glyph;             /* shortcut */
 884 
 885         /*
 886          * Added hdpi and vdpi to support images at non-screen resolutions, i.e. 300 dpi TIFF,
 887          * or 100h x 50v dpi FAX format. 2.0.23.
 888          * 2004/02/27 Mark Shackelford, mark.shackelford@acs-inc.com
 889          */
 890         hdpi = GD_RESOLUTION;
 891         vdpi = GD_RESOLUTION;
 892         if (strex && (strex->flags & gdFTEX_RESOLUTION)) {
 893                 hdpi = strex->hdpi;
 894                 vdpi = strex->vdpi;
 895         }
 896 
 897         if (FT_Set_Char_Size(face, 0, (FT_F26Dot6) (ptsize * 64), hdpi, vdpi)) {
 898                 gdCacheDelete(tc_cache);
 899                 gdMutexUnlock(gdFontCacheMutex);
 900                 return "Could not set character size";
 901         }
 902 
 903         matrix.xx = (FT_Fixed) (cos_a * (1 << 16));
 904         matrix.yx = (FT_Fixed) (sin_a * (1 << 16));
 905         matrix.xy = -matrix.yx;
 906         matrix.yy = matrix.xx;
 907 
 908         penf.x = penf.y = 0;            /* running position of non-rotated string */
 909         pen.x = pen.y = 0;              /* running position of rotated string */
 910         bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0;
 911 
 912         use_kerning = FT_HAS_KERNING (face);
 913         previous = 0;
 914         if (fg < 0) {
 915                 render_mode |= FT_LOAD_MONOCHROME;
 916         }
 917 
 918         /* Try all three types of maps, but start with the specified one */
 919         mfound = 0;
 920         for (i = 0; i < 3; i++) {
 921                 switch (m) {
 922                         case gdFTEX_Unicode:
 923                                 if (font->have_char_map_unicode) {
 924                                         mfound = 1;
 925                                 }
 926                                 break;
 927                         case gdFTEX_Shift_JIS:
 928                                 if (font->have_char_map_sjis) {
 929                                         mfound = 1;
 930                                 }
 931                                 break;
 932                         case gdFTEX_Big5:
 933                                 /* This was the 'else' case, we can't really 'detect' it */
 934                                 mfound = 1;
 935                                 break;
 936                 }
 937                 if (mfound) {
 938                         break;
 939                 }
 940                 m++;
 941                 m %= 3;
 942         }
 943         if (!mfound) {
 944                 /* No character set found! */
 945                 gdMutexUnlock(gdFontCacheMutex);
 946                 return "No character set found";
 947         }
 948 
 949 #ifndef JISX0208
 950         if (font->have_char_map_sjis) {
 951 #endif
 952                 tmpstr = (char *) gdMalloc(BUFSIZ);
 953                 any2eucjp(tmpstr, string, BUFSIZ);
 954                 next = tmpstr;
 955 #ifndef JISX0208
 956         } else {
 957                 next = string;
 958         }
 959 #endif
 960 
 961         i = 0;
 962         while (*next) {
 963                 ch = *next;
 964 
 965                 /* carriage returns */
 966                 if (ch == '\r') {
 967                         penf.x = 0;
 968                         x1 = (int)(- penf.y * sin_a + 32) / 64;
 969                         y1 = (int)(- penf.y * cos_a + 32) / 64;
 970                         pen.x = pen.y = 0;
 971                         previous = 0;           /* clear kerning flag */
 972                         next++;
 973                         continue;
 974                 }
 975                 /* newlines */
 976                 if (ch == '\n') {
 977                         if (!*(++next)) break;
 978                         /* 2.0.13: reset penf.x. Christopher J. Grayce */
 979                         penf.x = 0;
 980                           penf.y -= (long)(face->size->metrics.height * linespace);
 981                           penf.y = (penf.y - 32) & -64;         /* round to next pixel row */
 982                           x1 = (int)(- penf.y * sin_a + 32) / 64;
 983                           y1 = (int)(- penf.y * cos_a + 32) / 64;
 984                           xb = x + x1;
 985                           yb = y + y1;
 986                           yd = 0;
 987                           pen.x = pen.y = 0;
 988                           previous = 0;         /* clear kerning flag */
 989                           continue;
 990                 }
 991 
 992 /* EAM DEBUG */
 993 #if (defined(FREETYPE_MAJOR) && ((FREETYPE_MAJOR == 2 && ((FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 3) || FREETYPE_MINOR > 1) || FREETYPE_MAJOR > 2)))
 994                 if (font->face->family_name && font->face->charmap->encoding &&
 995                         font->face->charmap->encoding == FT_ENCODING_MS_SYMBOL && strcmp(font->face->family_name, "Symbol") == 0) {
 996                         /* I do not know the significance of the constant 0xf000.
 997                          * It was determined by inspection of the character codes
 998                          * stored in Microsoft font symbol.
 999                          * Added by Pierre (pajoye@php.net):
1000                          * Convert to the Symbol glyph range only for a Symbol family member
1001                          */
1002                         len = gdTcl_UtfToUniChar (next, &ch);
1003                         ch |= 0xf000;
1004                         next += len;
1005                 } else
1006 #endif /* Freetype 2.1 or better */
1007 /* EAM DEBUG */
1008 
1009                 switch (m) {
1010                         case gdFTEX_Unicode:
1011                                 if (font->have_char_map_unicode) {
1012                                         /* use UTF-8 mapping from ASCII */
1013                                         len = gdTcl_UtfToUniChar(next, &ch);
1014                                         next += len;
1015                                 }
1016                                 break;
1017                         case gdFTEX_Shift_JIS:
1018                                 if (font->have_char_map_sjis) {
1019                                         unsigned char c;
1020                                         int jiscode;
1021                                         c = *next;
1022                                         if (0xA1 <= c && c <= 0xFE) {
1023                                                 next++;
1024                                                 jiscode = 0x100 * (c & 0x7F) + ((*next) & 0x7F);
1025 
1026                                                 ch = (jiscode >> 8) & 0xFF;
1027                                                 jiscode &= 0xFF;
1028 
1029                                                 if (ch & 1) {
1030                                                         jiscode += 0x40 - 0x21;
1031                                                 } else {
1032                                                         jiscode += 0x9E - 0x21;
1033                                                 }
1034 
1035                                                 if (jiscode >= 0x7F) {
1036                                                         jiscode++;
1037                                                 }
1038                                                 ch = (ch - 0x21) / 2 + 0x81;
1039                                                 if (ch >= 0xA0) {
1040                                                         ch += 0x40;
1041                                                 }
1042 
1043                                                 ch = (ch << 8) + jiscode;
1044                                         } else {
1045                                                 ch = c & 0xFF;  /* don't extend sign */
1046                                         }
1047                                         if (*next) next++;
1048                                 }
1049                                 break;
1050                         case gdFTEX_Big5: {
1051                                 /*
1052                                  * Big 5 mapping:
1053                                  * use "JIS-8 half-width katakana" coding from 8-bit characters. Ref:
1054                                  * ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs
1055                                  */
1056                                 ch = (*next) & 0xFF;    /* don't extend sign */
1057                                 next++;
1058                                 if (ch >= 161   /* first code of JIS-8 pair */
1059                                         && *next) { /* don't advance past '\0' */
1060                                         /* TBB: Fix from Kwok Wah On: & 255 needed */
1061                                         ch = (ch * 256) + ((*next) & 255);
1062                                         next++;
1063                                 }
1064                         }
1065                         break;
1066                 }
1067 
1068                 /* set rotation transform */
1069                 FT_Set_Transform(face, &matrix, NULL);
1070                 /* Convert character code to glyph index */
1071                 glyph_index = FT_Get_Char_Index(face, ch);
1072 
1073                 /* retrieve kerning distance and move pen position */
1074                 if (use_kerning && previous && glyph_index) {
1075                         FT_Get_Kerning(face, previous, glyph_index, ft_kerning_default, &delta);
1076                         pen.x += delta.x;
1077                         penf.x += delta.x;
1078                 }
1079 
1080                 /* load glyph image into the slot (erase previous one) */
1081                 if (FT_Load_Glyph(face, glyph_index, render_mode)) {
1082                         if (tmpstr) {
1083                                 gdFree(tmpstr);
1084                         }
1085                         gdCacheDelete(tc_cache);
1086                         gdMutexUnlock(gdFontCacheMutex);
1087                         return "Problem loading glyph";
1088                 }
1089 
1090                 /* transform glyph image */
1091                 if (FT_Get_Glyph(slot, &image)) {
1092                         if (tmpstr) {
1093                                 gdFree(tmpstr);
1094                         }
1095                         gdCacheDelete(tc_cache);
1096                         gdMutexUnlock(gdFontCacheMutex);
1097                         return "Problem loading glyph";
1098                 }
1099 
1100                 if (brect) { /* only if need brect */
1101                         FT_Glyph_Get_CBox(image, ft_glyph_bbox_gridfit, &glyph_bbox);
1102                         glyph_bbox.xMin += penf.x;
1103                         glyph_bbox.yMin += penf.y;
1104                         glyph_bbox.xMax += penf.x;
1105                         glyph_bbox.yMax += penf.y;
1106                         if (ch == ' ') { /* special case for trailing space */
1107                                 glyph_bbox.xMax += slot->metrics.horiAdvance;
1108                         }
1109                         if (!i) { /* if first character, init BB corner values */
1110                                 yd = slot->metrics.height - slot->metrics.horiBearingY;
1111                                 bbox.xMin = glyph_bbox.xMin;
1112                                 bbox.yMin = glyph_bbox.yMin;
1113                                 bbox.xMax = glyph_bbox.xMax;
1114                                 bbox.yMax = glyph_bbox.yMax;
1115                         } else {
1116                                 FT_Pos desc;
1117 
1118                                 if ( (desc = (slot->metrics.height - slot->metrics.horiBearingY)) > yd) {
1119                                         yd = desc;
1120                                 }
1121                                 if (bbox.xMin > glyph_bbox.xMin) {
1122                                         bbox.xMin = glyph_bbox.xMin;
1123                                 }
1124                                 if (bbox.yMin > glyph_bbox.yMin) {
1125                                         bbox.yMin = glyph_bbox.yMin;
1126                                 }
1127                                 if (bbox.xMax < glyph_bbox.xMax) {
1128                                         bbox.xMax = glyph_bbox.xMax;
1129                                 }
1130                                 if (bbox.yMax < glyph_bbox.yMax) {
1131                                         bbox.yMax = glyph_bbox.yMax;
1132                                 }
1133                         }
1134                         i++;
1135                 }
1136 
1137                 if (render) {
1138                         if (image->format != ft_glyph_format_bitmap && FT_Glyph_To_Bitmap(&image, ft_render_mode_normal, 0, 1)) {
1139                                 FT_Done_Glyph(image);
1140                                 if (tmpstr) {
1141                                         gdFree(tmpstr);
1142                                 }
1143                                 gdCacheDelete(tc_cache);
1144                                 gdMutexUnlock(gdFontCacheMutex);
1145                                 return "Problem rendering glyph";
1146                         }
1147 
1148                         /* now, draw to our target surface */
1149                         bm = (FT_BitmapGlyph) image;
1150                         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);
1151                 }
1152 
1153                 /* record current glyph index for kerning */
1154                 previous = glyph_index;
1155 
1156                 /* increment pen position */
1157                 pen.x += image->advance.x >> 10;
1158                 pen.y -= image->advance.y >> 10;
1159 
1160                 penf.x += slot->metrics.horiAdvance;
1161 
1162                 FT_Done_Glyph(image);
1163         }
1164 
1165         if (brect) { /* only if need brect */
1166                 /* For perfect rounding, must get sin(a + pi/4) and sin(a - pi/4). */
1167                 double d1 = sin (angle + 0.78539816339744830962);
1168                 double d2 = sin (angle - 0.78539816339744830962);
1169 
1170                 /* make the center of rotation at (0, 0) */
1171                 FT_BBox normbox;
1172 
1173                 normbox.xMin = 0;
1174                 normbox.yMin = 0;
1175                 normbox.xMax = bbox.xMax - bbox.xMin;
1176                 normbox.yMax = bbox.yMax - bbox.yMin;
1177 
1178                 brect[0] = brect[2] = brect[4] = brect[6] = (int)  (yd * sin_a);
1179                 brect[1] = brect[3] = brect[5] = brect[7] = (int)(- yd * cos_a);
1180 
1181                 /* rotate bounding rectangle */
1182                 brect[0] += (int) (normbox.xMin * cos_a - normbox.yMin * sin_a);
1183                 brect[1] += (int) (normbox.xMin * sin_a + normbox.yMin * cos_a);
1184                 brect[2] += (int) (normbox.xMax * cos_a - normbox.yMin * sin_a);
1185                 brect[3] += (int) (normbox.xMax * sin_a + normbox.yMin * cos_a);
1186                 brect[4] += (int) (normbox.xMax * cos_a - normbox.yMax * sin_a);
1187                 brect[5] += (int) (normbox.xMax * sin_a + normbox.yMax * cos_a);
1188                 brect[6] += (int) (normbox.xMin * cos_a - normbox.yMax * sin_a);
1189                 brect[7] += (int) (normbox.xMin * sin_a + normbox.yMax * cos_a);
1190 
1191                 /* scale, round and offset brect */
1192                 brect[0] = xb + gdroundupdown(brect[0], d2 > 0);
1193                 brect[1] = yb - gdroundupdown(brect[1], d1 < 0);
1194                 brect[2] = xb + gdroundupdown(brect[2], d1 > 0);
1195                 brect[3] = yb - gdroundupdown(brect[3], d2 > 0);
1196                 brect[4] = xb + gdroundupdown(brect[4], d2 < 0);
1197                 brect[5] = yb - gdroundupdown(brect[5], d1 > 0);
1198                 brect[6] = xb + gdroundupdown(brect[6], d1 < 0);
1199                 brect[7] = yb - gdroundupdown(brect[7], d2 < 0);
1200         }
1201 
1202         if (tmpstr) {
1203                 gdFree(tmpstr);
1204         }
1205         gdCacheDelete(tc_cache);
1206         gdMutexUnlock(gdFontCacheMutex);
1207         return (char *) NULL;
1208 }
1209 
1210 #endif /* HAVE_LIBFREETYPE */

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