mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 17:28:49 -10:00
(Font) Take the file I/O out of the font renderers
The renderers each resolved and opened their own font file:
path_is_valid() loops in stb and freetype to pick a default,
filestream_read_file() in both to load it, and a path_is_valid() in
coretext that was checking a font name it could never find on disk.
Move it up. get_default_font() becomes get_default_fonts(), returning
the NULL-terminated candidate list rather than walking it, and
font_renderer_create_default() picks the first path that exists, reads
it once, and hands the bytes to init(). The renderer takes ownership
on success; the caller frees on failure.
The special cases survive because init() still gets the path as well
as the bytes:
- stb keeps its built-in glyphs, reached by an empty entry at the
end of the list, which means "no file needed" rather than "no font
available".
- the WiiU shared font is fetched in init() as before; its list is
just the empty entry.
- freetype under fontconfig likewise.
- coretext looks fonts up by name through CoreText, so it uses the
path and releases the bytes. Its path_is_valid() goes: non-NULL
bytes already prove the file is there. It still will not match
"Verdana" on disk, which is what happened before too - init()
rejected it at the same point.
One read remains, in freetype's fontconfig branch, and it is noted in
place: fontconfig resolves a path from the locale and system config at
init time, so there is no static candidate list to pre-read. Hoisting
it would put an optional dependency into font_driver.c, which is worse
layering than the read it removes.
stb, freetype and font_driver compile clean. coretext is Apple-only
and unbuildable here; its change is the signature, the dropped stat
and the list, all mechanical.
This commit is contained in:
1 parent
a717ad3cdd
commit
76f8821b7f
5 files changed
+126
-71
No files matched your search
@@ -519,8 +519,9 @@ static bool coretext_font_renderer_render_glyph(CTFontRef face, ct_font_renderer
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *font_renderer_ct_init(const char *font_path, float font_size,
|
||||
enum font_atlas_format fmt)
|
||||
static void *font_renderer_ct_init(const char *font_path,
|
||||
uint8_t *font_data, size_t font_data_len,
|
||||
float font_size, enum font_atlas_format fmt)
|
||||
{
|
||||
char err = 0;
|
||||
CFStringRef cf_font_path = NULL;
|
||||
@@ -530,11 +531,18 @@ static void *font_renderer_ct_init(const char *font_path, float font_size,
|
||||
CGFontRef theCGFont = NULL;
|
||||
ct_font_renderer_t *handle = (ct_font_renderer_t*)calloc(1, sizeof(*handle));
|
||||
|
||||
if (!handle || !path_is_valid(font_path))
|
||||
/* font_data being non-NULL is proof the path exists - it was read
|
||||
* by font_renderer_create_default() - so no stat is needed here.
|
||||
* CoreText loads by URL, so the bytes themselves are not used;
|
||||
* release them rather than hold them for the font's lifetime. */
|
||||
if (!handle || !font_data || !font_data_len)
|
||||
{
|
||||
free(font_data);
|
||||
err = 1;
|
||||
goto error;
|
||||
}
|
||||
free(font_data);
|
||||
font_data = NULL;
|
||||
|
||||
if (!(cf_font_path = CFStringCreateWithCString(
|
||||
NULL, font_path, kCFStringEncodingUTF8)))
|
||||
@@ -640,12 +648,14 @@ error:
|
||||
return handle;
|
||||
}
|
||||
|
||||
static const char *font_renderer_ct_get_default_font(void)
|
||||
static const char * const *font_renderer_ct_get_default_fonts(void)
|
||||
{
|
||||
/* We can't tell if a font is going to be there until we actually
|
||||
initialize CoreText and the best way to get fonts is by name, not
|
||||
by path. */
|
||||
return "Verdana";
|
||||
/* A name rather than a path: CoreText looks fonts up by name, and
|
||||
* there is no way to know one is present without initialising it.
|
||||
* font_renderer_create_default() will not find this on disk, which
|
||||
* matches the previous behaviour - init() rejected it too. */
|
||||
static const char * const names[] = { "Verdana", NULL };
|
||||
return names;
|
||||
}
|
||||
|
||||
static void font_renderer_ct_get_line_metrics(
|
||||
@@ -662,7 +672,7 @@ font_renderer_driver_t coretext_font_renderer = {
|
||||
font_renderer_ct_get_atlas,
|
||||
font_renderer_ct_get_glyph,
|
||||
font_renderer_ct_free,
|
||||
font_renderer_ct_get_default_font,
|
||||
font_renderer_ct_get_default_fonts,
|
||||
"font_renderer_ct",
|
||||
font_renderer_ct_get_line_metrics
|
||||
};
|
||||
@@ -387,8 +387,9 @@ static bool font_renderer_create_atlas(ft_font_renderer_t *handle,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *font_renderer_ft_init(const char *font_path, float font_size,
|
||||
enum font_atlas_format fmt)
|
||||
static void *font_renderer_ft_init(const char *font_path,
|
||||
uint8_t *font_data_in, size_t font_data_in_len,
|
||||
float font_size, enum font_atlas_format fmt)
|
||||
{
|
||||
FT_Error err;
|
||||
|
||||
@@ -478,6 +479,10 @@ static void *font_renderer_ft_init(const char *font_path, float font_size,
|
||||
if (FcPatternGetInteger(found, FC_INDEX, 0,
|
||||
&face_index) != FcResultMatch)
|
||||
goto fc_cleanup;
|
||||
/* The one read left in this directory. fontconfig resolves a
|
||||
* path here, from the locale and the system configuration, so
|
||||
* font_renderer_create_default() cannot have pre-read it from a
|
||||
* static candidate list. */
|
||||
if (!filestream_read_file((const char*)_font_path,
|
||||
(void**)&font_data,
|
||||
&font_data_size))
|
||||
@@ -520,21 +525,15 @@ fc_done:
|
||||
else
|
||||
#endif
|
||||
{
|
||||
uint8_t* font_data = NULL;
|
||||
int64_t font_data_size = 0;
|
||||
/* No path_is_valid() first: filestream_read_file() opens the
|
||||
* file itself and returns 0 when it cannot, so the stat only
|
||||
* repeats the open's own lookup. */
|
||||
if (!filestream_read_file(font_path,
|
||||
(void**)&font_data, &font_data_size))
|
||||
/* Bytes come from font_renderer_create_default(); no file I/O
|
||||
* here. Ownership transfers on success. */
|
||||
if (!font_data_in || !font_data_in_len)
|
||||
goto error;
|
||||
if ((err = FT_New_Memory_Face(handle->lib, (const FT_Byte*)font_data,
|
||||
(FT_Long)font_data_size, (FT_Long)0, &handle->face)))
|
||||
{
|
||||
free(font_data);
|
||||
if ((err = FT_New_Memory_Face(handle->lib,
|
||||
(const FT_Byte*)font_data_in,
|
||||
(FT_Long)font_data_in_len, (FT_Long)0, &handle->face)))
|
||||
goto error;
|
||||
}
|
||||
handle->file_data = font_data;
|
||||
handle->file_data = font_data_in;
|
||||
}
|
||||
|
||||
if ((err = FT_Select_Charmap(handle->face, FT_ENCODING_UNICODE)))
|
||||
@@ -560,8 +559,8 @@ error:
|
||||
/* Not the cleanest way to do things for sure,
|
||||
* but should hopefully work ... */
|
||||
|
||||
static const char *font_paths[] = {
|
||||
/* Assets directory OSD Font, @see font_renderer_ft_get_default_font() */
|
||||
static const char * const font_paths[] = {
|
||||
/* Assets directory OSD Font, @see font_renderer_ft_get_default_fonts() */
|
||||
"assets://pkg/osd-font.ttf",
|
||||
#if defined(_WIN32)
|
||||
"C:\\Windows\\Fonts\\consola.ttf",
|
||||
@@ -588,25 +587,23 @@ static const char *font_paths[] = {
|
||||
"/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", /* Debian, Ubuntu */
|
||||
#endif
|
||||
"osd-font.ttf", /* Magic font to search for, useful for distribution. */
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Highly OS/platform dependent. */
|
||||
static const char *font_renderer_ft_get_default_font(void)
|
||||
static const char * const *font_renderer_ft_get_default_fonts(void)
|
||||
{
|
||||
/* Since fontconfig will return parameters more than a simple path
|
||||
we will process these in the init function */
|
||||
#if defined(WIIU) || defined(HAVE_FONTCONFIG_SUPPORT)
|
||||
return "";
|
||||
/* Resolved in init() - fontconfig returns more than a path, and
|
||||
* WiiU uses the shared system font. */
|
||||
static const char * const none[] = { "", NULL };
|
||||
return none;
|
||||
#else
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(font_paths); i++)
|
||||
{
|
||||
if (path_is_valid(font_paths[i]))
|
||||
return font_paths[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
/* Selection happens in font_renderer_create_default(), which is
|
||||
* what keeps path_is_valid() out of this file. */
|
||||
return font_paths;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -624,7 +621,7 @@ font_renderer_driver_t freetype_font_renderer = {
|
||||
font_renderer_ft_get_atlas,
|
||||
font_renderer_ft_get_glyph,
|
||||
font_renderer_ft_free,
|
||||
font_renderer_ft_get_default_font,
|
||||
font_renderer_ft_get_default_fonts,
|
||||
"font_renderer_ft",
|
||||
font_renderer_ft_get_line_metrics
|
||||
};
|
||||
@@ -1636,8 +1636,9 @@ static bool font_renderer_stb_create_atlas_fmt(
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *font_renderer_stb_init(const char *font_path, float font_size,
|
||||
enum font_atlas_format fmt)
|
||||
static void *font_renderer_stb_init(const char *font_path,
|
||||
uint8_t *font_data, size_t font_data_len,
|
||||
float font_size, enum font_atlas_format fmt)
|
||||
{
|
||||
int ascent, descent, line_gap;
|
||||
stb_font_renderer_t *self =
|
||||
@@ -1664,22 +1665,19 @@ static void *font_renderer_stb_init(const char *font_path, float font_size,
|
||||
#endif
|
||||
if (!font_path || !*font_path)
|
||||
{
|
||||
/* get_default_font() found no TrueType font. */
|
||||
/* No TrueType font was found; use the built-in glyphs. */
|
||||
if (!font_renderer_stb_init_builtin(self, -font_size))
|
||||
goto error;
|
||||
return self;
|
||||
}
|
||||
else
|
||||
{
|
||||
int64_t len = 0;
|
||||
/* filestream_read_file() opens the file and returns 0 if it
|
||||
* cannot, so a path_is_valid() stat first only repeats that
|
||||
* lookup. */
|
||||
if (!filestream_read_file(font_path, (void**)&self->font_data, &len))
|
||||
/* Bytes come from font_renderer_create_default(); this renderer
|
||||
* does no file I/O. Ownership transfers on success. */
|
||||
if (!font_data || !font_data_len)
|
||||
goto error;
|
||||
if (len <= 0)
|
||||
goto error;
|
||||
self->font_data_size = (size_t)len;
|
||||
self->font_data = font_data;
|
||||
self->font_data_size = font_data_len;
|
||||
self->font_data_owned = true;
|
||||
}
|
||||
|
||||
@@ -1723,12 +1721,14 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *font_renderer_stb_get_default_font(void)
|
||||
static const char * const *font_renderer_stb_get_default_fonts(void)
|
||||
{
|
||||
#ifdef WIIU
|
||||
return "";
|
||||
/* The shared system font, fetched in init(); no file to open. */
|
||||
static const char * const wiiu_paths[] = { "", NULL };
|
||||
return wiiu_paths;
|
||||
#else
|
||||
static const char *paths[] = {
|
||||
static const char * const paths[] = {
|
||||
#if defined(_WIN32) && !defined(__WINRT__)
|
||||
"C:\\Windows\\Fonts\\consola.ttf",
|
||||
"C:\\Windows\\Fonts\\verdana.ttf",
|
||||
@@ -1767,19 +1767,12 @@ static const char *font_renderer_stb_get_default_font(void)
|
||||
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
||||
"osd-font.ttf",
|
||||
#endif
|
||||
"", /* built-in glyphs, no file needed */
|
||||
NULL
|
||||
};
|
||||
|
||||
const char **p;
|
||||
|
||||
for (p = paths; *p; ++p)
|
||||
if (path_is_valid(*p))
|
||||
return *p;
|
||||
|
||||
/* No TrueType font on this system. Returning "" rather than NULL
|
||||
* keeps this renderer in play: init() then builds the atlas from
|
||||
* the built-in bitmap glyphs. */
|
||||
return "";
|
||||
/* The empty entry is the fallback: no file, built-in glyphs. */
|
||||
return paths;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1795,7 +1788,7 @@ font_renderer_driver_t stb_font_renderer = {
|
||||
font_renderer_stb_get_atlas,
|
||||
font_renderer_stb_get_glyph,
|
||||
font_renderer_stb_free,
|
||||
font_renderer_stb_get_default_font,
|
||||
font_renderer_stb_get_default_fonts,
|
||||
"font_renderer_stb",
|
||||
font_renderer_stb_get_line_metrics
|
||||
};
|
||||
+48
-5
@@ -16,6 +16,8 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <file/file_path.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
@@ -57,19 +59,60 @@ int font_renderer_create_default(
|
||||
|
||||
for (i = 0; font_backends[i]; i++)
|
||||
{
|
||||
const char *path = font_path;
|
||||
const char *path = font_path;
|
||||
uint8_t *data = NULL;
|
||||
int64_t len = 0;
|
||||
|
||||
if (!path)
|
||||
path = font_backends[i]->get_default_font();
|
||||
if (!path)
|
||||
continue;
|
||||
{
|
||||
/* Pick the first candidate that exists. Doing it here rather
|
||||
* than in the renderer is what keeps file I/O out of them. */
|
||||
const char * const *cand = font_backends[i]->get_default_fonts
|
||||
? font_backends[i]->get_default_fonts()
|
||||
: NULL;
|
||||
|
||||
*handle = font_backends[i]->init(path, font_size, fmt);
|
||||
if (!cand)
|
||||
continue;
|
||||
|
||||
for (; *cand; cand++)
|
||||
{
|
||||
/* An empty entry means the renderer has an internal or
|
||||
* system source and wants no file. */
|
||||
if (!**cand)
|
||||
{
|
||||
path = *cand;
|
||||
break;
|
||||
}
|
||||
if (path_is_valid(*cand))
|
||||
{
|
||||
path = *cand;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Read it once, here. A failed read is not fatal: the renderer
|
||||
* may still have an internal source to fall back on. */
|
||||
if (path && *path)
|
||||
if (!filestream_read_file(path, (void**)&data, &len) || len <= 0)
|
||||
{
|
||||
data = NULL;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
*handle = font_backends[i]->init(path, data, (size_t)len,
|
||||
font_size, fmt);
|
||||
if (*handle)
|
||||
{
|
||||
/* The renderer owns data now. */
|
||||
*drv = font_backends[i];
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
*drv = NULL;
|
||||
|
||||
+15
-3
@@ -51,8 +51,16 @@ typedef struct font_renderer
|
||||
|
||||
typedef struct font_renderer_driver
|
||||
{
|
||||
void *(*init)(const char *font_path, float font_size,
|
||||
enum font_atlas_format fmt);
|
||||
/* font_data/font_data_len carry the contents of font_path, already
|
||||
* read by font_renderer_create_default(). Renderers do no file I/O
|
||||
* of their own: a NULL font_data means no usable file was found,
|
||||
* and the renderer should fall back to whatever internal or system
|
||||
* source it has (stb's built-in glyphs, the WiiU shared font,
|
||||
* fontconfig). On success the renderer takes ownership of
|
||||
* font_data and frees it in free(). */
|
||||
void *(*init)(const char *font_path,
|
||||
uint8_t *font_data, size_t font_data_len,
|
||||
float font_size, enum font_atlas_format fmt);
|
||||
|
||||
struct font_atlas *(*get_atlas)(void *data);
|
||||
|
||||
@@ -61,7 +69,11 @@ typedef struct font_renderer_driver
|
||||
|
||||
void (*free)(void *data);
|
||||
|
||||
const char *(*get_default_font)(void);
|
||||
/* NULL-terminated list of candidate paths, best first. Returning
|
||||
* the list rather than a chosen path keeps path_is_valid() - and so
|
||||
* all file I/O - out of the renderers. An empty first entry means
|
||||
* "no file needed", for renderers with an internal source. */
|
||||
const char * const *(*get_default_fonts)(void);
|
||||
|
||||
const char *ident;
|
||||
|
||||
|
||||
Reference in new issue
Block a user