123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- /* RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2017 - Daniel De Matteis
- * Copyright (C) 2016-2019 - Brad Parker
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
- #include <stdlib.h>
- #include <encodings/utf.h>
- #include <string/stdstring.h>
- #include <retro_math.h>
- #include "../common/gl2_common.h"
- #include "../font_driver.h"
- #include "../../configuration.h"
- /* TODO: Move viewport side effects to the caller: it's a source of bugs. */
- #define GL_RASTER_FONT_EMIT(c, vx, vy) \
- 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_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[ 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]; \
- font_lut_tex_coord[ 2 * (6 * i + c) + 0] = gl->coords.lut_tex_coord[0]; \
- font_lut_tex_coord[ 2 * (6 * i + c) + 1] = gl->coords.lut_tex_coord[1]
- #define MAX_MSG_LEN_CHUNK 64
- typedef struct
- {
- gl2_t *gl;
- GLuint tex;
- unsigned tex_width, tex_height;
- const font_renderer_driver_t *font_driver;
- void *font_data;
- struct font_atlas *atlas;
- video_font_raster_block_t *block;
- } gl2_raster_t;
- static void gl2_raster_font_free(void *data,
- bool is_threaded)
- {
- gl2_raster_t *font = (gl2_raster_t*)data;
- if (!font)
- return;
- if (font->font_driver && font->font_data)
- font->font_driver->free(font->font_data);
- if (is_threaded)
- {
- if (
- font->gl &&
- font->gl->ctx_driver &&
- font->gl->ctx_driver->make_current)
- font->gl->ctx_driver->make_current(true);
- }
- if (font->tex)
- {
- glDeleteTextures(1, &font->tex);
- font->tex = 0;
- }
- free(font);
- }
- static void gl2_raster_font_upload_atlas(gl2_raster_t *font)
- {
- int i, j;
- GLint gl_internal = GL_LUMINANCE_ALPHA;
- GLenum gl_format = GL_LUMINANCE_ALPHA;
- size_t ncomponents = 2;
- uint8_t *tmp = (uint8_t*)calloc(font->tex_height, font->tex_width * ncomponents);
- switch (ncomponents)
- {
- case 1:
- for (i = 0; i < (int)font->atlas->height; ++i)
- {
- const uint8_t *src = &font->atlas->buffer[i * font->atlas->width];
- uint8_t *dst = &tmp[i * font->tex_width * ncomponents];
- memcpy(dst, src, font->atlas->width);
- }
- break;
- case 2:
- for (i = 0; i < (int)font->atlas->height; ++i)
- {
- const uint8_t *src = &font->atlas->buffer[i * font->atlas->width];
- uint8_t *dst = &tmp[i * font->tex_width * ncomponents];
- for (j = 0; j < (int)font->atlas->width; ++j)
- {
- *dst++ = 0xff;
- *dst++ = *src++;
- }
- }
- break;
- }
- glTexImage2D(GL_TEXTURE_2D, 0, gl_internal,
- font->tex_width, font->tex_height,
- 0, gl_format, GL_UNSIGNED_BYTE, tmp);
- free(tmp);
- }
- static void *gl2_raster_font_init(void *data,
- const char *font_path, float font_size,
- bool is_threaded)
- {
- gl2_raster_t *font = (gl2_raster_t*)calloc(1, sizeof(*font));
- if (!font)
- return NULL;
- font->gl = (gl2_t*)data;
- if (!font_renderer_create_default(
- &font->font_driver,
- &font->font_data, font_path, font_size))
- {
- free(font);
- return NULL;
- }
- if (is_threaded)
- if (
- font->gl &&
- font->gl->ctx_driver &&
- font->gl->ctx_driver->make_current)
- font->gl->ctx_driver->make_current(false);
- glGenTextures(1, &font->tex);
- GL2_BIND_TEXTURE(font->tex, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR);
- font->atlas = font->font_driver->get_atlas(font->font_data);
- font->tex_width = next_pow2(font->atlas->width);
- font->tex_height = next_pow2(font->atlas->height);
- gl2_raster_font_upload_atlas(font);
- font->atlas->dirty = false;
- if (font->gl)
- glBindTexture(GL_TEXTURE_2D, font->gl->texture[font->gl->tex_index]);
- return font;
- }
- static int gl2_raster_font_get_message_width(void *data, const char *msg,
- size_t msg_len, float scale)
- {
- const struct font_glyph* glyph_q = NULL;
- gl2_raster_t *font = (gl2_raster_t*)data;
- const char* msg_end = msg + msg_len;
- int delta_x = 0;
- if ( !font
- || !font->font_driver
- || !font->font_driver->get_glyph
- || !font->font_data )
- return 0;
- glyph_q = font->font_driver->get_glyph(font->font_data, '?');
- while (msg < msg_end)
- {
- const struct font_glyph *glyph;
- unsigned code = utf8_walk(&msg);
- /* Do something smarter here ... */
- if (!(glyph = font->font_driver->get_glyph(
- font->font_data, code)))
- if (!(glyph = glyph_q))
- continue;
- delta_x += glyph->advance_x;
- }
- return delta_x * scale;
- }
- static void gl2_raster_font_draw_vertices(gl2_raster_t *font,
- const video_coords_t *coords)
- {
- if (font->atlas->dirty)
- {
- gl2_raster_font_upload_atlas(font);
- font->atlas->dirty = false;
- }
- if (font->gl && font->gl->shader)
- {
- font->gl->shader->set_coords(font->gl->shader_data, coords);
- font->gl->shader->set_mvp(font->gl->shader_data,
- &font->gl->mvp_no_rot);
- }
- glDrawArrays(GL_TRIANGLES, 0, coords->vertices);
- }
- static void gl2_raster_font_render_line(gl2_t *gl,
- gl2_raster_t *font, const char *msg, size_t msg_len,
- GLfloat scale, const GLfloat color[4], GLfloat pos_x,
- GLfloat pos_y, unsigned text_align)
- {
- int i;
- struct video_coords coords;
- const struct font_glyph* glyph_q = NULL;
- GLfloat font_tex_coords[2 * 6 * MAX_MSG_LEN_CHUNK];
- GLfloat font_vertex[2 * 6 * MAX_MSG_LEN_CHUNK];
- GLfloat font_color[4 * 6 * MAX_MSG_LEN_CHUNK];
- GLfloat font_lut_tex_coord[2 * 6 * MAX_MSG_LEN_CHUNK];
- const char* msg_end = msg + msg_len;
- int x = roundf(pos_x * gl->vp.width);
- int y = roundf(pos_y * gl->vp.height);
- int delta_x = 0;
- int delta_y = 0;
- 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 / gl->vp.width;
- float inv_win_height = 1.0f / gl->vp.height;
- switch (text_align)
- {
- case TEXT_ALIGN_RIGHT:
- x -= gl2_raster_font_get_message_width(font, msg, msg_len, scale);
- break;
- case TEXT_ALIGN_CENTER:
- x -= gl2_raster_font_get_message_width(font, msg, msg_len, scale) / 2.0;
- break;
- }
- glyph_q = font->font_driver->get_glyph(font->font_data, '?');
- while (msg < msg_end)
- {
- i = 0;
- while ((i < MAX_MSG_LEN_CHUNK) && (msg < msg_end))
- {
- const struct font_glyph *glyph;
- int off_x, off_y, tex_x, tex_y, width, height;
- unsigned code = utf8_walk(&msg);
- /* Do something smarter here ... */
- if (!(glyph = font->font_driver->get_glyph(
- font->font_data, code)))
- if (!(glyph = glyph_q))
- continue;
- off_x = glyph->draw_offset_x;
- off_y = glyph->draw_offset_y;
- tex_x = glyph->atlas_offset_x;
- tex_y = glyph->atlas_offset_y;
- width = glyph->width;
- height = glyph->height;
- GL_RASTER_FONT_EMIT(0, 0, 1); /* Bottom-left */
- GL_RASTER_FONT_EMIT(1, 1, 1); /* Bottom-right */
- GL_RASTER_FONT_EMIT(2, 0, 0); /* Top-left */
- GL_RASTER_FONT_EMIT(3, 1, 0); /* Top-right */
- GL_RASTER_FONT_EMIT(4, 0, 0); /* Top-left */
- GL_RASTER_FONT_EMIT(5, 1, 1); /* Bottom-right */
- i++;
- delta_x += glyph->advance_x;
- delta_y -= glyph->advance_y;
- }
- coords.tex_coord = font_tex_coords;
- coords.vertex = font_vertex;
- coords.color = font_color;
- coords.vertices = i * 6;
- coords.lut_tex_coord = font_lut_tex_coord;
- if (font->block)
- video_coord_array_append(&font->block->carr, &coords, coords.vertices);
- else
- gl2_raster_font_draw_vertices(font, &coords);
- }
- }
- static void gl2_raster_font_render_message(
- gl2_raster_t *font, const char *msg, GLfloat scale,
- const GLfloat color[4], GLfloat pos_x, GLfloat pos_y,
- unsigned text_align)
- {
- struct font_line_metrics *line_metrics = NULL;
- int lines = 0;
- float line_height;
- /* If font line metrics are not supported just draw as usual */
- if (!font->font_driver->get_line_metrics ||
- !font->font_driver->get_line_metrics(font->font_data, &line_metrics))
- {
- gl2_raster_font_render_line(font->gl, font,
- msg, strlen(msg), scale, color, pos_x,
- pos_y, text_align);
- return;
- }
- line_height = line_metrics->height * scale / font->gl->vp.height;
- for (;;)
- {
- const char *delim = strchr(msg, '\n');
- size_t msg_len = delim
- ? (delim - msg) : strlen(msg);
- /* Draw the line */
- gl2_raster_font_render_line(font->gl, font,
- msg, msg_len, scale, color, pos_x,
- pos_y - (float)lines*line_height, text_align);
- if (!delim)
- break;
- msg += msg_len + 1;
- lines++;
- }
- }
- static void gl2_raster_font_setup_viewport(
- gl2_raster_t *font,
- unsigned width, unsigned height,
- bool full_screen)
- {
- video_driver_set_viewport(width, height, full_screen, false);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glBlendEquation(GL_FUNC_ADD);
- glBindTexture(GL_TEXTURE_2D, font->tex);
- if (font->gl && font->gl->shader && font->gl->shader->use)
- font->gl->shader->use(font->gl,
- font->gl->shader_data, VIDEO_SHADER_STOCK_BLEND, true);
- }
- static void gl2_raster_font_render_msg(
- void *userdata,
- void *data,
- const char *msg,
- const struct font_params *params)
- {
- GLfloat color[4];
- int drop_x, drop_y;
- GLfloat x, y, scale, drop_mod, drop_alpha;
- enum text_alignment text_align = TEXT_ALIGN_LEFT;
- bool full_screen = false ;
- gl2_raster_t *font = (gl2_raster_t*)data;
- unsigned width = font->gl->video_width;
- unsigned height = font->gl->video_height;
- if (!font || string_is_empty(msg))
- return;
- if (params)
- {
- x = params->x;
- y = params->y;
- scale = params->scale;
- full_screen = params->full_screen;
- text_align = params->text_align;
- drop_x = params->drop_x;
- drop_y = params->drop_y;
- drop_mod = params->drop_mod;
- drop_alpha = params->drop_alpha;
- color[0] = FONT_COLOR_GET_RED(params->color) / 255.0f;
- color[1] = FONT_COLOR_GET_GREEN(params->color) / 255.0f;
- color[2] = FONT_COLOR_GET_BLUE(params->color) / 255.0f;
- color[3] = FONT_COLOR_GET_ALPHA(params->color) / 255.0f;
- /* If alpha is 0.0f, turn it into default 1.0f */
- if (color[3] <= 0.0f)
- color[3] = 1.0f;
- }
- else
- {
- settings_t *settings = config_get_ptr();
- float video_msg_pos_x = settings->floats.video_msg_pos_x;
- float video_msg_pos_y = settings->floats.video_msg_pos_y;
- float video_msg_color_r = settings->floats.video_msg_color_r;
- float video_msg_color_g = settings->floats.video_msg_color_g;
- float video_msg_color_b = settings->floats.video_msg_color_b;
- x = video_msg_pos_x;
- y = video_msg_pos_y;
- scale = 1.0f;
- full_screen = true;
- text_align = TEXT_ALIGN_LEFT;
- color[0] = video_msg_color_r;
- color[1] = video_msg_color_g;
- color[2] = video_msg_color_b;
- color[3] = 1.0f;
- drop_x = -2;
- drop_y = -2;
- drop_mod = 0.3f;
- drop_alpha = 1.0f;
- }
- if (font->block)
- font->block->fullscreen = full_screen;
- else
- gl2_raster_font_setup_viewport(font, width, height, full_screen);
- if (font->gl)
- {
- if ( !string_is_empty(msg)
- && font->font_data
- && font->font_driver)
- {
- if (drop_x || drop_y)
- {
- GLfloat color_dark[4];
- color_dark[0] = color[0] * drop_mod;
- color_dark[1] = color[1] * drop_mod;
- color_dark[2] = color[2] * drop_mod;
- color_dark[3] = color[3] * drop_alpha;
- gl2_raster_font_render_message(font, msg, scale, color_dark,
- x + scale * drop_x / font->gl->vp.width, y +
- scale * drop_y / font->gl->vp.height, text_align);
- }
- gl2_raster_font_render_message(font, msg, scale, color,
- x, y, text_align);
- }
- if (!font->block)
- {
- /* restore viewport */
- glBindTexture(GL_TEXTURE_2D,
- font->gl->texture[font->gl->tex_index]);
- glDisable(GL_BLEND);
- video_driver_set_viewport(width, height, false, true);
- }
- }
- }
- static const struct font_glyph *gl2_raster_font_get_glyph(
- void *data, uint32_t code)
- {
- gl2_raster_t *font = (gl2_raster_t*)data;
- if (font && font->font_driver && font->font_driver->ident)
- return font->font_driver->get_glyph((void*)font->font_driver, code);
- return NULL;
- }
- static void gl2_raster_font_flush_block(unsigned width, unsigned height,
- void *data)
- {
- gl2_raster_t *font = (gl2_raster_t*)data;
- video_font_raster_block_t *block = font ? font->block : NULL;
- if (!font || !block || !block->carr.coords.vertices)
- return;
- gl2_raster_font_setup_viewport(font, width, height, block->fullscreen);
- gl2_raster_font_draw_vertices(font, (video_coords_t*)&block->carr.coords);
- if (font->gl)
- {
- /* restore viewport */
- glBindTexture(GL_TEXTURE_2D, font->gl->texture[font->gl->tex_index]);
- glDisable(GL_BLEND);
- video_driver_set_viewport(width, height, block->fullscreen, true);
- }
- }
- static void gl2_raster_font_bind_block(void *data, void *userdata)
- {
- gl2_raster_t *font = (gl2_raster_t*)data;
- video_font_raster_block_t *block = (video_font_raster_block_t*)userdata;
- if (font)
- font->block = block;
- }
- static bool gl2_raster_font_get_line_metrics(void* data, struct font_line_metrics **metrics)
- {
- gl2_raster_t *font = (gl2_raster_t*)data;
- if (font && font->font_driver && font->font_data)
- return font->font_driver->get_line_metrics(font->font_data, metrics);
- return false;
- }
- font_renderer_t gl2_raster_font = {
- gl2_raster_font_init,
- gl2_raster_font_free,
- gl2_raster_font_render_msg,
- "gl2_raster_font",
- gl2_raster_font_get_glyph,
- gl2_raster_font_bind_block,
- gl2_raster_font_flush_block,
- gl2_raster_font_get_message_width,
- gl2_raster_font_get_line_metrics
- };
|