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