1 #include "lv_tiny_ttf.h"
2
3 #if LV_USE_TINY_TTF
4 #include <stdio.h>
5 #include "../../../misc/lv_lru.h"
6
7 #define STB_RECT_PACK_IMPLEMENTATION
8 #define STBRP_STATIC
9 #define STBTT_STATIC
10 #define STB_TRUETYPE_IMPLEMENTATION
11 #define STBTT_HEAP_FACTOR_SIZE_32 50
12 #define STBTT_HEAP_FACTOR_SIZE_128 20
13 #define STBTT_HEAP_FACTOR_SIZE_DEFAULT 10
14 #define STBTT_malloc(x, u) ((void)(u), lv_mem_alloc(x))
15 #define STBTT_free(x, u) ((void)(u), lv_mem_free(x))
16 #define TTF_MALLOC(x) (lv_mem_alloc(x))
17 #define TTF_FREE(x) (lv_mem_free(x))
18
19 #if LV_TINY_TTF_FILE_SUPPORT
20 /* a hydra stream that can be in memory or from a file*/
21 typedef struct ttf_cb_stream {
22 lv_fs_file_t * file;
23 const void * data;
24 size_t size;
25 size_t position;
26 } ttf_cb_stream_t;
27
ttf_cb_stream_read(ttf_cb_stream_t * stream,void * data,size_t to_read)28 static void ttf_cb_stream_read(ttf_cb_stream_t * stream, void * data, size_t to_read)
29 {
30 if(stream->file != NULL) {
31 uint32_t br;
32 lv_fs_read(stream->file, data, to_read, &br);
33 }
34 else {
35 if(to_read + stream->position >= stream->size) {
36 to_read = stream->size - stream->position;
37 }
38 lv_memcpy(data, ((const unsigned char *)stream->data + stream->position), to_read);
39 stream->position += to_read;
40 }
41 }
ttf_cb_stream_seek(ttf_cb_stream_t * stream,size_t position)42 static void ttf_cb_stream_seek(ttf_cb_stream_t * stream, size_t position)
43 {
44 if(stream->file != NULL) {
45 lv_fs_seek(stream->file, position, LV_FS_SEEK_SET);
46 }
47 else {
48 if(position > stream->size) {
49 stream->position = stream->size;
50 }
51 else {
52 stream->position = position;
53 }
54 }
55 }
56
57 /* for stream support */
58 #define STBTT_STREAM_TYPE ttf_cb_stream_t *
59 #define STBTT_STREAM_SEEK(s, x) ttf_cb_stream_seek(s, x);
60 #define STBTT_STREAM_READ(s, x, y) ttf_cb_stream_read(s, x, y);
61 #endif /*LV_TINY_TTF_FILE_SUPPORT*/
62
63 #include "stb_rect_pack.h"
64 #include "stb_truetype_htcw.h"
65
66 typedef struct ttf_font_desc {
67 lv_fs_file_t file;
68 #if LV_TINY_TTF_FILE_SUPPORT
69 ttf_cb_stream_t stream;
70 #else
71 const uint8_t * stream;
72 #endif
73 stbtt_fontinfo info;
74 float scale;
75 int ascent;
76 int descent;
77 lv_lru_t * bitmap_cache;
78 } ttf_font_desc_t;
79
80 typedef struct ttf_bitmap_cache_key {
81 uint32_t unicode_letter;
82 lv_coord_t line_height;
83 } ttf_bitmap_cache_key_t;
84
ttf_get_glyph_dsc_cb(const lv_font_t * font,lv_font_glyph_dsc_t * dsc_out,uint32_t unicode_letter,uint32_t unicode_letter_next)85 static bool ttf_get_glyph_dsc_cb(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter,
86 uint32_t unicode_letter_next)
87 {
88 if(unicode_letter < 0x20 ||
89 unicode_letter == 0xf8ff || /*LV_SYMBOL_DUMMY*/
90 unicode_letter == 0x200c) { /*ZERO WIDTH NON-JOINER*/
91 dsc_out->box_w = 0;
92 dsc_out->adv_w = 0;
93 dsc_out->box_h = 0; /*height of the bitmap in [px]*/
94 dsc_out->ofs_x = 0; /*X offset of the bitmap in [pf]*/
95 dsc_out->ofs_y = 0; /*Y offset of the bitmap in [pf]*/
96 dsc_out->bpp = 0;
97 dsc_out->is_placeholder = false;
98 return true;
99 }
100 ttf_font_desc_t * dsc = (ttf_font_desc_t *)font->dsc;
101 int g1 = stbtt_FindGlyphIndex(&dsc->info, (int)unicode_letter);
102 if(g1 == 0) {
103 /* Glyph not found */
104 return false;
105 }
106 int x1, y1, x2, y2;
107
108 stbtt_GetGlyphBitmapBox(&dsc->info, g1, dsc->scale, dsc->scale, &x1, &y1, &x2, &y2);
109 int g2 = 0;
110 if(unicode_letter_next != 0) {
111 g2 = stbtt_FindGlyphIndex(&dsc->info, (int)unicode_letter_next);
112 }
113 int advw, lsb;
114 stbtt_GetGlyphHMetrics(&dsc->info, g1, &advw, &lsb);
115 int k = stbtt_GetGlyphKernAdvance(&dsc->info, g1, g2);
116 dsc_out->adv_w = (uint16_t)floor((((float)advw + (float)k) * dsc->scale) +
117 0.5f); /*Horizontal space required by the glyph in [px]*/
118
119 dsc_out->adv_w = (uint16_t)floor((((float)advw + (float)k) * dsc->scale) +
120 0.5f); /*Horizontal space required by the glyph in [px]*/
121 dsc_out->box_w = (x2 - x1 + 1); /*width of the bitmap in [px]*/
122 dsc_out->box_h = (y2 - y1 + 1); /*height of the bitmap in [px]*/
123 dsc_out->ofs_x = x1; /*X offset of the bitmap in [pf]*/
124 dsc_out->ofs_y = -y2; /*Y offset of the bitmap measured from the as line*/
125 dsc_out->bpp = 8; /*Bits per pixel: 1/2/4/8*/
126 dsc_out->is_placeholder = false;
127 return true; /*true: glyph found; false: glyph was not found*/
128 }
129
ttf_get_glyph_bitmap_cb(const lv_font_t * font,uint32_t unicode_letter)130 static const uint8_t * ttf_get_glyph_bitmap_cb(const lv_font_t * font, uint32_t unicode_letter)
131 {
132 ttf_font_desc_t * dsc = (ttf_font_desc_t *)font->dsc;
133 const stbtt_fontinfo * info = (const stbtt_fontinfo *)&dsc->info;
134 int g1 = stbtt_FindGlyphIndex(info, (int)unicode_letter);
135 if(g1 == 0) {
136 /* Glyph not found */
137 return NULL;
138 }
139 int x1, y1, x2, y2;
140 stbtt_GetGlyphBitmapBox(info, g1, dsc->scale, dsc->scale, &x1, &y1, &x2, &y2);
141 int w, h;
142 w = x2 - x1 + 1;
143 h = y2 - y1 + 1;
144 uint32_t stride = w;
145 /*Try to load from cache*/
146 ttf_bitmap_cache_key_t cache_key;
147 lv_memset(&cache_key, 0, sizeof(cache_key)); /*Zero padding*/
148 cache_key.unicode_letter = unicode_letter;
149 cache_key.line_height = font->line_height;
150 uint8_t * buffer = NULL;
151 lv_lru_get(dsc->bitmap_cache, &cache_key, sizeof(cache_key), (void **)&buffer);
152 if(buffer) {
153 return buffer;
154 }
155 LV_LOG_TRACE("cache miss for letter: %u", unicode_letter);
156 /*Prepare space in cache*/
157 size_t szb = h * stride;
158 buffer = lv_mem_alloc(szb);
159 if(!buffer) {
160 LV_LOG_ERROR("failed to allocate cache value");
161 return NULL;
162 }
163 lv_memset(buffer, 0, szb);
164 if(LV_LRU_OK != lv_lru_set(dsc->bitmap_cache, &cache_key, sizeof(cache_key), buffer, szb)) {
165 LV_LOG_ERROR("failed to add cache value");
166 lv_mem_free(buffer);
167 return NULL;
168 }
169 /*Render into cache*/
170 stbtt_MakeGlyphBitmap(info, buffer, w, h, stride, dsc->scale, dsc->scale, g1);
171 return buffer;
172 }
173
lv_tiny_ttf_create(const char * path,const void * data,size_t data_size,lv_coord_t font_size,size_t cache_size)174 static lv_font_t * lv_tiny_ttf_create(const char * path, const void * data, size_t data_size, lv_coord_t font_size,
175 size_t cache_size)
176 {
177 if((path == NULL && data == NULL) || 0 >= font_size) {
178 LV_LOG_ERROR("tiny_ttf: invalid argument\n");
179 return NULL;
180 }
181 ttf_font_desc_t * dsc = (ttf_font_desc_t *)TTF_MALLOC(sizeof(ttf_font_desc_t));
182 if(dsc == NULL) {
183 LV_LOG_ERROR("tiny_ttf: out of memory\n");
184 return NULL;
185 }
186 #if LV_TINY_TTF_FILE_SUPPORT
187 if(path != NULL) {
188 if(LV_FS_RES_OK != lv_fs_open(&dsc->file, path, LV_FS_MODE_RD)) {
189 LV_LOG_ERROR("tiny_ttf: unable to open %s\n", path);
190 goto err_after_dsc;
191 }
192 dsc->stream.file = &dsc->file;
193 }
194 else {
195 dsc->stream.file = NULL;
196 dsc->stream.data = (const uint8_t *)data;
197 dsc->stream.size = data_size;
198 dsc->stream.position = 0;
199 }
200 if(0 == stbtt_InitFont(&dsc->info, &dsc->stream, stbtt_GetFontOffsetForIndex(&dsc->stream, 0))) {
201 LV_LOG_ERROR("tiny_ttf: init failed\n");
202 goto err_after_dsc;
203 }
204
205 #else
206 dsc->stream = (const uint8_t *)data;
207 LV_UNUSED(data_size);
208 if(0 == stbtt_InitFont(&dsc->info, dsc->stream, stbtt_GetFontOffsetForIndex(dsc->stream, 0))) {
209 LV_LOG_ERROR("tiny_ttf: init failed\n");
210 goto err_after_dsc;
211 }
212 #endif
213
214 dsc->bitmap_cache = lv_lru_create(cache_size, font_size * font_size, lv_mem_free, lv_mem_free);
215 if(dsc->bitmap_cache == NULL) {
216 LV_LOG_ERROR("failed to create lru cache");
217 goto err_after_dsc;
218 }
219
220 lv_font_t * out_font = (lv_font_t *)TTF_MALLOC(sizeof(lv_font_t));
221 if(out_font == NULL) {
222 LV_LOG_ERROR("tiny_ttf: out of memory\n");
223 goto err_after_bitmap_cache;
224 }
225 lv_memset(out_font, 0, sizeof(lv_font_t));
226 out_font->get_glyph_dsc = ttf_get_glyph_dsc_cb;
227 out_font->get_glyph_bitmap = ttf_get_glyph_bitmap_cb;
228 out_font->dsc = dsc;
229 lv_tiny_ttf_set_size(out_font, font_size);
230 return out_font;
231 err_after_bitmap_cache:
232 lv_lru_del(dsc->bitmap_cache);
233 err_after_dsc:
234 TTF_FREE(dsc);
235 return NULL;
236 }
237 #if LV_TINY_TTF_FILE_SUPPORT
lv_tiny_ttf_create_file_ex(const char * path,lv_coord_t font_size,size_t cache_size)238 lv_font_t * lv_tiny_ttf_create_file_ex(const char * path, lv_coord_t font_size, size_t cache_size)
239 {
240 return lv_tiny_ttf_create(path, NULL, 0, font_size, cache_size);
241 }
lv_tiny_ttf_create_file(const char * path,lv_coord_t font_size)242 lv_font_t * lv_tiny_ttf_create_file(const char * path, lv_coord_t font_size)
243 {
244 return lv_tiny_ttf_create_file_ex(path, font_size, 4096);
245 }
246 #endif /*LV_TINY_TTF_FILE_SUPPORT*/
lv_tiny_ttf_create_data_ex(const void * data,size_t data_size,lv_coord_t font_size,size_t cache_size)247 lv_font_t * lv_tiny_ttf_create_data_ex(const void * data, size_t data_size, lv_coord_t font_size, size_t cache_size)
248 {
249 return lv_tiny_ttf_create(NULL, data, data_size, font_size, cache_size);
250 }
lv_tiny_ttf_create_data(const void * data,size_t data_size,lv_coord_t font_size)251 lv_font_t * lv_tiny_ttf_create_data(const void * data, size_t data_size, lv_coord_t font_size)
252 {
253 return lv_tiny_ttf_create_data_ex(data, data_size, font_size, 4096);
254 }
lv_tiny_ttf_set_size(lv_font_t * font,lv_coord_t font_size)255 void lv_tiny_ttf_set_size(lv_font_t * font, lv_coord_t font_size)
256 {
257 if(font_size <= 0) {
258 LV_LOG_ERROR("invalid font size: %"PRIx32, font_size);
259 return;
260 }
261 ttf_font_desc_t * dsc = (ttf_font_desc_t *)font->dsc;
262 dsc->scale = stbtt_ScaleForMappingEmToPixels(&dsc->info, font_size);
263 int line_gap = 0;
264 stbtt_GetFontVMetrics(&dsc->info, &dsc->ascent, &dsc->descent, &line_gap);
265 font->line_height = (lv_coord_t)(dsc->scale * (dsc->ascent - dsc->descent + line_gap));
266 font->base_line = (lv_coord_t)(dsc->scale * (line_gap - dsc->descent));
267 }
lv_tiny_ttf_destroy(lv_font_t * font)268 void lv_tiny_ttf_destroy(lv_font_t * font)
269 {
270 if(font != NULL) {
271 if(font->dsc != NULL) {
272 ttf_font_desc_t * ttf = (ttf_font_desc_t *)font->dsc;
273 #if LV_TINY_TTF_FILE_SUPPORT
274 if(ttf->stream.file != NULL) {
275 lv_fs_close(&ttf->file);
276 }
277 #endif
278 lv_lru_del(ttf->bitmap_cache);
279 TTF_FREE(ttf);
280 }
281 TTF_FREE(font);
282 }
283 }
284 #endif /*LV_USE_TINY_TTF*/
285