diff --git a/Makefile b/Makefile index 89399186c1..6e227052c4 100644 --- a/Makefile +++ b/Makefile @@ -38,13 +38,14 @@ OBJ = frontend/frontend.o \ gfx/scaler/scaler_filter.o \ gfx/image/image_rpng.o \ gfx/fonts/fonts.o \ - gfx/fonts/bitmapfont.o \ audio/resampler.o \ audio/dsp_filter.o \ audio/sinc.o \ audio/cc_resampler.o \ performance.o +# gfx/fonts/bitmapfont.o \ + JOYCONFIG_OBJ = tools/retroarch-joyconfig.o \ conf/config_file.o \ file_path.o \ diff --git a/driver.h b/driver.h index 5d63a777ca..3615ab6152 100644 --- a/driver.h +++ b/driver.h @@ -342,6 +342,15 @@ typedef struct video_overlay_interface } video_overlay_interface_t; #endif +struct font_params +{ + float x; + float y; + float alpha; + float scale; + uint32_t color; +}; + // Optionally implemented interface to poke more deeply into video driver. typedef struct video_poke_interface { @@ -357,7 +366,7 @@ typedef struct video_poke_interface void (*set_texture_frame)(void *data, const void *frame, bool rgb32, unsigned width, unsigned height, float alpha); // Update texture. void (*set_texture_enable)(void *data, bool enable, bool full_screen); // Enable/disable rendering. #endif - void (*set_osd_msg)(void *data, const char *msg, void *userdata); + void (*set_osd_msg)(void *data, const char *msg, const struct font_params *params); void (*show_mouse)(void *data, bool state); void (*grab_mouse_toggle)(void *data); diff --git a/frontend/menu/backend/menu_common_backend.c b/frontend/menu/backend/menu_common_backend.c index c202a69f45..e5ee1448b9 100644 --- a/frontend/menu/backend/menu_common_backend.c +++ b/frontend/menu/backend/menu_common_backend.c @@ -205,7 +205,6 @@ static void menu_common_entries_init(void *data, unsigned menu_type) case RGUI_SETTINGS_FONT_OPTIONS: file_list_clear(rgui->selection_buf); file_list_push(rgui->selection_buf, "OSD Font Enable", RGUI_SETTINGS_FONT_ENABLE, 0); - file_list_push(rgui->selection_buf, "OSD Font Scale to Window", RGUI_SETTINGS_FONT_SCALE, 0); file_list_push(rgui->selection_buf, "OSD Font Size", RGUI_SETTINGS_FONT_SIZE, 0); break; case RGUI_SETTINGS_CORE_OPTIONS: @@ -4944,12 +4943,6 @@ static int menu_common_setting_set(unsigned setting, unsigned action) else if (action == RGUI_ACTION_START) g_settings.video.font_enable = true; break; - case RGUI_SETTINGS_FONT_SCALE: - if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) - g_settings.video.font_scale = !g_settings.video.font_scale; - else if (action == RGUI_ACTION_START) - g_settings.video.font_scale = true; - break; case RGUI_SETTINGS_FONT_SIZE: if (action == RGUI_ACTION_LEFT) g_settings.video.font_size -= 1.0f; @@ -5468,9 +5461,6 @@ static void menu_common_setting_set_label(char *type_str, size_t type_str_size, case RGUI_SETTINGS_FONT_ENABLE: snprintf(type_str, type_str_size, g_settings.video.font_enable ? "ON" : "OFF"); break; - case RGUI_SETTINGS_FONT_SCALE: - snprintf(type_str, type_str_size, g_settings.video.font_scale ? "ON" : "OFF"); - break; case RGUI_SETTINGS_FONT_SIZE: snprintf(type_str, type_str_size, "%.1f", g_settings.video.font_size); break; diff --git a/frontend/menu/backend/menu_common_backend.h b/frontend/menu/backend/menu_common_backend.h index e5da1e451f..99c77dedff 100644 --- a/frontend/menu/backend/menu_common_backend.h +++ b/frontend/menu/backend/menu_common_backend.h @@ -61,7 +61,6 @@ typedef enum RGUI_SETTINGS_VIDEO_OPTIONS_LAST, RGUI_SETTINGS_FONT_OPTIONS, RGUI_SETTINGS_FONT_ENABLE, - RGUI_SETTINGS_FONT_SCALE, RGUI_SETTINGS_FONT_SIZE, RGUI_SETTINGS_SLOWMOTION_RATIO, RGUI_SETTINGS_FASTFORWARD_RATIO, diff --git a/general.h b/general.h index 26e7bbb1f4..167da8fa13 100644 --- a/general.h +++ b/general.h @@ -185,7 +185,6 @@ struct settings char font_path[PATH_MAX]; float font_size; bool font_enable; - bool font_scale; float msg_pos_x; float msg_pos_y; float msg_color_r; diff --git a/gfx/fonts/fonts.c b/gfx/fonts/fonts.c index 251ff00693..2b956eb746 100644 --- a/gfx/fonts/fonts.c +++ b/gfx/fonts/fonts.c @@ -25,24 +25,24 @@ static const font_renderer_driver_t *font_backends[] = { &ft_font_renderer, #endif #if !defined(DONT_HAVE_BITMAPFONTS) - &bitmap_font_renderer, +// &bitmap_font_renderer, #endif NULL }; -bool font_renderer_create_default(const font_renderer_driver_t **driver, void **handle) +bool font_renderer_create_default(const font_renderer_driver_t **driver, void **handle, + const char *font_path, unsigned font_size) { unsigned i; for (i = 0; font_backends[i]; i++) { - const char *font_path = *g_settings.video.font_path ? g_settings.video.font_path : NULL; - if (!font_path) - font_path = font_backends[i]->get_default_font(); - + const char *path = NULL; if (!font_path) + path = font_backends[i]->get_default_font(); + if (!path) continue; - *handle = font_backends[i]->init(font_path, g_settings.video.font_size); + *handle = font_backends[i]->init(path, font_size); if (*handle) { RARCH_LOG("Using font rendering backend: %s.\n", font_backends[i]->ident); diff --git a/gfx/fonts/fonts.h b/gfx/fonts/fonts.h index 42b190ff13..beeab11534 100644 --- a/gfx/fonts/fonts.h +++ b/gfx/fonts/fonts.h @@ -20,30 +20,43 @@ #include #include "../../boolean.h" -typedef struct font_renderer font_renderer_t; +// All coordinates and offsets are top-left oriented. +// +// This is a texture-atlas approach which allows text to be drawn in a single draw call. +// It is up to the code using this interface to actually generate proper vertex buffers and upload the atlas texture to GPU. -struct font_output +struct font_glyph { - uint8_t *output; // 8-bit alpha. - unsigned width, height, pitch; - unsigned color; - unsigned scaling_factor; - int off_x, off_y; - int advance_x, advance_y, char_off_x, char_off_y; // for advanced font rendering - struct font_output *next; // linked list. + unsigned width; + unsigned height; + + // Texel coordiate offset for top-left pixel of this glyph. + unsigned atlas_offset_x; + unsigned atlas_offset_y; + + // When drawing this glyph, apply an offset to current X/Y draw coordinate. + int draw_offset_x; + int draw_offset_y; + + // Advance X/Y draw coordinates after drawing this glyph. + int advance_x; + int advance_y; }; -struct font_output_list +struct font_atlas { - struct font_output *head; + uint8_t *buffer; // Alpha channel. + unsigned width; + unsigned height; }; typedef struct font_renderer_driver { void *(*init)(const char *font_path, float font_size); - void (*render_msg)(void *data, const char *msg, struct font_output_list *output); - void (*free_output)(void *data, struct font_output_list *list); + const struct font_atlas *(*get_atlas)(void *data); + const struct font_glyph *(*get_glyph)(void *data, uint32_t code); // Returns NULL if no glyph for this code is found. void (*free)(void *data); + const char *(*get_default_font)(void); const char *ident; } font_renderer_driver_t; @@ -51,7 +64,8 @@ typedef struct font_renderer_driver extern const font_renderer_driver_t ft_font_renderer; extern const font_renderer_driver_t bitmap_font_renderer; -bool font_renderer_create_default(const font_renderer_driver_t **driver, void **handle); +// font_path can be NULL for default font. +bool font_renderer_create_default(const font_renderer_driver_t **driver, void **handle, const char *font_path, unsigned font_size); #endif diff --git a/gfx/fonts/freetype.c b/gfx/fonts/freetype.c index 3447b268b9..7d577723d5 100644 --- a/gfx/fonts/freetype.c +++ b/gfx/fonts/freetype.c @@ -22,18 +22,39 @@ #include #include FT_FREETYPE_H -struct font_renderer +#define ATLAS_ROWS 8 +#define ATLAS_COLS 16 +#define ATLAS_SIZE (ATLAS_ROWS * ATLAS_COLS) + +typedef struct ft_renderer { FT_Library lib; FT_Face face; -}; + + struct font_atlas atlas; + struct font_glyph glyphs[ATLAS_SIZE]; +} ft_renderer_t; + +static const struct font_atlas *ft_renderer_get_atlas(void *data) +{ + ft_renderer_t *handle = (ft_renderer_t*)data; + return &handle->atlas; +} + +static const struct font_glyph *ft_renderer_get_glyph(void *data, uint32_t code) +{ + ft_renderer_t *handle = (ft_renderer_t*)data; + return code < ATLAS_SIZE ? &handle->glyphs[code] : NULL; +} static void ft_renderer_free(void *data) { - font_renderer_t *handle = (font_renderer_t*)data; + ft_renderer_t *handle = (ft_renderer_t*)data; if (!handle) return; + free(handle->atlas.buffer); + if (handle->face) FT_Done_Face(handle->face); if (handle->lib) @@ -41,11 +62,92 @@ static void ft_renderer_free(void *data) free(handle); } +static bool ft_renderer_create_atlas(ft_renderer_t *handle) +{ + unsigned i; + bool ret = true; + + uint8_t *buffer[ATLAS_SIZE] = {NULL}; + unsigned pitches[ATLAS_SIZE] = {0}; + + unsigned max_width = 0; + unsigned max_height = 0; + + for (i = 0; i < ATLAS_SIZE; i++) + { + struct font_glyph *glyph = &handle->glyphs[i]; + + if (FT_Load_Char(handle->face, i, FT_LOAD_RENDER)) + { + ret = false; + goto end; + } + + FT_Render_Glyph(handle->face->glyph, FT_RENDER_MODE_NORMAL); + FT_GlyphSlot slot = handle->face->glyph; + + buffer[i] = (uint8_t*)calloc(slot->bitmap.rows * slot->bitmap.pitch, 1); + if (!buffer[i]) + { + ret = false; + goto end; + } + + glyph->width = slot->bitmap.width; + glyph->height = slot->bitmap.rows; + pitches[i] = slot->bitmap.pitch; + + glyph->advance_x = slot->advance.x >> 6; + glyph->advance_y = slot->advance.y >> 6; + glyph->draw_offset_x = slot->bitmap_left; + glyph->draw_offset_y = -slot->bitmap_top; + + memcpy(buffer[i], slot->bitmap.buffer, slot->bitmap.rows * pitches[i]); + max_width = max(max_width, slot->bitmap.width); + max_height = max(max_height, slot->bitmap.rows); + } + + handle->atlas.width = max_width * ATLAS_COLS; + handle->atlas.height = max_height * 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 < ATLAS_SIZE; i++) + { + unsigned r, c; + const uint8_t *src = buffer[i]; + + unsigned offset_x = (i % ATLAS_COLS) * max_width; + unsigned offset_y = (i / ATLAS_COLS) * max_height; + + handle->glyphs[i].atlas_offset_x = offset_x; + handle->glyphs[i].atlas_offset_y = offset_y; + + uint8_t *dst = handle->atlas.buffer; + dst += offset_x + offset_y * handle->atlas.width; + + for (r = 0; r < handle->glyphs[i].height; r++, dst += handle->atlas.width, src += pitches[i]) + for (c = 0; c < handle->glyphs[i].width; c++) + dst[c] = src[c]; + } + +end: + for (i = 0; i < ATLAS_SIZE; i++) + free(buffer[i]); + return ret; +} + static void *ft_renderer_init(const char *font_path, float font_size) { - (void)font_size; FT_Error err; - font_renderer_t *handle = (font_renderer_t*)calloc(1, sizeof(*handle)); + + ft_renderer_t *handle = (ft_renderer_t*)calloc(1, sizeof(*handle)); if (!handle) goto error; @@ -61,6 +163,9 @@ static void *ft_renderer_init(const char *font_path, float font_size) if (err) goto error; + if (!ft_renderer_create_atlas(handle)) + goto error; + return handle; error: @@ -68,76 +173,6 @@ error: return NULL; } -static void ft_renderer_msg(void *data, const char *msg, struct font_output_list *output) -{ - size_t i; - font_renderer_t *handle = (font_renderer_t*)data; - output->head = NULL; - - FT_GlyphSlot slot = handle->face->glyph; - struct font_output *cur = NULL; - size_t len = strlen(msg); - int off_x = 0, off_y = 0; - - for (i = 0; i < len; i++) - { - FT_Error err = FT_Load_Char(handle->face, msg[i], FT_LOAD_RENDER); - - if (!err) - { - struct font_output *tmp = (struct font_output*)calloc(1, sizeof(*tmp)); - if (!tmp) - break; - - tmp->output = (uint8_t*)malloc(slot->bitmap.pitch * slot->bitmap.rows); - if (!tmp->output) - { - free(tmp); - break; - } - - memcpy(tmp->output, slot->bitmap.buffer, slot->bitmap.pitch * slot->bitmap.rows); - - tmp->width = slot->bitmap.width; - tmp->height = slot->bitmap.rows; - tmp->pitch = slot->bitmap.pitch; - tmp->advance_x = slot->advance.x >> 6; - tmp->advance_y = slot->advance.y >> 6; - tmp->char_off_x = slot->bitmap_left; - tmp->char_off_y = slot->bitmap_top - slot->bitmap.rows; - tmp->off_x = off_x + tmp->char_off_x; - tmp->off_y = off_y + tmp->char_off_y; - tmp->next = NULL; - - if (i == 0) - output->head = tmp; - else - cur->next = tmp; - - cur = tmp; - } - - off_x += slot->advance.x >> 6; - off_y += slot->advance.y >> 6; - } -} - -static void ft_renderer_free_output(void *data, struct font_output_list *output) -{ - (void)data; - - struct font_output *itr = output->head; - struct font_output *tmp = NULL; - while (itr != NULL) - { - free(itr->output); - tmp = itr; - itr = itr->next; - free(tmp); - } - output->head = NULL; -} - // Not the cleanest way to do things for sure, but should hopefully work ... :) static const char *font_paths[] = { @@ -172,8 +207,8 @@ static const char *ft_renderer_get_default_font(void) const font_renderer_driver_t ft_font_renderer = { ft_renderer_init, - ft_renderer_msg, - ft_renderer_free_output, + ft_renderer_get_atlas, + ft_renderer_get_glyph, ft_renderer_free, ft_renderer_get_default_font, "freetype", diff --git a/gfx/fonts/gl_font.c b/gfx/fonts/gl_font.c index bb5a3573cf..1a2c2740bc 100644 --- a/gfx/fonts/gl_font.c +++ b/gfx/fonts/gl_font.c @@ -23,18 +23,24 @@ static const gl_font_renderer_t *gl_font_backends[] = { #else &gl_raster_font, #endif + NULL, }; -const gl_font_renderer_t *gl_font_init_first(void *data, const char *font_path, float font_size, - unsigned win_width, unsigned win_height) +bool gl_font_init_first(const gl_font_renderer_t **font_driver, void **font_handle, + void *gl_data, const char *font_path, float font_size, unsigned win_width, unsigned win_height) { - size_t i; - for (i = 0; i < ARRAY_SIZE(gl_font_backends); i++) + unsigned i; + for (i = 0; gl_font_backends[i]; i++) { - if (gl_font_backends[i]->init(data, font_path, font_size, win_width, win_height)) - return gl_font_backends[i]; + void *data = gl_font_backends[i]->init(gl_data, font_path, font_size, win_width, win_height); + if (data) + { + *font_driver = gl_font_backends[i]; + *font_handle = data; + return true; + } } - return NULL; + return false; } diff --git a/gfx/fonts/gl_font.h b/gfx/fonts/gl_font.h index 3b24f104b6..2c2151e66c 100644 --- a/gfx/fonts/gl_font.h +++ b/gfx/fonts/gl_font.h @@ -16,23 +16,23 @@ #ifndef GL_FONT_H__ #define GL_FONT_H__ -#include +#include "../../driver.h" #include "../../boolean.h" typedef struct gl_font_renderer { - bool (*init)(void *data, const char *font_path, float font_size, + void *(*init)(void *data, const char *font_path, float font_size, unsigned win_width, unsigned win_height); - void (*deinit)(void *data); - void (*render_msg)(void *data, const char *msg, void *parms); + void (*free)(void *data); + void (*render_msg)(void *data, const char *msg, const struct font_params *parms); const char *ident; } gl_font_renderer_t; extern const gl_font_renderer_t gl_raster_font; extern const gl_font_renderer_t libdbg_font; -const gl_font_renderer_t *gl_font_init_first(void *data, - const char *font_path, float font_size, unsigned win_width, unsigned win_height); +bool gl_font_init_first(const gl_font_renderer_t **font_driver, void **font_handle, + void *gl_data, const char *font_path, float font_size, unsigned win_width, unsigned win_height); #endif diff --git a/gfx/fonts/gl_raster_font.c b/gfx/fonts/gl_raster_font.c index b09e05378d..c9d4c14f08 100644 --- a/gfx/fonts/gl_raster_font.c +++ b/gfx/fonts/gl_raster_font.c @@ -18,245 +18,107 @@ #include "../gl_common.h" #include "../shader_common.h" -static bool gl_init_font(void *data, const char *font_path, float font_size, unsigned win_width, unsigned win_height) +typedef struct +{ + gl_t *gl; + GLuint tex; + unsigned tex_width, tex_height; + + const font_renderer_driver_t *font_driver; + void *font_data; +} gl_raster_t; + +static void *gl_init_font(void *gl_data, const char *font_path, float font_size, unsigned win_width, unsigned win_height) { - size_t i, j; (void)win_width; (void)win_height; - if (!g_settings.video.font_enable) - return false; + gl_raster_t *font = (gl_raster_t*)calloc(1, sizeof(*font)); + if (!font) + return NULL; - (void)font_size; - gl_t *gl = (gl_t*)data; + font->gl = (gl_t*)gl_data; - if (font_renderer_create_default(&gl->font_driver, &gl->font)) - { - glGenTextures(1, &gl->font_tex); - glBindTexture(GL_TEXTURE_2D, gl->font_tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl->max_font_size); - } - else + if (!font_renderer_create_default(&font->font_driver, &font->font_data, font_path, font_size)) { RARCH_WARN("Couldn't init font renderer.\n"); - return false; + free(font); + return NULL; } - for (i = 0; i < 4; i++) + glGenTextures(1, &font->tex); + glBindTexture(GL_TEXTURE_2D, font->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + const struct font_atlas *atlas = font->font_driver->get_atlas(font->font_data); + + unsigned width = next_pow2(atlas->width); + unsigned height = next_pow2(atlas->height); + // Ideally, we'd use single component textures, but the difference in ways to do that between core GL and GLES/legacy GL + // is too great to bother going down that route. + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + uint8_t *tmp_buffer = (uint8_t*)malloc(atlas->width * atlas->height * 4); + if (tmp_buffer) { - gl->font_color[4 * i + 0] = g_settings.video.msg_color_r; - gl->font_color[4 * i + 1] = g_settings.video.msg_color_g; - gl->font_color[4 * i + 2] = g_settings.video.msg_color_b; - gl->font_color[4 * i + 3] = 1.0; - } - - for (i = 0; i < 4; i++) - { - for (j = 0; j < 3; j++) - gl->font_color_dark[4 * i + j] = 0.3 * gl->font_color[4 * i + j]; - gl->font_color_dark[4 * i + 3] = 1.0; - } - - return true; -} - -void gl_deinit_font(void *data) -{ - gl_t *gl = (gl_t*)data; - - if (gl->font) - { - gl->font_driver->free(gl->font); - glDeleteTextures(1, &gl->font_tex); - - if (gl->font_tex_buf) - free(gl->font_tex_buf); - } -} - -// Somewhat overwhelming code just to render some damn fonts. -// We aim to use NPOT textures for compatibility with old and shitty cards. -// Also, we want to avoid reallocating a texture for each glyph (performance dips), so we -// contruct the whole texture using one call, and copy straight to it with -// glTexSubImage. - -struct font_rect -{ - int x, y; - int width, height; - int pot_width, pot_height; -}; - -static void calculate_msg_geometry(const struct font_output *head, struct font_rect *rect) -{ - int x_min = head->off_x; - int x_max = head->off_x + head->width; - int y_min = head->off_y; - int y_max = head->off_y + head->height; - - while ((head = head->next)) - { - int left = head->off_x; - int right = head->off_x + head->width; - int bottom = head->off_y; - int top = head->off_y + head->height; - - if (left < x_min) - x_min = left; - if (right > x_max) - x_max = right; - - if (bottom < y_min) - y_min = bottom; - if (top > y_max) - y_max = top; - } - - rect->x = x_min; - rect->y = y_min; - rect->width = x_max - x_min; - rect->height = y_max - y_min; -} - -static void adjust_power_of_two(gl_t *gl, struct font_rect *geom) -{ - // Some systems really hate NPOT textures. - geom->pot_width = next_pow2(geom->width); - geom->pot_height = next_pow2(geom->height); - - if (geom->pot_width > gl->max_font_size) - geom->pot_width = gl->max_font_size; - if (geom->pot_height > gl->max_font_size) - geom->pot_height = gl->max_font_size; - - if ((geom->pot_width > gl->font_tex_w) || (geom->pot_height > gl->font_tex_h)) - { - gl->font_tex_buf = (uint32_t*)realloc(gl->font_tex_buf, - geom->pot_width * geom->pot_height * sizeof(uint32_t)); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, geom->pot_width, geom->pot_height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - - gl->font_tex_w = geom->pot_width; - gl->font_tex_h = geom->pot_height; - } -} - -static void copy_glyph(const struct font_output *head, const struct font_rect *geom, uint32_t *buffer, unsigned width, unsigned height) -{ - int h, w; - // head has top-left oriented coords. - int x = head->off_x - geom->x; - int y = head->off_y - geom->y; - y = height - head->height - y - 1; - - const uint8_t *src = head->output; - int font_width = head->width + ((x < 0) ? x : 0); - int font_height = head->height + ((y < 0) ? y : 0); - - if (x < 0) - { - src += -x; - x = 0; - } - - if (y < 0) - { - src += -y * head->pitch; - y = 0; - } - - if (x + font_width > (int)width) - font_width = width - x; - - if (y + font_height > (int)height) - font_height = height - y; - - uint32_t *dst = buffer + y * width + x; - for (h = 0; h < font_height; h++, dst += width, src += head->pitch) - { - uint8_t *d = (uint8_t*)dst; - for (w = 0; w < font_width; w++) + unsigned i; + uint8_t *dst = tmp_buffer; + const uint8_t *src = atlas->buffer; + for (i = 0; i < atlas->width * atlas->height; i++) { - *d++ = 0xff; - *d++ = 0xff; - *d++ = 0xff; - *d++ = src[w]; + *dst++ = 0xff; + *dst++ = 0xff; + *dst++ = 0xff; + *dst++ = *src++; } + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, atlas->width, atlas->height, GL_RGBA, GL_UNSIGNED_BYTE, tmp_buffer); + free(tmp_buffer); } + + font->tex_width = width; + font->tex_height = height; + + glBindTexture(GL_TEXTURE_2D, font->gl->texture[font->gl->tex_index]); + return font; } -// Old style "blitting", so we can render all the fonts in one go. -// TODO: Is it possible that fonts could overlap if we blit without alpha blending? -static void blit_fonts(gl_t *gl, const struct font_output *head, const struct font_rect *geom) +void gl_free_font(void *data) { - memset(gl->font_tex_buf, 0, gl->font_tex_w * gl->font_tex_h * sizeof(uint32_t)); + gl_raster_t *font = (gl_raster_t*)data; + if (!font) + return; - while (head) - { - copy_glyph(head, geom, gl->font_tex_buf, gl->font_tex_w, gl->font_tex_h); - head = head->next; - } + if (font->font_driver && font->font_data) + font->font_driver->free(font->font_data); - glPixelStorei(GL_UNPACK_ALIGNMENT, 8); - glTexSubImage2D(GL_TEXTURE_2D, - 0, 0, 0, gl->font_tex_w, gl->font_tex_h, - GL_RGBA, GL_UNSIGNED_BYTE, gl->font_tex_buf); + glDeleteTextures(1, &font->tex); + free(font); } -static void calculate_font_coords(gl_t *gl, - GLfloat font_vertex[8], GLfloat font_vertex_dark[8], GLfloat font_tex_coords[8], GLfloat scale, GLfloat pos_x, GLfloat pos_y) +#define emit(c, vx, vy) do { \ + font_vertex[ 2 * (6 * i + c) + 0] = (x + (delta_x + off_x + vx * width) * scale) * inv_win_width; \ + font_vertex[ 2 * (6 * i + c) + 1] = (y + (delta_y - off_y - vy * height) * scale) * inv_win_height; \ + font_vertex_dark[2 * (6 * i + c) + 0] = (x + (delta_x + off_x - 2 + vx * width) * scale) * inv_win_width; \ + font_vertex_dark[2 * (6 * i + c) + 1] = (y + (delta_y - off_y - 2 - vy * height) * scale) * inv_win_height; \ + font_tex_coords[ 2 * (6 * i + c) + 0] = (tex_x + vx * width) * inv_tex_size_x; \ + font_tex_coords[ 2 * (6 * i + c) + 1] = (tex_y + vy * height) * inv_tex_size_y; \ + font_color_dark[ 4 * (6 * i + c) + 0] = 0.3f * color[0]; \ + font_color_dark[ 4 * (6 * i + c) + 1] = 0.3f * color[1]; \ + font_color_dark[ 4 * (6 * i + c) + 2] = 0.3f * color[2]; \ + font_color_dark[ 4 * (6 * i + c) + 3] = color[3]; \ + font_color[ 4 * (6 * i + c) + 0] = color[0]; \ + font_color[ 4 * (6 * i + c) + 1] = color[1]; \ + font_color[ 4 * (6 * i + c) + 2] = color[2]; \ + font_color[ 4 * (6 * i + c) + 3] = color[3]; \ +} while(0) + +static void render_message(gl_raster_t *font, const char *msg, GLfloat scale, const GLfloat color[4], GLfloat pos_x, GLfloat pos_y) { unsigned i; - GLfloat scale_factor = scale; - - GLfloat lx = pos_x; - GLfloat hx = (GLfloat)gl->font_last_width * scale_factor / gl->vp.width + lx; - GLfloat ly = pos_y; - GLfloat hy = (GLfloat)gl->font_last_height * scale_factor / gl->vp.height + ly; - - font_vertex[0] = lx; - font_vertex[2] = hx; - font_vertex[4] = lx; - font_vertex[6] = hx; - font_vertex[1] = hy; - font_vertex[3] = hy; - font_vertex[5] = ly; - font_vertex[7] = ly; - - GLfloat shift_x = 2.0f / gl->vp.width; - GLfloat shift_y = 2.0f / gl->vp.height; - for (i = 0; i < 4; i++) - { - font_vertex_dark[2 * i + 0] = font_vertex[2 * i + 0] - shift_x; - font_vertex_dark[2 * i + 1] = font_vertex[2 * i + 1] - shift_y; - } - - lx = 0.0f; - hx = (GLfloat)gl->font_last_width / gl->font_tex_w; - ly = 1.0f - (GLfloat)gl->font_last_height / gl->font_tex_h; - hy = 1.0f; - - font_tex_coords[0] = lx; - font_tex_coords[2] = hx; - font_tex_coords[4] = lx; - font_tex_coords[6] = hx; - font_tex_coords[1] = ly; - font_tex_coords[3] = ly; - font_tex_coords[5] = hy; - font_tex_coords[7] = hy; -} - -static void setup_font(void *data, const char *msg, GLfloat scale, GLfloat pos_x, GLfloat pos_y) -{ - gl_t *gl = (gl_t*)data; - if (!gl->font) - return; + gl_t *gl = font->gl; if (gl->shader && gl->shader->use) gl->shader->use(gl, GL_SHADER_STOCK_BLEND); @@ -264,73 +126,95 @@ static void setup_font(void *data, const char *msg, GLfloat scale, GLfloat pos_x gl_set_viewport(gl, gl->win_width, gl->win_height, false, false); glEnable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, font->tex); - GLfloat font_vertex[8]; - GLfloat font_vertex_dark[8]; - GLfloat font_tex_coords[8]; +#define MAX_MSG_LEN_CHUNK 32 + GLfloat font_tex_coords[2 * 6 * MAX_MSG_LEN_CHUNK]; + GLfloat font_vertex[2 * 6 * MAX_MSG_LEN_CHUNK]; + GLfloat font_vertex_dark[2 * 6 * MAX_MSG_LEN_CHUNK]; + GLfloat font_color[4 * 6 * MAX_MSG_LEN_CHUNK]; + GLfloat font_color_dark[4 * 6 * MAX_MSG_LEN_CHUNK]; - glBindTexture(GL_TEXTURE_2D, gl->font_tex); + unsigned msg_len_full = strlen(msg); + unsigned msg_len = min(msg_len_full, MAX_MSG_LEN_CHUNK); - gl->coords.tex_coord = font_tex_coords; + int x = roundf(pos_x * gl->win_width); + int y = roundf(pos_y * gl->win_height); + int delta_x = 0; + int delta_y = 0; - struct font_output_list out; + float inv_tex_size_x = 1.0f / font->tex_width; + float inv_tex_size_y = 1.0f / font->tex_height; + float inv_win_width = 1.0f / font->gl->win_width; + float inv_win_height = 1.0f / font->gl->win_height; - // If we get the same message, there's obviously no need to render fonts again ... - if (strcmp(gl->font_last_msg, msg) != 0) + while (msg_len_full) { - gl->font_driver->render_msg(gl->font, msg, &out); - struct font_output *head = out.head; + for (i = 0; i < msg_len; i++) + { + const struct font_glyph *gly = font->font_driver->get_glyph(font->font_data, (uint8_t)msg[i]); + if (!gly) + gly = font->font_driver->get_glyph(font->font_data, '?'); // Do something smarter here ... + if (!gly) + continue; - struct font_rect geom; - calculate_msg_geometry(head, &geom); - adjust_power_of_two(gl, &geom); - blit_fonts(gl, head, &geom); + int off_x = gly->draw_offset_x; + int off_y = gly->draw_offset_y; + int tex_x = gly->atlas_offset_x; + int tex_y = gly->atlas_offset_y; + int width = gly->width; + int height = gly->height; - gl->font_driver->free_output(gl->font, &out); - strlcpy(gl->font_last_msg, msg, sizeof(gl->font_last_msg)); + emit(0, 0, 1); // Bottom-left + emit(1, 1, 1); // Bottom-right + emit(2, 0, 0); // Top-left - gl->font_last_width = geom.width; - gl->font_last_height = geom.height; + emit(3, 1, 0); // Top-right + emit(4, 0, 0); // Top-left + emit(5, 1, 1); // Bottom-right +#undef emit + + delta_x += gly->advance_x; + delta_y -= gly->advance_y; + } + + // TODO: Make drop shadows parameterized? + gl->coords.tex_coord = font_tex_coords; + gl->coords.vertex = font_vertex_dark; + gl->coords.color = font_color_dark; + gl->coords.vertices = 6 * msg_len; + gl_shader_set_coords(gl, &gl->coords, &gl->mvp_no_rot); + glDrawArrays(GL_TRIANGLES, 0, 6 * msg_len); + + gl->coords.tex_coord = font_tex_coords; + gl->coords.vertex = font_vertex; + gl->coords.color = font_color; + gl->coords.vertices = 6 * msg_len; + gl_shader_set_coords(gl, &gl->coords, &gl->mvp_no_rot); + glDrawArrays(GL_TRIANGLES, 0, 6 * msg_len); + + msg_len_full -= msg_len; + msg += msg_len; + msg_len = min(msg_len_full, MAX_MSG_LEN_CHUNK); } - calculate_font_coords(gl, font_vertex, font_vertex_dark, font_tex_coords, - scale, pos_x, pos_y); - - gl->coords.vertex = font_vertex_dark; - gl->coords.color = gl->font_color_dark; - gl_shader_set_coords(gl, &gl->coords, &gl->mvp); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - gl->coords.vertex = font_vertex; - gl->coords.color = gl->font_color; - gl_shader_set_coords(gl, &gl->coords, &gl->mvp); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // Post - Go back to old rendering path. gl->coords.vertex = gl->vertex_ptr; gl->coords.tex_coord = gl->tex_coords; gl->coords.color = gl->white_color_ptr; + gl->coords.vertices = 4; glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); glDisable(GL_BLEND); - - struct gl_ortho ortho = {0, 1, 0, 1, -1, 1}; - gl_set_projection(gl, &ortho, true); } -static void gl_render_msg(void *data, const char *msg, void *parms) +static void gl_render_msg(void *data, const char *msg, const struct font_params *params) { - GLfloat x, y, scale, alpha; - gl_t *gl; - font_params_t *params; - int i; + GLfloat x, y, scale; + GLfloat color[4]; - (void)data; - (void)msg; - - gl = (gl_t*)data; - params = (font_params_t*)parms; - - if (!gl) + gl_raster_t *font = (gl_raster_t*)data; + if (!font) return; if (params) @@ -338,32 +222,34 @@ static void gl_render_msg(void *data, const char *msg, void *parms) x = params->x; y = params->y; scale = params->scale; - alpha = params->alpha; + + color[0] = ((params->color >> 16) & 0xff) / 255.0f; + color[1] = ((params->color >> 8) & 0xff) / 255.0f; + color[2] = ((params->color >> 0) & 0xff) / 255.0f; + color[3] = params->alpha; // If alpha is 0.0f, turn it into default 1.0f - if (alpha <= 0.0f) - alpha = 1.0f; + if (color[3] <= 0.0f) + color[3] = 1.0f; } else { x = g_settings.video.msg_pos_x; y = g_settings.video.msg_pos_y; - scale = g_settings.video.font_scale ? (GLfloat)gl->vp.width / (GLfloat)gl->full_x : 1.0f; - alpha = 1.0f; + scale = 1.0f; + + color[0] = g_settings.video.msg_color_r; + color[1] = g_settings.video.msg_color_g; + color[2] = g_settings.video.msg_color_b; + color[3] = 1.0f; } - for (i = 0; i < 4; i++) - { - gl->font_color[4 * i + 3] = alpha; - gl->font_color_dark[4 * i + 3] = alpha; - } - - setup_font(data, msg, scale, x, y); + render_message(font, msg, scale, color, x, y); } const gl_font_renderer_t gl_raster_font = { gl_init_font, - gl_deinit_font, + gl_free_font, gl_render_msg, "GL raster", }; diff --git a/gfx/fonts/ps_libdbgfont.c b/gfx/fonts/ps_libdbgfont.c index d6a0bc3184..3c2b34b6dd 100644 --- a/gfx/fonts/ps_libdbgfont.c +++ b/gfx/fonts/ps_libdbgfont.c @@ -32,16 +32,12 @@ #define DbgFontExit cellDbgFontExit #endif -static bool gl_init_font(void *data, const char *font_path, float font_size, +static void *gl_init_font(void *data, const char *font_path, float font_size, unsigned win_width, unsigned win_height) { (void)font_path; (void)font_size; - font_renderer_t *handle = (font_renderer_t*)calloc(1, sizeof(*handle)); - if (!handle) - return NULL; - DbgFontConfig cfg; #if defined(SN_TARGET_PSP2) cfg.fontSize = SCE_DBGFONT_FONTSIZE_LARGE; @@ -52,24 +48,22 @@ static bool gl_init_font(void *data, const char *font_path, float font_size, #endif DbgFontInit(&cfg); - free(handle); - return true; + // Doesn't need any state. + return (void*)-1; } static void gl_deinit_font(void *data) { (void)data; - DbgFontExit(); } -static void gl_render_msg(void *data, const char *msg, void *parms) +static void gl_render_msg(void *data, const char *msg, const struct font_params *params) { (void)data; float x, y, scale; unsigned color; - font_params_t *params = (font_params_t*)parms; if (params) { diff --git a/gfx/gfx_common.h b/gfx/gfx_common.h index 0137d3178f..82ceac18f7 100644 --- a/gfx/gfx_common.h +++ b/gfx/gfx_common.h @@ -21,6 +21,7 @@ extern "C" { #endif #include +#include #include "../general.h" #include "../boolean.h" #include "../performance.h" @@ -40,15 +41,6 @@ void gfx_set_dwm(void); void gfx_scale_integer(struct rarch_viewport *vp, unsigned win_width, unsigned win_height, float aspect_ratio, bool keep_aspect); -typedef struct -{ - float x; - float y; - float scale; - float alpha; - unsigned color; -} font_params_t; - #define MIN_SCALING_FACTOR (1.0f) #if defined(__CELLOS_LV2__) diff --git a/gfx/gl.c b/gfx/gl.c index ac3a775e24..03a074f527 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1630,8 +1630,8 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei gl_draw_texture(gl); #endif - if (msg && gl->font_ctx) - gl->font_ctx->render_msg(gl, msg, NULL); + if (msg && gl->font_driver && gl->font_handle) + gl->font_driver->render_msg(gl->font_handle, msg, NULL); #ifdef HAVE_OVERLAY if (gl->overlay_enable) @@ -1750,8 +1750,8 @@ static void gl_free(void *data) } #endif - if (gl->font_ctx) - gl->font_ctx->deinit(gl); + if (gl->font_driver && gl->font_handle) + gl->font_driver->free(gl->font_handle); gl_shader_deinit(gl); #ifndef NO_GL_FF_VERTEX @@ -2314,6 +2314,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo gl->coords.tex_coord = gl->tex_coords; gl->coords.color = gl->white_color_ptr; gl->coords.lut_tex_coord = tex_coords; + gl->coords.vertices = 4; // Empty buffer that we use to clear out the texture with on res change. gl->empty_buf = calloc(sizeof(uint32_t), gl->tex_w * gl->tex_h); @@ -2352,8 +2353,10 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo if (g_settings.video.font_enable) #endif { - gl->font_ctx = gl_font_init_first(gl, g_settings.video.font_path, g_settings.video.font_size, - gl->win_width, gl->win_height); + if (!gl_font_init_first(&gl->font_driver, &gl->font_handle, + gl, *g_settings.video.font_path ? g_settings.video.font_path : NULL, g_settings.video.font_size, + gl->win_width, gl->win_height)) + RARCH_ERR("[GL]: Failed to init font renderer.\n"); } #ifdef HAVE_GL_ASYNC_READBACK @@ -2934,19 +2937,18 @@ static void gl_apply_state_changes(void *data) gl->should_resize = true; } -static void gl_set_osd_msg(void *data, const char *msg, void *userdata) +static void gl_set_osd_msg(void *data, const char *msg, const struct font_params *params) { gl_t *gl = (gl_t*)data; - if (!gl) return; - context_bind_hw_render(gl, false); - font_params_t *params = (font_params_t*)userdata; - - if (gl->font_ctx) - gl->font_ctx->render_msg(gl, msg, params); - context_bind_hw_render(gl, true); + if (gl->font_driver && gl->font_handle) + { + context_bind_hw_render(gl, false); + gl->font_driver->render_msg(gl->font_handle, msg, params); + context_bind_hw_render(gl, true); + } } static void gl_show_mouse(void *data, bool state) diff --git a/gfx/gl_common.h b/gfx/gl_common.h index fa40a083a8..f2ceecc94d 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -189,6 +189,7 @@ struct gl_coords const GLfloat *color; const GLfloat *tex_coord; const GLfloat *lut_tex_coord; + unsigned vertices; }; typedef struct gl_shader_backend gl_shader_backend_t; @@ -282,17 +283,8 @@ typedef struct gl #endif // Fonts - void *font; - const gl_font_renderer_t *font_ctx; - const font_renderer_driver_t *font_driver; - GLuint font_tex; - GLint max_font_size; - int font_tex_w, font_tex_h; - uint32_t *font_tex_buf; - char font_last_msg[256]; - int font_last_width, font_last_height; - GLfloat font_color[16]; - GLfloat font_color_dark[16]; + const gl_font_renderer_t *font_driver; + void *font_handle; bool egl_images; video_info_t video_info; diff --git a/settings.c b/settings.c index 3f3138b94c..57d529a45a 100644 --- a/settings.c +++ b/settings.c @@ -292,7 +292,6 @@ void config_set_defaults(void) g_settings.video.font_enable = font_enable; g_settings.video.font_size = font_size; - g_settings.video.font_scale = font_scale; g_settings.video.msg_pos_x = message_pos_offset_x; g_settings.video.msg_pos_y = message_pos_offset_y; @@ -839,7 +838,6 @@ bool config_load_file(const char *path, bool set_defaults) CONFIG_GET_PATH(video.font_path, "video_font_path"); CONFIG_GET_FLOAT(video.font_size, "video_font_size"); CONFIG_GET_BOOL(video.font_enable, "video_font_enable"); - CONFIG_GET_BOOL(video.font_scale, "video_font_scale"); CONFIG_GET_FLOAT(video.msg_pos_x, "video_message_pos_x"); CONFIG_GET_FLOAT(video.msg_pos_y, "video_message_pos_y"); CONFIG_GET_INT(video.rotation, "video_rotation"); @@ -1397,7 +1395,6 @@ bool config_save_file(const char *path) config_set_bool(conf, "location_allow", g_settings.location.allow); #endif - config_set_bool(conf, "video_font_scale", g_settings.video.font_scale); config_set_float(conf, "video_font_size", g_settings.video.font_size); config_set_bool(conf, "video_font_enable", g_settings.video.font_enable); diff --git a/settings_data.c b/settings_data.c index 1e6731edf3..7e61732a55 100644 --- a/settings_data.c +++ b/settings_data.c @@ -611,7 +611,6 @@ const rarch_setting_t* setting_data_get_list(void) CONFIG_PATH(g_settings.video.font_path, "video_font_path", "Font Path", DEFAULT_ME_YO) WITH_FLAGS(SD_FLAG_ALLOW_EMPTY) CONFIG_FLOAT(g_settings.video.font_size, "video_font_size", "OSD Font Size", font_size) CONFIG_BOOL(g_settings.video.font_enable, "video_font_enable", "OSD Font Enable", font_enable) - CONFIG_BOOL(g_settings.video.font_scale, "video_font_scale", "OSD Font Scale To Window", font_scale) CONFIG_FLOAT(g_settings.video.msg_pos_x, "video_message_pos_x", "Message X Position", message_pos_offset_x) CONFIG_FLOAT(g_settings.video.msg_pos_y, "video_message_pos_y", "Message Y Position", message_pos_offset_y) /* message color */