1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3  *  Copyright (C) 2011-2017 - Daniel De Matteis
4  *
5  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
6  *  of the GNU General Public License as published by the Free Software Found-
7  *  ation, either version 3 of the License, or (at your option) any later version.
8  *
9  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11  *  PURPOSE.  See the GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License along with RetroArch.
14  *  If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <stdlib.h>
18 #include <math.h>
19 
20 #ifdef HAVE_CONFIG_H
21 #include "../config.h"
22 #endif
23 
24 #include "font_driver.h"
25 #include "video_thread_wrapper.h"
26 
27 #include "../retroarch.h"
28 #include "../verbosity.h"
29 
30 static const font_renderer_driver_t *font_backends[] = {
31 #ifdef HAVE_FREETYPE
32    &freetype_font_renderer,
33 #endif
34 #if defined(__APPLE__) && defined(HAVE_CORETEXT)
35    &coretext_font_renderer,
36 #endif
37 #ifdef HAVE_STB_FONT
38 #if defined(VITA) || defined(WIIU) || defined(ANDROID) || (defined(_WIN32) && !defined(_XBOX) && !defined(_MSC_VER) && _MSC_VER >= 1400) || (defined(_WIN32) && !defined(_XBOX) && defined(_MSC_VER)) || defined(HAVE_LIBNX) || defined(__linux__) || defined (HAVE_EMSCRIPTEN) || defined(__APPLE__) || defined(HAVE_ODROIDGO2) || defined(__PS3__)
39    &stb_unicode_font_renderer,
40 #else
41    &stb_font_renderer,
42 #endif
43 #endif
44    &bitmap_font_renderer,
45    NULL
46 };
47 
48 /* TODO/FIXME - global */
49 static void *video_font_driver = NULL;
50 
font_renderer_create_default(const font_renderer_driver_t ** drv,void ** handle,const char * font_path,unsigned font_size)51 int font_renderer_create_default(
52       const font_renderer_driver_t **drv,
53       void **handle,
54       const char *font_path, unsigned font_size)
55 {
56    unsigned i;
57 
58    for (i = 0; font_backends[i]; i++)
59    {
60       const char *path = font_path;
61 
62       if (!path)
63          path = font_backends[i]->get_default_font();
64       if (!path)
65          continue;
66 
67       *handle = font_backends[i]->init(path, font_size);
68       if (*handle)
69       {
70          RARCH_LOG("[Font]: Using font rendering backend: %s.\n",
71                font_backends[i]->ident);
72          *drv = font_backends[i];
73          return 1;
74       }
75 
76       RARCH_ERR("[Font]: Failed to create rendering backend: %s.\n",
77             font_backends[i]->ident);
78    }
79 
80    *drv    = NULL;
81    *handle = NULL;
82 
83    return 0;
84 }
85 
86 #ifdef HAVE_D3D8
87 static const font_renderer_t *d3d8_font_backends[] = {
88 #if defined(_XBOX1)
89    &d3d_xdk1_font,
90 #endif
91    NULL
92 };
93 
d3d8_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)94 static bool d3d8_font_init_first(
95       const void **font_driver, void **font_handle,
96       void *video_data, const char *font_path,
97       float font_size, bool is_threaded)
98 {
99    unsigned i;
100 
101    for (i = 0; i < ARRAY_SIZE(d3d8_font_backends); i++)
102    {
103       void *data = d3d8_font_backends[i] ? d3d8_font_backends[i]->init(
104             video_data, font_path, font_size,
105             is_threaded) : NULL;
106 
107       if (!data)
108          continue;
109 
110       *font_driver = d3d8_font_backends[i];
111       *font_handle = data;
112 
113       return true;
114    }
115 
116    return false;
117 }
118 #endif
119 
120 #ifdef HAVE_D3D9
121 static const font_renderer_t *d3d9_font_backends[] = {
122 #if defined(_WIN32) && defined(HAVE_D3DX)
123    &d3d_win32_font,
124 #endif
125    NULL
126 };
127 
d3d9_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)128 static bool d3d9_font_init_first(
129       const void **font_driver, void **font_handle,
130       void *video_data, const char *font_path,
131       float font_size, bool is_threaded)
132 {
133    unsigned i;
134 
135    for (i = 0; i < ARRAY_SIZE(d3d9_font_backends); i++)
136    {
137       void *data = d3d9_font_backends[i] ? d3d9_font_backends[i]->init(
138             video_data, font_path, font_size,
139             is_threaded) : NULL;
140 
141       if (!data)
142          continue;
143 
144       *font_driver = d3d9_font_backends[i];
145       *font_handle = data;
146 
147       return true;
148    }
149 
150    return false;
151 }
152 #endif
153 
154 #ifdef HAVE_OPENGL1
155 static const font_renderer_t *gl1_font_backends[] = {
156    &gl1_raster_font,
157    NULL,
158 };
159 
gl1_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)160 static bool gl1_font_init_first(
161       const void **font_driver, void **font_handle,
162       void *video_data, const char *font_path,
163       float font_size, bool is_threaded)
164 {
165    unsigned i;
166 
167    for (i = 0; gl1_font_backends[i]; i++)
168    {
169       void *data = gl1_font_backends[i]->init(
170             video_data, font_path, font_size,
171             is_threaded);
172 
173       if (!data)
174          continue;
175 
176       *font_driver = gl1_font_backends[i];
177       *font_handle = data;
178       return true;
179    }
180 
181    return false;
182 }
183 #endif
184 
185 #if defined(HAVE_OPENGL)
186 static const font_renderer_t *gl_font_backends[] = {
187    &gl_raster_font,
188    NULL,
189 };
190 
gl_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)191 static bool gl_font_init_first(
192       const void **font_driver, void **font_handle,
193       void *video_data, const char *font_path,
194       float font_size, bool is_threaded)
195 {
196    unsigned i;
197 
198    for (i = 0; gl_font_backends[i]; i++)
199    {
200       void *data = gl_font_backends[i]->init(
201             video_data, font_path, font_size,
202             is_threaded);
203 
204       if (!data)
205          continue;
206 
207       *font_driver = gl_font_backends[i];
208       *font_handle = data;
209       return true;
210    }
211 
212    return false;
213 }
214 #endif
215 
216 #ifdef HAVE_OPENGL_CORE
217 static const font_renderer_t *gl_core_font_backends[] = {
218    &gl_core_raster_font,
219    NULL,
220 };
221 
gl_core_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)222 static bool gl_core_font_init_first(
223       const void **font_driver, void **font_handle,
224       void *video_data, const char *font_path,
225       float font_size, bool is_threaded)
226 {
227    unsigned i;
228 
229    for (i = 0; gl_core_font_backends[i]; i++)
230    {
231       void *data = gl_core_font_backends[i]->init(
232             video_data, font_path, font_size,
233             is_threaded);
234 
235       if (!data)
236          continue;
237 
238       *font_driver = gl_core_font_backends[i];
239       *font_handle = data;
240       return true;
241    }
242 
243    return false;
244 }
245 #endif
246 
247 #ifdef HAVE_CACA
248 static const font_renderer_t *caca_font_backends[] = {
249    &caca_font,
250    NULL,
251 };
252 
caca_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)253 static bool caca_font_init_first(
254       const void **font_driver, void **font_handle,
255       void *video_data, const char *font_path,
256       float font_size, bool is_threaded)
257 {
258    unsigned i;
259 
260    for (i = 0; caca_font_backends[i]; i++)
261    {
262       void *data = caca_font_backends[i]->init(
263             video_data, font_path, font_size,
264             is_threaded);
265 
266       if (!data)
267          continue;
268 
269       *font_driver = caca_font_backends[i];
270       *font_handle = data;
271       return true;
272    }
273 
274    return false;
275 }
276 #endif
277 
278 #ifdef HAVE_SIXEL
279 static const font_renderer_t *sixel_font_backends[] = {
280    &sixel_font,
281    NULL,
282 };
283 
sixel_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)284 static bool sixel_font_init_first(
285       const void **font_driver, void **font_handle,
286       void *video_data, const char *font_path,
287       float font_size, bool is_threaded)
288 {
289    unsigned i;
290 
291    for (i = 0; sixel_font_backends[i]; i++)
292    {
293       void *data = sixel_font_backends[i]->init(
294             video_data, font_path, font_size,
295             is_threaded);
296 
297       if (!data)
298          continue;
299 
300       *font_driver = sixel_font_backends[i];
301       *font_handle = data;
302       return true;
303    }
304 
305    return false;
306 }
307 #endif
308 
309 #ifdef DJGPP
310 static const font_renderer_t *vga_font_backends[] = {
311    &vga_font,
312    NULL,
313 };
314 
vga_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)315 static bool vga_font_init_first(
316       const void **font_driver, void **font_handle,
317       void *video_data, const char *font_path,
318       float font_size, bool is_threaded)
319 {
320    unsigned i;
321 
322    for (i = 0; vga_font_backends[i]; i++)
323    {
324       void *data = vga_font_backends[i]->init(
325             video_data, font_path, font_size,
326             is_threaded);
327 
328       if (!data)
329          continue;
330 
331       *font_driver = vga_font_backends[i];
332       *font_handle = data;
333       return true;
334    }
335 
336    return false;
337 }
338 #endif
339 
340 #ifdef HAVE_GDI
341 #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
342 static const font_renderer_t *gdi_font_backends[] = {
343    &gdi_font,
344    NULL,
345 };
346 
gdi_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)347 static bool gdi_font_init_first(
348       const void **font_driver, void **font_handle,
349       void *video_data, const char *font_path,
350       float font_size, bool is_threaded)
351 {
352    unsigned i;
353 
354    for (i = 0; gdi_font_backends[i]; i++)
355    {
356       void *data = gdi_font_backends[i]->init(
357             video_data, font_path, font_size,
358             is_threaded);
359 
360       if (!data)
361          continue;
362 
363       *font_driver = gdi_font_backends[i];
364       *font_handle = data;
365       return true;
366    }
367 
368    return false;
369 }
370 #endif
371 #endif
372 
373 #ifdef HAVE_VULKAN
374 static const font_renderer_t *vulkan_font_backends[] = {
375    &vulkan_raster_font,
376    NULL,
377 };
378 
vulkan_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)379 static bool vulkan_font_init_first(
380       const void **font_driver, void **font_handle,
381       void *video_data, const char *font_path,
382       float font_size, bool is_threaded)
383 {
384    unsigned i;
385 
386    for (i = 0; vulkan_font_backends[i]; i++)
387    {
388       void *data = vulkan_font_backends[i]->init(video_data,
389             font_path, font_size,
390             is_threaded);
391 
392       if (!data)
393          continue;
394 
395       *font_driver = vulkan_font_backends[i];
396       *font_handle = data;
397       return true;
398    }
399 
400    return false;
401 }
402 #endif
403 
404 #ifdef HAVE_METAL
405 static const font_renderer_t *metal_font_backends[] = {
406    &metal_raster_font,
407    NULL,
408 };
409 
metal_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)410 static bool metal_font_init_first(
411    const void **font_driver, void **font_handle,
412    void *video_data, const char *font_path,
413    float font_size, bool is_threaded)
414 {
415    unsigned i;
416 
417    for (i = 0; metal_font_backends[i]; i++)
418    {
419       void *data = metal_font_backends[i]->init(video_data,
420                                                  font_path, font_size,
421                                                  is_threaded);
422 
423       if (!data)
424          continue;
425 
426       *font_driver = metal_font_backends[i];
427       *font_handle = data;
428       return true;
429    }
430 
431    return false;
432 }
433 #endif
434 
435 #ifdef HAVE_D3D10
436 static const font_renderer_t *d3d10_font_backends[] = {
437    &d3d10_font,
438    NULL,
439 };
440 
d3d10_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)441 static bool d3d10_font_init_first(
442       const void **font_driver, void **font_handle,
443       void *video_data, const char *font_path,
444       float font_size, bool is_threaded)
445 {
446    unsigned i;
447 
448    for (i = 0; d3d10_font_backends[i]; i++)
449    {
450       void *data = d3d10_font_backends[i]->init(video_data,
451             font_path, font_size,
452             is_threaded);
453 
454       if (!data)
455          continue;
456 
457       *font_driver = d3d10_font_backends[i];
458       *font_handle = data;
459       return true;
460    }
461 
462    return false;
463 }
464 #endif
465 
466 #ifdef HAVE_D3D11
467 static const font_renderer_t *d3d11_font_backends[] = {
468    &d3d11_font,
469    NULL,
470 };
471 
d3d11_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)472 static bool d3d11_font_init_first(
473       const void **font_driver, void **font_handle,
474       void *video_data, const char *font_path,
475       float font_size, bool is_threaded)
476 {
477    unsigned i;
478 
479    for (i = 0; d3d11_font_backends[i]; i++)
480    {
481       void *data = d3d11_font_backends[i]->init(video_data,
482             font_path, font_size,
483             is_threaded);
484 
485       if (!data)
486          continue;
487 
488       *font_driver = d3d11_font_backends[i];
489       *font_handle = data;
490       return true;
491    }
492 
493    return false;
494 }
495 #endif
496 
497 #ifdef HAVE_D3D12
498 static const font_renderer_t *d3d12_font_backends[] = {
499    &d3d12_font,
500    NULL,
501 };
502 
d3d12_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)503 static bool d3d12_font_init_first(
504       const void **font_driver, void **font_handle,
505       void *video_data, const char *font_path,
506       float font_size, bool is_threaded)
507 {
508    unsigned i;
509 
510    for (i = 0; d3d12_font_backends[i]; i++)
511    {
512       void *data = d3d12_font_backends[i]->init(video_data,
513             font_path, font_size,
514             is_threaded);
515 
516       if (!data)
517          continue;
518 
519       *font_driver = d3d12_font_backends[i];
520       *font_handle = data;
521       return true;
522    }
523 
524    return false;
525 }
526 #endif
527 
528 #ifdef PS2
529 static const font_renderer_t *ps2_font_backends[] = {
530    &ps2_font
531 };
532 
ps2_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)533 static bool ps2_font_init_first(
534       const void **font_driver, void **font_handle,
535       void *video_data, const char *font_path,
536       float font_size, bool is_threaded)
537 {
538    unsigned i;
539 
540    for (i = 0; ps2_font_backends[i]; i++)
541    {
542       void *data = ps2_font_backends[i]->init(
543             video_data, font_path, font_size,
544             is_threaded);
545 
546       if (!data)
547          continue;
548 
549       *font_driver = ps2_font_backends[i];
550       *font_handle = data;
551       return true;
552    }
553 
554    return false;
555 }
556 #endif
557 
558 #ifdef HAVE_VITA2D
559 static const font_renderer_t *vita2d_font_backends[] = {
560    &vita2d_vita_font
561 };
562 
vita2d_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)563 static bool vita2d_font_init_first(
564       const void **font_driver, void **font_handle,
565       void *video_data, const char *font_path,
566       float font_size, bool is_threaded)
567 {
568    unsigned i;
569 
570    for (i = 0; vita2d_font_backends[i]; i++)
571    {
572       void *data = vita2d_font_backends[i]->init(
573             video_data, font_path, font_size,
574             is_threaded);
575 
576       if (!data)
577          continue;
578 
579       *font_driver = vita2d_font_backends[i];
580       *font_handle = data;
581       return true;
582    }
583 
584    return false;
585 }
586 #endif
587 
588 #ifdef _3DS
589 static const font_renderer_t *ctr_font_backends[] = {
590    &ctr_font
591 };
592 
ctr_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)593 static bool ctr_font_init_first(
594       const void **font_driver, void **font_handle,
595       void *video_data, const char *font_path,
596       float font_size, bool is_threaded)
597 {
598    unsigned i;
599 
600    for (i = 0; ctr_font_backends[i]; i++)
601    {
602       void *data = ctr_font_backends[i]->init(
603             video_data, font_path, font_size,
604             is_threaded);
605 
606       if (!data)
607          continue;
608 
609       *font_driver = ctr_font_backends[i];
610       *font_handle = data;
611       return true;
612    }
613 
614    return false;
615 }
616 #endif
617 
618 #ifdef HAVE_LIBNX
619 static const font_renderer_t *switch_font_backends[] = {
620    &switch_font,
621    NULL
622 };
623 
switch_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)624 static bool switch_font_init_first(
625       const void **font_driver, void **font_handle,
626       void *video_data, const char *font_path,
627       float font_size, bool is_threaded)
628 {
629    unsigned i;
630 
631    for (i = 0; switch_font_backends[i]; i++)
632    {
633       void *data = switch_font_backends[i]->init(
634             video_data, font_path, font_size,
635             is_threaded);
636 
637       if (!data)
638          continue;
639 
640       *font_driver = switch_font_backends[i];
641       *font_handle = data;
642       return true;
643    }
644 
645    return false;
646 }
647 #endif
648 
649 #ifdef WIIU
650 static const font_renderer_t *wiiu_font_backends[] = {
651    &wiiu_font,
652    NULL
653 };
654 
wiiu_font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)655 static bool wiiu_font_init_first(
656       const void **font_driver, void **font_handle,
657       void *video_data, const char *font_path,
658       float font_size, bool is_threaded)
659 {
660    unsigned i;
661 
662    for (i = 0; wiiu_font_backends[i]; i++)
663    {
664       void *data = wiiu_font_backends[i]->init(
665             video_data, font_path, font_size,
666             is_threaded);
667 
668       if (!data)
669          continue;
670 
671       *font_driver = wiiu_font_backends[i];
672       *font_handle = data;
673       return true;
674    }
675 
676    return false;
677 }
678 #endif
679 
font_init_first(const void ** font_driver,void ** font_handle,void * video_data,const char * font_path,float font_size,enum font_driver_render_api api,bool is_threaded)680 static bool font_init_first(
681       const void **font_driver, void **font_handle,
682       void *video_data, const char *font_path, float font_size,
683       enum font_driver_render_api api, bool is_threaded)
684 {
685    if (font_path && !font_path[0])
686       font_path = NULL;
687 
688    switch (api)
689    {
690 #ifdef HAVE_OPENGL1
691       case FONT_DRIVER_RENDER_OPENGL1_API:
692          return gl1_font_init_first(font_driver, font_handle,
693                video_data, font_path, font_size, is_threaded);
694 #endif
695 #ifdef HAVE_OPENGL
696       case FONT_DRIVER_RENDER_OPENGL_API:
697          return gl_font_init_first(font_driver, font_handle,
698                video_data, font_path, font_size, is_threaded);
699 #endif
700 #ifdef HAVE_OPENGL_CORE
701       case FONT_DRIVER_RENDER_OPENGL_CORE_API:
702          return gl_core_font_init_first(font_driver, font_handle,
703                                         video_data, font_path, font_size, is_threaded);
704 #endif
705 #ifdef HAVE_VULKAN
706       case FONT_DRIVER_RENDER_VULKAN_API:
707          return vulkan_font_init_first(font_driver, font_handle,
708                video_data, font_path, font_size, is_threaded);
709 #endif
710 #ifdef HAVE_METAL
711    case FONT_DRIVER_RENDER_METAL_API:
712       return metal_font_init_first(font_driver, font_handle,
713                video_data, font_path, font_size, is_threaded);
714 #endif
715 #ifdef HAVE_D3D8
716       case FONT_DRIVER_RENDER_D3D8_API:
717          return d3d8_font_init_first(font_driver, font_handle,
718                video_data, font_path, font_size, is_threaded);
719 #endif
720 #ifdef HAVE_D3D9
721       case FONT_DRIVER_RENDER_D3D9_API:
722          return d3d9_font_init_first(font_driver, font_handle,
723                video_data, font_path, font_size, is_threaded);
724 #endif
725 #ifdef HAVE_D3D10
726       case FONT_DRIVER_RENDER_D3D10_API:
727          return d3d10_font_init_first(font_driver, font_handle,
728                video_data, font_path, font_size, is_threaded);
729 #endif
730 #ifdef HAVE_D3D11
731       case FONT_DRIVER_RENDER_D3D11_API:
732          return d3d11_font_init_first(font_driver, font_handle,
733                video_data, font_path, font_size, is_threaded);
734 #endif
735 #ifdef HAVE_D3D12
736       case FONT_DRIVER_RENDER_D3D12_API:
737          return d3d12_font_init_first(font_driver, font_handle,
738                video_data, font_path, font_size, is_threaded);
739 #endif
740 #ifdef HAVE_VITA2D
741       case FONT_DRIVER_RENDER_VITA2D:
742          return vita2d_font_init_first(font_driver, font_handle,
743                video_data, font_path, font_size, is_threaded);
744 #endif
745 #ifdef PS2
746       case FONT_DRIVER_RENDER_PS2:
747          return ps2_font_init_first(font_driver, font_handle,
748                video_data, font_path, font_size, is_threaded);
749 #endif
750 #ifdef _3DS
751       case FONT_DRIVER_RENDER_CTR:
752          return ctr_font_init_first(font_driver, font_handle,
753                video_data, font_path, font_size, is_threaded);
754 #endif
755 #ifdef WIIU
756       case FONT_DRIVER_RENDER_WIIU:
757          return wiiu_font_init_first(font_driver, font_handle,
758                video_data, font_path, font_size, is_threaded);
759 #endif
760 #ifdef HAVE_CACA
761       case FONT_DRIVER_RENDER_CACA:
762          return caca_font_init_first(font_driver, font_handle,
763                video_data, font_path, font_size, is_threaded);
764 #endif
765 #ifdef HAVE_SIXEL
766       case FONT_DRIVER_RENDER_SIXEL:
767          return sixel_font_init_first(font_driver, font_handle,
768                video_data, font_path, font_size, is_threaded);
769 #endif
770 #ifdef HAVE_LIBNX
771       case FONT_DRIVER_RENDER_SWITCH:
772          return switch_font_init_first(font_driver, font_handle,
773                video_data, font_path, font_size, is_threaded);
774 #endif
775 #ifdef HAVE_GDI
776 #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
777       case FONT_DRIVER_RENDER_GDI:
778          return gdi_font_init_first(font_driver, font_handle,
779                video_data, font_path, font_size, is_threaded);
780 #endif
781 #endif
782 #ifdef DJGPP
783       case FONT_DRIVER_RENDER_VGA:
784          return vga_font_init_first(font_driver, font_handle,
785                video_data, font_path, font_size, is_threaded);
786 #endif
787       case FONT_DRIVER_RENDER_DONT_CARE:
788          /* TODO/FIXME - lookup graphics driver's 'API' */
789          break;
790       default:
791          break;
792    }
793 
794    return false;
795 }
796 
797 #ifdef HAVE_LANGEXTRA
798 
799 /* ACII:        0xxxxxxx  (c & 0x80) == 0x00
800  * other start: 11xxxxxx  (c & 0xC0) == 0xC0
801  * other cont:  10xxxxxx  (c & 0xC0) == 0x80
802  * Neutral :
803  * 0020 - 002F : 001xxxxx (c & 0xE0) == 0x20
804  * Arabic:
805  * 0600 - 06FF : 110110xx (c & 0xFC) == 0xD8 (2 bytes) */
806 
807 /* clang-format off */
808 #define IS_ASCII(p)        ((*(p)&0x80) == 0x00)
809 #define IS_MBSTART(p)      ((*(p)&0xC0) == 0xC0)
810 #define IS_MBCONT(p)       ((*(p)&0xC0) == 0x80)
811 #define IS_DIR_NEUTRAL(p)  ((*(p)&0xE0) == 0x20)
812 #define IS_ARABIC(p)       ((*(p)&0xFC) == 0xD8)
813 #define IS_RTL(p)          IS_ARABIC(p)
814 #define GET_ID_ARABIC(p)   (((unsigned char)(p)[0] << 6) | ((unsigned char)(p)[1] & 0x3F))
815 
816 /* 0x0620 to 0x064F */
817 static const unsigned arabic_shape_map[0x100][0x4] = {
818    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0600 */
819    { 0 }, { 0 }, { 0 }, { 0 },
820    { 0 }, { 0 }, { 0 }, { 0 },
821    { 0 }, { 0 }, { 0 }, { 0 },
822 
823    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0610 */
824    { 0 }, { 0 }, { 0 }, { 0 },
825    { 0 }, { 0 }, { 0 }, { 0 },
826    { 0 }, { 0 }, { 0 }, { 0 },
827 
828    { 0 },                               /* 0x0620 */
829    { 0xFE80 },
830    { 0xFE81, 0xFE82 },
831    { 0xFE83, 0xFE84 },
832    { 0xFE85, 0xFE86 },
833    { 0xFE87, 0xFE88 },
834    { 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C },
835    { 0xFE8D, 0xFE8E },
836 
837    { 0xFE8F, 0xFE90, 0xFE91, 0xFE92 },
838    { 0xFE93, 0xFE94 },
839    { 0xFE95, 0xFE96, 0xFE97, 0xFE98 },
840    { 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C },
841    { 0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0 },
842    { 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4 },
843    { 0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8 },
844    { 0xFEA9, 0xFEAA },
845 
846    { 0xFEAB, 0xFEAC },                  /* 0x0630 */
847    { 0xFEAD, 0xFEAE },
848    { 0xFEAF, 0xFEB0 },
849    { 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4 },
850    { 0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8 },
851    { 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC },
852    { 0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0 },
853    { 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4 },
854 
855    { 0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8 },
856    { 0xFEC9, 0xFECA, 0xFECB, 0xFECC },
857    { 0xFECD, 0xFECE, 0xFECF, 0xFED0 },
858    { 0 },
859    { 0 }, { 0 }, { 0 }, { 0 },
860 
861    { 0 },                               /* 0x0640 */
862    { 0xFED1, 0xFED2, 0xFED3, 0xFED4 },
863    { 0xFED5, 0xFED6, 0xFED7, 0xFED8 },
864    { 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC },
865    { 0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0 },
866    { 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4 },
867    { 0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8 },
868    { 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC },
869 
870    { 0xFEED, 0xFEEE },
871    { 0xFEEF, 0xFEF0, 0xFBE8, 0xFBE9 },
872    { 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4 },
873    { 0 },
874    { 0 }, { 0 }, { 0 }, { 0 },
875 
876    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0650 */
877    { 0 }, { 0 }, { 0 }, { 0 },
878    { 0 }, { 0 }, { 0 }, { 0 },
879    { 0 }, { 0 }, { 0 }, { 0 },
880 
881 
882    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0660 */
883    { 0 }, { 0 }, { 0 }, { 0 },
884    { 0 }, { 0 }, { 0 }, { 0 },
885    { 0 }, { 0 }, { 0 }, { 0 },
886 
887 
888    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0670 */
889    { 0 }, { 0 }, { 0 }, { 0 },
890    { 0 }, { 0 }, { 0 }, { 0 },
891 
892    { 0 }, { 0 },
893    { 0xFB56, 0xFB57, 0xFB58, 0xFB59 },
894    { 0 },
895 
896 
897    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0680 */
898    { 0 }, { 0 }, { 0 }, { 0 },
899    { 0 }, { 0 }, { 0 }, { 0 },
900    { 0 }, { 0 }, { 0 }, { 0 },
901 
902 
903    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x0690 */
904    { 0 }, { 0 }, { 0 }, { 0 },
905    { 0 }, { 0 }, { 0 }, { 0 },
906    { 0 }, { 0 }, { 0 }, { 0 },
907 
908 
909    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x06A0 */
910    { 0 }, { 0 }, { 0 }, { 0 },
911 
912    { 0 },
913    { 0xFB8E, 0xFB8F, 0xFB90, 0xFB91 },
914    { 0 }, { 0 },
915 
916    { 0 }, { 0 }, { 0 },
917    { 0xFB92, 0xFB93, 0xFB94, 0xFB95 },
918 
919 
920    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x06B0 */
921    { 0 }, { 0 }, { 0 }, { 0 },
922    { 0 }, { 0 }, { 0 }, { 0 },
923    { 0 }, { 0 }, { 0 }, { 0 },
924 
925 
926    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x06C0 */
927    { 0 }, { 0 }, { 0 }, { 0 },
928    { 0 }, { 0 }, { 0 }, { 0 },
929 
930    { 0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF },
931    { 0 }, { 0 }, { 0 },
932 
933 
934    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x06D0 */
935    { 0 }, { 0 }, { 0 }, { 0 },
936    { 0 }, { 0 }, { 0 }, { 0 },
937    { 0 }, { 0 }, { 0 }, { 0 },
938 
939 
940    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x06E0 */
941    { 0 }, { 0 }, { 0 }, { 0 },
942    { 0 }, { 0 }, { 0 }, { 0 },
943    { 0 }, { 0 }, { 0 }, { 0 },
944 
945 
946    { 0 }, { 0 }, { 0 }, { 0 },          /* 0x06F0 */
947    { 0 }, { 0 }, { 0 }, { 0 },
948    { 0 }, { 0 }, { 0 }, { 0 },
949    { 0 }, { 0 }, { 0 }, { 0 },
950 };
951 /* clang-format on */
952 
font_get_replacement(const char * src,const char * start)953 static INLINE unsigned font_get_replacement(const char* src, const char* start)
954 {
955    if (IS_ARABIC(src)) /* 0x0600 to 0x06FF */
956    {
957       unsigned      result         = 0;
958       bool          prev_connected = false;
959       bool          next_connected = false;
960       unsigned char id             = GET_ID_ARABIC(src);
961       const char*   prev           = src - 2;
962       const char*   next           = src + 2;
963 
964       if ((prev >= start) && IS_ARABIC(prev))
965       {
966          unsigned char prev_id = GET_ID_ARABIC(prev);
967 
968          /* nonspacing diacritics 0x4b -- 0x5f */
969          while (prev_id > 0x4A && prev_id < 0x60)
970          {
971             prev -= 2;
972             if ((prev >= start) && IS_ARABIC(prev))
973                prev_id = GET_ID_ARABIC(prev);
974             else
975                break;
976          }
977 
978 
979          if (prev_id == 0x44) /* Arabic Letter Lam */
980          {
981             unsigned char prev2_id = 0;
982             const char*   prev2    = prev - 2;
983 
984             if (prev2 >= start)
985                prev2_id = (prev2[0] << 6) | (prev2[1] & 0x3F);
986 
987             /* nonspacing diacritics 0x4b -- 0x5f */
988             while (prev2_id > 0x4A && prev2_id < 0x60)
989             {
990                prev2 -= 2;
991                if ((prev2 >= start) && IS_ARABIC(prev2))
992                   prev2_id = GET_ID_ARABIC(prev2);
993                else
994                   break;
995             }
996 
997             prev_connected = !!arabic_shape_map[prev2_id][2];
998 
999             switch (id)
1000             {
1001                case 0x22: /* Arabic Letter Alef with Madda Above */
1002                   return 0xFEF5 + prev_connected;
1003                case 0x23: /* Arabic Letter Alef with Hamza Above */
1004                   return 0xFEF7 + prev_connected;
1005                case 0x25: /* Arabic Letter Alef with Hamza Below */
1006                   return 0xFEF9 + prev_connected;
1007                case 0x27: /* Arabic Letter Alef */
1008                   return 0xFEFB + prev_connected;
1009             }
1010          }
1011          prev_connected = !!arabic_shape_map[prev_id][2];
1012       }
1013 
1014       if (IS_ARABIC(next))
1015       {
1016          unsigned char next_id = GET_ID_ARABIC(next);
1017 
1018          /* nonspacing diacritics 0x4b -- 0x5f */
1019          while (next_id > 0x4A && next_id < 0x60)
1020          {
1021             next += 2;
1022             if (IS_ARABIC(next))
1023                next_id = GET_ID_ARABIC(next);
1024             else
1025                break;
1026          }
1027 
1028          next_connected = !!arabic_shape_map[next_id][1];
1029       }
1030 
1031       result = arabic_shape_map[id][prev_connected | (next_connected << 1)];
1032 
1033       if (result)
1034          return result;
1035 
1036       return arabic_shape_map[id][prev_connected];
1037    }
1038 
1039    return 0;
1040 }
1041 
font_driver_reshape_msg(const char * msg,unsigned char * buffer,size_t buffer_size)1042 static char* font_driver_reshape_msg(const char* msg, unsigned char *buffer, size_t buffer_size)
1043 {
1044    unsigned char*       dst_buffer = buffer;
1045    const unsigned char* src     = (const unsigned char*)msg;
1046    unsigned char*       dst;
1047    bool                 reverse = false;
1048    size_t              msg_size = (strlen(msg) * 2) + 1;
1049 
1050    /* fallback to heap allocated buffer if the buffer is too small */
1051    /* worst case transformations are 2 bytes to 4 bytes -- aliaspider */
1052    if (buffer_size < msg_size)
1053       dst_buffer = (unsigned char*)malloc(msg_size);
1054 
1055    dst = (unsigned char*)dst_buffer;
1056 
1057    while (*src || reverse)
1058    {
1059       if (reverse)
1060       {
1061          src--;
1062          while (src > (const unsigned char*)msg && IS_MBCONT(src))
1063             src--;
1064 
1065          if (src >= (const unsigned char*)msg && (IS_RTL(src) || IS_DIR_NEUTRAL(src)))
1066          {
1067             unsigned replacement = font_get_replacement((const char*)src, msg);
1068             if (replacement)
1069             {
1070                if (replacement < 0x80)
1071                   *dst++ = replacement;
1072                else if (replacement < 0x800)
1073                {
1074                   *dst++ = 0xC0 | (replacement >> 6);
1075                   *dst++ = 0x80 | (replacement & 0x3F);
1076                }
1077                else if (replacement < 0x10000)
1078                {
1079                   /* merged glyphs */
1080                   if ((replacement >= 0xFEF5) && (replacement <= 0xFEFC))
1081                      src -= 2;
1082 
1083                   *dst++ = 0xE0 | (replacement >> 12);
1084                   *dst++ = 0x80 | ((replacement >> 6) & 0x3F);
1085                   *dst++ = 0x80 | (replacement & 0x3F);
1086                }
1087                else
1088                {
1089                   *dst++ = 0xF0 | (replacement >> 18);
1090                   *dst++ = 0x80 | ((replacement >> 12) & 0x3F);
1091                   *dst++ = 0x80 | ((replacement >> 6) & 0x3F);
1092                   *dst++ = 0x80 | (replacement & 0x3F);
1093                }
1094 
1095                continue;
1096             }
1097 
1098             *dst++ = *src++;
1099             while (IS_MBCONT(src))
1100                *dst++ = *src++;
1101             src--;
1102 
1103             while (IS_MBCONT(src))
1104                src--;
1105          }
1106          else
1107          {
1108             reverse = false;
1109             src++;
1110             while (IS_MBCONT(src) || IS_RTL(src) || IS_DIR_NEUTRAL(src))
1111                src++;
1112          }
1113       }
1114       else
1115       {
1116          if (IS_RTL(src))
1117          {
1118             reverse = true;
1119             while (IS_MBCONT(src) || IS_RTL(src) || IS_DIR_NEUTRAL(src))
1120                src++;
1121          }
1122          else
1123             *dst++ = *src++;
1124       }
1125    }
1126 
1127    *dst = '\0';
1128 
1129    return (char*)dst_buffer;
1130 }
1131 #endif
1132 
font_driver_render_msg(void * data,const char * msg,const void * _params,void * font_data)1133 void font_driver_render_msg(
1134       void *data,
1135       const char *msg,
1136       const void *_params,
1137       void *font_data)
1138 {
1139    const struct font_params *params = (const struct font_params*)_params;
1140    font_data_t                *font = (font_data_t*)(font_data
1141          ? font_data : video_font_driver);
1142 
1143    if (msg && *msg && font && font->renderer && font->renderer->render_msg)
1144    {
1145 #ifdef HAVE_LANGEXTRA
1146       unsigned char tmp_buffer[64];
1147       char *new_msg = font_driver_reshape_msg(msg, tmp_buffer, sizeof(tmp_buffer));
1148 #else
1149       char *new_msg = (char*)msg;
1150 #endif
1151       font->renderer->render_msg(data,
1152             font->renderer_data, new_msg, params);
1153 #ifdef HAVE_LANGEXTRA
1154       if (new_msg != (char*)tmp_buffer)
1155          free(new_msg);
1156 #endif
1157    }
1158 }
1159 
font_driver_bind_block(void * font_data,void * block)1160 void font_driver_bind_block(void *font_data, void *block)
1161 {
1162    font_data_t *font = (font_data_t*)(font_data ? font_data : video_font_driver);
1163 
1164    if (font && font->renderer && font->renderer->bind_block)
1165       font->renderer->bind_block(font->renderer_data, block);
1166 }
1167 
font_driver_flush(unsigned width,unsigned height,void * font_data)1168 void font_driver_flush(unsigned width, unsigned height, void *font_data)
1169 {
1170    font_data_t *font = (font_data_t*)(font_data ? font_data : video_font_driver);
1171    if (font && font->renderer && font->renderer->flush)
1172       font->renderer->flush(width, height, font->renderer_data);
1173 }
1174 
font_driver_get_message_width(void * font_data,const char * msg,unsigned len,float scale)1175 int font_driver_get_message_width(void *font_data,
1176       const char *msg, unsigned len, float scale)
1177 {
1178    font_data_t *font = (font_data_t*)(font_data ? font_data : video_font_driver);
1179    if (len == 0 && msg)
1180       len = (unsigned)strlen(msg);
1181    if (font && font->renderer && font->renderer->get_message_width)
1182       return font->renderer->get_message_width(font->renderer_data, msg, len, scale);
1183    return -1;
1184 }
1185 
font_driver_get_line_height(void * font_data,float scale)1186 int font_driver_get_line_height(void *font_data, float scale)
1187 {
1188    struct font_line_metrics *metrics = NULL;
1189    font_data_t *font = (font_data_t*)(font_data ? font_data : video_font_driver);
1190 
1191    /* First try the line metrics implementation */
1192    if (font && font->renderer && font->renderer->get_line_metrics)
1193       if ((font->renderer->get_line_metrics(font->renderer_data, &metrics)))
1194          return (int)roundf(metrics->height * scale);
1195 
1196    /* Else return an approximation
1197     * (uses a fudge of standard font metrics - mostly garbage...)
1198     * > font_size = (width of 'a') / 0.6
1199     * > line_height = font_size * 1.7f */
1200    return (int)roundf(1.7f * (float)font_driver_get_message_width(font_data, "a", 1, scale) / 0.6f);
1201 }
1202 
font_driver_get_line_ascender(void * font_data,float scale)1203 int font_driver_get_line_ascender(void *font_data, float scale)
1204 {
1205    struct font_line_metrics *metrics = NULL;
1206    font_data_t *font = (font_data_t*)(font_data ? font_data : video_font_driver);
1207 
1208    /* First try the line metrics implementation */
1209    if (font && font->renderer && font->renderer->get_line_metrics)
1210       if ((font->renderer->get_line_metrics(font->renderer_data, &metrics)))
1211          return (int)roundf(metrics->ascender * scale);
1212 
1213    /* Else return an approximation
1214     * (uses a fudge of standard font metrics - mostly garbage...)
1215     * > font_size = (width of 'a') / 0.6
1216     * > ascender = 1.58 * font_size * 0.75 */
1217    return (int)roundf(1.58f * 0.75f * (float)font_driver_get_message_width(font_data, "a", 1, scale) / 0.6f);
1218 }
1219 
font_driver_get_line_descender(void * font_data,float scale)1220 int font_driver_get_line_descender(void *font_data, float scale)
1221 {
1222    struct font_line_metrics *metrics = NULL;
1223    font_data_t *font = (font_data_t*)(font_data ? font_data : video_font_driver);
1224 
1225    /* First try the line metrics implementation */
1226    if (font && font->renderer && font->renderer->get_line_metrics)
1227       if ((font->renderer->get_line_metrics(font->renderer_data, &metrics)))
1228          return (int)roundf(metrics->descender * scale);
1229 
1230    /* Else return an approximation
1231     * (uses a fudge of standard font metrics - mostly garbage...)
1232     * > font_size = (width of 'a') / 0.6
1233     * > descender = 1.58 * font_size * 0.25 */
1234    return (int)roundf(1.58f * 0.25f * (float)font_driver_get_message_width(font_data, "a", 1, scale) / 0.6f);
1235 }
1236 
font_driver_get_line_centre_offset(void * font_data,float scale)1237 int font_driver_get_line_centre_offset(void *font_data, float scale)
1238 {
1239    struct font_line_metrics *metrics = NULL;
1240    font_data_t *font = (font_data_t*)(font_data ? font_data : video_font_driver);
1241 
1242    /* First try the line metrics implementation */
1243    if (font && font->renderer && font->renderer->get_line_metrics)
1244       if ((font->renderer->get_line_metrics(font->renderer_data, &metrics)))
1245          return (int)roundf((metrics->ascender - metrics->descender) * 0.5f * scale);
1246 
1247    /* Else return an approximation... */
1248    return (int)roundf((1.58f * 0.5f * (float)font_driver_get_message_width(font_data, "a", 1, scale) / 0.6f) / 2.0f);
1249 }
1250 
font_driver_free(void * font_data)1251 void font_driver_free(void *font_data)
1252 {
1253    font_data_t *font = (font_data_t*)font_data;
1254 
1255    if (font)
1256    {
1257       bool is_threaded        = false;
1258 #ifdef HAVE_THREADS
1259       bool *is_threaded_tmp   = video_driver_get_threaded();
1260       is_threaded             = *is_threaded_tmp;
1261 #endif
1262 
1263       if (font->renderer && font->renderer->free)
1264          font->renderer->free(font->renderer_data, is_threaded);
1265 
1266       font->renderer      = NULL;
1267       font->renderer_data = NULL;
1268 
1269       free(font);
1270    }
1271 }
1272 
font_driver_init_first(void * video_data,const char * font_path,float font_size,bool threading_hint,bool is_threaded,enum font_driver_render_api api)1273 font_data_t *font_driver_init_first(
1274       void *video_data, const char *font_path, float font_size,
1275       bool threading_hint, bool is_threaded,
1276       enum font_driver_render_api api)
1277 {
1278    const void *font_driver = NULL;
1279    void *font_handle       = NULL;
1280    bool ok                 = false;
1281 #ifdef HAVE_THREADS
1282 
1283    if (     threading_hint
1284          && is_threaded
1285          && !video_driver_is_hw_context())
1286       ok = video_thread_font_init(&font_driver, &font_handle,
1287             video_data, font_path, font_size, api, font_init_first,
1288             is_threaded);
1289    else
1290 #endif
1291    ok = font_init_first(&font_driver, &font_handle,
1292          video_data, font_path, font_size, api, is_threaded);
1293 
1294    if (ok)
1295    {
1296       font_data_t *font   = (font_data_t*)malloc(sizeof(*font));
1297       font->renderer      = (const font_renderer_t*)font_driver;
1298       font->renderer_data = font_handle;
1299       font->size          = font_size;
1300       return font;
1301    }
1302 
1303    return NULL;
1304 }
1305 
font_driver_init_osd(void * video_data,const void * video_info_data,bool threading_hint,bool is_threaded,enum font_driver_render_api api)1306 void font_driver_init_osd(
1307       void *video_data,
1308       const void *video_info_data,
1309       bool threading_hint,
1310       bool is_threaded,
1311       enum font_driver_render_api api)
1312 {
1313    const video_info_t *video_info = (const video_info_t*)video_info_data;
1314    if (video_font_driver || !video_info)
1315       return;
1316 
1317    video_font_driver = font_driver_init_first(video_data,
1318          *video_info->path_font ? video_info->path_font : NULL,
1319          video_info->font_size, threading_hint, is_threaded, api);
1320 
1321    if (!video_font_driver)
1322       RARCH_ERR("[Font]: Failed to initialize OSD font.\n");
1323 }
1324 
font_driver_free_osd(void)1325 void font_driver_free_osd(void)
1326 {
1327    if (video_font_driver)
1328       font_driver_free(video_font_driver);
1329 
1330    video_font_driver = NULL;
1331 }
1332