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