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