(font driver) update freetype with unicode support (up to U+FFFF) using

a dynamic atlas.
This commit is contained in:
aliaspider 2016-10-21 17:31:53 +01:00
parent 6f81a19f60
commit 28c2dcf253

View File

@ -24,23 +24,34 @@
#include <retro_miscellaneous.h> #include <retro_miscellaneous.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
#include "../font_driver.h" #include "../font_driver.h"
#define FT_ATLAS_ROWS 16 #define FT_ATLAS_ROWS 16
#define FT_ATLAS_COLS 16 #define FT_ATLAS_COLS 16
#define FT_ATLAS_SIZE (FT_ATLAS_ROWS * FT_ATLAS_COLS) #define FT_ATLAS_SIZE (FT_ATLAS_ROWS * FT_ATLAS_COLS)
#if FT_ATLAS_SIZE <= 256
typedef uint8_t atlas_index_t;
#else
typedef uint16_t atlas_index_t;
#endif
typedef struct freetype_renderer typedef struct freetype_renderer
{ {
FT_Library lib; FT_Library lib;
FT_Face face; FT_Face face;
int max_glyph_width;
int max_glyph_height;
struct font_atlas atlas; struct font_atlas atlas;
struct font_glyph glyphs[FT_ATLAS_SIZE]; struct font_glyph glyphs[FT_ATLAS_SIZE];
atlas_index_t uc_to_id[0x10000];
uint16_t id_to_uc[FT_ATLAS_SIZE];
unsigned last_used[FT_ATLAS_SIZE];
unsigned usage_counter;
} ft_font_renderer_t; } ft_font_renderer_t;
static const struct font_atlas *font_renderer_ft_get_atlas(void *data) static struct font_atlas *font_renderer_ft_get_atlas(void *data)
{ {
ft_font_renderer_t *handle = (ft_font_renderer_t*)data; ft_font_renderer_t *handle = (ft_font_renderer_t*)data;
if (!handle) if (!handle)
@ -48,15 +59,6 @@ static const struct font_atlas *font_renderer_ft_get_atlas(void *data)
return &handle->atlas; return &handle->atlas;
} }
static const struct font_glyph *font_renderer_ft_get_glyph(
void *data, uint32_t code)
{
ft_font_renderer_t *handle = (ft_font_renderer_t*)data;
if (!handle)
return NULL;
return code < FT_ATLAS_SIZE ? &handle->glyphs[code] : NULL;
}
static void font_renderer_ft_free(void *data) static void font_renderer_ft_free(void *data)
{ {
ft_font_renderer_t *handle = (ft_font_renderer_t*)data; ft_font_renderer_t *handle = (ft_font_renderer_t*)data;
@ -72,96 +74,110 @@ static void font_renderer_ft_free(void *data)
free(handle); free(handle);
} }
static bool font_renderer_create_atlas(ft_font_renderer_t *handle) static unsigned font_renderer_get_slot(ft_font_renderer_t *handle)
{ {
unsigned i; int i;
bool ret = true; unsigned oldest = 1;
uint8_t *buffer[FT_ATLAS_SIZE] = {NULL}; for (i = 2; i < FT_ATLAS_SIZE; i++)
unsigned pitches[FT_ATLAS_SIZE] = {0}; if(handle->last_used[i] < handle->last_used[oldest])
oldest = i;
unsigned max_width = 0; handle->uc_to_id[handle->id_to_uc[oldest]] = 0;
unsigned max_height = 0; handle->id_to_uc[oldest] = 0;
return oldest;
}
for (i = 0; i < FT_ATLAS_SIZE; i++) static unsigned font_renderer_update_atlas(ft_font_renderer_t *handle, FT_ULong charcode)
{ {
FT_GlyphSlot slot; FT_GlyphSlot slot;
struct font_glyph *glyph = &handle->glyphs[i];
if (!glyph) if(charcode > 0x10000)
continue; return 0;
if(handle->uc_to_id[charcode])
return handle->uc_to_id[charcode];
if (FT_Load_Char(handle->face, i, FT_LOAD_RENDER)) unsigned id = font_renderer_get_slot(handle);
{ handle->id_to_uc[id] = charcode;
ret = false; handle->uc_to_id[charcode] = id;
goto end; handle->atlas.dirty = true;
}
struct font_glyph *glyph = &handle->glyphs[id];
memset(glyph, 0, sizeof(*glyph)) ;
if (FT_Load_Char(handle->face, charcode, FT_LOAD_RENDER))
return -1;
FT_Render_Glyph(handle->face->glyph, FT_RENDER_MODE_NORMAL); FT_Render_Glyph(handle->face->glyph, FT_RENDER_MODE_NORMAL);
slot = handle->face->glyph; slot = handle->face->glyph;
/* Some glyphs can be blank. */ /* Some glyphs can be blank. */
buffer[i] = (uint8_t*)calloc(slot->bitmap.rows * slot->bitmap.pitch, 1);
glyph->width = slot->bitmap.width; glyph->width = slot->bitmap.width;
glyph->height = slot->bitmap.rows; glyph->height = slot->bitmap.rows;
pitches[i] = slot->bitmap.pitch;
glyph->advance_x = slot->advance.x >> 6; glyph->advance_x = slot->advance.x >> 6;
glyph->advance_y = slot->advance.y >> 6; glyph->advance_y = slot->advance.y >> 6;
glyph->draw_offset_x = slot->bitmap_left; glyph->draw_offset_x = slot->bitmap_left;
glyph->draw_offset_y = -slot->bitmap_top; glyph->draw_offset_y = -slot->bitmap_top;
if (buffer[i] && slot->bitmap.buffer)
memcpy(buffer[i], slot->bitmap.buffer,
slot->bitmap.rows * pitches[i]);
max_width = MAX(max_width, (unsigned)slot->bitmap.width);
max_height = MAX(max_height, (unsigned)slot->bitmap.rows);
}
handle->atlas.width = max_width * FT_ATLAS_COLS;
handle->atlas.height = max_height * FT_ATLAS_ROWS;
handle->atlas.buffer = (uint8_t*)
calloc(handle->atlas.width * handle->atlas.height, 1);
if (!handle->atlas.buffer)
{
ret = false;
goto end;
}
/* Blit our texture atlas. */
for (i = 0; i < FT_ATLAS_SIZE; i++)
{
uint8_t *dst = NULL; uint8_t *dst = NULL;
unsigned offset_x = (i % FT_ATLAS_COLS) * max_width; unsigned offset_x = (id % FT_ATLAS_COLS) * handle->max_glyph_width;
unsigned offset_y = (i / FT_ATLAS_COLS) * max_height; unsigned offset_y = (id / FT_ATLAS_COLS) * handle->max_glyph_height;
handle->glyphs[i].atlas_offset_x = offset_x; handle->glyphs[id].atlas_offset_x = offset_x;
handle->glyphs[i].atlas_offset_y = offset_y; handle->glyphs[id].atlas_offset_y = offset_y;
dst = (uint8_t*)handle->atlas.buffer; dst = (uint8_t*)handle->atlas.buffer;
dst += offset_x + offset_y * handle->atlas.width; dst += offset_x + offset_y * handle->atlas.width;
if (buffer[i]) if (slot->bitmap.buffer)
{ {
unsigned r, c; unsigned r, c;
const uint8_t *src = (const uint8_t*)buffer[i]; const uint8_t *src = (const uint8_t*)slot->bitmap.buffer;
for (r = 0; r < handle->glyphs[i].height; for (r = 0; r < handle->glyphs[id].height;
r++, dst += handle->atlas.width, src += pitches[i]) r++, dst += handle->atlas.width, src += slot->bitmap.pitch)
for (c = 0; c < handle->glyphs[i].width; c++) for (c = 0; c < handle->glyphs[id].width; c++)
dst[c] = src[c]; dst[c] = src[c];
} }
}
end: handle->usage_counter++;
for (i = 0; i < FT_ATLAS_SIZE; i++) return id;
free(buffer[i]);
return ret;
} }
static const struct font_glyph *font_renderer_ft_get_glyph(
void *data, uint32_t code)
{
ft_font_renderer_t *handle = (ft_font_renderer_t*)data;
if (!handle)
return NULL;
if(code > 0x10000)
return NULL;
unsigned id = handle->uc_to_id[code];
if(!id)
id = font_renderer_update_atlas(handle, (FT_ULong)code);
handle->last_used[id] = handle->usage_counter++;
return &handle->glyphs[id];
}
static bool font_renderer_create_atlas(ft_font_renderer_t *handle)
{
unsigned i;
for (i = 0; i < 256; i++)
if(font_renderer_update_atlas(handle, i) < 0)
return false;
return true;
}
#include "locale.h"
static void *font_renderer_ft_init(const char *font_path, float font_size) static void *font_renderer_ft_init(const char *font_path, float font_size)
{ {
FT_Error err; FT_Error err;
@ -172,6 +188,9 @@ static void *font_renderer_ft_init(const char *font_path, float font_size)
if (!handle) if (!handle)
goto error; goto error;
if (font_size < 1.0)
goto error;
err = FT_Init_FreeType(&handle->lib); err = FT_Init_FreeType(&handle->lib);
if (err) if (err)
goto error; goto error;
@ -180,10 +199,26 @@ static void *font_renderer_ft_init(const char *font_path, float font_size)
if (err) if (err)
goto error; goto error;
err = FT_Select_Charmap(handle->face, FT_ENCODING_UNICODE);
if (err)
goto error;
err = FT_Set_Pixel_Sizes(handle->face, 0, font_size); err = FT_Set_Pixel_Sizes(handle->face, 0, font_size);
if (err) if (err)
goto error; goto error;
/* TODO: find a better way to determine onmax_glyph_width/height */
handle->max_glyph_width = font_size;
handle->max_glyph_height = font_size;
handle->atlas.width = handle->max_glyph_width * 2 * FT_ATLAS_COLS;
handle->atlas.height = handle->max_glyph_height * 2 * FT_ATLAS_ROWS;
handle->atlas.buffer = (uint8_t*)
calloc(handle->atlas.width * handle->atlas.height, 1);
if (!handle->atlas.buffer)
return false;
if (!font_renderer_create_atlas(handle)) if (!font_renderer_create_atlas(handle))
goto error; goto error;