blob: de9271560e9aa9dd1f2bec1d6af854f6191281fe [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);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400166 delete e;
bigbiffd58ba182020-03-23 10:02:29 -0400167 return true;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200168}
169
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400170void twrpTruetype::gr_ttf_freeStringCache(void *key, void *value, void *context __unused) {
bigbiffd58ba182020-03-23 10:02:29 -0400171 StringCacheKey *k = (StringCacheKey *)key;
bigbiffd58ba182020-03-23 10:02:29 -0400172 delete k;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200173
bigbiffd58ba182020-03-23 10:02:29 -0400174 StringCacheEntry *e = (StringCacheEntry *)value;
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400175 free(e->surface.data);
bigbiffd58ba182020-03-23 10:02:29 -0400176 delete e;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200177}
178
bigbiffd58ba182020-03-23 10:02:29 -0400179void twrpTruetype::gr_ttf_freeFont(void *font) {
180 pthread_mutex_lock(&font_data.mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200181
bigbiffd58ba182020-03-23 10:02:29 -0400182 TrueTypeFont *d = (TrueTypeFont *)font;
183 if(--d->refcount == 0)
184 {
185 delete d->key->path;
186 delete d->key;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200187
bigbiffd58ba182020-03-23 10:02:29 -0400188 FT_Done_Face(d->face);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200189
bigbiffd58ba182020-03-23 10:02:29 -0400190 StringCacheMap::iterator stringCacheEntryIt = d->string_cache.begin();
191 while (stringCacheEntryIt != d->string_cache.end()) {
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400192 gr_ttf_freeStringCache(stringCacheEntryIt->second->key, stringCacheEntryIt->second, nullptr);
bigbiffd58ba182020-03-23 10:02:29 -0400193 stringCacheEntryIt = d->string_cache.erase(stringCacheEntryIt);
194 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200195
bigbiffd58ba182020-03-23 10:02:29 -0400196 TrueTypeCacheEntryMap::iterator ttcIt = d->glyph_cache.begin();
197 while(ttcIt != d->glyph_cache.end()) {
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400198 gr_ttf_freeFontCache(ttcIt->second, nullptr);
bigbiffd58ba182020-03-23 10:02:29 -0400199 ttcIt = d->glyph_cache.erase(ttcIt);
200 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200201
bigbiffd58ba182020-03-23 10:02:29 -0400202 pthread_mutex_destroy(&d->mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200203
bigbiffd58ba182020-03-23 10:02:29 -0400204 TrueTypeFontMap::iterator trueTypeFontIt = font_data.fonts.find(*(d->key));
205 delete d;
206 font_data.fonts.erase(trueTypeFontIt);
207
208 }
209
210 pthread_mutex_unlock(&font_data.mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200211}
212
bigbiffd58ba182020-03-23 10:02:29 -0400213TrueTypeCacheEntry* twrpTruetype::gr_ttf_glyph_cache_peek(TrueTypeFont *font, int char_index) {
214 TrueTypeCacheEntryMap::iterator glyphCacheItr = font->glyph_cache.find(char_index);
215
216 if(glyphCacheItr != font->glyph_cache.end()) {
217 return font->glyph_cache[char_index];
218 }
219 return nullptr;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200220}
221
bigbiffd58ba182020-03-23 10:02:29 -0400222TrueTypeCacheEntry* twrpTruetype::gr_ttf_glyph_cache_get(TrueTypeFont *font, int char_index) {
223 TrueTypeCacheEntryMap::iterator glyphCacheItr = font->glyph_cache.find(char_index);
224 TrueTypeCacheEntry* res = nullptr;
225 if(glyphCacheItr == font->glyph_cache.end())
226 {
227 int error = FT_Load_Glyph(font->face, char_index, FT_LOAD_RENDER);
228 if(error)
229 {
230 fprintf(stderr, "Failed to load glyph idx %d: %d\n", char_index, error);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400231 return nullptr;
bigbiffd58ba182020-03-23 10:02:29 -0400232 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200233
bigbiffd58ba182020-03-23 10:02:29 -0400234 FT_BitmapGlyph glyph;
235 error = FT_Get_Glyph(font->face->glyph, (FT_Glyph*)&glyph);
236 if(error)
237 {
238 fprintf(stderr, "Failed to copy glyph %d: %d\n", char_index, error);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400239 return nullptr;
bigbiffd58ba182020-03-23 10:02:29 -0400240 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200241
bigbiffd58ba182020-03-23 10:02:29 -0400242 res = new TrueTypeCacheEntry;
243 res->glyph = glyph;
244 FT_Glyph_Get_CBox((FT_Glyph)glyph, FT_GLYPH_BBOX_PIXELS, &res->bbox);
245 font->glyph_cache[char_index] = res;
246 }
247 else {
248 res = glyphCacheItr->second;
249 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200250
bigbiffd58ba182020-03-23 10:02:29 -0400251 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200252}
253
bigbiffd58ba182020-03-23 10:02:29 -0400254int twrpTruetype::gr_ttf_copy_glyph_to_surface(GGLSurface *dest, FT_BitmapGlyph glyph, int offX, int offY, int base) {
255 unsigned y;
256 uint8_t *src_itr = glyph->bitmap.buffer;
257 uint8_t *dest_itr = dest->data;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200258
bigbiffd58ba182020-03-23 10:02:29 -0400259 if(glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
260 {
261 fprintf(stderr, "Unsupported pixel mode in FT_BitmapGlyph %d\n", glyph->bitmap.pixel_mode);
262 return -1;
263 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200264
bigbiffd58ba182020-03-23 10:02:29 -0400265 dest_itr += (offY + base - glyph->top)*dest->stride + (offX + glyph->left);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200266
bigbiffd58ba182020-03-23 10:02:29 -0400267 // FIXME: if glyph->left is negative and everything else is 0 (e.g. letter 'j' in Roboto-Regular),
268 // the result might end up being before the buffer - I'm not sure how to properly handle this.
269 if(dest_itr < dest->data)
270 dest_itr = dest->data;
Vojtech Bocek54cf1082015-03-15 16:50:26 +0100271
bigbiffd58ba182020-03-23 10:02:29 -0400272 for(y = 0; y < glyph->bitmap.rows; ++y)
273 {
274 memcpy(dest_itr, src_itr, glyph->bitmap.width);
275 src_itr += glyph->bitmap.pitch;
276 dest_itr += dest->stride;
277 }
278 return 0;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200279}
280
bigbiffd58ba182020-03-23 10:02:29 -0400281void twrpTruetype::gr_ttf_calcMaxFontHeight(TrueTypeFont *f) {
282 char c;
283 int char_idx;
284 int error;
285 FT_Glyph glyph;
286 FT_BBox bbox;
287 FT_BBox bbox_glyph;
288 TrueTypeCacheEntry *ent;
Vojtech Boceka482f252015-03-15 17:03:50 +0100289
bigbiffd58ba182020-03-23 10:02:29 -0400290 bbox.yMin = bbox_glyph.yMin = LONG_MAX;
291 bbox.yMax = bbox_glyph.yMax = LONG_MIN;
Vojtech Boceka482f252015-03-15 17:03:50 +0100292
bigbiffd58ba182020-03-23 10:02:29 -0400293 for(c = '!'; c <= '~'; ++c)
294 {
295 char_idx = FT_Get_Char_Index(f->face, c);
296 ent = gr_ttf_glyph_cache_peek(f, char_idx);
297 if(ent)
298 {
299 bbox.yMin = MIN(bbox.yMin, ent->bbox.yMin);
300 bbox.yMax = MAX(bbox.yMax, ent->bbox.yMax);
301 }
302 else
303 {
304 error = FT_Load_Glyph(f->face, char_idx, 0);
305 if(error)
306 continue;
Vojtech Boceka482f252015-03-15 17:03:50 +0100307
bigbiffd58ba182020-03-23 10:02:29 -0400308 error = FT_Get_Glyph(f->face->glyph, &glyph);
309 if(error)
310 continue;
Vojtech Boceka482f252015-03-15 17:03:50 +0100311
bigbiffd58ba182020-03-23 10:02:29 -0400312 FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox_glyph);
313 bbox.yMin = MIN(bbox.yMin, bbox_glyph.yMin);
314 bbox.yMax = MAX(bbox.yMax, bbox_glyph.yMax);
Vojtech Boceka482f252015-03-15 17:03:50 +0100315
bigbiffd58ba182020-03-23 10:02:29 -0400316 FT_Done_Glyph(glyph);
317 }
318 }
Vojtech Boceka482f252015-03-15 17:03:50 +0100319
bigbiffd58ba182020-03-23 10:02:29 -0400320 if(bbox.yMin > bbox.yMax)
321 bbox.yMin = bbox.yMax = 0;
Vojtech Boceka482f252015-03-15 17:03:50 +0100322
bigbiffd58ba182020-03-23 10:02:29 -0400323 f->max_height = bbox.yMax - bbox.yMin;
324 f->base = bbox.yMax;
Vojtech Boceka482f252015-03-15 17:03:50 +0100325
bigbiffd58ba182020-03-23 10:02:29 -0400326 // FIXME: twrp fonts have some padding on top, I'll add it here
327 // Should be fixed in the themes
328 f->max_height += f->size / 4;
329 f->base += f->size / 4;
Vojtech Boceka482f252015-03-15 17:03:50 +0100330}
331
xiaolue738da52015-02-22 20:49:35 +0800332// returns number of bytes from const char *text rendered to fit max_width, not number of UTF8 characters!
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400333int twrpTruetype::gr_ttf_render_text(TrueTypeFont *font, GGLSurface *surface, const std::string text, int max_width) {
bigbiffd58ba182020-03-23 10:02:29 -0400334 TrueTypeFont *f = font;
335 TrueTypeCacheEntry *ent;
336 int bytes_rendered = 0, total_w = 0;
337 int utf_bytes = 0;
338 unsigned int unicode = 0;
339 int i, x, diff, char_idx, prev_idx = 0;
340 int height;
341 FT_Vector delta;
342 uint8_t *data = NULL;
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400343 const char *text_itr = text.c_str();
bigbiffd58ba182020-03-23 10:02:29 -0400344 int *char_idxs;
345 int char_idxs_len = 0;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200346
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400347 char_idxs = new int[text.length()];
xiaolue738da52015-02-22 20:49:35 +0800348
bigbiffd58ba182020-03-23 10:02:29 -0400349 while(*text_itr)
350 {
351 utf_bytes = utf8_to_unicode(text_itr, &unicode);
352 text_itr += utf_bytes;
353 bytes_rendered += utf_bytes;
xiaolue738da52015-02-22 20:49:35 +0800354
bigbiffd58ba182020-03-23 10:02:29 -0400355 char_idx = FT_Get_Char_Index(f->face, unicode);
356 char_idxs[char_idxs_len] = char_idx;
357 ent = gr_ttf_glyph_cache_get(f, char_idx);
358 if(ent)
359 {
360 diff = ent->glyph->root.advance.x >> 16;
xiaolue738da52015-02-22 20:49:35 +0800361
bigbiffd58ba182020-03-23 10:02:29 -0400362 if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
363 {
364 FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
365 diff += delta.x >> 6;
366 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200367
bigbiffd58ba182020-03-23 10:02:29 -0400368 if(max_width != -1 && total_w + diff > max_width)
369 break;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200370
bigbiffd58ba182020-03-23 10:02:29 -0400371 total_w += diff;
372 }
373 prev_idx = char_idx;
374 ++char_idxs_len;
375 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200376
bigbiffd58ba182020-03-23 10:02:29 -0400377 if(font->max_height == -1)
378 gr_ttf_calcMaxFontHeight(font);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200379
bigbiffd58ba182020-03-23 10:02:29 -0400380 if(font->max_height == -1)
381 {
382 delete [] char_idxs;
383 return -1;
384 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200385
bigbiffd58ba182020-03-23 10:02:29 -0400386 height = font->max_height;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200387
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400388 data = (uint8_t *)malloc(total_w*height);
389 memset(data, 0, total_w*height); x = 0;
bigbiffd58ba182020-03-23 10:02:29 -0400390 prev_idx = 0;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200391
bigbiffd58ba182020-03-23 10:02:29 -0400392 surface->version = sizeof(*surface);
393 surface->width = total_w;
394 surface->height = height;
395 surface->stride = total_w;
396 surface->data = (GGLubyte*)data;
397 surface->format = GGL_PIXEL_FORMAT_A_8;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200398
bigbiffd58ba182020-03-23 10:02:29 -0400399 for(i = 0; i < char_idxs_len; ++i)
400 {
401 char_idx = char_idxs[i];
402 if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
403 {
404 FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
405 x += delta.x >> 6;
406 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200407
bigbiffd58ba182020-03-23 10:02:29 -0400408 ent = gr_ttf_glyph_cache_get(f, char_idx);
409 if(ent)
410 {
411 gr_ttf_copy_glyph_to_surface(surface, ent->glyph, x, 0, font->base);
412 x += ent->glyph->root.advance.x >> 16;
413 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200414
bigbiffd58ba182020-03-23 10:02:29 -0400415 prev_idx = char_idx;
416 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200417
bigbiffd58ba182020-03-23 10:02:29 -0400418 delete [] char_idxs;
419 return bytes_rendered;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200420}
421
bigbiffd58ba182020-03-23 10:02:29 -0400422StringCacheEntry* twrpTruetype::gr_ttf_string_cache_peek(TrueTypeFont *font,
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400423 const std::string text,
bigbiffd58ba182020-03-23 10:02:29 -0400424 __attribute__((unused)) int max_width) {
425 StringCacheKey k = {
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400426 .text = text,
bigbiffd58ba182020-03-23 10:02:29 -0400427 .max_width = max_width
428 };
429 StringCacheMap::iterator stringCacheItr = font->string_cache.find(k);
430 if (stringCacheItr != font->string_cache.end()) {
431 return stringCacheItr->second;
432 }
433 else {
434 return nullptr;
435 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200436}
437
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400438void twrpTruetype::gr_ttf_string_cache_truncate(TrueTypeFont *font) {
439 StringCacheMap::iterator stringCacheItr;
440
441 if (font->string_cache.size() == STRING_CACHE_MAX_ENTRIES) {
442 StringCacheEntry *truncateEntry = nullptr;
443 stringCacheItr = font->string_cache.begin();
444 int deleteCtr = 0;
445 while (stringCacheItr != font->string_cache.end() || deleteCtr == (STRING_CACHE_MAX_ENTRIES - 1)) {
446 truncateEntry = stringCacheItr->second;
447 gr_ttf_freeStringCache(truncateEntry->key, truncateEntry, nullptr);
448 stringCacheItr = font->string_cache.erase(stringCacheItr);
449 deleteCtr++;
450 }
451 }
452}
453
454StringCacheEntry* twrpTruetype::gr_ttf_string_cache_get(TrueTypeFont *font, const std::string text, int max_width) {
bigbiffd58ba182020-03-23 10:02:29 -0400455 StringCacheEntry *res = nullptr;
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400456 StringCacheMap::iterator stringCacheItr;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200457
bigbiffd58ba182020-03-23 10:02:29 -0400458 StringCacheKey k = {
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400459 .text = text,
bigbiffd58ba182020-03-23 10:02:29 -0400460 .max_width = max_width
461 };
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200462
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400463 stringCacheItr = font->string_cache.find(k);
464 if (stringCacheItr == font->string_cache.end()) {
bigbiffd58ba182020-03-23 10:02:29 -0400465 res = new StringCacheEntry;
466 res->rendered_bytes = gr_ttf_render_text(font, &res->surface, text, max_width);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400467 if(res->rendered_bytes < 0) {
bigbiffd58ba182020-03-23 10:02:29 -0400468 delete res;
469 return nullptr;
470 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200471
bigbiffd58ba182020-03-23 10:02:29 -0400472 StringCacheKey *new_key = new StringCacheKey;
473 new_key->max_width = max_width;
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400474 new_key->text = text;
bigbiffd58ba182020-03-23 10:02:29 -0400475 res->key = new_key;
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400476 font->string_cache[*new_key] = res;
bigbiffd58ba182020-03-23 10:02:29 -0400477 }
478 else
479 {
480 res = stringCacheItr->second;
bigbiffd58ba182020-03-23 10:02:29 -0400481 }
482 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200483}
484
bigbiffd58ba182020-03-23 10:02:29 -0400485int twrpTruetype::gr_ttf_measureEx(const char *s, void *font) {
486 TrueTypeFont *f = (TrueTypeFont *)font;
487 int res = -1;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200488
bigbiffd58ba182020-03-23 10:02:29 -0400489 pthread_mutex_lock(&f->mutex);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400490 gr_ttf_string_cache_truncate(f);
bigbiffd58ba182020-03-23 10:02:29 -0400491 StringCacheEntry *e = gr_ttf_string_cache_get(f, s, -1);
492 if(e)
493 res = e->surface.width;
494 pthread_mutex_unlock(&f->mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200495
bigbiffd58ba182020-03-23 10:02:29 -0400496 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200497}
498
bigbiffd58ba182020-03-23 10:02:29 -0400499int twrpTruetype::gr_ttf_maxExW(const char *s, void *font, int max_width) {
500 TrueTypeFont *f = (TrueTypeFont *)font;
501 TrueTypeCacheEntry *ent;
502 int max_bytes = 0, total_w = 0;
503 int utf_bytes, prev_utf_bytes = 0;
504 unsigned int unicode = 0;
505 int char_idx, prev_idx = 0;
506 FT_Vector delta;
507 StringCacheEntry *e;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200508
bigbiffd58ba182020-03-23 10:02:29 -0400509 pthread_mutex_lock(&f->mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200510
bigbiffd58ba182020-03-23 10:02:29 -0400511 e = gr_ttf_string_cache_peek(f, s, max_width);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400512 if (e) {
bigbiffd58ba182020-03-23 10:02:29 -0400513 max_bytes = e->rendered_bytes;
514 pthread_mutex_unlock(&f->mutex);
515 return max_bytes;
516 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200517
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400518 while(*s) {
bigbiffd58ba182020-03-23 10:02:29 -0400519 utf_bytes = utf8_to_unicode(s, &unicode);
520 s += utf_bytes;
xiaolue738da52015-02-22 20:49:35 +0800521
bigbiffd58ba182020-03-23 10:02:29 -0400522 char_idx = FT_Get_Char_Index(f->face, unicode);
523 if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
524 {
525 FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
526 total_w += delta.x >> 6;
527 }
528 prev_idx = char_idx;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200529
bigbiffd58ba182020-03-23 10:02:29 -0400530 if(total_w > max_width)
531 {
532 max_bytes -= prev_utf_bytes;
533 break;
534 }
535 prev_utf_bytes = utf_bytes;
536 ent = gr_ttf_glyph_cache_get(f, char_idx);
537 if(!ent)
538 continue;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200539
bigbiffd58ba182020-03-23 10:02:29 -0400540 total_w += ent->glyph->root.advance.x >> 16;
541 max_bytes += utf_bytes;
542 }
543 pthread_mutex_unlock(&f->mutex);
544 return max_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200545}
546
bigbiffd58ba182020-03-23 10:02:29 -0400547int twrpTruetype::gr_ttf_textExWH(void *context, int x, int y,
548 const char *s, void *pFont,
549 int max_width, int max_height,
550 const gr_surface gr_draw_surface) {
551 GGLContext *gl = (GGLContext *)context;
552 TrueTypeFont *font = (TrueTypeFont *)pFont;
553 const GRSurface *gr_draw = (const GRSurface*) gr_draw_surface;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200554
bigbiffd58ba182020-03-23 10:02:29 -0400555 // not actualy max width, but max_width + x
556 if(max_width != -1)
557 {
558 max_width -= x;
559 if(max_width <= 0)
560 return 0;
561 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200562
bigbiffd58ba182020-03-23 10:02:29 -0400563 pthread_mutex_lock(&font->mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200564
bigbiffd58ba182020-03-23 10:02:29 -0400565 StringCacheEntry *e = gr_ttf_string_cache_get(font, s, max_width);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400566 if (!e) {
bigbiffd58ba182020-03-23 10:02:29 -0400567 pthread_mutex_unlock(&font->mutex);
568 return -1;
569 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200570
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300571#if TW_ROTATION != 0
bigbiffd58ba182020-03-23 10:02:29 -0400572 // Do not perform relatively expensive operation if not needed
573 GGLSurface string_surface_rotated;
574 string_surface_rotated.version = sizeof(string_surface_rotated);
575 // Skip the **(TW_ROTATION == 0)** || (TW_ROTATION == 180) check
576 // because we are under a TW_ROTATION != 0 conditional compilation statement
577 string_surface_rotated.width = (TW_ROTATION == 180) ? e->surface.width : e->surface.height;
578 string_surface_rotated.height = (TW_ROTATION == 180) ? e->surface.height : e->surface.width;
579 string_surface_rotated.stride = string_surface_rotated.width;
580 string_surface_rotated.format = e->surface.format;
581 // e->surface.format is GGL_PIXEL_FORMAT_A_8 (grayscale)
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400582 string_surface_rotated.data = (GGLubyte*) malloc(string_surface_rotated.stride * string_surface_rotated.height * 1);
583 surface_ROTATION_transform((gr_surface) &string_surface_rotated, (const gr_surface) &e->surface, 1);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300584#endif
585
bigbiffd58ba182020-03-23 10:02:29 -0400586 int y_bottom = y + e->surface.height;
587 int res = e->rendered_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200588
bigbiffd58ba182020-03-23 10:02:29 -0400589 if(max_height != -1 && max_height < y_bottom)
590 {
591 y_bottom = max_height;
592 if(y_bottom <= y)
593 {
594 pthread_mutex_unlock(&font->mutex);
595 return 0;
596 }
597 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200598
bigbiffd58ba182020-03-23 10:02:29 -0400599 // Figuring out display coordinates works for TW_ROTATION == 0 too,
600 // and isn't as expensive as allocating and rotating another surface,
601 // so we do this anyway.
602 int x0_disp, y0_disp, x1_disp, y1_disp;
603 int l_disp, r_disp, t_disp, b_disp;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300604
bigbiffd58ba182020-03-23 10:02:29 -0400605 x0_disp = ROTATION_X_DISP(x, y, gr_draw);
606 y0_disp = ROTATION_Y_DISP(x, y, gr_draw);
607 x1_disp = ROTATION_X_DISP(x + e->surface.width, y_bottom, gr_draw);
608 y1_disp = ROTATION_Y_DISP(x + e->surface.width, y_bottom, gr_draw);
609 l_disp = std::min(x0_disp, x1_disp);
610 r_disp = std::max(x0_disp, x1_disp);
611 t_disp = std::min(y0_disp, y1_disp);
612 b_disp = std::max(y0_disp, y1_disp);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300613
614#if TW_ROTATION != 0
bigbiffd58ba182020-03-23 10:02:29 -0400615 gl->bindTexture(gl, &string_surface_rotated);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300616#else
bigbiffd58ba182020-03-23 10:02:29 -0400617 gl->bindTexture(gl, &e->surface);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300618#endif
bigbiffd58ba182020-03-23 10:02:29 -0400619 gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
620 gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
621 gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200622
bigbiffd58ba182020-03-23 10:02:29 -0400623 gl->enable(gl, GGL_TEXTURE_2D);
624 gl->texCoord2i(gl, -l_disp, -t_disp);
625 gl->recti(gl, l_disp, t_disp, r_disp, b_disp);
626 gl->disable(gl, GGL_TEXTURE_2D);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200627
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300628#if TW_ROTATION != 0
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400629 free(string_surface_rotated.data);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300630#endif
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400631 pthread_mutex_unlock(&font->mutex);;
bigbiffd58ba182020-03-23 10:02:29 -0400632 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200633}
634
bigbiffd58ba182020-03-23 10:02:29 -0400635int twrpTruetype::gr_ttf_getMaxFontHeight(void *font) {
636 int res;
637 TrueTypeFont *f = (TrueTypeFont *)font;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200638
bigbiffd58ba182020-03-23 10:02:29 -0400639 pthread_mutex_lock(&f->mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200640
bigbiffd58ba182020-03-23 10:02:29 -0400641 if(f->max_height == -1)
642 gr_ttf_calcMaxFontHeight(f);
643 res = f->max_height;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200644
bigbiffd58ba182020-03-23 10:02:29 -0400645 pthread_mutex_unlock(&f->mutex);
646 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200647}