1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2015-2014 - Hans-Kristian Arntzen
3  *  Copyright (C) 2011-2017 - Daniel De Matteis
4  *
5  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
6  *  of the GNU General Public License as published by the Free Software Found-
7  *  ation, either version 3 of the License, or (at your option) any later version.
8  *
9  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11  *  PURPOSE.  See the GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License along with RetroArch.
14  *  If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <ctype.h>
18 
19 #include <file/file_path.h>
20 #include <streams/file_stream.h>
21 #include <retro_miscellaneous.h>
22 #include <string/stdstring.h>
23 
24 #include "../font_driver.h"
25 #include "../../verbosity.h"
26 
27 #ifndef STB_TRUETYPE_IMPLEMENTATION
28 #define STB_TRUETYPE_IMPLEMENTATION
29 #define STB_RECT_PACK_IMPLEMENTATION
30 #define STBTT_STATIC
31 #define STBRP_STATIC
32 #define STATIC static INLINE
33 #include "../../deps/stb/stb_rect_pack.h"
34 #include "../../deps/stb/stb_truetype.h"
35 #undef STATIC
36 #endif
37 
38 typedef struct
39 {
40    struct font_atlas atlas;               /* ptr   alignment */
41    struct font_glyph glyphs[256];         /* unsigned alignment */
42    struct font_line_metrics line_metrics; /* float alignment */
43 } stb_font_renderer_t;
44 
font_renderer_stb_get_atlas(void * data)45 static struct font_atlas *font_renderer_stb_get_atlas(void *data)
46 {
47    stb_font_renderer_t *self = (stb_font_renderer_t*)data;
48    return &self->atlas;
49 }
50 
font_renderer_stb_get_glyph(void * data,uint32_t code)51 static const struct font_glyph *font_renderer_stb_get_glyph(
52       void *data, uint32_t code)
53 {
54    stb_font_renderer_t *self = (stb_font_renderer_t*)data;
55    return code < 256 ? &self->glyphs[code] : NULL;
56 }
57 
font_renderer_stb_free(void * data)58 static void font_renderer_stb_free(void *data)
59 {
60    stb_font_renderer_t *self = (stb_font_renderer_t*)data;
61 
62    free(self->atlas.buffer);
63    free(self);
64 }
65 
font_renderer_stb_create_atlas(stb_font_renderer_t * self,uint8_t * font_data,float font_size,unsigned width,unsigned height)66 static bool font_renderer_stb_create_atlas(stb_font_renderer_t *self,
67       uint8_t *font_data, float font_size, unsigned width, unsigned height)
68 {
69    int i;
70    stbtt_packedchar   chardata[256];
71    stbtt_pack_context pc = {NULL};
72 
73    if (width > 2048 || height > 2048)
74    {
75       RARCH_WARN("[stb] Font atlas too big: %ux%u\n", width, height);
76       goto error;
77    }
78 
79    if (self->atlas.buffer)
80       free(self->atlas.buffer);
81 
82    self->atlas.buffer = (uint8_t*)calloc(height, width);
83    self->atlas.width  = width;
84    self->atlas.height = height;
85 
86    if (!self->atlas.buffer)
87       goto error;
88 
89    /* Note: 1 pixel of padding is added to
90     * prevent texture bleed when drawing with
91     * linear filtering enabled */
92    stbtt_PackBegin(&pc, self->atlas.buffer,
93          self->atlas.width, self->atlas.height,
94          self->atlas.width, 1, NULL);
95 
96    stbtt_PackFontRange(&pc, font_data, 0, font_size, 0, 256, chardata);
97    stbtt_PackEnd(&pc);
98 
99    self->atlas.dirty = true;
100 
101    for (i = 0; i < 256; ++i)
102    {
103       struct font_glyph *g = &self->glyphs[i];
104       stbtt_packedchar  *c = &chardata[i];
105 
106       g->advance_x         = c->xadvance;
107       g->atlas_offset_x    = c->x0;
108       g->atlas_offset_y    = c->y0;
109       g->draw_offset_x     = c->xoff;
110       g->draw_offset_y     = c->yoff;
111       g->width             = c->x1 - c->x0;
112       g->height            = c->y1 - c->y0;
113 
114       /* Make sure important characters fit */
115       if (ISALNUM(i) && (!g->width || !g->height))
116       {
117          int new_width  = width  * 1.2;
118          int new_height = height * 1.2;
119 
120          /* Limit growth to 2048x2048 unless we already reached that */
121          if (width < 2048 || height < 2048)
122          {
123             new_width  = MIN(new_width,  2048);
124             new_height = MIN(new_height, 2048);
125          }
126 
127          return font_renderer_stb_create_atlas(self, font_data, font_size,
128                new_width, new_height);
129       }
130    }
131 
132    return true;
133 
134 error:
135    self->atlas.width = self->atlas.height = 0;
136 
137    if (self->atlas.buffer)
138       free(self->atlas.buffer);
139 
140    self->atlas.buffer = NULL;
141 
142    return false;
143 }
144 
font_renderer_stb_init(const char * font_path,float font_size)145 static void *font_renderer_stb_init(const char *font_path, float font_size)
146 {
147    int ascent, descent, line_gap;
148    float scale_factor;
149    stbtt_fontinfo info;
150    uint8_t *font_data = NULL;
151    stb_font_renderer_t *self = (stb_font_renderer_t*) calloc(1, sizeof(*self));
152 
153    /* See https://github.com/nothings/stb/blob/master/stb_truetype.h#L539 */
154    font_size = STBTT_POINT_SIZE(font_size);
155 
156    if (!self)
157       goto error;
158 
159    if (!path_is_valid(font_path) || !filestream_read_file(font_path, (void**)&font_data, NULL))
160       goto error;
161 
162    if (!font_renderer_stb_create_atlas(self, font_data, font_size, 512, 512))
163       goto error;
164 
165    if (!stbtt_InitFont(&info, font_data, stbtt_GetFontOffsetForIndex(font_data, 0)))
166       goto error;
167 
168    stbtt_GetFontVMetrics(&info, &ascent, &descent, &line_gap);
169 
170    scale_factor = (font_size < 0) ?
171          stbtt_ScaleForMappingEmToPixels(&info, -font_size) :
172          stbtt_ScaleForPixelHeight(&info, font_size);
173 
174    /* Ascender, descender and line_gap values always
175     * end up ~0.5 pixels too small when scaled...
176     * > Add a manual correction factor */
177    self->line_metrics.ascender  = 0.5f + (float)ascent * scale_factor;
178    self->line_metrics.descender = 0.5f + (float)(-descent) * scale_factor;
179    self->line_metrics.height    = 0.5f + (float)(ascent - descent + line_gap) * scale_factor;
180 
181    free(font_data);
182 
183    return self;
184 
185 error:
186    if (font_data)
187       free(font_data);
188 
189    if (self)
190       font_renderer_stb_free(self);
191    return NULL;
192 }
193 
font_renderer_stb_get_default_font(void)194 static const char *font_renderer_stb_get_default_font(void)
195 {
196    static const char *paths[] = {
197 #if defined(_WIN32) && !defined(__WINRT__)
198       "C:\\Windows\\Fonts\\consola.ttf",
199       "C:\\Windows\\Fonts\\verdana.ttf",
200 #elif defined(__APPLE__)
201       "/Library/Fonts/Microsoft/Candara.ttf",
202       "/Library/Fonts/Verdana.ttf",
203       "/Library/Fonts/Tahoma.ttf",
204       "/Library/Fonts/Andale Mono.ttf",
205       "/Library/Fonts/Courier New.ttf",
206 #elif defined(__ANDROID_API__)
207       "/system/fonts/DroidSansMono.ttf",
208       "/system/fonts/CutiveMono.ttf",
209       "/system/fonts/DroidSans.ttf",
210 #elif defined(VITA)
211       "vs0:data/external/font/pvf/c041056ts.ttf",
212       "vs0:data/external/font/pvf/d013013ds.ttf",
213       "vs0:data/external/font/pvf/e046323ms.ttf",
214       "vs0:data/external/font/pvf/e046323ts.ttf",
215       "vs0:data/external/font/pvf/k006004ds.ttf",
216       "vs0:data/external/font/pvf/n023055ms.ttf",
217       "vs0:data/external/font/pvf/n023055ts.ttf",
218 #elif !defined(__WINRT__)
219       "/usr/share/fonts/TTF/DejaVuSansMono.ttf",
220       "/usr/share/fonts/TTF/DejaVuSans.ttf",
221       "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf",
222       "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf",
223       "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",
224       "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
225       "osd-font.ttf",
226 #endif
227       NULL
228    };
229 
230    const char **p;
231 
232    for (p = paths; *p; ++p)
233       if (path_is_valid(*p))
234          return *p;
235 
236    return NULL;
237 }
238 
font_renderer_stb_get_line_metrics(void * data,struct font_line_metrics ** metrics)239 static bool font_renderer_stb_get_line_metrics(
240       void* data, struct font_line_metrics **metrics)
241 {
242    stb_font_renderer_t *handle = (stb_font_renderer_t*)data;
243    if (!handle)
244       return false;
245    *metrics = &handle->line_metrics;
246    return true;
247 }
248 
249 font_renderer_driver_t stb_font_renderer = {
250    font_renderer_stb_init,
251    font_renderer_stb_get_atlas,
252    font_renderer_stb_get_glyph,
253    font_renderer_stb_free,
254    font_renderer_stb_get_default_font,
255    "stb",
256    font_renderer_stb_get_line_metrics
257 };
258