From 5700febb2ca56c6a670e996cadf49d5fe036692f Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 14 Dec 2012 20:25:40 +0100 Subject: [PATCH] Refactor font renderering. Virtualize for multiple backends, and name files more sanely. --- Makefile | 6 +- Makefile.win | 6 +- gfx/ext_gfx.c | 7 - gfx/fonts/bitmapfont.c | 23 ++- gfx/fonts/fonts.c | 168 ---------------- gfx/fonts/fonts.h | 22 ++- gfx/fonts/freetype.c | 432 +++++++++++++---------------------------- gfx/fonts/gl_font.c | 314 ++++++++++++++++++++++++++++++ gfx/gl_common.h | 4 +- gfx/sdl_gfx.c | 45 +---- gfx/xvideo.c | 55 ++---- 11 files changed, 511 insertions(+), 571 deletions(-) delete mode 100644 gfx/fonts/fonts.c create mode 100644 gfx/fonts/gl_font.c diff --git a/Makefile b/Makefile index c7ade66337..b59085c929 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,8 @@ OBJ = retroarch.o \ gfx/scaler/scaler_int.o \ gfx/scaler/filter.o \ gfx/image.o \ + gfx/fonts/fonts.o \ + gfx/fonts/bitmapfont.o \ performance.o JOYCONFIG_OBJ = tools/retroarch-joyconfig.o \ @@ -160,7 +162,7 @@ ifeq ($(HAVE_SDL), 1) endif ifeq ($(HAVE_OPENGL), 1) - OBJ += gfx/gl.o gfx/gfx_context.o gfx/fonts/freetype.o gfx/math/matrix.o + OBJ += gfx/gl.o gfx/gfx_context.o gfx/fonts/gl_font.o gfx/math/matrix.o ifeq ($(HAVE_KMS), 1) OBJ += gfx/context/drm_egl_ctx.o @@ -250,7 +252,7 @@ ifeq ($(HAVE_DYLIB), 1) endif ifeq ($(HAVE_FREETYPE), 1) - OBJ += gfx/fonts/fonts.o + OBJ += gfx/fonts/freetype.o LIBS += $(FREETYPE_LIBS) DEFINES += $(FREETYPE_CFLAGS) endif diff --git a/Makefile.win b/Makefile.win index 078a9497f2..f364bb79a8 100644 --- a/Makefile.win +++ b/Makefile.win @@ -28,6 +28,8 @@ OBJ = retroarch.o \ gfx/scaler/scaler_int.o \ gfx/scaler/filter.o \ gfx/state_tracker.o \ + gfx/fonts/fonts.o \ + gfx/fonts/bitmapfont.o \ gfx/image.o \ performance.o @@ -100,7 +102,7 @@ ifeq ($(HAVE_THREADS), 1) endif ifeq ($(HAVE_OPENGL), 1) - OBJ += gfx/gl.o gfx/math/matrix.o gfx/fonts/freetype.o gfx/gfx_context.o gfx/context/wgl_ctx.o + OBJ += gfx/gl.o gfx/math/matrix.o gfx/fonts/gl_font.o gfx/gfx_context.o gfx/context/wgl_ctx.o DEFINES += -DHAVE_OPENGL LIBS += -lopengl32 -lgdi32 endif @@ -164,7 +166,7 @@ ifeq ($(HAVE_NETPLAY), 1) endif ifeq ($(HAVE_FREETYPE), 1) - OBJ += gfx/fonts/fonts.o + OBJ += gfx/fonts/freetype.o DEFINES += -DHAVE_FREETYPE -Ifreetype2 LIBS += -lfreetype -lz endif diff --git a/gfx/ext_gfx.c b/gfx/ext_gfx.c index a5d4c50cac..4900d22072 100644 --- a/gfx/ext_gfx.c +++ b/gfx/ext_gfx.c @@ -276,15 +276,8 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive const char *font = NULL; if (g_settings.video.font_enable) { -#ifdef HAVE_FREETYPE - if (*g_settings.video.font_path) - font = g_settings.video.font_path; - else - font = font_renderer_get_default_font(); -#else font = *g_settings.video.font_path ? g_settings.video.font_path : NULL; -#endif } char title_buf[128]; diff --git a/gfx/fonts/bitmapfont.c b/gfx/fonts/bitmapfont.c index 2ecb16c1a2..4d0b6f443a 100644 --- a/gfx/fonts/bitmapfont.c +++ b/gfx/fonts/bitmapfont.c @@ -44,10 +44,8 @@ static void char_to_texture(uint8_t letter, uint8_t *buffer) } -font_renderer_t *font_renderer_new(const char *font_path, unsigned font_size) +static font_renderer_t *font_renderer_init(const char *font_path, unsigned font_size) { - (void)font_size; - font_renderer_t *handle = (font_renderer_t*)calloc(1, sizeof(*handle)); if (!handle) return NULL; @@ -63,7 +61,7 @@ font_renderer_t *font_renderer_new(const char *font_path, unsigned font_size) return handle; } -void font_renderer_msg(font_renderer_t *handle, const char *msg, struct font_output_list *output) +static void font_renderer_msg(font_renderer_t *handle, const char *msg, struct font_output_list *output) { output->head = NULL; @@ -113,8 +111,9 @@ void font_renderer_msg(font_renderer_t *handle, const char *msg, struct font_out } } -void font_renderer_free_output(struct font_output_list *output) +static void font_renderer_free_output(font_renderer_t *handle, struct font_output_list *output) { + (void)handle; struct font_output *itr = output->head; struct font_output *tmp = NULL; while (itr != NULL) @@ -127,12 +126,22 @@ void font_renderer_free_output(struct font_output_list *output) output->head = NULL; } -void font_renderer_free(font_renderer_t *handle) +static void font_renderer_free(font_renderer_t *handle) { free(handle); } -const char *font_renderer_get_default_font(void) +static const char *font_renderer_get_default_font(void) { return ""; } + +const font_renderer_driver_t bitmap_font_renderer = { + font_renderer_init, + font_renderer_msg, + font_renderer_free_output, + font_renderer_free, + font_renderer_get_default_font, + "bitmap", +}; + diff --git a/gfx/fonts/fonts.c b/gfx/fonts/fonts.c deleted file mode 100644 index b1f0a574ce..0000000000 --- a/gfx/fonts/fonts.c +++ /dev/null @@ -1,168 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2012 - Hans-Kristian Arntzen - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#include "fonts.h" -#include "../../file.h" -#include -#include -#include - -#include -#include FT_FREETYPE_H - -struct font_renderer -{ - FT_Library lib; - FT_Face face; -}; - -font_renderer_t *font_renderer_new(const char *font_path, unsigned font_size) -{ - (void)font_size; - FT_Error err; - font_renderer_t *handle = (font_renderer_t*)calloc(1, sizeof(*handle)); - if (!handle) - goto error; - - err = FT_Init_FreeType(&handle->lib); - if (err) - goto error; - - err = FT_New_Face(handle->lib, font_path, 0, &handle->face); - if (err) - goto error; - - err = FT_Set_Pixel_Sizes(handle->face, 0, font_size); - if (err) - goto error; - - return handle; - -error: - font_renderer_free(handle); - return NULL; -} - -void font_renderer_msg(font_renderer_t *handle, const char *msg, struct font_output_list *output) -{ - 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 (size_t 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; - } -} - -void font_renderer_free_output(struct font_output_list *output) -{ - 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; -} - -void font_renderer_free(font_renderer_t *handle) -{ - if (!handle) - return; - - if (handle->face) - FT_Done_Face(handle->face); - if (handle->lib) - FT_Done_FreeType(handle->lib); - free(handle); -} - -// Not the cleanest way to do things for sure, but should hopefully work ... :) - -#if defined(_WIN32) -static const char *font_paths[] = { - "C:\\Windows\\Fonts\\consola.ttf", - "C:\\Windows\\Fonts\\verdana.ttf", -#elif defined(__APPLE__) -static const char *font_paths[] = { - "/Library/Fonts/Microsoft/Candara.ttf", - "/Library/Fonts/Verdana.ttf", - "/Library/Fonts/Tahoma.ttf", -#else -static const char *font_paths[] = { - "/usr/share/fonts/TTF/DejaVuSansMono.ttf", - "/usr/share/fonts/TTF/DejaVuSans.ttf", - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf", - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", -#endif - "osd-font.ttf", // Magic font to search for, useful for distribution. -}; - -// Highly OS/platform dependent. -const char *font_renderer_get_default_font(void) -{ - for (size_t i = 0; i < sizeof(font_paths) / sizeof(font_paths[0]); i++) - { - if (path_file_exists(font_paths[i])) - return font_paths[i]; - } - - return NULL; -} - diff --git a/gfx/fonts/fonts.h b/gfx/fonts/fonts.h index f99f94df2c..71f060771d 100644 --- a/gfx/fonts/fonts.h +++ b/gfx/fonts/fonts.h @@ -18,12 +18,13 @@ #define __RARCH_FONTS_H #include +#include "../../boolean.h" typedef struct font_renderer font_renderer_t; struct font_output { - uint8_t *output; // 8-bit intensity. + uint8_t *output; // 8-bit alpha. unsigned width, height, pitch; int off_x, off_y; int advance_x, advance_y, char_off_x, char_off_y; // for advanced font rendering @@ -35,13 +36,20 @@ struct font_output_list struct font_output *head; }; -font_renderer_t *font_renderer_new(const char *font_path, unsigned font_size); -void font_renderer_msg(font_renderer_t *handle, const char *msg, - struct font_output_list *output); +typedef struct font_renderer_driver +{ + font_renderer_t *(*init)(const char *font_path, unsigned font_size); + void (*render_msg)(font_renderer_t *handle, const char *msg, struct font_output_list *output); + void (*free_output)(font_renderer_t *handle, struct font_output_list *list); + void (*free)(font_renderer_t *handle); + const char *(*get_default_font)(void); + const char *ident; +} font_renderer_driver_t; -void font_renderer_free_output(struct font_output_list *list); -void font_renderer_free(font_renderer_t *handle); +extern const font_renderer_driver_t ft_font_renderer; +extern const font_renderer_driver_t bitmap_font_renderer; -const char *font_renderer_get_default_font(void); +bool font_renderer_create_default(const font_renderer_driver_t **driver, font_renderer_t **handle); #endif + diff --git a/gfx/fonts/freetype.c b/gfx/fonts/freetype.c index bc8615981f..0e31530f7a 100644 --- a/gfx/fonts/freetype.c +++ b/gfx/fonts/freetype.c @@ -1,6 +1,5 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2012 - Hans-Kristian Arntzen - * Copyright (C) 2011-2012 - Daniel De Matteis * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- @@ -14,326 +13,165 @@ * If not, see . */ -#include "../gl_common.h" +#include "fonts.h" +#include "../../file.h" +#include +#include +#include -void gl_init_font(void *data, const char *font_path, unsigned font_size) +#include +#include FT_FREETYPE_H + +struct font_renderer { -#ifdef HAVE_FREETYPE - if (!g_settings.video.font_enable) - return; - - gl_t *gl = (gl_t*)data; - - const char *path = font_path; - if (!*path) - path = font_renderer_get_default_font(); - - if (path) - { - gl->font = font_renderer_new(path, font_size); - if (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]); - } - else - RARCH_WARN("Couldn't init font renderer with font \"%s\"...\n", font_path); - } - else - RARCH_LOG("Did not find default font.\n"); - - for (unsigned i = 0; i < 4; i++) - { - 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 (unsigned i = 0; i < 4; i++) - { - for (unsigned 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; - } -#else - (void)data; - (void)font_path; - (void)font_size; -#endif -} - -void gl_deinit_font(void *data) -{ -#ifdef HAVE_FREETYPE - gl_t *gl = (gl_t*)data; - - if (gl->font) - { - font_renderer_free(gl->font); - glDeleteTextures(1, &gl->font_tex); - - if (gl->font_tex_buf) - free(gl->font_tex_buf); - } -#else - (void)data; -#endif -} - -#ifdef HAVE_FREETYPE -// 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; + FT_Library lib; + FT_Face face; }; -static void calculate_msg_geometry(const struct font_output *head, struct font_rect *rect) +static void font_renderer_free(font_renderer_t *handle) { - 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->font_tex_w) || (geom->pot_height > gl->font_tex_h)) - { - gl->font_tex_buf = (uint16_t*)realloc(gl->font_tex_buf, - geom->pot_width * geom->pot_height * sizeof(uint16_t)); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, geom->pot_width, geom->pot_height, - 0, GL_LUMINANCE_ALPHA, 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, uint16_t *buffer, unsigned width, unsigned height) -{ - // 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; - - uint16_t *dst = buffer + y * width + x; - - for (int h = 0; h < font_height; h++, dst += width, src += head->pitch) - for (int w = 0; w < font_width; w++) - dst[w] = 0xff | (src[w] << 8); // Assume little endian for now. -} - -// 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) -{ - memset(gl->font_tex_buf, 0, gl->font_tex_w * gl->font_tex_h * sizeof(uint16_t)); - - while (head) - { - copy_glyph(head, geom, gl->font_tex_buf, gl->font_tex_w, gl->font_tex_h); - head = head->next; - } - - glPixelStorei(GL_UNPACK_ALIGNMENT, 8); - glTexSubImage2D(GL_TEXTURE_2D, - 0, 0, 0, gl->font_tex_w, gl->font_tex_h, - GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, gl->font_tex_buf); -} - -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) -{ - 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 (unsigned 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; -} - -extern const GLfloat vertexes_flipped[]; -extern const GLfloat white_color[]; - -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) + if (!handle) return; - gl_shader_use(gl, 0); - gl_set_viewport(gl, gl->win_width, gl->win_height, false, false); + if (handle->face) + FT_Done_Face(handle->face); + if (handle->lib) + FT_Done_FreeType(handle->lib); + free(handle); +} - glEnable(GL_BLEND); +static font_renderer_t *font_renderer_init(const char *font_path, unsigned font_size) +{ + (void)font_size; + FT_Error err; + font_renderer_t *handle = (font_renderer_t*)calloc(1, sizeof(*handle)); + if (!handle) + goto error; - GLfloat font_vertex[8]; - GLfloat font_vertex_dark[8]; - GLfloat font_tex_coords[8]; + err = FT_Init_FreeType(&handle->lib); + if (err) + goto error; - glBindTexture(GL_TEXTURE_2D, gl->font_tex); + err = FT_New_Face(handle->lib, font_path, 0, &handle->face); + if (err) + goto error; - gl->coords.tex_coord = font_tex_coords; + err = FT_Set_Pixel_Sizes(handle->face, 0, font_size); + if (err) + goto error; - struct font_output_list out; + return handle; - // If we get the same message, there's obviously no need to render fonts again ... - if (strcmp(gl->font_last_msg, msg) != 0) +error: + font_renderer_free(handle); + return NULL; +} + +static void font_renderer_msg(font_renderer_t *handle, const char *msg, struct font_output_list *output) +{ + 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 (size_t i = 0; i < len; i++) { - font_renderer_msg(gl->font, msg, &out); - struct font_output *head = out.head; + FT_Error err = FT_Load_Char(handle->face, msg[i], FT_LOAD_RENDER); - struct font_rect geom; - calculate_msg_geometry(head, &geom); - adjust_power_of_two(gl, &geom); - blit_fonts(gl, head, &geom); + if (!err) + { + struct font_output *tmp = (struct font_output*)calloc(1, sizeof(*tmp)); + if (!tmp) + break; - font_renderer_free_output(&out); - strlcpy(gl->font_last_msg, msg, sizeof(gl->font_last_msg)); + tmp->output = (uint8_t*)malloc(slot->bitmap.pitch * slot->bitmap.rows); + if (!tmp->output) + { + free(tmp); + break; + } - gl->font_last_width = geom.width; - gl->font_last_height = geom.height; + 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; } - 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 = vertexes_flipped; - gl->coords.tex_coord = gl->tex_coords; - gl->coords.color = white_color; - 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); } -#endif - -void gl_render_msg(void *data, const char *msg) +static void font_renderer_free_output(font_renderer_t *handle, struct font_output_list *output) { - (void)data; - (void)msg; + (void)handle; -#ifdef HAVE_FREETYPE - gl_t *gl = (gl_t*)data; - setup_font(data, msg, - g_settings.video.font_scale ? (GLfloat)gl->vp.width / (GLfloat)gl->full_x : 1.0f, - g_settings.video.msg_pos_x, g_settings.video.msg_pos_y); -#endif + 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; } -void gl_render_msg_place(void *data, float pos_x, float pos_y, float scale, uint32_t color, const char *msg) +// Not the cleanest way to do things for sure, but should hopefully work ... :) + +static const char *font_paths[] = { +#if defined(_WIN32) + "C:\\Windows\\Fonts\\consola.ttf", + "C:\\Windows\\Fonts\\verdana.ttf", +#elif defined(__APPLE__) + "/Library/Fonts/Microsoft/Candara.ttf", + "/Library/Fonts/Verdana.ttf", + "/Library/Fonts/Tahoma.ttf", +#else + "/usr/share/fonts/TTF/DejaVuSansMono.ttf", + "/usr/share/fonts/TTF/DejaVuSans.ttf", + "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf", + "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", +#endif + "osd-font.ttf", // Magic font to search for, useful for distribution. +}; + +// Highly OS/platform dependent. +static const char *font_renderer_get_default_font(void) { - (void)data; - (void)msg; - (void)color; + for (size_t i = 0; i < sizeof(font_paths) / sizeof(font_paths[0]); i++) + { + if (path_file_exists(font_paths[i])) + return font_paths[i]; + } -#ifdef HAVE_FREETYPE - setup_font(data, msg, scale, pos_x, pos_y); -#endif + return NULL; } + +const font_renderer_driver_t ft_font_renderer = { + font_renderer_init, + font_renderer_msg, + font_renderer_free_output, + font_renderer_free, + font_renderer_get_default_font, + "freetype", +}; + diff --git a/gfx/fonts/gl_font.c b/gfx/fonts/gl_font.c new file mode 100644 index 0000000000..af7ecc0a77 --- /dev/null +++ b/gfx/fonts/gl_font.c @@ -0,0 +1,314 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * Copyright (C) 2011-2012 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "../gl_common.h" + +void gl_init_font(void *data, const char *font_path, unsigned font_size) +{ + if (!g_settings.video.font_enable) + return; + + gl_t *gl = (gl_t*)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]); + } + else + RARCH_WARN("Couldn't init font renderer.\n"); + + for (unsigned i = 0; i < 4; i++) + { + 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 (unsigned i = 0; i < 4; i++) + { + for (unsigned 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; + } +} + +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->font_tex_w) || (geom->pot_height > gl->font_tex_h)) + { + gl->font_tex_buf = (uint16_t*)realloc(gl->font_tex_buf, + geom->pot_width * geom->pot_height * sizeof(uint16_t)); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, geom->pot_width, geom->pot_height, + 0, GL_LUMINANCE_ALPHA, 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, uint16_t *buffer, unsigned width, unsigned height) +{ + // 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; + + uint16_t *dst = buffer + y * width + x; + + for (int h = 0; h < font_height; h++, dst += width, src += head->pitch) + for (int w = 0; w < font_width; w++) + dst[w] = 0xff | (src[w] << 8); // Assume little endian for now. +} + +// 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) +{ + memset(gl->font_tex_buf, 0, gl->font_tex_w * gl->font_tex_h * sizeof(uint16_t)); + + while (head) + { + copy_glyph(head, geom, gl->font_tex_buf, gl->font_tex_w, gl->font_tex_h); + head = head->next; + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, 8); + glTexSubImage2D(GL_TEXTURE_2D, + 0, 0, 0, gl->font_tex_w, gl->font_tex_h, + GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, gl->font_tex_buf); +} + +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) +{ + 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 (unsigned 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; +} + +extern const GLfloat vertexes_flipped[]; +extern const GLfloat white_color[]; + +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_shader_use(gl, 0); + gl_set_viewport(gl, gl->win_width, gl->win_height, false, false); + + glEnable(GL_BLEND); + + GLfloat font_vertex[8]; + GLfloat font_vertex_dark[8]; + GLfloat font_tex_coords[8]; + + glBindTexture(GL_TEXTURE_2D, gl->font_tex); + + gl->coords.tex_coord = font_tex_coords; + + struct font_output_list out; + + // If we get the same message, there's obviously no need to render fonts again ... + if (strcmp(gl->font_last_msg, msg) != 0) + { + gl->font_driver->render_msg(gl->font, msg, &out); + struct font_output *head = out.head; + + struct font_rect geom; + calculate_msg_geometry(head, &geom); + adjust_power_of_two(gl, &geom); + blit_fonts(gl, head, &geom); + + gl->font_driver->free_output(gl->font, &out); + strlcpy(gl->font_last_msg, msg, sizeof(gl->font_last_msg)); + + gl->font_last_width = geom.width; + gl->font_last_height = geom.height; + } + 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 = vertexes_flipped; + gl->coords.tex_coord = gl->tex_coords; + gl->coords.color = white_color; + 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); +} + +void gl_render_msg(void *data, const char *msg) +{ + (void)data; + (void)msg; + + gl_t *gl = (gl_t*)data; + setup_font(data, msg, + g_settings.video.font_scale ? (GLfloat)gl->vp.width / (GLfloat)gl->full_x : 1.0f, + g_settings.video.msg_pos_x, g_settings.video.msg_pos_y); +} + +void gl_render_msg_place(void *data, float pos_x, float pos_y, float scale, uint32_t color, const char *msg) +{ + (void)data; + (void)msg; + (void)color; + +#ifdef HAVE_FREETYPE + setup_font(data, msg, scale, pos_x, pos_y); +#endif +} diff --git a/gfx/gl_common.h b/gfx/gl_common.h index fca464d851..c0729b536b 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -264,8 +264,9 @@ typedef struct gl GLenum border_type; unsigned base_size; // 2 or 4 -#ifdef HAVE_FREETYPE + // Fonts font_renderer_t *font; + const font_renderer_driver_t *font_driver; GLuint font_tex; int font_tex_w, font_tex_h; uint16_t *font_tex_buf; @@ -273,7 +274,6 @@ typedef struct gl int font_last_width, font_last_height; GLfloat font_color[16]; GLfloat font_color_dark[16]; -#endif #ifdef HAVE_RMENU bool block_swap; diff --git a/gfx/sdl_gfx.c b/gfx/sdl_gfx.c index f52ea1a9e3..c981565e2b 100644 --- a/gfx/sdl_gfx.c +++ b/gfx/sdl_gfx.c @@ -21,6 +21,7 @@ #include "scaler/scaler.h" #include "gfx_common.h" #include "gfx_context.h" +#include "fonts/fonts.h" #ifdef HAVE_X11 #include "context/x11_common.h" @@ -30,10 +31,6 @@ #include "config.h" #endif -#ifdef HAVE_FREETYPE -#include "fonts/fonts.h" -#endif - #ifndef __APPLE__ // Broken on OSX. #include "SDL/SDL_syswm.h" #endif @@ -43,12 +40,11 @@ typedef struct sdl_video SDL_Surface *screen; bool quitting; -#ifdef HAVE_FREETYPE font_renderer_t *font; + const font_renderer_driver_t *font_driver; uint8_t font_r; uint8_t font_g; uint8_t font_b; -#endif struct scaler_ctx scaler; unsigned last_width; @@ -63,10 +59,8 @@ static void sdl_gfx_free(void *data) SDL_QuitSubSystem(SDL_INIT_VIDEO); -#ifdef HAVE_FREETYPE if (vid->font) - font_renderer_free(vid->font); -#endif + vid->font_driver->free(vid->font); scaler_ctx_gen_reset(&vid->scaler); @@ -75,19 +69,11 @@ static void sdl_gfx_free(void *data) static void sdl_init_font(sdl_video_t *vid, const char *font_path, unsigned font_size) { -#ifdef HAVE_FREETYPE if (!g_settings.video.font_enable) return; - const char *path = font_path; - if (!*path) - path = font_renderer_get_default_font(); - - if (path) + if (font_renderer_create_default(&vid->font_driver, &vid->font)) { - vid->font = font_renderer_new(path, font_size); - if (vid->font) - { int r = g_settings.video.msg_color_r * 255; int g = g_settings.video.msg_color_g * 255; int b = g_settings.video.msg_color_b * 255; @@ -99,28 +85,19 @@ static void sdl_init_font(sdl_video_t *vid, const char *font_path, unsigned font vid->font_r = r; vid->font_g = g; vid->font_b = b; - } - else - RARCH_WARN("Failed to init font.\n"); } else - RARCH_LOG("Did not find default font.\n"); -#else - (void)vid; - (void)font_path; - (void)font_size; -#endif + RARCH_LOG("Could not initialize fonts.\n"); } static void sdl_render_msg(sdl_video_t *vid, SDL_Surface *buffer, const char *msg, unsigned width, unsigned height, const SDL_PixelFormat *fmt) { -#ifdef HAVE_FREETYPE if (!vid->font) return; struct font_output_list out; - font_renderer_msg(vid->font, msg, &out); + vid->font_driver->render_msg(vid->font, msg, &out); struct font_output *head = out.head; int msg_base_x = g_settings.video.msg_pos_x * width; @@ -185,15 +162,7 @@ static void sdl_render_msg(sdl_video_t *vid, SDL_Surface *buffer, } } - font_renderer_free_output(&out); - -#else - (void)vid; - (void)buffer; - (void)msg; - (void)width; - (void)height; -#endif + vid->font_driver->free_output(vid->font, &out); } static void sdl_gfx_set_handles(void) diff --git a/gfx/xvideo.c b/gfx/xvideo.c index b67aed8417..a16f7d7e2f 100644 --- a/gfx/xvideo.c +++ b/gfx/xvideo.c @@ -20,10 +20,7 @@ #include #include #include "gfx_common.h" - -#ifdef HAVE_FREETYPE #include "fonts/fonts.h" -#endif #include "context/x11_common.h" @@ -65,8 +62,8 @@ typedef struct xv uint8_t *utable; uint8_t *vtable; -#ifdef HAVE_FREETYPE font_renderer_t *font; + const font_renderer_driver_t *font_driver; unsigned luma_index[2]; unsigned chroma_u_index; @@ -75,7 +72,6 @@ typedef struct xv uint8_t font_y; uint8_t font_u; uint8_t font_v; -#endif void (*render_func)(struct xv*, const void *frame, unsigned width, unsigned height, unsigned pitch); } xv_t; @@ -127,35 +123,23 @@ static void init_yuv_tables(xv_t *xv) static void xv_init_font(xv_t *xv, const char *font_path, unsigned font_size) { -#ifdef HAVE_FREETYPE if (!g_settings.video.font_enable) return; - const char *path = font_path; - if (!*path) - path = font_renderer_get_default_font(); - - if (path) + if (font_renderer_create_default(&xv->font_driver, &xv->font)) { - xv->font = font_renderer_new(path, font_size); - if (xv->font) - { - int r = g_settings.video.msg_color_r * 255; - r = (r < 0 ? 0 : (r > 255 ? 255 : r)); - int g = g_settings.video.msg_color_g * 255; - g = (g < 0 ? 0 : (g > 255 ? 255 : g)); - int b = g_settings.video.msg_color_b * 255; - b = (b < 0 ? 0 : (b > 255 ? 255 : b)); + int r = g_settings.video.msg_color_r * 255; + r = (r < 0 ? 0 : (r > 255 ? 255 : r)); + int g = g_settings.video.msg_color_g * 255; + g = (g < 0 ? 0 : (g > 255 ? 255 : g)); + int b = g_settings.video.msg_color_b * 255; + b = (b < 0 ? 0 : (b > 255 ? 255 : b)); - calculate_yuv(&xv->font_y, &xv->font_u, &xv->font_v, - r, g, b); - } - else - RARCH_WARN("Failed to init font.\n"); + calculate_yuv(&xv->font_y, &xv->font_u, &xv->font_v, + r, g, b); } else - RARCH_LOG("Did not find default font.\n"); -#endif + RARCH_LOG("Could not initialize fonts.\n"); } // We render @ 2x scale to combat chroma downsampling. Also makes fonts more bearable :) @@ -325,12 +309,10 @@ static bool adaptor_set_format(xv_t *xv, Display *dpy, XvPortID port, const vide xv->fourcc = format[i].id; xv->render_func = video->rgb32 ? formats[j].render_32 : formats[j].render_16; -#ifdef HAVE_FREETYPE xv->luma_index[0] = formats[j].luma_index[0]; xv->luma_index[1] = formats[j].luma_index[1]; xv->chroma_u_index = formats[j].u_index; xv->chroma_v_index = formats[j].v_index; -#endif XFree(format); return true; } @@ -590,12 +572,11 @@ static void calc_out_rect(bool keep_aspect, struct rarch_viewport *vp, unsigned // Hacky C code is hacky :D Yay. static void xv_render_msg(xv_t *xv, const char *msg, unsigned width, unsigned height) { -#ifdef HAVE_FREETYPE if (!xv->font) return; struct font_output_list out; - font_renderer_msg(xv->font, msg, &out); + xv->font_driver->render_msg(xv->font, msg, &out); struct font_output *head = out.head; int msg_base_x = g_settings.video.msg_pos_x * width; @@ -677,13 +658,7 @@ static void xv_render_msg(xv_t *xv, const char *msg, unsigned width, unsigned he } } - font_renderer_free_output(&out); -#else - (void)xv; - (void)msg; - (void)width; - (void)height; -#endif + xv->font_driver->free_output(xv->font, &out); } static bool xv_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg) @@ -774,10 +749,8 @@ static void xv_free(void *data) free(xv->utable); free(xv->vtable); -#ifdef HAVE_FREETYPE if (xv->font) - font_renderer_free(xv->font); -#endif + xv->font_driver->free(xv->font); free(xv); }