blob: d224fbb120af57eb2770cc58905aea1e5273cab6 [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>
bigbiffd81833a2021-01-17 11:06:57 -050029#include "minuitwrp/truetype.hpp"
Vojtech Bocek76ee9032014-09-07 15:01:56 +020030
Aaron Kling4eccd9a2019-06-30 12:20:09 -050031extern unsigned int gr_rotation;
32
Vojtech Bocek76ee9032014-09-07 15:01:56 +020033static FontData font_data = {
bigbiffd58ba182020-03-23 10:02:29 -040034 .ft_library = NULL,
35 .mutex = PTHREAD_MUTEX_INITIALIZER
Vojtech Bocek76ee9032014-09-07 15:01:56 +020036};
37
bigbiffd58ba182020-03-23 10:02:29 -040038twrpTruetype::twrpTruetype(void) {
Vojtech Bocek76ee9032014-09-07 15:01:56 +020039
Vojtech Bocek76ee9032014-09-07 15:01:56 +020040}
41
bigbiffd58ba182020-03-23 10:02:29 -040042int twrpTruetype::utf8_to_unicode(const char* pIn, unsigned int *pOut) {
43 int utf_bytes = 1;
44 unsigned int unicode = 0;
45 unsigned char tmp;
46 tmp = (unsigned char)*pIn++;
47 if (tmp < 0x80)
48 {
49 *pOut = tmp;
50 }
51 else
52 {
53 unsigned int high_bit_mask = 0x3F;
54 unsigned int high_bit_shift = 0;
55 int total_bits = 0;
56 while((tmp & 0xC0) == 0xC0)
57 {
58 utf_bytes ++;
59 if(utf_bytes > 6)
60 {
61 *pOut = tmp;
62 return 1;
63 }
64 tmp = 0xFF & (tmp << 1);
65 total_bits += 6;
66 high_bit_mask >>= 1;
67 high_bit_shift++;
68 unicode <<= 6;
69 unicode |= (*pIn++) & 0x3F;
70 }
71 unicode |= ((tmp >> high_bit_shift) & high_bit_mask) << total_bits;
72 *pOut = unicode;
73 }
74
75 return utf_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +020076}
77
bigbiffd58ba182020-03-23 10:02:29 -040078void* twrpTruetype::gr_ttf_loadFont(const char *filename, int size, int dpi) {
79 int error;
80 TrueTypeFont* res = nullptr;
81 TrueTypeFontKey* key;
bigbifffdca6b82020-09-12 13:36:33 -040082 std::string fontFileName(filename);
soyud5a7c0e2014-11-28 14:33:53 +080083
bigbiffd58ba182020-03-23 10:02:29 -040084 pthread_mutex_lock(&font_data.mutex);
soyud5a7c0e2014-11-28 14:33:53 +080085
bigbiffd58ba182020-03-23 10:02:29 -040086 TrueTypeFontKey k = {
87 .size = size,
88 .dpi = dpi,
bigbifffdca6b82020-09-12 13:36:33 -040089 .path = fontFileName
bigbiffd58ba182020-03-23 10:02:29 -040090 };
Vojtech Bocek76ee9032014-09-07 15:01:56 +020091
bigbiffd58ba182020-03-23 10:02:29 -040092 TrueTypeFontMap::iterator ttfIter = font_data.fonts.find(k);
Vojtech Bocek76ee9032014-09-07 15:01:56 +020093
bigbiffd58ba182020-03-23 10:02:29 -040094 if (ttfIter != font_data.fonts.end())
95 {
96 res = ttfIter->second;
97 ++res->refcount;
98 goto exit;
99 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200100
bigbiffd58ba182020-03-23 10:02:29 -0400101 if(!font_data.ft_library)
102 {
103 error = FT_Init_FreeType(&font_data.ft_library);
104 if(error)
105 {
106 fprintf(stderr, "Failed to init libfreetype! %d\n", error);
107 goto exit;
108 }
109 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200110
bigbiffd58ba182020-03-23 10:02:29 -0400111 FT_Face face;
112 error = FT_New_Face(font_data.ft_library, filename, 0, &face);
113 if(error)
114 {
115 fprintf(stderr, "Failed to load truetype face %s: %d\n", filename, error);
116 goto exit;
117 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200118
bigbiffd58ba182020-03-23 10:02:29 -0400119 error = FT_Set_Char_Size(face, 0, size*16, dpi, dpi);
120 if(error)
121 {
122 fprintf(stderr, "Failed to set truetype face size to %d, dpi %d: %d\n", size, dpi, error);
123 FT_Done_Face(face);
124 goto exit;
125 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200126
bigbiffd58ba182020-03-23 10:02:29 -0400127 res = new TrueTypeFont;
128 res->type = FONT_TYPE_TTF;
129 res->size = size;
130 res->dpi = dpi;
131 res->face = face;
132 res->max_height = -1;
133 res->base = -1;
134 res->refcount = 1;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200135
bigbiffd58ba182020-03-23 10:02:29 -0400136 pthread_mutex_init(&res->mutex, 0);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200137
bigbiffd58ba182020-03-23 10:02:29 -0400138 key = new TrueTypeFontKey;
139 key->path = strdup(filename);
140 key->size = size;
141 key->dpi = dpi;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200142
bigbiffd58ba182020-03-23 10:02:29 -0400143 res->key = key;
144 font_data.fonts[*key] = res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200145
146exit:
bigbiffd58ba182020-03-23 10:02:29 -0400147 pthread_mutex_unlock(&font_data.mutex);
148 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200149}
150
bigbiffd58ba182020-03-23 10:02:29 -0400151void* twrpTruetype::gr_ttf_scaleFont(void *font, int max_width, int measured_width) {
152 if (!font)
153 return nullptr;
Ethan Yonkerb7a54a32015-10-05 10:16:27 -0500154
bigbiffd58ba182020-03-23 10:02:29 -0400155 TrueTypeFont *f = (TrueTypeFont *)font;
156 float scale_value = (float)(max_width) / (float)(measured_width);
157 int new_size = ((int)((float)f->size * scale_value)) - 1;
158 if (new_size < 1)
159 new_size = 1;
bigbifffdca6b82020-09-12 13:36:33 -0400160 const char* file = f->key->path.c_str();
bigbiffd58ba182020-03-23 10:02:29 -0400161 int dpi = f->dpi;
162 return gr_ttf_loadFont(file, new_size, dpi);
Ethan Yonkerb7a54a32015-10-05 10:16:27 -0500163}
164
bigbiffd58ba182020-03-23 10:02:29 -0400165static bool gr_ttf_freeFontCache(void *value, void *context __unused)
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200166{
bigbiffd58ba182020-03-23 10:02:29 -0400167 TrueTypeCacheEntry *e = (TrueTypeCacheEntry *)value;
168 FT_Done_Glyph((FT_Glyph)e->glyph);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400169 delete e;
bigbiffd58ba182020-03-23 10:02:29 -0400170 return true;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200171}
172
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400173void twrpTruetype::gr_ttf_freeStringCache(void *key, void *value, void *context __unused) {
bigbiffd58ba182020-03-23 10:02:29 -0400174 StringCacheKey *k = (StringCacheKey *)key;
bigbiffd58ba182020-03-23 10:02:29 -0400175 delete k;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200176
bigbiffd58ba182020-03-23 10:02:29 -0400177 StringCacheEntry *e = (StringCacheEntry *)value;
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400178 free(e->surface.data);
bigbiffd58ba182020-03-23 10:02:29 -0400179 delete e;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200180}
181
bigbiffd58ba182020-03-23 10:02:29 -0400182void twrpTruetype::gr_ttf_freeFont(void *font) {
183 pthread_mutex_lock(&font_data.mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200184
bigbiffd58ba182020-03-23 10:02:29 -0400185 TrueTypeFont *d = (TrueTypeFont *)font;
186 if(--d->refcount == 0)
187 {
bigbiffd58ba182020-03-23 10:02:29 -0400188 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()) {
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400194 gr_ttf_freeStringCache(stringCacheEntryIt->second->key, stringCacheEntryIt->second, nullptr);
bigbiffd58ba182020-03-23 10:02:29 -0400195 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()) {
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400200 gr_ttf_freeFontCache(ttcIt->second, nullptr);
bigbiffd58ba182020-03-23 10:02:29 -0400201 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);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400233 return nullptr;
bigbiffd58ba182020-03-23 10:02:29 -0400234 }
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);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400241 return nullptr;
bigbiffd58ba182020-03-23 10:02:29 -0400242 }
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!
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400335int twrpTruetype::gr_ttf_render_text(TrueTypeFont *font, GGLSurface *surface, const std::string text, int max_width) {
bigbiffd58ba182020-03-23 10:02:29 -0400336 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;
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400345 const char *text_itr = text.c_str();
bigbiffd58ba182020-03-23 10:02:29 -0400346 int *char_idxs;
347 int char_idxs_len = 0;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200348
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400349 char_idxs = new int[text.length()];
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
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400390 data = (uint8_t *)malloc(total_w*height);
391 memset(data, 0, total_w*height); x = 0;
bigbiffd58ba182020-03-23 10:02:29 -0400392 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,
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400425 const std::string text,
bigbiffd58ba182020-03-23 10:02:29 -0400426 __attribute__((unused)) int max_width) {
427 StringCacheKey k = {
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400428 .text = text,
bigbiffd58ba182020-03-23 10:02:29 -0400429 .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
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400440void twrpTruetype::gr_ttf_string_cache_truncate(TrueTypeFont *font) {
441 StringCacheMap::iterator stringCacheItr;
442
443 if (font->string_cache.size() == STRING_CACHE_MAX_ENTRIES) {
444 StringCacheEntry *truncateEntry = nullptr;
445 stringCacheItr = font->string_cache.begin();
446 int deleteCtr = 0;
447 while (stringCacheItr != font->string_cache.end() || deleteCtr == (STRING_CACHE_MAX_ENTRIES - 1)) {
448 truncateEntry = stringCacheItr->second;
449 gr_ttf_freeStringCache(truncateEntry->key, truncateEntry, nullptr);
450 stringCacheItr = font->string_cache.erase(stringCacheItr);
451 deleteCtr++;
452 }
453 }
454}
455
456StringCacheEntry* twrpTruetype::gr_ttf_string_cache_get(TrueTypeFont *font, const std::string text, int max_width) {
bigbiffd58ba182020-03-23 10:02:29 -0400457 StringCacheEntry *res = nullptr;
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400458 StringCacheMap::iterator stringCacheItr;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200459
bigbiffd58ba182020-03-23 10:02:29 -0400460 StringCacheKey k = {
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400461 .text = text,
bigbiffd58ba182020-03-23 10:02:29 -0400462 .max_width = max_width
463 };
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200464
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400465 stringCacheItr = font->string_cache.find(k);
466 if (stringCacheItr == font->string_cache.end()) {
bigbiffd58ba182020-03-23 10:02:29 -0400467 res = new StringCacheEntry;
468 res->rendered_bytes = gr_ttf_render_text(font, &res->surface, text, max_width);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400469 if(res->rendered_bytes < 0) {
bigbiffd58ba182020-03-23 10:02:29 -0400470 delete res;
471 return nullptr;
472 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200473
bigbiffd58ba182020-03-23 10:02:29 -0400474 StringCacheKey *new_key = new StringCacheKey;
475 new_key->max_width = max_width;
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400476 new_key->text = text;
bigbiffd58ba182020-03-23 10:02:29 -0400477 res->key = new_key;
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400478 font->string_cache[*new_key] = res;
bigbiffd58ba182020-03-23 10:02:29 -0400479 }
480 else
481 {
482 res = stringCacheItr->second;
bigbiffd58ba182020-03-23 10:02:29 -0400483 }
484 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200485}
486
bigbiffd58ba182020-03-23 10:02:29 -0400487int twrpTruetype::gr_ttf_measureEx(const char *s, void *font) {
488 TrueTypeFont *f = (TrueTypeFont *)font;
489 int res = -1;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200490
bigbiffd58ba182020-03-23 10:02:29 -0400491 pthread_mutex_lock(&f->mutex);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400492 gr_ttf_string_cache_truncate(f);
bigbiffd58ba182020-03-23 10:02:29 -0400493 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);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400514 if (e) {
bigbiffd58ba182020-03-23 10:02:29 -0400515 max_bytes = e->rendered_bytes;
516 pthread_mutex_unlock(&f->mutex);
517 return max_bytes;
518 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200519
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400520 while(*s) {
bigbiffd58ba182020-03-23 10:02:29 -0400521 utf_bytes = utf8_to_unicode(s, &unicode);
522 s += utf_bytes;
xiaolue738da52015-02-22 20:49:35 +0800523
bigbiffd58ba182020-03-23 10:02:29 -0400524 char_idx = FT_Get_Char_Index(f->face, unicode);
525 if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
526 {
527 FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
528 total_w += delta.x >> 6;
529 }
530 prev_idx = char_idx;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200531
bigbiffd58ba182020-03-23 10:02:29 -0400532 if(total_w > max_width)
533 {
534 max_bytes -= prev_utf_bytes;
535 break;
536 }
537 prev_utf_bytes = utf_bytes;
538 ent = gr_ttf_glyph_cache_get(f, char_idx);
539 if(!ent)
540 continue;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200541
bigbiffd58ba182020-03-23 10:02:29 -0400542 total_w += ent->glyph->root.advance.x >> 16;
543 max_bytes += utf_bytes;
544 }
545 pthread_mutex_unlock(&f->mutex);
546 return max_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200547}
548
bigbiffd58ba182020-03-23 10:02:29 -0400549int twrpTruetype::gr_ttf_textExWH(void *context, int x, int y,
550 const char *s, void *pFont,
551 int max_width, int max_height,
552 const gr_surface gr_draw_surface) {
553 GGLContext *gl = (GGLContext *)context;
554 TrueTypeFont *font = (TrueTypeFont *)pFont;
555 const GRSurface *gr_draw = (const GRSurface*) gr_draw_surface;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200556
bigbiffd58ba182020-03-23 10:02:29 -0400557 // not actualy max width, but max_width + x
558 if(max_width != -1)
559 {
560 max_width -= x;
561 if(max_width <= 0)
562 return 0;
563 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200564
bigbiffd58ba182020-03-23 10:02:29 -0400565 pthread_mutex_lock(&font->mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200566
bigbiffd58ba182020-03-23 10:02:29 -0400567 StringCacheEntry *e = gr_ttf_string_cache_get(font, s, max_width);
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400568 if (!e) {
bigbiffd58ba182020-03-23 10:02:29 -0400569 pthread_mutex_unlock(&font->mutex);
570 return -1;
571 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200572
bigbiffd58ba182020-03-23 10:02:29 -0400573 GGLSurface string_surface_rotated;
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500574 if (gr_rotation != 0) {
575 // Do not perform relatively expensive operation if not needed
576 string_surface_rotated.version = sizeof(string_surface_rotated);
577 // Skip the **(gr_rotation == 0)** || (gr_rotation == 180) check
578 // because we are under a gr_rotation != 0 conditional compilation statement
579 string_surface_rotated.width = (gr_rotation == 180) ? e->surface.width : e->surface.height;
580 string_surface_rotated.height = (gr_rotation == 180) ? e->surface.height : e->surface.width;
581 string_surface_rotated.stride = string_surface_rotated.width;
582 string_surface_rotated.format = e->surface.format;
583 // e->surface.format is GGL_PIXEL_FORMAT_A_8 (grayscale)
584 string_surface_rotated.data = (GGLubyte*) malloc(string_surface_rotated.stride * string_surface_rotated.height * 1);
585 surface_ROTATION_transform((gr_surface) &string_surface_rotated, (const gr_surface) &e->surface, 1);
586 }
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300587
bigbiffd58ba182020-03-23 10:02:29 -0400588 int y_bottom = y + e->surface.height;
589 int res = e->rendered_bytes;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200590
bigbiffd58ba182020-03-23 10:02:29 -0400591 if(max_height != -1 && max_height < y_bottom)
592 {
593 y_bottom = max_height;
594 if(y_bottom <= y)
595 {
596 pthread_mutex_unlock(&font->mutex);
597 return 0;
598 }
599 }
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200600
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500601 // Figuring out display coordinates works for gr_rotation == 0 too,
bigbiffd58ba182020-03-23 10:02:29 -0400602 // and isn't as expensive as allocating and rotating another surface,
603 // so we do this anyway.
604 int x0_disp, y0_disp, x1_disp, y1_disp;
605 int l_disp, r_disp, t_disp, b_disp;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300606
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500607 x0_disp = ROTATION_X_DISP(x, y, gr_draw->width);
608 y0_disp = ROTATION_Y_DISP(x, y, gr_draw->height);
609 x1_disp = ROTATION_X_DISP(x + e->surface.width, y_bottom, gr_draw->width);
610 y1_disp = ROTATION_Y_DISP(x + e->surface.width, y_bottom, gr_draw->height);
bigbiffd58ba182020-03-23 10:02:29 -0400611 l_disp = std::min(x0_disp, x1_disp);
612 r_disp = std::max(x0_disp, x1_disp);
613 t_disp = std::min(y0_disp, y1_disp);
614 b_disp = std::max(y0_disp, y1_disp);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300615
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500616 if (gr_rotation != 0) {
617 gl->bindTexture(gl, &string_surface_rotated);
618 } else {
619 gl->bindTexture(gl, &e->surface);
620 }
bigbiffd58ba182020-03-23 10:02:29 -0400621 gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
622 gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
623 gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200624
bigbiffd58ba182020-03-23 10:02:29 -0400625 gl->enable(gl, GGL_TEXTURE_2D);
626 gl->texCoord2i(gl, -l_disp, -t_disp);
627 gl->recti(gl, l_disp, t_disp, r_disp, b_disp);
628 gl->disable(gl, GGL_TEXTURE_2D);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200629
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500630 if (gr_rotation != 0)
631 free(string_surface_rotated.data);
632
bigbiff bigbiffac6cc082019-11-01 19:00:27 -0400633 pthread_mutex_unlock(&font->mutex);;
bigbiffd58ba182020-03-23 10:02:29 -0400634 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200635}
636
bigbiffd58ba182020-03-23 10:02:29 -0400637int twrpTruetype::gr_ttf_getMaxFontHeight(void *font) {
638 int res;
639 TrueTypeFont *f = (TrueTypeFont *)font;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200640
bigbiffd58ba182020-03-23 10:02:29 -0400641 pthread_mutex_lock(&f->mutex);
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200642
bigbiffd58ba182020-03-23 10:02:29 -0400643 if(f->max_height == -1)
644 gr_ttf_calcMaxFontHeight(f);
645 res = f->max_height;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200646
bigbiffd58ba182020-03-23 10:02:29 -0400647 pthread_mutex_unlock(&f->mutex);
648 return res;
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200649}