blob: 3dffbe4183f93d06a4fd37e1c774d944d54b6394 [file] [log] [blame]
bigbiffd58ba182020-03-23 10:02:29 -04001/*
2 Copyright 2012 to 2020 TeamWin
3 This file is part of TWRP/TeamWin Recovery Project.
4
5 TWRP is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 TWRP is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with TWRP. If not, see <http://www.gnu.org/licenses/>.
17*/
18
Vojtech Bocek76ee9032014-09-07 15:01:56 +020019#include <stdbool.h>
20#include <stdlib.h>
21#include <unistd.h>
22
23#include <errno.h>
24#include <stdio.h>
25
Vojtech Bocek76ee9032014-09-07 15:01:56 +020026#include <pthread.h>
Vladimir Olteand32b7eb2018-07-03 00:04:03 +030027#include <algorithm>
bigbiffd58ba182020-03-23 10:02:29 -040028#include <string>
29#include "truetype.hpp"
Vojtech Bocek76ee9032014-09-07 15:01:56 +020030
31static FontData font_data = {
bigbiffd58ba182020-03-23 10:02:29 -040032 .ft_library = NULL,
33 .mutex = PTHREAD_MUTEX_INITIALIZER
Vojtech Bocek76ee9032014-09-07 15:01:56 +020034};
35
bigbiffd58ba182020-03-23 10:02:29 -040036twrpTruetype::twrpTruetype(void) {
Vojtech Bocek76ee9032014-09-07 15:01:56 +020037
Vojtech Bocek76ee9032014-09-07 15:01:56 +020038}
39
bigbiffd58ba182020-03-23 10:02:29 -040040int twrpTruetype::utf8_to_unicode(const char* pIn, unsigned int *pOut) {
41 int utf_bytes = 1;
42 unsigned int unicode = 0;
43 unsigned char tmp;
44 tmp = (unsigned char)*pIn++;
45 if (tmp < 0x80)
46 {
47 *pOut = tmp;
48 }
49 else
50 {
51 unsigned int high_bit_mask = 0x3F;
52 unsigned int high_bit_shift = 0;
53 int total_bits = 0;
54 while((tmp & 0xC0) == 0xC0)
55 {
56 utf_bytes ++;
57 if(utf_bytes > 6)
58 {
59 *pOut = tmp;
60 return 1;
61 }
62 tmp = 0xFF & (tmp << 1);
63 total_bits += 6;
64 high_bit_mask >>= 1;
65 high_bit_shift++;
66 unicode <<= 6;
67 unicode |= (*pIn++) & 0x3F;
68 }
69 unicode |= ((tmp >> high_bit_shift) & high_bit_mask) << total_bits;
70 *pOut = unicode;
71 }
72
73 return utf_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +020074}
75
bigbiffd58ba182020-03-23 10:02:29 -040076void* twrpTruetype::gr_ttf_loadFont(const char *filename, int size, int dpi) {
77 int error;
78 TrueTypeFont* res = nullptr;
79 TrueTypeFontKey* key;
soyud5a7c0e2014-11-28 14:33:53 +080080
bigbiffd58ba182020-03-23 10:02:29 -040081 pthread_mutex_lock(&font_data.mutex);
soyud5a7c0e2014-11-28 14:33:53 +080082
bigbiffd58ba182020-03-23 10:02:29 -040083 TrueTypeFontKey k = {
84 .size = size,
85 .dpi = dpi,
86 .path = (char*)filename
87 };
Vojtech Bocek76ee9032014-09-07 15:01:56 +020088
bigbiffd58ba182020-03-23 10:02:29 -040089 TrueTypeFontMap::iterator ttfIter = font_data.fonts.find(k);
Vojtech Bocek76ee9032014-09-07 15:01:56 +020090
bigbiffd58ba182020-03-23 10:02:29 -040091 if (ttfIter != font_data.fonts.end())
92 {
93 res = ttfIter->second;
94 ++res->refcount;
95 goto exit;
96 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +020097
bigbiffd58ba182020-03-23 10:02:29 -040098 if(!font_data.ft_library)
99 {
100 error = FT_Init_FreeType(&font_data.ft_library);
101 if(error)
102 {
103 fprintf(stderr, "Failed to init libfreetype! %d\n", error);
104 goto exit;
105 }
106 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200107
bigbiffd58ba182020-03-23 10:02:29 -0400108 FT_Face face;
109 error = FT_New_Face(font_data.ft_library, filename, 0, &face);
110 if(error)
111 {
112 fprintf(stderr, "Failed to load truetype face %s: %d\n", filename, error);
113 goto exit;
114 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200115
bigbiffd58ba182020-03-23 10:02:29 -0400116 error = FT_Set_Char_Size(face, 0, size*16, dpi, dpi);
117 if(error)
118 {
119 fprintf(stderr, "Failed to set truetype face size to %d, dpi %d: %d\n", size, dpi, error);
120 FT_Done_Face(face);
121 goto exit;
122 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200123
bigbiffd58ba182020-03-23 10:02:29 -0400124 res = new TrueTypeFont;
125 res->type = FONT_TYPE_TTF;
126 res->size = size;
127 res->dpi = dpi;
128 res->face = face;
129 res->max_height = -1;
130 res->base = -1;
131 res->refcount = 1;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200132
bigbiffd58ba182020-03-23 10:02:29 -0400133 pthread_mutex_init(&res->mutex, 0);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200134
bigbiffd58ba182020-03-23 10:02:29 -0400135 key = new TrueTypeFontKey;
136 key->path = strdup(filename);
137 key->size = size;
138 key->dpi = dpi;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200139
bigbiffd58ba182020-03-23 10:02:29 -0400140 res->key = key;
141 font_data.fonts[*key] = res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200142
143exit:
bigbiffd58ba182020-03-23 10:02:29 -0400144 pthread_mutex_unlock(&font_data.mutex);
145 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200146}
147
bigbiffd58ba182020-03-23 10:02:29 -0400148void* twrpTruetype::gr_ttf_scaleFont(void *font, int max_width, int measured_width) {
149 if (!font)
150 return nullptr;
Ethan Yonkerb7a54a32015-10-05 10:16:27 -0500151
bigbiffd58ba182020-03-23 10:02:29 -0400152 TrueTypeFont *f = (TrueTypeFont *)font;
153 float scale_value = (float)(max_width) / (float)(measured_width);
154 int new_size = ((int)((float)f->size * scale_value)) - 1;
155 if (new_size < 1)
156 new_size = 1;
157 const char* file = f->key->path;
158 int dpi = f->dpi;
159 return gr_ttf_loadFont(file, new_size, dpi);
Ethan Yonkerb7a54a32015-10-05 10:16:27 -0500160}
161
bigbiffd58ba182020-03-23 10:02:29 -0400162static bool gr_ttf_freeFontCache(void *value, void *context __unused)
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200163{
bigbiffd58ba182020-03-23 10:02:29 -0400164 TrueTypeCacheEntry *e = (TrueTypeCacheEntry *)value;
165 FT_Done_Glyph((FT_Glyph)e->glyph);
166 free(e);
167 return true;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200168}
169
bigbiffd58ba182020-03-23 10:02:29 -0400170bool twrpTruetype::gr_ttf_freeStringCache(void *key, void *value, void *context __unused) {
171 StringCacheKey *k = (StringCacheKey *)key;
172 delete k->text;
173 delete k;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200174
bigbiffd58ba182020-03-23 10:02:29 -0400175 StringCacheEntry *e = (StringCacheEntry *)value;
176 delete e->surface.data;
177 delete e;
178 return true;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200179}
180
bigbiffd58ba182020-03-23 10:02:29 -0400181void twrpTruetype::gr_ttf_freeFont(void *font) {
182 pthread_mutex_lock(&font_data.mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200183
bigbiffd58ba182020-03-23 10:02:29 -0400184 TrueTypeFont *d = (TrueTypeFont *)font;
185 if(--d->refcount == 0)
186 {
187 delete d->key->path;
188 delete d->key;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200189
bigbiffd58ba182020-03-23 10:02:29 -0400190 FT_Done_Face(d->face);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200191
bigbiffd58ba182020-03-23 10:02:29 -0400192 StringCacheMap::iterator stringCacheEntryIt = d->string_cache.begin();
193 while (stringCacheEntryIt != d->string_cache.end()) {
194 gr_ttf_freeStringCache(stringCacheEntryIt->second->key, stringCacheEntryIt->second, NULL);
195 stringCacheEntryIt = d->string_cache.erase(stringCacheEntryIt);
196 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200197
bigbiffd58ba182020-03-23 10:02:29 -0400198 TrueTypeCacheEntryMap::iterator ttcIt = d->glyph_cache.begin();
199 while(ttcIt != d->glyph_cache.end()) {
200 gr_ttf_freeFontCache(ttcIt->second, NULL);
201 ttcIt = d->glyph_cache.erase(ttcIt);
202 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200203
bigbiffd58ba182020-03-23 10:02:29 -0400204 pthread_mutex_destroy(&d->mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200205
bigbiffd58ba182020-03-23 10:02:29 -0400206 TrueTypeFontMap::iterator trueTypeFontIt = font_data.fonts.find(*(d->key));
207 delete d;
208 font_data.fonts.erase(trueTypeFontIt);
209
210 }
211
212 pthread_mutex_unlock(&font_data.mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200213}
214
bigbiffd58ba182020-03-23 10:02:29 -0400215TrueTypeCacheEntry* twrpTruetype::gr_ttf_glyph_cache_peek(TrueTypeFont *font, int char_index) {
216 TrueTypeCacheEntryMap::iterator glyphCacheItr = font->glyph_cache.find(char_index);
217
218 if(glyphCacheItr != font->glyph_cache.end()) {
219 return font->glyph_cache[char_index];
220 }
221 return nullptr;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200222}
223
bigbiffd58ba182020-03-23 10:02:29 -0400224TrueTypeCacheEntry* twrpTruetype::gr_ttf_glyph_cache_get(TrueTypeFont *font, int char_index) {
225 TrueTypeCacheEntryMap::iterator glyphCacheItr = font->glyph_cache.find(char_index);
226 TrueTypeCacheEntry* res = nullptr;
227 if(glyphCacheItr == font->glyph_cache.end())
228 {
229 int error = FT_Load_Glyph(font->face, char_index, FT_LOAD_RENDER);
230 if(error)
231 {
232 fprintf(stderr, "Failed to load glyph idx %d: %d\n", char_index, error);
233 return NULL;
234 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200235
bigbiffd58ba182020-03-23 10:02:29 -0400236 FT_BitmapGlyph glyph;
237 error = FT_Get_Glyph(font->face->glyph, (FT_Glyph*)&glyph);
238 if(error)
239 {
240 fprintf(stderr, "Failed to copy glyph %d: %d\n", char_index, error);
241 return NULL;
242 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200243
bigbiffd58ba182020-03-23 10:02:29 -0400244 res = new TrueTypeCacheEntry;
245 res->glyph = glyph;
246 FT_Glyph_Get_CBox((FT_Glyph)glyph, FT_GLYPH_BBOX_PIXELS, &res->bbox);
247 font->glyph_cache[char_index] = res;
248 }
249 else {
250 res = glyphCacheItr->second;
251 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200252
bigbiffd58ba182020-03-23 10:02:29 -0400253 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200254}
255
bigbiffd58ba182020-03-23 10:02:29 -0400256int twrpTruetype::gr_ttf_copy_glyph_to_surface(GGLSurface *dest, FT_BitmapGlyph glyph, int offX, int offY, int base) {
257 unsigned y;
258 uint8_t *src_itr = glyph->bitmap.buffer;
259 uint8_t *dest_itr = dest->data;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200260
bigbiffd58ba182020-03-23 10:02:29 -0400261 if(glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
262 {
263 fprintf(stderr, "Unsupported pixel mode in FT_BitmapGlyph %d\n", glyph->bitmap.pixel_mode);
264 return -1;
265 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200266
bigbiffd58ba182020-03-23 10:02:29 -0400267 dest_itr += (offY + base - glyph->top)*dest->stride + (offX + glyph->left);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200268
bigbiffd58ba182020-03-23 10:02:29 -0400269 // FIXME: if glyph->left is negative and everything else is 0 (e.g. letter 'j' in Roboto-Regular),
270 // the result might end up being before the buffer - I'm not sure how to properly handle this.
271 if(dest_itr < dest->data)
272 dest_itr = dest->data;
Vojtech Bocek54cf1082015-03-15 16:50:26 +0100273
bigbiffd58ba182020-03-23 10:02:29 -0400274 for(y = 0; y < glyph->bitmap.rows; ++y)
275 {
276 memcpy(dest_itr, src_itr, glyph->bitmap.width);
277 src_itr += glyph->bitmap.pitch;
278 dest_itr += dest->stride;
279 }
280 return 0;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200281}
282
bigbiffd58ba182020-03-23 10:02:29 -0400283void twrpTruetype::gr_ttf_calcMaxFontHeight(TrueTypeFont *f) {
284 char c;
285 int char_idx;
286 int error;
287 FT_Glyph glyph;
288 FT_BBox bbox;
289 FT_BBox bbox_glyph;
290 TrueTypeCacheEntry *ent;
Vojtech Boceka482f252015-03-15 17:03:50 +0100291
bigbiffd58ba182020-03-23 10:02:29 -0400292 bbox.yMin = bbox_glyph.yMin = LONG_MAX;
293 bbox.yMax = bbox_glyph.yMax = LONG_MIN;
Vojtech Boceka482f252015-03-15 17:03:50 +0100294
bigbiffd58ba182020-03-23 10:02:29 -0400295 for(c = '!'; c <= '~'; ++c)
296 {
297 char_idx = FT_Get_Char_Index(f->face, c);
298 ent = gr_ttf_glyph_cache_peek(f, char_idx);
299 if(ent)
300 {
301 bbox.yMin = MIN(bbox.yMin, ent->bbox.yMin);
302 bbox.yMax = MAX(bbox.yMax, ent->bbox.yMax);
303 }
304 else
305 {
306 error = FT_Load_Glyph(f->face, char_idx, 0);
307 if(error)
308 continue;
Vojtech Boceka482f252015-03-15 17:03:50 +0100309
bigbiffd58ba182020-03-23 10:02:29 -0400310 error = FT_Get_Glyph(f->face->glyph, &glyph);
311 if(error)
312 continue;
Vojtech Boceka482f252015-03-15 17:03:50 +0100313
bigbiffd58ba182020-03-23 10:02:29 -0400314 FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox_glyph);
315 bbox.yMin = MIN(bbox.yMin, bbox_glyph.yMin);
316 bbox.yMax = MAX(bbox.yMax, bbox_glyph.yMax);
Vojtech Boceka482f252015-03-15 17:03:50 +0100317
bigbiffd58ba182020-03-23 10:02:29 -0400318 FT_Done_Glyph(glyph);
319 }
320 }
Vojtech Boceka482f252015-03-15 17:03:50 +0100321
bigbiffd58ba182020-03-23 10:02:29 -0400322 if(bbox.yMin > bbox.yMax)
323 bbox.yMin = bbox.yMax = 0;
Vojtech Boceka482f252015-03-15 17:03:50 +0100324
bigbiffd58ba182020-03-23 10:02:29 -0400325 f->max_height = bbox.yMax - bbox.yMin;
326 f->base = bbox.yMax;
Vojtech Boceka482f252015-03-15 17:03:50 +0100327
bigbiffd58ba182020-03-23 10:02:29 -0400328 // FIXME: twrp fonts have some padding on top, I'll add it here
329 // Should be fixed in the themes
330 f->max_height += f->size / 4;
331 f->base += f->size / 4;
Vojtech Boceka482f252015-03-15 17:03:50 +0100332}
333
xiaolue738da52015-02-22 20:49:35 +0800334// returns number of bytes from const char *text rendered to fit max_width, not number of UTF8 characters!
bigbiffd58ba182020-03-23 10:02:29 -0400335int twrpTruetype::gr_ttf_render_text(TrueTypeFont *font, GGLSurface *surface, const char *text, int max_width) {
336 TrueTypeFont *f = font;
337 TrueTypeCacheEntry *ent;
338 int bytes_rendered = 0, total_w = 0;
339 int utf_bytes = 0;
340 unsigned int unicode = 0;
341 int i, x, diff, char_idx, prev_idx = 0;
342 int height;
343 FT_Vector delta;
344 uint8_t *data = NULL;
345 const char *text_itr = text;
346 int *char_idxs;
347 int char_idxs_len = 0;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200348
bigbiffd58ba182020-03-23 10:02:29 -0400349 char_idxs = new int[strlen(text)];
xiaolue738da52015-02-22 20:49:35 +0800350
bigbiffd58ba182020-03-23 10:02:29 -0400351 while(*text_itr)
352 {
353 utf_bytes = utf8_to_unicode(text_itr, &unicode);
354 text_itr += utf_bytes;
355 bytes_rendered += utf_bytes;
xiaolue738da52015-02-22 20:49:35 +0800356
bigbiffd58ba182020-03-23 10:02:29 -0400357 char_idx = FT_Get_Char_Index(f->face, unicode);
358 char_idxs[char_idxs_len] = char_idx;
359 ent = gr_ttf_glyph_cache_get(f, char_idx);
360 if(ent)
361 {
362 diff = ent->glyph->root.advance.x >> 16;
xiaolue738da52015-02-22 20:49:35 +0800363
bigbiffd58ba182020-03-23 10:02:29 -0400364 if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
365 {
366 FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
367 diff += delta.x >> 6;
368 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200369
bigbiffd58ba182020-03-23 10:02:29 -0400370 if(max_width != -1 && total_w + diff > max_width)
371 break;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200372
bigbiffd58ba182020-03-23 10:02:29 -0400373 total_w += diff;
374 }
375 prev_idx = char_idx;
376 ++char_idxs_len;
377 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200378
bigbiffd58ba182020-03-23 10:02:29 -0400379 if(font->max_height == -1)
380 gr_ttf_calcMaxFontHeight(font);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200381
bigbiffd58ba182020-03-23 10:02:29 -0400382 if(font->max_height == -1)
383 {
384 delete [] char_idxs;
385 return -1;
386 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200387
bigbiffd58ba182020-03-23 10:02:29 -0400388 height = font->max_height;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200389
bigbiffd58ba182020-03-23 10:02:29 -0400390 data = new uint8_t[total_w * height];
391 x = 0;
392 prev_idx = 0;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200393
bigbiffd58ba182020-03-23 10:02:29 -0400394 surface->version = sizeof(*surface);
395 surface->width = total_w;
396 surface->height = height;
397 surface->stride = total_w;
398 surface->data = (GGLubyte*)data;
399 surface->format = GGL_PIXEL_FORMAT_A_8;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200400
bigbiffd58ba182020-03-23 10:02:29 -0400401 for(i = 0; i < char_idxs_len; ++i)
402 {
403 char_idx = char_idxs[i];
404 if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
405 {
406 FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
407 x += delta.x >> 6;
408 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200409
bigbiffd58ba182020-03-23 10:02:29 -0400410 ent = gr_ttf_glyph_cache_get(f, char_idx);
411 if(ent)
412 {
413 gr_ttf_copy_glyph_to_surface(surface, ent->glyph, x, 0, font->base);
414 x += ent->glyph->root.advance.x >> 16;
415 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200416
bigbiffd58ba182020-03-23 10:02:29 -0400417 prev_idx = char_idx;
418 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200419
bigbiffd58ba182020-03-23 10:02:29 -0400420 delete [] char_idxs;
421 return bytes_rendered;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200422}
423
bigbiffd58ba182020-03-23 10:02:29 -0400424StringCacheEntry* twrpTruetype::gr_ttf_string_cache_peek(TrueTypeFont *font,
425 const char *text,
426 __attribute__((unused)) int max_width) {
427 StringCacheKey k = {
428 .text = (char*)text,
429 .max_width = max_width
430 };
431 StringCacheMap::iterator stringCacheItr = font->string_cache.find(k);
432 if (stringCacheItr != font->string_cache.end()) {
433 return stringCacheItr->second;
434 }
435 else {
436 return nullptr;
437 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200438}
439
bigbiffd58ba182020-03-23 10:02:29 -0400440StringCacheEntry* twrpTruetype::gr_ttf_string_cache_get(TrueTypeFont *font, const char *text, int max_width) {
441 StringCacheEntry *res = nullptr;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200442
bigbiffd58ba182020-03-23 10:02:29 -0400443 StringCacheKey k = {
444 .text = (char*)text,
445 .max_width = max_width
446 };
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200447
bigbiffd58ba182020-03-23 10:02:29 -0400448 StringCacheMap::iterator stringCacheItr = font->string_cache.find(k);
449 if (stringCacheItr == font->string_cache.end())
450 {
451 res = new StringCacheEntry;
452 res->rendered_bytes = gr_ttf_render_text(font, &res->surface, text, max_width);
453 if(res->rendered_bytes < 0)
454 {
455 delete res;
456 return nullptr;
457 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200458
bigbiffd58ba182020-03-23 10:02:29 -0400459 StringCacheKey *new_key = new StringCacheKey;
460 new_key->max_width = max_width;
461 new_key->text = strdup(text);
462 res->key = new_key;
463 font->string_cache[k] = res;
464 }
465 else
466 {
467 res = stringCacheItr->second;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200468
bigbiffd58ba182020-03-23 10:02:29 -0400469 // truncate old entries
470 if (font->string_cache.size() >= STRING_CACHE_MAX_ENTRIES)
471 {
472 StringCacheEntry *ent;
473 for(int i = 0; i < STRING_CACHE_TRUNCATE_ENTRIES; ++i)
474 {
475 StringCacheMap::iterator stringTruncCacheItr = font->string_cache.find(k);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200476
bigbiffd58ba182020-03-23 10:02:29 -0400477 if (stringTruncCacheItr != font->string_cache.end()) {
478 ent = stringTruncCacheItr->second;
479 gr_ttf_freeStringCache(ent->key, ent, nullptr);
480 font->string_cache.erase(stringTruncCacheItr);
481 }
482 }
483 }
484 }
485 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200486}
487
bigbiffd58ba182020-03-23 10:02:29 -0400488int twrpTruetype::gr_ttf_measureEx(const char *s, void *font) {
489 TrueTypeFont *f = (TrueTypeFont *)font;
490 int res = -1;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200491
bigbiffd58ba182020-03-23 10:02:29 -0400492 pthread_mutex_lock(&f->mutex);
493 StringCacheEntry *e = gr_ttf_string_cache_get(f, s, -1);
494 if(e)
495 res = e->surface.width;
496 pthread_mutex_unlock(&f->mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200497
bigbiffd58ba182020-03-23 10:02:29 -0400498 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200499}
500
bigbiffd58ba182020-03-23 10:02:29 -0400501int twrpTruetype::gr_ttf_maxExW(const char *s, void *font, int max_width) {
502 TrueTypeFont *f = (TrueTypeFont *)font;
503 TrueTypeCacheEntry *ent;
504 int max_bytes = 0, total_w = 0;
505 int utf_bytes, prev_utf_bytes = 0;
506 unsigned int unicode = 0;
507 int char_idx, prev_idx = 0;
508 FT_Vector delta;
509 StringCacheEntry *e;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200510
bigbiffd58ba182020-03-23 10:02:29 -0400511 pthread_mutex_lock(&f->mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200512
bigbiffd58ba182020-03-23 10:02:29 -0400513 e = gr_ttf_string_cache_peek(f, s, max_width);
514 if(e)
515 {
516 max_bytes = e->rendered_bytes;
517 pthread_mutex_unlock(&f->mutex);
518 return max_bytes;
519 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200520
bigbiffd58ba182020-03-23 10:02:29 -0400521 while(*s)
522 {
523 utf_bytes = utf8_to_unicode(s, &unicode);
524 s += utf_bytes;
xiaolue738da52015-02-22 20:49:35 +0800525
bigbiffd58ba182020-03-23 10:02:29 -0400526 char_idx = FT_Get_Char_Index(f->face, unicode);
527 if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
528 {
529 FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
530 total_w += delta.x >> 6;
531 }
532 prev_idx = char_idx;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200533
bigbiffd58ba182020-03-23 10:02:29 -0400534 if(total_w > max_width)
535 {
536 max_bytes -= prev_utf_bytes;
537 break;
538 }
539 prev_utf_bytes = utf_bytes;
540 ent = gr_ttf_glyph_cache_get(f, char_idx);
541 if(!ent)
542 continue;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200543
bigbiffd58ba182020-03-23 10:02:29 -0400544 total_w += ent->glyph->root.advance.x >> 16;
545 max_bytes += utf_bytes;
546 }
547 pthread_mutex_unlock(&f->mutex);
548 return max_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200549}
550
bigbiffd58ba182020-03-23 10:02:29 -0400551int twrpTruetype::gr_ttf_textExWH(void *context, int x, int y,
552 const char *s, void *pFont,
553 int max_width, int max_height,
554 const gr_surface gr_draw_surface) {
555 GGLContext *gl = (GGLContext *)context;
556 TrueTypeFont *font = (TrueTypeFont *)pFont;
557 const GRSurface *gr_draw = (const GRSurface*) gr_draw_surface;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200558
bigbiffd58ba182020-03-23 10:02:29 -0400559 // not actualy max width, but max_width + x
560 if(max_width != -1)
561 {
562 max_width -= x;
563 if(max_width <= 0)
564 return 0;
565 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200566
bigbiffd58ba182020-03-23 10:02:29 -0400567 pthread_mutex_lock(&font->mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200568
bigbiffd58ba182020-03-23 10:02:29 -0400569 StringCacheEntry *e = gr_ttf_string_cache_get(font, s, max_width);
570 if(!e)
571 {
572 pthread_mutex_unlock(&font->mutex);
573 return -1;
574 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200575
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300576#if TW_ROTATION != 0
bigbiffd58ba182020-03-23 10:02:29 -0400577 // Do not perform relatively expensive operation if not needed
578 GGLSurface string_surface_rotated;
579 string_surface_rotated.version = sizeof(string_surface_rotated);
580 // Skip the **(TW_ROTATION == 0)** || (TW_ROTATION == 180) check
581 // because we are under a TW_ROTATION != 0 conditional compilation statement
582 string_surface_rotated.width = (TW_ROTATION == 180) ? e->surface.width : e->surface.height;
583 string_surface_rotated.height = (TW_ROTATION == 180) ? e->surface.height : e->surface.width;
584 string_surface_rotated.stride = string_surface_rotated.width;
585 string_surface_rotated.format = e->surface.format;
586 // e->surface.format is GGL_PIXEL_FORMAT_A_8 (grayscale)
587 string_surface_rotated.data = new GGLubyte[string_surface_rotated.stride * string_surface_rotated.height * 1];
588 surface_ROTATION_transform((gr_surface) &string_surface_rotated, (const gr_surface) &e->surface, 1);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300589#endif
590
bigbiffd58ba182020-03-23 10:02:29 -0400591 int y_bottom = y + e->surface.height;
592 int res = e->rendered_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200593
bigbiffd58ba182020-03-23 10:02:29 -0400594 if(max_height != -1 && max_height < y_bottom)
595 {
596 y_bottom = max_height;
597 if(y_bottom <= y)
598 {
599 pthread_mutex_unlock(&font->mutex);
600 return 0;
601 }
602 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200603
bigbiffd58ba182020-03-23 10:02:29 -0400604 // Figuring out display coordinates works for TW_ROTATION == 0 too,
605 // and isn't as expensive as allocating and rotating another surface,
606 // so we do this anyway.
607 int x0_disp, y0_disp, x1_disp, y1_disp;
608 int l_disp, r_disp, t_disp, b_disp;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300609
bigbiffd58ba182020-03-23 10:02:29 -0400610 x0_disp = ROTATION_X_DISP(x, y, gr_draw);
611 y0_disp = ROTATION_Y_DISP(x, y, gr_draw);
612 x1_disp = ROTATION_X_DISP(x + e->surface.width, y_bottom, gr_draw);
613 y1_disp = ROTATION_Y_DISP(x + e->surface.width, y_bottom, gr_draw);
614 l_disp = std::min(x0_disp, x1_disp);
615 r_disp = std::max(x0_disp, x1_disp);
616 t_disp = std::min(y0_disp, y1_disp);
617 b_disp = std::max(y0_disp, y1_disp);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300618
619#if TW_ROTATION != 0
bigbiffd58ba182020-03-23 10:02:29 -0400620 gl->bindTexture(gl, &string_surface_rotated);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300621#else
bigbiffd58ba182020-03-23 10:02:29 -0400622 gl->bindTexture(gl, &e->surface);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300623#endif
bigbiffd58ba182020-03-23 10:02:29 -0400624 gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
625 gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
626 gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200627
bigbiffd58ba182020-03-23 10:02:29 -0400628 gl->enable(gl, GGL_TEXTURE_2D);
629 gl->texCoord2i(gl, -l_disp, -t_disp);
630 gl->recti(gl, l_disp, t_disp, r_disp, b_disp);
631 gl->disable(gl, GGL_TEXTURE_2D);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200632
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300633#if TW_ROTATION != 0
bigbiffd58ba182020-03-23 10:02:29 -0400634 delete [] string_surface_rotated.data;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300635#endif
636
bigbiffd58ba182020-03-23 10:02:29 -0400637 pthread_mutex_unlock(&font->mutex);
638 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200639}
640
bigbiffd58ba182020-03-23 10:02:29 -0400641int twrpTruetype::gr_ttf_getMaxFontHeight(void *font) {
642 int res;
643 TrueTypeFont *f = (TrueTypeFont *)font;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200644
bigbiffd58ba182020-03-23 10:02:29 -0400645 pthread_mutex_lock(&f->mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200646
bigbiffd58ba182020-03-23 10:02:29 -0400647 if(f->max_height == -1)
648 gr_ttf_calcMaxFontHeight(f);
649 res = f->max_height;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200650
bigbiffd58ba182020-03-23 10:02:29 -0400651 pthread_mutex_unlock(&f->mutex);
652 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200653}