1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *
12  *      See readme.txt for copyright information.
13  */
14 
15 #include <string.h>
16 #include "allegro5/allegro.h"
17 #include "allegro5/allegro_font.h"
18 #include "allegro5/internal/aintern.h"
19 #include "allegro5/internal/aintern_bitmap.h"
20 #include "allegro5/internal/aintern_font.h"
21 #include "allegro5/internal/aintern_exitfunc.h"
22 #include "allegro5/internal/aintern_vector.h"
23 
24 #include "font.h"
25 #include <ctype.h>
26 
27 ALLEGRO_DEBUG_CHANNEL("font")
28 
29 
30 typedef struct
31 {
32    ALLEGRO_USTR *extension;
33    ALLEGRO_FONT *(*load_font)(char const *filename, int size, int flags);
34 } FONT_HANDLER;
35 
36 
37 /* globals */
38 static bool font_inited = false;
39 static _AL_VECTOR font_handlers = _AL_VECTOR_INITIALIZER(FONT_HANDLER);
40 
41 
42 /* al_font_404_character:
43  *  This is what we render missing glyphs as.
44  */
45 static int al_font_404_character = '^';
46 
47 
48 
49 /* font_height:
50  *  (mono and color vtable entry)
51  *  Returns the height, in pixels of the font.
52  */
font_height(const ALLEGRO_FONT * f)53 static int font_height(const ALLEGRO_FONT *f)
54 {
55    ASSERT(f);
56    return f->height;
57 }
58 
59 
60 
font_ascent(const ALLEGRO_FONT * f)61 static int font_ascent(const ALLEGRO_FONT *f)
62 {
63     return font_height(f);
64 }
65 
66 
67 
font_descent(const ALLEGRO_FONT * f)68 static int font_descent(const ALLEGRO_FONT *f)
69 {
70     (void)f;
71     return 0;
72 }
73 
74 
75 
76 /* length:
77  *  (mono and color vtable entry)
78  *  Returns the length, in pixels, of a string as rendered in a font.
79  */
length(const ALLEGRO_FONT * f,const ALLEGRO_USTR * text)80 static int length(const ALLEGRO_FONT* f, const ALLEGRO_USTR *text)
81 {
82     int ch = 0, w = 0;
83     int pos = 0;
84     ASSERT(f);
85 
86     while ((ch = al_ustr_get_next(text, &pos)) >= 0) {
87         w += f->vtable->char_length(f, ch);
88     }
89 
90     return w;
91 }
92 
93 
94 
color_get_text_dimensions(ALLEGRO_FONT const * f,const ALLEGRO_USTR * text,int * bbx,int * bby,int * bbw,int * bbh)95 static void color_get_text_dimensions(ALLEGRO_FONT const *f,
96    const ALLEGRO_USTR *text,
97    int *bbx, int *bby, int *bbw, int *bbh)
98 {
99    /* Dummy implementation - for A4-style bitmap fonts the bounding
100     * box of text is its width and line-height.
101     */
102    int h = al_get_font_line_height(f);
103    if (bbx) *bbx = 0;
104    if (bby) *bby = 0;
105    if (bbw) *bbw = length(f, text);
106    if (bbh) *bbh = h;
107 }
108 
109 
110 
_al_font_find_page(ALLEGRO_FONT_COLOR_DATA * cf,int ch)111 static ALLEGRO_FONT_COLOR_DATA *_al_font_find_page(
112    ALLEGRO_FONT_COLOR_DATA *cf, int ch)
113 {
114     while (cf) {
115         if (ch >= cf->begin && ch < cf->end)
116             return cf;
117         cf = cf->next;
118     }
119     return NULL;
120 }
121 
122 
123 /* _color_find_glyph:
124  *  Helper for color vtable entries, below.
125  */
_al_font_color_find_glyph(const ALLEGRO_FONT * f,int ch)126 static ALLEGRO_BITMAP* _al_font_color_find_glyph(const ALLEGRO_FONT* f, int ch)
127 {
128     ALLEGRO_FONT_COLOR_DATA* cf = (ALLEGRO_FONT_COLOR_DATA*)(f->data);
129 
130     cf = _al_font_find_page(cf, ch);
131     if (cf) {
132         return cf->bitmaps[ch - cf->begin];
133     }
134 
135     /* if we don't find the character, then search for the missing
136        glyph, but don't get stuck in a loop. */
137     if (ch != al_font_404_character && !f->fallback)
138         return _al_font_color_find_glyph(f, al_font_404_character);
139     return 0;
140 }
141 
142 
143 
144 /* color_char_length:
145  *  (color vtable entry)
146  *  Returns the length of a character, in pixels, as it would be rendered
147  *  in this font.
148  */
color_char_length(const ALLEGRO_FONT * f,int ch)149 static int color_char_length(const ALLEGRO_FONT* f, int ch)
150 {
151    ALLEGRO_BITMAP* g = _al_font_color_find_glyph(f, ch);
152    if (g)
153       return al_get_bitmap_width(g);
154    if (f->fallback)
155       return al_get_glyph_width(f->fallback, ch);
156    return 0;
157 }
158 
159 
160 
161 /* color_render_char:
162  *  (color vtable entry)
163  *  Renders a color character onto a bitmap, at the specified location,
164  *  using the specified color.
165  *  Returns the character width, in pixels.
166  */
color_render_char(const ALLEGRO_FONT * f,ALLEGRO_COLOR color,int ch,float x,float y)167 static int color_render_char(const ALLEGRO_FONT* f,
168    ALLEGRO_COLOR color, int ch, float x,
169    float y)
170 {
171    int w = 0;
172    int h = f->vtable->font_height(f);
173    ALLEGRO_BITMAP *g;
174 
175    g = _al_font_color_find_glyph(f, ch);
176    if (g) {
177       al_draw_tinted_bitmap(g, color, x,
178          y + ((float)h - al_get_bitmap_height(g))/2.0f, 0);
179 
180       w = al_get_bitmap_width(g);
181    }
182    else if (f->fallback) {
183       al_draw_glyph(f->fallback, color, x, y, ch);
184       w = al_get_glyph_width(f->fallback, ch);
185    }
186 
187    return w;
188 }
189 
190 /* color_render:
191  *  (color vtable entry)
192  *  Renders a color font onto a bitmap, at the specified location, using
193  *  the specified color.
194  */
color_render(const ALLEGRO_FONT * f,ALLEGRO_COLOR color,const ALLEGRO_USTR * text,float x,float y)195 static int color_render(const ALLEGRO_FONT* f, ALLEGRO_COLOR color,
196    const ALLEGRO_USTR *text,
197     float x, float y)
198 {
199     int pos = 0;
200     int advance = 0;
201     int32_t ch;
202     bool held = al_is_bitmap_drawing_held();
203 
204     al_hold_bitmap_drawing(true);
205     while ((ch = al_ustr_get_next(text, &pos)) >= 0) {
206         advance += f->vtable->render_char(f, color, ch, x + advance, y);
207     }
208     al_hold_bitmap_drawing(held);
209     return advance;
210 }
211 
212 
color_get_glyph(const ALLEGRO_FONT * f,int prev_codepoint,int codepoint,ALLEGRO_GLYPH * glyph)213 static bool color_get_glyph(const ALLEGRO_FONT *f, int prev_codepoint, int codepoint, ALLEGRO_GLYPH *glyph)
214 {
215    ALLEGRO_BITMAP *g = _al_font_color_find_glyph(f, codepoint);
216    if (g) {
217       glyph->bitmap = g;
218       glyph->x = 0;
219       glyph->y = 0;
220       glyph->w = al_get_bitmap_width(g);
221       glyph->h = al_get_bitmap_height(g);
222       glyph->kerning = 0;
223       glyph->offset_x = 0;
224       glyph->offset_y = 0;
225       glyph->advance = glyph->w;
226       return true;
227    }
228    if (f->fallback) {
229       return f->fallback->vtable->get_glyph(f->fallback, prev_codepoint, codepoint, glyph);
230    }
231    return false;
232 }
233 
234 
235 /* color_destroy:
236  *  (color vtable entry)
237  *  Destroys a color font.
238  */
color_destroy(ALLEGRO_FONT * f)239 static void color_destroy(ALLEGRO_FONT* f)
240 {
241     ALLEGRO_FONT_COLOR_DATA* cf;
242     ALLEGRO_BITMAP *glyphs = NULL;
243 
244     if (!f)
245         return;
246 
247     cf = (ALLEGRO_FONT_COLOR_DATA*)(f->data);
248 
249     if (cf)
250         glyphs = cf->glyphs;
251 
252     while (cf) {
253         ALLEGRO_FONT_COLOR_DATA* next = cf->next;
254         int i = 0;
255 
256         for (i = cf->begin; i < cf->end; i++) al_destroy_bitmap(cf->bitmaps[i - cf->begin]);
257         /* Each range might point to the same bitmap. */
258         if (cf->glyphs != glyphs) {
259             al_destroy_bitmap(cf->glyphs);
260             cf->glyphs = NULL;
261         }
262 
263         if (!next && cf->glyphs)
264             al_destroy_bitmap(cf->glyphs);
265 
266         al_free(cf->bitmaps);
267         al_free(cf);
268 
269         cf = next;
270     }
271 
272     al_free(f);
273 }
274 
275 
color_get_font_ranges(ALLEGRO_FONT * font,int ranges_count,int * ranges)276 static int color_get_font_ranges(ALLEGRO_FONT *font, int ranges_count,
277    int *ranges)
278 {
279    ALLEGRO_FONT_COLOR_DATA *cf = font->data;
280    int i = 0;
281    while (cf) {
282       if (i < ranges_count) {
283          ranges[i * 2 + 0] = cf->begin;
284          ranges[i * 2 + 1] = cf->end - 1;
285       }
286       i++;
287       cf = cf->next;
288    }
289    return i;
290 }
291 
color_get_glyph_dimensions(ALLEGRO_FONT const * f,int codepoint,int * bbx,int * bby,int * bbw,int * bbh)292 static bool color_get_glyph_dimensions(ALLEGRO_FONT const *f,
293    int codepoint, int *bbx, int *bby, int *bbw, int *bbh)
294 {
295    ALLEGRO_BITMAP *glyph = _al_font_color_find_glyph(f, codepoint);
296    if(!glyph) {
297       if (f->fallback) {
298          return al_get_glyph_dimensions(f->fallback, codepoint,
299             bbx, bby, bbw, bbh);
300       }
301       return false;
302    }
303    if (bbx) *bbx = 0;
304    if (bby) *bby = 0;
305    if (bbw) *bbw = glyph ? al_get_bitmap_width(glyph) : 0;
306    if (bbh) *bbh = al_get_font_line_height(f);
307    return true;
308 }
309 
color_get_glyph_advance(ALLEGRO_FONT const * f,int codepoint1,int codepoint2)310 static int color_get_glyph_advance(ALLEGRO_FONT const *f,
311    int codepoint1, int codepoint2)
312 {
313    (void) codepoint2;
314 
315    /* Handle special case to simplify use in a loop. */
316    if (codepoint1 == ALLEGRO_NO_KERNING) {
317       return 0;
318    }
319 
320    /* For other cases, bitmap fonts don't use any kerning, so just use the
321     * width of codepoint1. */
322 
323    return color_char_length(f, codepoint1);
324 }
325 
326 /********
327  * vtable declarations
328  ********/
329 
330 ALLEGRO_FONT_VTABLE _al_font_vtable_color = {
331     font_height,
332     font_ascent,
333     font_descent,
334     color_char_length,
335     length,
336     color_render_char,
337     color_render,
338     color_destroy,
339     color_get_text_dimensions,
340     color_get_font_ranges,
341     color_get_glyph_dimensions,
342     color_get_glyph_advance,
343     color_get_glyph
344 };
345 
346 
font_shutdown(void)347 static void font_shutdown(void)
348 {
349     if (!font_inited)
350        return;
351 
352     while (!_al_vector_is_empty(&font_handlers)) {
353        FONT_HANDLER *h = _al_vector_ref_back(&font_handlers);
354        al_ustr_free(h->extension);
355        _al_vector_delete_at(&font_handlers, _al_vector_size(&font_handlers)-1);
356     }
357     _al_vector_free(&font_handlers);
358 
359     font_inited = false;
360 }
361 
362 
363 /* Function: al_init_font_addon
364  */
al_init_font_addon(void)365 bool al_init_font_addon(void)
366 {
367    if (font_inited) {
368       ALLEGRO_WARN("Font addon already initialised.\n");
369       return true;
370    }
371 
372    al_register_font_loader(".bmp", _al_load_bitmap_font);
373    al_register_font_loader(".jpg", _al_load_bitmap_font);
374    al_register_font_loader(".pcx", _al_load_bitmap_font);
375    al_register_font_loader(".png", _al_load_bitmap_font);
376    al_register_font_loader(".tga", _al_load_bitmap_font);
377 
378    al_register_font_loader(".xml", _al_load_bmfont_xml);
379    al_register_font_loader(".fnt", _al_load_bmfont_xml);
380 
381    _al_add_exit_func(font_shutdown, "font_shutdown");
382 
383    font_inited = true;
384    return font_inited;
385 }
386 
387 
388 
389 /* Function: al_is_font_addon_initialized
390  */
al_is_font_addon_initialized(void)391 bool al_is_font_addon_initialized(void)
392 {
393    return font_inited;
394 }
395 
396 
397 
398 /* Function: al_shutdown_font_addon
399  */
al_shutdown_font_addon(void)400 void al_shutdown_font_addon(void)
401 {
402    font_shutdown();
403 }
404 
405 
find_extension(char const * extension)406 static FONT_HANDLER *find_extension(char const *extension)
407 {
408    int i;
409    /* Go backwards so a handler registered later for the same extension
410     * has precedence.
411     */
412    for (i = _al_vector_size(&font_handlers) - 1; i >= 0 ; i--) {
413       FONT_HANDLER *handler = _al_vector_ref(&font_handlers, i);
414       if (0 == _al_stricmp(al_cstr(handler->extension), extension))
415          return handler;
416    }
417    return NULL;
418 }
419 
420 
421 
422 /* Function: al_register_font_loader
423  */
al_register_font_loader(char const * extension,ALLEGRO_FONT * (* load_font)(char const * filename,int size,int flags))424 bool al_register_font_loader(char const *extension,
425    ALLEGRO_FONT *(*load_font)(char const *filename, int size, int flags))
426 {
427    FONT_HANDLER *handler = find_extension(extension);
428    if (!handler) {
429       if (!load_font)
430          return false; /* Nothing to remove. */
431       handler = _al_vector_alloc_back(&font_handlers);
432       handler->extension = al_ustr_new(extension);
433    }
434    else {
435       if (!load_font) {
436          al_ustr_free(handler->extension);
437          return _al_vector_find_and_delete(&font_handlers, handler);
438       }
439    }
440    handler->load_font = load_font;
441    return true;
442 }
443 
444 
445 
446 /* Function: al_load_font
447  */
al_load_font(char const * filename,int size,int flags)448 ALLEGRO_FONT *al_load_font(char const *filename, int size, int flags)
449 {
450    int i;
451    const char *ext;
452    FONT_HANDLER *handler;
453 
454    ASSERT(filename);
455 
456    if (!font_inited) {
457       ALLEGRO_ERROR("Font addon not initialised.\n");
458       return NULL;
459    }
460 
461    ext = strrchr(filename, '.');
462    if (!ext) {
463       ALLEGRO_ERROR("Unable to determine filetype: '%s'\n", filename);
464       return NULL;
465    }
466    handler = find_extension(ext);
467    if (handler)
468       return handler->load_font(filename, size, flags);
469 
470    /* No handler for the extension was registered - try to load with
471     * all registered font_handlers and see if one works. So if the user
472     * does:
473     *
474     * al_init_font_addon()
475     * al_init_ttf_addon()
476     *
477     * This will first try to load an unknown (let's say Type1) font file
478     * with Freetype (and load it successfully in this case), then try
479     * to load it as a bitmap font.
480     */
481    for (i = _al_vector_size(&font_handlers) - 1; i >= 0 ; i--) {
482       FONT_HANDLER *handler = _al_vector_ref(&font_handlers, i);
483       ALLEGRO_FONT *try = handler->load_font(filename, size, flags);
484       if (try)
485          return try;
486    }
487 
488    return NULL;
489 }
490 
491 
492 
493 /* Function: al_get_allegro_font_version
494  */
al_get_allegro_font_version(void)495 uint32_t al_get_allegro_font_version(void)
496 {
497    return ALLEGRO_VERSION_INT;
498 }
499 
500 /* vim: set sts=4 sw=4 et: */
501 
502