blob: a286449edcd4a4c656e885ee83a45e566e0add75 [file] [log] [blame]
Vojtech Bocek76ee9032014-09-07 15:01:56 +02001#include <stdbool.h>
2#include <stdlib.h>
3#include <unistd.h>
4
5#include <errno.h>
6#include <stdio.h>
7
8#include "minui.h"
9
10#include <cutils/hashmap.h>
11#include <ft2build.h>
12#include FT_FREETYPE_H
13#include FT_GLYPH_H
14
15#include <pixelflinger/pixelflinger.h>
16#include <pthread.h>
17
18#define STRING_CACHE_MAX_ENTRIES 400
19#define STRING_CACHE_TRUNCATE_ENTRIES 150
20
21typedef struct
22{
23 int size;
24 int dpi;
25 char *path;
26} TrueTypeFontKey;
27
28typedef struct
29{
30 int type;
31 int refcount;
32 int size;
33 int dpi;
34 int max_height;
35 int base;
36 FT_Face face;
37 Hashmap *glyph_cache;
38 Hashmap *string_cache;
39 struct StringCacheEntry *string_cache_head;
40 struct StringCacheEntry *string_cache_tail;
41 pthread_mutex_t mutex;
42 TrueTypeFontKey *key;
43} TrueTypeFont;
44
45typedef struct
46{
47 FT_BBox bbox;
48 FT_BitmapGlyph glyph;
49} TrueTypeCacheEntry;
50
51typedef struct
52{
53 char *text;
54 int max_width;
55} StringCacheKey;
56
57struct StringCacheEntry
58{
59 GGLSurface surface;
xiaolue738da52015-02-22 20:49:35 +080060 int rendered_bytes; // number of bytes from C string rendered, not number of UTF8 characters!
Vojtech Bocek76ee9032014-09-07 15:01:56 +020061 StringCacheKey *key;
62 struct StringCacheEntry *prev;
63 struct StringCacheEntry *next;
64};
65
66typedef struct StringCacheEntry StringCacheEntry;
67
xiaolue738da52015-02-22 20:49:35 +080068typedef struct
Vojtech Bocek76ee9032014-09-07 15:01:56 +020069{
70 FT_Library ft_library;
71 Hashmap *fonts;
72 pthread_mutex_t mutex;
73} FontData;
74
75static FontData font_data = {
76 .ft_library = NULL,
77 .fonts = NULL,
78 .mutex = PTHREAD_MUTEX_INITIALIZER,
79};
80
81#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
82#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
83
84// 32bit FNV-1a hash algorithm
85// http://isthe.com/chongo/tech/comp/fnv/#FNV-1a
86static const uint32_t FNV_prime = 16777619U;
87static const uint32_t offset_basis = 2166136261U;
88
89static uint32_t fnv_hash(void *data, uint32_t len)
90{
91 uint8_t *d8 = data;
92 uint32_t *d32 = data;
93 uint32_t i, max;
94 uint32_t hash = offset_basis;
95
96 max = len/4;
97
98 // 32 bit data
99 for(i = 0; i < max; ++i)
100 {
101 hash ^= *d32++;
102 hash *= FNV_prime;
103 }
104
105 // last bits
106 for(i *= 4; i < len; ++i)
107 {
108 hash ^= (uint32_t) d8[i];
109 hash *= FNV_prime;
110 }
111 return hash;
112}
113
114static inline uint32_t fnv_hash_add(uint32_t cur_hash, uint32_t word)
115{
116 cur_hash ^= word;
117 cur_hash *= FNV_prime;
118 return cur_hash;
119}
120
soyud5a7c0e2014-11-28 14:33:53 +0800121int utf8_to_unicode(unsigned char* pIn, unsigned int *pOut)
122{
123 if(pIn == NULL || pOut == NULL)
124 return 0;
125
126 int utf_bytes = 1;
127 unsigned int unicode = 0;
128 unsigned char tmp;
129 tmp = *pIn++;
130 if (tmp < 0x80)
131 {
132 *pOut = tmp;
133 }
134 else
135 {
136 unsigned int high_bit_mask = 0x3F;
137 unsigned int high_bit_shift = 0;
138 int total_bits = 0;
139 while((tmp & 0xC0) == 0xC0)
140 {
141 utf_bytes ++;
142 if(utf_bytes > 6) return 0;
143 tmp = 0xFF & (tmp << 1);
144 total_bits += 6;
145 high_bit_mask >>= 1;
146 high_bit_shift++;
147 unicode <<= 6;
148 unicode |= (*pIn++) & 0x3F;
149 }
150 unicode |= ((tmp >> high_bit_shift) & high_bit_mask) << total_bits;
151 *pOut = unicode;
152 }
153
154 return utf_bytes;
155}
156
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200157static bool gr_ttf_string_cache_equals(void *keyA, void *keyB)
158{
159 StringCacheKey *a = keyA;
160 StringCacheKey *b = keyB;
161 return a->max_width == b->max_width && strcmp(a->text, b->text) == 0;
162}
163
164static int gr_ttf_string_cache_hash(void *key)
165{
166 StringCacheKey *k = key;
167 return fnv_hash(k->text, strlen(k->text));
168}
169
170static bool gr_ttf_font_cache_equals(void *keyA, void *keyB)
171{
172 TrueTypeFontKey *a = keyA;
173 TrueTypeFontKey *b = keyB;
174 return (a->size == b->size) && (a->dpi == b->dpi) && !strcmp(a->path, b->path);
175}
176
177static int gr_ttf_font_cache_hash(void *key)
178{
179 TrueTypeFontKey *k = key;
180
181 uint32_t hash = fnv_hash(k->path, strlen(k->path));
182 hash = fnv_hash_add(hash, k->size);
183 hash = fnv_hash_add(hash, k->dpi);
184 return hash;
185}
186
187void *gr_ttf_loadFont(const char *filename, int size, int dpi)
188{
189 int error;
190 TrueTypeFont *res = NULL;
191
192 pthread_mutex_lock(&font_data.mutex);
193
194 if(font_data.fonts)
195 {
196 TrueTypeFontKey k = {
197 .size = size,
198 .dpi = dpi,
199 .path = (char*)filename
200 };
201
202 res = hashmapGet(font_data.fonts, &k);
203 if(res)
204 {
205 ++res->refcount;
206 goto exit;
207 }
208 }
209
210 if(!font_data.ft_library)
211 {
212 error = FT_Init_FreeType(&font_data.ft_library);
213 if(error)
214 {
215 fprintf(stderr, "Failed to init libfreetype! %d\n", error);
216 goto exit;
217 }
218 }
219
220 FT_Face face;
221 error = FT_New_Face(font_data.ft_library, filename, 0, &face);
222 if(error)
223 {
224 fprintf(stderr, "Failed to load truetype face %s: %d\n", filename, error);
225 goto exit;
226 }
227
228 error = FT_Set_Char_Size(face, 0, size*16, dpi, dpi);
229 if(error)
230 {
231 fprintf(stderr, "Failed to set truetype face size to %d, dpi %d: %d\n", size, dpi, error);
232 FT_Done_Face(face);
233 goto exit;
234 }
235
236 res = malloc(sizeof(TrueTypeFont));
237 memset(res, 0, sizeof(TrueTypeFont));
238 res->type = FONT_TYPE_TTF;
239 res->size = size;
240 res->dpi = dpi;
241 res->face = face;
242 res->max_height = -1;
243 res->base = -1;
244 res->refcount = 1;
245 res->glyph_cache = hashmapCreate(32, hashmapIntHash, hashmapIntEquals);
246 res->string_cache = hashmapCreate(128, gr_ttf_string_cache_hash, gr_ttf_string_cache_equals);
247 pthread_mutex_init(&res->mutex, 0);
248
249 if(!font_data.fonts)
250 font_data.fonts = hashmapCreate(4, gr_ttf_font_cache_hash, gr_ttf_font_cache_equals);
251
252 TrueTypeFontKey *key = malloc(sizeof(TrueTypeFontKey));
253 memset(key, 0, sizeof(TrueTypeFontKey));
254 key->path = strdup(filename);
255 key->size = size;
256 key->dpi = dpi;
257
258 res->key = key;
259
260 hashmapPut(font_data.fonts, key, res);
261
262exit:
263 pthread_mutex_unlock(&font_data.mutex);
264 return res;
265}
266
267static bool gr_ttf_freeFontCache(void *key, void *value, void *context)
268{
269 TrueTypeCacheEntry *e = value;
270 FT_Done_Glyph((FT_Glyph)e->glyph);
271 free(e);
272 free(key);
273 return true;
274}
275
276static bool gr_ttf_freeStringCache(void *key, void *value, void *context)
277{
278 StringCacheKey *k = key;
279 free(k->text);
280 free(k);
281
282 StringCacheEntry *e = value;
283 free(e->surface.data);
284 free(e);
285 return true;
286}
287
288void gr_ttf_freeFont(void *font)
289{
290 pthread_mutex_lock(&font_data.mutex);
291
292 TrueTypeFont *d = font;
293
294 if(--d->refcount == 0)
295 {
296 hashmapRemove(font_data.fonts, d->key);
297
298 if(hashmapSize(font_data.fonts) == 0)
299 {
300 hashmapFree(font_data.fonts);
301 font_data.fonts = NULL;
302 }
303
304 free(d->key->path);
305 free(d->key);
306
307 FT_Done_Face(d->face);
308 hashmapForEach(d->string_cache, gr_ttf_freeStringCache, NULL);
309 hashmapFree(d->string_cache);
310 hashmapForEach(d->glyph_cache, gr_ttf_freeFontCache, NULL);
311 hashmapFree(d->glyph_cache);
312 pthread_mutex_destroy(&d->mutex);
313 free(d);
314 }
315
316 pthread_mutex_unlock(&font_data.mutex);
317}
318
319static TrueTypeCacheEntry *gr_ttf_glyph_cache_peek(TrueTypeFont *font, int char_index)
320{
321 return hashmapGet(font->glyph_cache, &char_index);
322}
323
324static TrueTypeCacheEntry *gr_ttf_glyph_cache_get(TrueTypeFont *font, int char_index)
325{
326 TrueTypeCacheEntry *res = hashmapGet(font->glyph_cache, &char_index);
327 if(!res)
328 {
329 int error = FT_Load_Glyph(font->face, char_index, FT_LOAD_RENDER);
330 if(error)
331 {
332 fprintf(stderr, "Failed to load glyph idx %d: %d\n", char_index, error);
333 return NULL;
334 }
335
336 FT_BitmapGlyph glyph;
337 error = FT_Get_Glyph(font->face->glyph, (FT_Glyph*)&glyph);
338 if(error)
339 {
340 fprintf(stderr, "Failed to copy glyph %d: %d\n", char_index, error);
341 return NULL;
342 }
343
344 res = malloc(sizeof(TrueTypeCacheEntry));
345 memset(res, 0, sizeof(TrueTypeCacheEntry));
346 res->glyph = glyph;
347 FT_Glyph_Get_CBox((FT_Glyph)glyph, FT_GLYPH_BBOX_PIXELS, &res->bbox);
348
349 int *key = malloc(sizeof(int));
350 *key = char_index;
351
352 hashmapPut(font->glyph_cache, key, res);
353 }
354
355 return res;
356}
357
358static int gr_ttf_copy_glyph_to_surface(GGLSurface *dest, FT_BitmapGlyph glyph, int offX, int offY, int base)
359{
360 int y;
361 uint8_t *src_itr = glyph->bitmap.buffer;
362 uint8_t *dest_itr = dest->data;
363
364 if(glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
365 {
366 fprintf(stderr, "Unsupported pixel mode in FT_BitmapGlyph %d\n", glyph->bitmap.pixel_mode);
367 return -1;
368 }
369
370 dest_itr += (offY + base - glyph->top)*dest->stride + (offX + glyph->left);
371
Vojtech Bocek54cf1082015-03-15 16:50:26 +0100372 // FIXME: if glyph->left is negative and everything else is 0 (e.g. letter 'j' in Roboto-Regular),
373 // the result might end up being before the buffer - I'm not sure how to properly handle this.
374 if(dest_itr < dest->data)
375 dest_itr = dest->data;
376
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200377 for(y = 0; y < glyph->bitmap.rows; ++y)
378 {
379 memcpy(dest_itr, src_itr, glyph->bitmap.width);
380 src_itr += glyph->bitmap.pitch;
381 dest_itr += dest->stride;
382 }
383 return 0;
384}
385
Vojtech Boceka482f252015-03-15 17:03:50 +0100386static void gr_ttf_calcMaxFontHeight(TrueTypeFont *f)
387{
388 char c;
389 int char_idx;
390 int error;
391 FT_Glyph glyph;
392 FT_BBox bbox;
393 FT_BBox bbox_glyph;
394 TrueTypeCacheEntry *ent;
395
396 bbox.yMin = bbox_glyph.yMin = LONG_MAX;
397 bbox.yMax = bbox_glyph.yMax = LONG_MIN;
398
399 for(c = '!'; c <= '~'; ++c)
400 {
401 char_idx = FT_Get_Char_Index(f->face, c);
402 ent = gr_ttf_glyph_cache_peek(f, char_idx);
403 if(ent)
404 {
405 bbox.yMin = MIN(bbox.yMin, ent->bbox.yMin);
406 bbox.yMax = MAX(bbox.yMax, ent->bbox.yMax);
407 }
408 else
409 {
410 error = FT_Load_Glyph(f->face, char_idx, 0);
411 if(error)
412 continue;
413
414 error = FT_Get_Glyph(f->face->glyph, &glyph);
415 if(error)
416 continue;
417
418 FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox_glyph);
419 bbox.yMin = MIN(bbox.yMin, bbox_glyph.yMin);
420 bbox.yMax = MAX(bbox.yMax, bbox_glyph.yMax);
421
422 FT_Done_Glyph(glyph);
423 }
424 }
425
426 if(bbox.yMin > bbox.yMax)
427 bbox.yMin = bbox.yMax = 0;
428
429 f->max_height = bbox.yMax - bbox.yMin;
430 f->base = bbox.yMax;
431
432 // FIXME: twrp fonts have some padding on top, I'll add it here
433 // Should be fixed in the themes
434 f->max_height += f->size / 4;
435 f->base += f->size / 4;
436}
437
xiaolue738da52015-02-22 20:49:35 +0800438// returns number of bytes from const char *text rendered to fit max_width, not number of UTF8 characters!
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200439static int gr_ttf_render_text(TrueTypeFont *font, GGLSurface *surface, const char *text, int max_width)
440{
441 TrueTypeFont *f = font;
442 TrueTypeCacheEntry *ent;
xiaolue738da52015-02-22 20:49:35 +0800443 int bytes_rendered = 0, total_w = 0;
soyud5a7c0e2014-11-28 14:33:53 +0800444 int utf_bytes = 0;
445 unsigned int unicode = 0;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200446 int i, x, diff, char_idx, prev_idx = 0;
447 int height, base;
448 FT_Vector delta;
449 uint8_t *data = NULL;
450 const char *text_itr = text;
soyud5a7c0e2014-11-28 14:33:53 +0800451 int *char_idxs;
xiaolue738da52015-02-22 20:49:35 +0800452 int char_idxs_len = 0;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200453
xiaolue738da52015-02-22 20:49:35 +0800454 char_idxs = malloc(strlen(text) * sizeof(int));
455
soyud5a7c0e2014-11-28 14:33:53 +0800456 while(*text_itr)
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200457 {
soyud5a7c0e2014-11-28 14:33:53 +0800458 utf_bytes = utf8_to_unicode(text_itr, &unicode);
xiaolue738da52015-02-22 20:49:35 +0800459 text_itr += utf_bytes;
460 bytes_rendered += utf_bytes;
461
soyud5a7c0e2014-11-28 14:33:53 +0800462 char_idx = FT_Get_Char_Index(f->face, unicode);
xiaolue738da52015-02-22 20:49:35 +0800463 char_idxs[char_idxs_len] = char_idx;
464
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200465 ent = gr_ttf_glyph_cache_get(f, char_idx);
466 if(ent)
467 {
468 diff = ent->glyph->root.advance.x >> 16;
469
470 if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
471 {
472 FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
473 diff += delta.x >> 6;
474 }
475
476 if(max_width != -1 && total_w + diff > max_width)
477 break;
478
479 total_w += diff;
480 }
481 prev_idx = char_idx;
xiaolue738da52015-02-22 20:49:35 +0800482 ++char_idxs_len;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200483 }
484
485 if(font->max_height == -1)
Vojtech Boceka482f252015-03-15 17:03:50 +0100486 gr_ttf_calcMaxFontHeight(font);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200487
488 if(font->max_height == -1)
soyud5a7c0e2014-11-28 14:33:53 +0800489 {
490 free(char_idxs);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200491 return -1;
soyud5a7c0e2014-11-28 14:33:53 +0800492 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200493
494 height = font->max_height;
495
496 data = malloc(total_w*height);
497 memset(data, 0, total_w*height);
498 x = 0;
499 prev_idx = 0;
500
501 surface->version = sizeof(*surface);
502 surface->width = total_w;
503 surface->height = height;
504 surface->stride = total_w;
505 surface->data = (void*)data;
506 surface->format = GGL_PIXEL_FORMAT_A_8;
507
xiaolue738da52015-02-22 20:49:35 +0800508 for(i = 0; i < char_idxs_len; ++i)
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200509 {
soyud5a7c0e2014-11-28 14:33:53 +0800510 char_idx = char_idxs[i];
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200511 if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
512 {
513 FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
514 x += delta.x >> 6;
515 }
516
517 ent = gr_ttf_glyph_cache_get(f, char_idx);
518 if(ent)
519 {
520 gr_ttf_copy_glyph_to_surface(surface, ent->glyph, x, 0, font->base);
521 x += ent->glyph->root.advance.x >> 16;
522 }
523
524 prev_idx = char_idx;
525 }
526
soyud5a7c0e2014-11-28 14:33:53 +0800527 free(char_idxs);
xiaolue738da52015-02-22 20:49:35 +0800528 return bytes_rendered;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200529}
530
531static StringCacheEntry *gr_ttf_string_cache_peek(TrueTypeFont *font, const char *text, int max_width)
532{
533 StringCacheEntry *res;
534 StringCacheKey k = {
535 .text = (char*)text,
536 .max_width = max_width
537 };
538
539 return hashmapGet(font->string_cache, &k);
540}
541
542static StringCacheEntry *gr_ttf_string_cache_get(TrueTypeFont *font, const char *text, int max_width)
543{
544 StringCacheEntry *res;
545 StringCacheKey k = {
546 .text = (char*)text,
547 .max_width = max_width
548 };
549
550 res = hashmapGet(font->string_cache, &k);
551 if(!res)
552 {
553 res = malloc(sizeof(StringCacheEntry));
554 memset(res, 0, sizeof(StringCacheEntry));
xiaolue738da52015-02-22 20:49:35 +0800555 res->rendered_bytes = gr_ttf_render_text(font, &res->surface, text, max_width);
556 if(res->rendered_bytes < 0)
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200557 {
558 free(res);
559 return NULL;
560 }
561
562 StringCacheKey *new_key = malloc(sizeof(StringCacheKey));
563 memset(new_key, 0, sizeof(StringCacheKey));
564 new_key->max_width = max_width;
565 new_key->text = strdup(text);
566
567 res->key = new_key;
568
569 if(font->string_cache_tail)
570 {
571 res->prev = font->string_cache_tail;
572 res->prev->next = res;
573 }
574 else
575 font->string_cache_head = res;
576 font->string_cache_tail = res;
577
578 hashmapPut(font->string_cache, new_key, res);
579 }
580 else if(res->next)
581 {
582 // move this entry to the tail of the linked list
583 // if it isn't already there
584 if(res->prev)
585 res->prev->next = res->next;
586
587 res->next->prev = res->prev;
588
589 if(!res->prev)
590 font->string_cache_head = res->next;
591
592 res->next = NULL;
593 res->prev = font->string_cache_tail;
594 res->prev->next = res;
595 font->string_cache_tail = res;
596
597 // truncate old entries
598 if(hashmapSize(font->string_cache) >= STRING_CACHE_MAX_ENTRIES)
599 {
600 printf("Truncating string cache entries.\n");
601 int i;
602 StringCacheEntry *ent;
603 for(i = 0; i < STRING_CACHE_TRUNCATE_ENTRIES; ++i)
604 {
605 ent = font->string_cache_head;
606 font->string_cache_head = ent->next;
607 font->string_cache_head->prev = NULL;
608
609 hashmapRemove(font->string_cache, ent->key);
610
611 gr_ttf_freeStringCache(ent->key, ent, NULL);
612 }
613 }
614 }
615 return res;
616}
617
618int gr_ttf_measureEx(const char *s, void *font)
619{
620 TrueTypeFont *f = font;
621 int res = -1;
622
623 pthread_mutex_lock(&f->mutex);
624 StringCacheEntry *e = gr_ttf_string_cache_get(font, s, -1);
625 if(e)
626 res = e->surface.width;
627 pthread_mutex_unlock(&f->mutex);
628
629 return res;
630}
631
632int gr_ttf_maxExW(const char *s, void *font, int max_width)
633{
634 TrueTypeFont *f = font;
635 TrueTypeCacheEntry *ent;
xiaolue738da52015-02-22 20:49:35 +0800636 int max_bytes = 0, total_w = 0;
637 int utf_bytes, prev_utf_bytes = 0;
638 unsigned int unicode = 0;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200639 int char_idx, prev_idx = 0;
640 FT_Vector delta;
641 StringCacheEntry *e;
642
643 pthread_mutex_lock(&f->mutex);
644
645 e = gr_ttf_string_cache_peek(font, s, max_width);
646 if(e)
647 {
xiaolue738da52015-02-22 20:49:35 +0800648 max_bytes = e->rendered_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200649 pthread_mutex_unlock(&f->mutex);
xiaolue738da52015-02-22 20:49:35 +0800650 return max_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200651 }
652
xiaolue738da52015-02-22 20:49:35 +0800653 while(*s)
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200654 {
soyud5a7c0e2014-11-28 14:33:53 +0800655 utf_bytes = utf8_to_unicode(s, &unicode);
xiaolue738da52015-02-22 20:49:35 +0800656 s += utf_bytes;
657
658 char_idx = FT_Get_Char_Index(f->face, unicode);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200659 if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
660 {
661 FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
662 total_w += delta.x >> 6;
663 }
664 prev_idx = char_idx;
665
666 if(total_w > max_width)
xiaolue738da52015-02-22 20:49:35 +0800667 {
668 max_bytes -= prev_utf_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200669 break;
xiaolue738da52015-02-22 20:49:35 +0800670 }
671 prev_utf_bytes = utf_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200672
673 ent = gr_ttf_glyph_cache_get(f, char_idx);
674 if(!ent)
675 continue;
676
677 total_w += ent->glyph->root.advance.x >> 16;
xiaolue738da52015-02-22 20:49:35 +0800678 max_bytes += utf_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200679 }
680 pthread_mutex_unlock(&f->mutex);
xiaolue738da52015-02-22 20:49:35 +0800681 return max_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200682}
683
684int gr_ttf_textExWH(void *context, int x, int y, const char *s, void *pFont, int max_width, int max_height)
685{
686 GGLContext *gl = context;
687 TrueTypeFont *font = pFont;
688
689 // not actualy max width, but max_width + x
690 if(max_width != -1)
691 {
692 max_width -= x;
693 if(max_width <= 0)
694 return 0;
695 }
696
697 pthread_mutex_lock(&font->mutex);
698
699 StringCacheEntry *e = gr_ttf_string_cache_get(font, s, max_width);
700 if(!e)
701 {
702 pthread_mutex_unlock(&font->mutex);
703 return -1;
704 }
705
706 int y_bottom = y + e->surface.height;
xiaolue738da52015-02-22 20:49:35 +0800707 int res = e->rendered_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200708
709 if(max_height != -1 && max_height < y_bottom)
710 {
711 y_bottom = max_height;
712 if(y_bottom <= y)
713 {
714 pthread_mutex_unlock(&font->mutex);
715 return 0;
716 }
717 }
718
719 gl->bindTexture(gl, &e->surface);
720 gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
721 gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
722 gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200723
Vojtech Bocek3041c882015-03-06 00:28:21 +0100724 gl->enable(gl, GGL_TEXTURE_2D);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200725 gl->texCoord2i(gl, -x, -y);
726 gl->recti(gl, x, y, x + e->surface.width, y_bottom);
Vojtech Bocek3041c882015-03-06 00:28:21 +0100727 gl->disable(gl, GGL_TEXTURE_2D);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200728
729 pthread_mutex_unlock(&font->mutex);
730 return res;
731}
732
733int gr_ttf_getMaxFontHeight(void *font)
734{
735 int res;
736 TrueTypeFont *f = font;
737
738 pthread_mutex_lock(&f->mutex);
739
740 if(f->max_height == -1)
Vojtech Boceka482f252015-03-15 17:03:50 +0100741 gr_ttf_calcMaxFontHeight(f);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200742 res = f->max_height;
743
744 pthread_mutex_unlock(&f->mutex);
745 return res;
746}
747
748static bool gr_ttf_dump_stats_count_string_cache(void *key, void *value, void *context)
749{
750 int *string_cache_size = context;
751 StringCacheEntry *e = value;
752 *string_cache_size += e->surface.height*e->surface.width + sizeof(StringCacheEntry);
753 return true;
754}
755
756static bool gr_ttf_dump_stats_font(void *key, void *value, void *context)
757{
758 TrueTypeFontKey *k = key;
759 TrueTypeFont *f = value;
760 int *total_string_cache_size = context;
761 int string_cache_size = 0;
762
763 pthread_mutex_lock(&f->mutex);
764
765 hashmapForEach(f->string_cache, gr_ttf_dump_stats_count_string_cache, &string_cache_size);
766
767 printf(" Font %s (size %d, dpi %d):\n"
768 " refcount: %d\n"
769 " max_height: %d\n"
770 " base: %d\n"
771 " glyph_cache: %d entries\n"
772 " string_cache: %d entries (%.2f kB)\n",
773 k->path, k->size, k->dpi,
774 f->refcount, f->max_height, f->base,
775 hashmapSize(f->glyph_cache),
776 hashmapSize(f->string_cache), ((double)string_cache_size)/1024);
777
778 pthread_mutex_unlock(&f->mutex);
779
780 *total_string_cache_size += string_cache_size;
781 return true;
782}
783
784void gr_ttf_dump_stats(void)
785{
786 pthread_mutex_lock(&font_data.mutex);
787
788 printf("TrueType fonts system stats: ");
789 if(!font_data.fonts)
790 printf("no truetype fonts loaded.\n");
791 else
792 {
793 int total_string_cache_size = 0;
794 printf("%d fonts loaded.\n", hashmapSize(font_data.fonts));
795 hashmapForEach(font_data.fonts, gr_ttf_dump_stats_font, &total_string_cache_size);
796 printf(" Total string cache size: %.2f kB\n", ((double)total_string_cache_size)/1024);
797 }
798
799 pthread_mutex_unlock(&font_data.mutex);
800}