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