gl2_raster_font.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. /* RetroArch - A frontend for libretro.
  2. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
  3. * Copyright (C) 2011-2017 - Daniel De Matteis
  4. * Copyright (C) 2016-2019 - Brad Parker
  5. *
  6. * RetroArch is free software: you can redistribute it and/or modify it under the terms
  7. * of the GNU General Public License as published by the Free Software Found-
  8. * ation, either version 3 of the License, or (at your option) any later version.
  9. *
  10. * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  11. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  12. * PURPOSE. See the GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along with RetroArch.
  15. * If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include <stdlib.h>
  18. #include <encodings/utf.h>
  19. #include <string/stdstring.h>
  20. #include <retro_math.h>
  21. #include "../common/gl2_common.h"
  22. #include "../font_driver.h"
  23. #include "../../configuration.h"
  24. /* TODO: Move viewport side effects to the caller: it's a source of bugs. */
  25. #define GL_RASTER_FONT_EMIT(c, vx, vy) \
  26. font_vertex[ 2 * (6 * i + c) + 0] = (x + (delta_x + off_x + vx * width) * scale) * inv_win_width; \
  27. font_vertex[ 2 * (6 * i + c) + 1] = (y + (delta_y - off_y - vy * height) * scale) * inv_win_height; \
  28. font_tex_coords[ 2 * (6 * i + c) + 0] = (tex_x + vx * width) * inv_tex_size_x; \
  29. font_tex_coords[ 2 * (6 * i + c) + 1] = (tex_y + vy * height) * inv_tex_size_y; \
  30. font_color[ 4 * (6 * i + c) + 0] = color[0]; \
  31. font_color[ 4 * (6 * i + c) + 1] = color[1]; \
  32. font_color[ 4 * (6 * i + c) + 2] = color[2]; \
  33. font_color[ 4 * (6 * i + c) + 3] = color[3]; \
  34. font_lut_tex_coord[ 2 * (6 * i + c) + 0] = gl->coords.lut_tex_coord[0]; \
  35. font_lut_tex_coord[ 2 * (6 * i + c) + 1] = gl->coords.lut_tex_coord[1]
  36. #define MAX_MSG_LEN_CHUNK 64
  37. typedef struct
  38. {
  39. gl2_t *gl;
  40. GLuint tex;
  41. unsigned tex_width, tex_height;
  42. const font_renderer_driver_t *font_driver;
  43. void *font_data;
  44. struct font_atlas *atlas;
  45. video_font_raster_block_t *block;
  46. } gl2_raster_t;
  47. static void gl2_raster_font_free(void *data,
  48. bool is_threaded)
  49. {
  50. gl2_raster_t *font = (gl2_raster_t*)data;
  51. if (!font)
  52. return;
  53. if (font->font_driver && font->font_data)
  54. font->font_driver->free(font->font_data);
  55. if (is_threaded)
  56. {
  57. if (
  58. font->gl &&
  59. font->gl->ctx_driver &&
  60. font->gl->ctx_driver->make_current)
  61. font->gl->ctx_driver->make_current(true);
  62. }
  63. if (font->tex)
  64. {
  65. glDeleteTextures(1, &font->tex);
  66. font->tex = 0;
  67. }
  68. free(font);
  69. }
  70. static void gl2_raster_font_upload_atlas(gl2_raster_t *font)
  71. {
  72. int i, j;
  73. GLint gl_internal = GL_LUMINANCE_ALPHA;
  74. GLenum gl_format = GL_LUMINANCE_ALPHA;
  75. size_t ncomponents = 2;
  76. uint8_t *tmp = (uint8_t*)calloc(font->tex_height, font->tex_width * ncomponents);
  77. switch (ncomponents)
  78. {
  79. case 1:
  80. for (i = 0; i < (int)font->atlas->height; ++i)
  81. {
  82. const uint8_t *src = &font->atlas->buffer[i * font->atlas->width];
  83. uint8_t *dst = &tmp[i * font->tex_width * ncomponents];
  84. memcpy(dst, src, font->atlas->width);
  85. }
  86. break;
  87. case 2:
  88. for (i = 0; i < (int)font->atlas->height; ++i)
  89. {
  90. const uint8_t *src = &font->atlas->buffer[i * font->atlas->width];
  91. uint8_t *dst = &tmp[i * font->tex_width * ncomponents];
  92. for (j = 0; j < (int)font->atlas->width; ++j)
  93. {
  94. *dst++ = 0xff;
  95. *dst++ = *src++;
  96. }
  97. }
  98. break;
  99. }
  100. glTexImage2D(GL_TEXTURE_2D, 0, gl_internal,
  101. font->tex_width, font->tex_height,
  102. 0, gl_format, GL_UNSIGNED_BYTE, tmp);
  103. free(tmp);
  104. }
  105. static void *gl2_raster_font_init(void *data,
  106. const char *font_path, float font_size,
  107. bool is_threaded)
  108. {
  109. gl2_raster_t *font = (gl2_raster_t*)calloc(1, sizeof(*font));
  110. if (!font)
  111. return NULL;
  112. font->gl = (gl2_t*)data;
  113. if (!font_renderer_create_default(
  114. &font->font_driver,
  115. &font->font_data, font_path, font_size))
  116. {
  117. free(font);
  118. return NULL;
  119. }
  120. if (is_threaded)
  121. if (
  122. font->gl &&
  123. font->gl->ctx_driver &&
  124. font->gl->ctx_driver->make_current)
  125. font->gl->ctx_driver->make_current(false);
  126. glGenTextures(1, &font->tex);
  127. GL2_BIND_TEXTURE(font->tex, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR);
  128. font->atlas = font->font_driver->get_atlas(font->font_data);
  129. font->tex_width = next_pow2(font->atlas->width);
  130. font->tex_height = next_pow2(font->atlas->height);
  131. gl2_raster_font_upload_atlas(font);
  132. font->atlas->dirty = false;
  133. if (font->gl)
  134. glBindTexture(GL_TEXTURE_2D, font->gl->texture[font->gl->tex_index]);
  135. return font;
  136. }
  137. static int gl2_raster_font_get_message_width(void *data, const char *msg,
  138. size_t msg_len, float scale)
  139. {
  140. const struct font_glyph* glyph_q = NULL;
  141. gl2_raster_t *font = (gl2_raster_t*)data;
  142. const char* msg_end = msg + msg_len;
  143. int delta_x = 0;
  144. if ( !font
  145. || !font->font_driver
  146. || !font->font_driver->get_glyph
  147. || !font->font_data )
  148. return 0;
  149. glyph_q = font->font_driver->get_glyph(font->font_data, '?');
  150. while (msg < msg_end)
  151. {
  152. const struct font_glyph *glyph;
  153. unsigned code = utf8_walk(&msg);
  154. /* Do something smarter here ... */
  155. if (!(glyph = font->font_driver->get_glyph(
  156. font->font_data, code)))
  157. if (!(glyph = glyph_q))
  158. continue;
  159. delta_x += glyph->advance_x;
  160. }
  161. return delta_x * scale;
  162. }
  163. static void gl2_raster_font_draw_vertices(gl2_raster_t *font,
  164. const video_coords_t *coords)
  165. {
  166. if (font->atlas->dirty)
  167. {
  168. gl2_raster_font_upload_atlas(font);
  169. font->atlas->dirty = false;
  170. }
  171. if (font->gl && font->gl->shader)
  172. {
  173. font->gl->shader->set_coords(font->gl->shader_data, coords);
  174. font->gl->shader->set_mvp(font->gl->shader_data,
  175. &font->gl->mvp_no_rot);
  176. }
  177. glDrawArrays(GL_TRIANGLES, 0, coords->vertices);
  178. }
  179. static void gl2_raster_font_render_line(gl2_t *gl,
  180. gl2_raster_t *font, const char *msg, size_t msg_len,
  181. GLfloat scale, const GLfloat color[4], GLfloat pos_x,
  182. GLfloat pos_y, unsigned text_align)
  183. {
  184. int i;
  185. struct video_coords coords;
  186. const struct font_glyph* glyph_q = NULL;
  187. GLfloat font_tex_coords[2 * 6 * MAX_MSG_LEN_CHUNK];
  188. GLfloat font_vertex[2 * 6 * MAX_MSG_LEN_CHUNK];
  189. GLfloat font_color[4 * 6 * MAX_MSG_LEN_CHUNK];
  190. GLfloat font_lut_tex_coord[2 * 6 * MAX_MSG_LEN_CHUNK];
  191. const char* msg_end = msg + msg_len;
  192. int x = roundf(pos_x * gl->vp.width);
  193. int y = roundf(pos_y * gl->vp.height);
  194. int delta_x = 0;
  195. int delta_y = 0;
  196. float inv_tex_size_x = 1.0f / font->tex_width;
  197. float inv_tex_size_y = 1.0f / font->tex_height;
  198. float inv_win_width = 1.0f / gl->vp.width;
  199. float inv_win_height = 1.0f / gl->vp.height;
  200. switch (text_align)
  201. {
  202. case TEXT_ALIGN_RIGHT:
  203. x -= gl2_raster_font_get_message_width(font, msg, msg_len, scale);
  204. break;
  205. case TEXT_ALIGN_CENTER:
  206. x -= gl2_raster_font_get_message_width(font, msg, msg_len, scale) / 2.0;
  207. break;
  208. }
  209. glyph_q = font->font_driver->get_glyph(font->font_data, '?');
  210. while (msg < msg_end)
  211. {
  212. i = 0;
  213. while ((i < MAX_MSG_LEN_CHUNK) && (msg < msg_end))
  214. {
  215. const struct font_glyph *glyph;
  216. int off_x, off_y, tex_x, tex_y, width, height;
  217. unsigned code = utf8_walk(&msg);
  218. /* Do something smarter here ... */
  219. if (!(glyph = font->font_driver->get_glyph(
  220. font->font_data, code)))
  221. if (!(glyph = glyph_q))
  222. continue;
  223. off_x = glyph->draw_offset_x;
  224. off_y = glyph->draw_offset_y;
  225. tex_x = glyph->atlas_offset_x;
  226. tex_y = glyph->atlas_offset_y;
  227. width = glyph->width;
  228. height = glyph->height;
  229. GL_RASTER_FONT_EMIT(0, 0, 1); /* Bottom-left */
  230. GL_RASTER_FONT_EMIT(1, 1, 1); /* Bottom-right */
  231. GL_RASTER_FONT_EMIT(2, 0, 0); /* Top-left */
  232. GL_RASTER_FONT_EMIT(3, 1, 0); /* Top-right */
  233. GL_RASTER_FONT_EMIT(4, 0, 0); /* Top-left */
  234. GL_RASTER_FONT_EMIT(5, 1, 1); /* Bottom-right */
  235. i++;
  236. delta_x += glyph->advance_x;
  237. delta_y -= glyph->advance_y;
  238. }
  239. coords.tex_coord = font_tex_coords;
  240. coords.vertex = font_vertex;
  241. coords.color = font_color;
  242. coords.vertices = i * 6;
  243. coords.lut_tex_coord = font_lut_tex_coord;
  244. if (font->block)
  245. video_coord_array_append(&font->block->carr, &coords, coords.vertices);
  246. else
  247. gl2_raster_font_draw_vertices(font, &coords);
  248. }
  249. }
  250. static void gl2_raster_font_render_message(
  251. gl2_raster_t *font, const char *msg, GLfloat scale,
  252. const GLfloat color[4], GLfloat pos_x, GLfloat pos_y,
  253. unsigned text_align)
  254. {
  255. struct font_line_metrics *line_metrics = NULL;
  256. int lines = 0;
  257. float line_height;
  258. /* If font line metrics are not supported just draw as usual */
  259. if (!font->font_driver->get_line_metrics ||
  260. !font->font_driver->get_line_metrics(font->font_data, &line_metrics))
  261. {
  262. gl2_raster_font_render_line(font->gl, font,
  263. msg, strlen(msg), scale, color, pos_x,
  264. pos_y, text_align);
  265. return;
  266. }
  267. line_height = line_metrics->height * scale / font->gl->vp.height;
  268. for (;;)
  269. {
  270. const char *delim = strchr(msg, '\n');
  271. size_t msg_len = delim
  272. ? (delim - msg) : strlen(msg);
  273. /* Draw the line */
  274. gl2_raster_font_render_line(font->gl, font,
  275. msg, msg_len, scale, color, pos_x,
  276. pos_y - (float)lines*line_height, text_align);
  277. if (!delim)
  278. break;
  279. msg += msg_len + 1;
  280. lines++;
  281. }
  282. }
  283. static void gl2_raster_font_setup_viewport(
  284. gl2_raster_t *font,
  285. unsigned width, unsigned height,
  286. bool full_screen)
  287. {
  288. video_driver_set_viewport(width, height, full_screen, false);
  289. glEnable(GL_BLEND);
  290. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  291. glBlendEquation(GL_FUNC_ADD);
  292. glBindTexture(GL_TEXTURE_2D, font->tex);
  293. if (font->gl && font->gl->shader && font->gl->shader->use)
  294. font->gl->shader->use(font->gl,
  295. font->gl->shader_data, VIDEO_SHADER_STOCK_BLEND, true);
  296. }
  297. static void gl2_raster_font_render_msg(
  298. void *userdata,
  299. void *data,
  300. const char *msg,
  301. const struct font_params *params)
  302. {
  303. GLfloat color[4];
  304. int drop_x, drop_y;
  305. GLfloat x, y, scale, drop_mod, drop_alpha;
  306. enum text_alignment text_align = TEXT_ALIGN_LEFT;
  307. bool full_screen = false ;
  308. gl2_raster_t *font = (gl2_raster_t*)data;
  309. unsigned width = font->gl->video_width;
  310. unsigned height = font->gl->video_height;
  311. if (!font || string_is_empty(msg))
  312. return;
  313. if (params)
  314. {
  315. x = params->x;
  316. y = params->y;
  317. scale = params->scale;
  318. full_screen = params->full_screen;
  319. text_align = params->text_align;
  320. drop_x = params->drop_x;
  321. drop_y = params->drop_y;
  322. drop_mod = params->drop_mod;
  323. drop_alpha = params->drop_alpha;
  324. color[0] = FONT_COLOR_GET_RED(params->color) / 255.0f;
  325. color[1] = FONT_COLOR_GET_GREEN(params->color) / 255.0f;
  326. color[2] = FONT_COLOR_GET_BLUE(params->color) / 255.0f;
  327. color[3] = FONT_COLOR_GET_ALPHA(params->color) / 255.0f;
  328. /* If alpha is 0.0f, turn it into default 1.0f */
  329. if (color[3] <= 0.0f)
  330. color[3] = 1.0f;
  331. }
  332. else
  333. {
  334. settings_t *settings = config_get_ptr();
  335. float video_msg_pos_x = settings->floats.video_msg_pos_x;
  336. float video_msg_pos_y = settings->floats.video_msg_pos_y;
  337. float video_msg_color_r = settings->floats.video_msg_color_r;
  338. float video_msg_color_g = settings->floats.video_msg_color_g;
  339. float video_msg_color_b = settings->floats.video_msg_color_b;
  340. x = video_msg_pos_x;
  341. y = video_msg_pos_y;
  342. scale = 1.0f;
  343. full_screen = true;
  344. text_align = TEXT_ALIGN_LEFT;
  345. color[0] = video_msg_color_r;
  346. color[1] = video_msg_color_g;
  347. color[2] = video_msg_color_b;
  348. color[3] = 1.0f;
  349. drop_x = -2;
  350. drop_y = -2;
  351. drop_mod = 0.3f;
  352. drop_alpha = 1.0f;
  353. }
  354. if (font->block)
  355. font->block->fullscreen = full_screen;
  356. else
  357. gl2_raster_font_setup_viewport(font, width, height, full_screen);
  358. if (font->gl)
  359. {
  360. if ( !string_is_empty(msg)
  361. && font->font_data
  362. && font->font_driver)
  363. {
  364. if (drop_x || drop_y)
  365. {
  366. GLfloat color_dark[4];
  367. color_dark[0] = color[0] * drop_mod;
  368. color_dark[1] = color[1] * drop_mod;
  369. color_dark[2] = color[2] * drop_mod;
  370. color_dark[3] = color[3] * drop_alpha;
  371. gl2_raster_font_render_message(font, msg, scale, color_dark,
  372. x + scale * drop_x / font->gl->vp.width, y +
  373. scale * drop_y / font->gl->vp.height, text_align);
  374. }
  375. gl2_raster_font_render_message(font, msg, scale, color,
  376. x, y, text_align);
  377. }
  378. if (!font->block)
  379. {
  380. /* restore viewport */
  381. glBindTexture(GL_TEXTURE_2D,
  382. font->gl->texture[font->gl->tex_index]);
  383. glDisable(GL_BLEND);
  384. video_driver_set_viewport(width, height, false, true);
  385. }
  386. }
  387. }
  388. static const struct font_glyph *gl2_raster_font_get_glyph(
  389. void *data, uint32_t code)
  390. {
  391. gl2_raster_t *font = (gl2_raster_t*)data;
  392. if (font && font->font_driver && font->font_driver->ident)
  393. return font->font_driver->get_glyph((void*)font->font_driver, code);
  394. return NULL;
  395. }
  396. static void gl2_raster_font_flush_block(unsigned width, unsigned height,
  397. void *data)
  398. {
  399. gl2_raster_t *font = (gl2_raster_t*)data;
  400. video_font_raster_block_t *block = font ? font->block : NULL;
  401. if (!font || !block || !block->carr.coords.vertices)
  402. return;
  403. gl2_raster_font_setup_viewport(font, width, height, block->fullscreen);
  404. gl2_raster_font_draw_vertices(font, (video_coords_t*)&block->carr.coords);
  405. if (font->gl)
  406. {
  407. /* restore viewport */
  408. glBindTexture(GL_TEXTURE_2D, font->gl->texture[font->gl->tex_index]);
  409. glDisable(GL_BLEND);
  410. video_driver_set_viewport(width, height, block->fullscreen, true);
  411. }
  412. }
  413. static void gl2_raster_font_bind_block(void *data, void *userdata)
  414. {
  415. gl2_raster_t *font = (gl2_raster_t*)data;
  416. video_font_raster_block_t *block = (video_font_raster_block_t*)userdata;
  417. if (font)
  418. font->block = block;
  419. }
  420. static bool gl2_raster_font_get_line_metrics(void* data, struct font_line_metrics **metrics)
  421. {
  422. gl2_raster_t *font = (gl2_raster_t*)data;
  423. if (font && font->font_driver && font->font_data)
  424. return font->font_driver->get_line_metrics(font->font_data, metrics);
  425. return false;
  426. }
  427. font_renderer_t gl2_raster_font = {
  428. gl2_raster_font_init,
  429. gl2_raster_font_free,
  430. gl2_raster_font_render_msg,
  431. "gl2_raster_font",
  432. gl2_raster_font_get_glyph,
  433. gl2_raster_font_bind_block,
  434. gl2_raster_font_flush_block,
  435. gl2_raster_font_get_message_width,
  436. gl2_raster_font_get_line_metrics
  437. };