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 "gfx_display.h"
18 
19 #include "video_coord_array.h"
20 #include "../configuration.h"
21 #include "../verbosity.h"
22 
23 /* Standard reference DPI value, used when determining
24  * DPI-aware scaling factors */
25 #define REFERENCE_DPI 96.0f
26 
27 /* 'OZONE_SIDEBAR_WIDTH' must be kept in sync
28  * with Ozone driver metrics */
29 #define OZONE_SIDEBAR_WIDTH 408
30 
31 /* TODO/FIXME - global that gets referenced outside,
32  * needs to be refactored */
33 uintptr_t gfx_display_white_texture;
34 
gfx_display_null_font_init_first(void ** font_handle,void * video_data,const char * font_path,float font_size,bool is_threaded)35 static bool gfx_display_null_font_init_first(
36       void **font_handle, void *video_data,
37       const char *font_path, float font_size,
38       bool is_threaded)
39 {
40    font_data_t **handle = (font_data_t**)font_handle;
41    if ((*handle = font_driver_init_first(video_data,
42          font_path, font_size, true,
43          is_threaded,
44          FONT_DRIVER_RENDER_DONT_CARE)))
45       return true;
46    return false;
47 }
48 
null_get_default_matrix(void)49 static const float *null_get_default_matrix(void)
50 {
51    static float dummy[16] = {0.0f};
52    return &dummy[0];
53 }
54 
55 gfx_display_ctx_driver_t gfx_display_ctx_null = {
56    NULL,                                     /* draw            */
57    NULL,                                     /* draw_pipeline   */
58    NULL,                                     /* blend_begin     */
59    NULL,                                     /* blend_end       */
60    NULL,                                     /* get_default_mvp */
61    null_get_default_matrix,
62    null_get_default_matrix,
63    gfx_display_null_font_init_first,
64    GFX_VIDEO_DRIVER_GENERIC,
65    "null",
66    false,
67    NULL,
68    NULL
69 };
70 
71 /* Menu display drivers */
72 static gfx_display_ctx_driver_t *gfx_display_ctx_drivers[] = {
73 #ifdef HAVE_D3D8
74    &gfx_display_ctx_d3d8,
75 #endif
76 #ifdef HAVE_D3D9
77    &gfx_display_ctx_d3d9,
78 #endif
79 #ifdef HAVE_D3D10
80    &gfx_display_ctx_d3d10,
81 #endif
82 #ifdef HAVE_D3D11
83    &gfx_display_ctx_d3d11,
84 #endif
85 #ifdef HAVE_D3D12
86    &gfx_display_ctx_d3d12,
87 #endif
88 #ifdef HAVE_OPENGL
89    &gfx_display_ctx_gl,
90 #endif
91 #ifdef HAVE_OPENGL1
92    &gfx_display_ctx_gl1,
93 #endif
94 #ifdef HAVE_OPENGL_CORE
95    &gfx_display_ctx_gl_core,
96 #endif
97 #ifdef HAVE_VULKAN
98    &gfx_display_ctx_vulkan,
99 #endif
100 #ifdef HAVE_METAL
101    &gfx_display_ctx_metal,
102 #endif
103 #ifdef HAVE_VITA2D
104    &gfx_display_ctx_vita2d,
105 #endif
106 #ifdef _3DS
107    &gfx_display_ctx_ctr,
108 #endif
109 #ifdef WIIU
110    &gfx_display_ctx_wiiu,
111 #endif
112 #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
113 #ifdef HAVE_GDI
114    &gfx_display_ctx_gdi,
115 #endif
116 #endif
117    &gfx_display_ctx_null,
118    NULL,
119 };
120 
gfx_display_get_adjusted_scale(gfx_display_t * p_disp,float base_scale,float scale_factor,unsigned width)121 float gfx_display_get_adjusted_scale(
122       gfx_display_t *p_disp,
123       float base_scale, float scale_factor, unsigned width)
124 {
125    /* Apply user-set scaling factor */
126    float adjusted_scale   = base_scale * scale_factor;
127 #ifdef HAVE_OZONE
128    /* Ozone has a capped scale factor */
129    if (p_disp->menu_driver_id == MENU_DRIVER_ID_OZONE)
130    {
131       float new_width    = (float)width * 0.3333333f;
132       if (((float)OZONE_SIDEBAR_WIDTH * adjusted_scale)
133             > new_width)
134          adjusted_scale  = (new_width / (float)OZONE_SIDEBAR_WIDTH);
135    }
136 #endif
137 
138    /* Ensure final scale is 'sane' */
139    if (adjusted_scale <= 0.0001f)
140       return 1.0f;
141    return adjusted_scale;
142 }
143 
144 /* Check if the current menu driver is compatible
145  * with your video driver. */
gfx_display_check_compatibility(enum gfx_display_driver_type type,bool video_is_threaded)146 static bool gfx_display_check_compatibility(
147       enum gfx_display_driver_type type,
148       bool video_is_threaded)
149 {
150    const char *video_driver = video_driver_get_ident();
151 
152    switch (type)
153    {
154       case GFX_VIDEO_DRIVER_GENERIC:
155          return true;
156       case GFX_VIDEO_DRIVER_OPENGL:
157          if (string_is_equal(video_driver, "gl"))
158             return true;
159          break;
160       case GFX_VIDEO_DRIVER_OPENGL1:
161          if (string_is_equal(video_driver, "gl1"))
162             return true;
163          break;
164       case GFX_VIDEO_DRIVER_OPENGL_CORE:
165          if (string_is_equal(video_driver, "glcore"))
166             return true;
167          break;
168       case GFX_VIDEO_DRIVER_VULKAN:
169          if (string_is_equal(video_driver, "vulkan"))
170             return true;
171          break;
172       case GFX_VIDEO_DRIVER_METAL:
173          if (string_is_equal(video_driver, "metal"))
174             return true;
175          break;
176       case GFX_VIDEO_DRIVER_DIRECT3D8:
177          if (string_is_equal(video_driver, "d3d8"))
178             return true;
179          break;
180       case GFX_VIDEO_DRIVER_DIRECT3D9:
181          if (string_is_equal(video_driver, "d3d9"))
182             return true;
183          break;
184       case GFX_VIDEO_DRIVER_DIRECT3D10:
185          if (string_is_equal(video_driver, "d3d10"))
186             return true;
187          break;
188       case GFX_VIDEO_DRIVER_DIRECT3D11:
189          if (string_is_equal(video_driver, "d3d11"))
190             return true;
191          break;
192       case GFX_VIDEO_DRIVER_DIRECT3D12:
193          if (string_is_equal(video_driver, "d3d12"))
194             return true;
195          break;
196       case GFX_VIDEO_DRIVER_VITA2D:
197          if (string_is_equal(video_driver, "vita2d"))
198             return true;
199          break;
200       case GFX_VIDEO_DRIVER_CTR:
201          if (string_is_equal(video_driver, "ctr"))
202             return true;
203          break;
204       case GFX_VIDEO_DRIVER_WIIU:
205          if (string_is_equal(video_driver, "gx2"))
206             return true;
207          break;
208       case GFX_VIDEO_DRIVER_GDI:
209          if (string_is_equal(video_driver, "gdi"))
210             return true;
211          break;
212       case GFX_VIDEO_DRIVER_SWITCH:
213          if (string_is_equal(video_driver, "switch"))
214             return true;
215          break;
216    }
217 
218    return false;
219 }
220 
gfx_display_get_dpi_scale_internal(unsigned width,unsigned height)221 float gfx_display_get_dpi_scale_internal(
222       unsigned width, unsigned height)
223 {
224    float dpi;
225    float diagonal_pixels;
226    float pixel_scale;
227    static unsigned last_width  = 0;
228    static unsigned last_height = 0;
229    static float scale          = 0.0f;
230    static bool scale_cached    = false;
231    gfx_ctx_metrics_t metrics;
232 
233    if (scale_cached &&
234        (width == last_width) &&
235        (height == last_height))
236       return scale;
237 
238    /* Determine the diagonal 'size' of the display
239     * (or window) in terms of pixels */
240    diagonal_pixels = (float)sqrt(
241          (double)((width * width) + (height * height)));
242 
243    /* TODO/FIXME: On Mac, calling video_context_driver_get_metrics()
244     * here causes RetroArch to crash (EXC_BAD_ACCESS). This is
245     * unfortunate, and needs to be fixed at the gfx context driver
246     * level. Until this is done, all we can do is fallback to using
247     * the old legacy 'magic number' scaling on Mac platforms. */
248 #if !defined(HAVE_COCOATOUCH) && (defined(HAVE_COCOA) || defined(HAVE_COCOA_METAL))
249    if (true)
250    {
251       scale        = (diagonal_pixels / 6.5f) / 212.0f;
252       scale_cached = true;
253       last_width   = width;
254       last_height  = height;
255       return scale;
256    }
257 #endif
258 
259    /* Get pixel scale relative to baseline 1080p display */
260    pixel_scale = diagonal_pixels / DIAGONAL_PIXELS_1080P;
261 
262    /* Attempt to get display DPI */
263    metrics.type  = DISPLAY_METRIC_DPI;
264    metrics.value = &dpi;
265 
266    if (video_context_driver_get_metrics(&metrics) && (dpi > 0.0f))
267    {
268       float display_size;
269       float dpi_scale;
270 
271 #if defined(ANDROID) || defined(HAVE_COCOATOUCH)
272       /* Android/iOS devices tell complete lies when
273        * reporting DPI values. From the Android devices
274        * I've had access to, the DPI is generally
275        * overestimated by 17%. All we can do is apply
276        * a blind correction factor... */
277       dpi *= 0.83f;
278 #endif
279 
280       /* Note: If we are running in windowed mode, this
281        * 'display size' is actually the window size - which
282        * kinda makes a mess of everything. Since we cannot
283        * get fullscreen resolution when running in windowed
284        * mode, there is nothing we can do about this. So just
285        * treat the window as a display, and hope for the best... */
286       display_size = diagonal_pixels / dpi;
287       dpi_scale    = dpi / REFERENCE_DPI;
288 
289       /* Note: We have tried leveraging every possible metric
290        * (and numerous studies on TV/monitor/mobile device
291        * usage habits) to determine an appropriate auto scaling
292        * factor. *None of these 'smart'/technical methods work
293        * consistently in the real world* - there is simply too
294        * much variance.
295        * So instead we have implemented a very fuzzy/loose
296        * method which is crude as can be, but actually has
297        * some semblance of usability... */
298 
299       if (display_size > 24.0f)
300       {
301          /* DPI scaling fails miserably when using large
302           * displays. Having a UI element that's 1 inch high
303           * on all screens might seem like a good idea - until
304           * you realise that a HTPC user is probably sitting
305           * several metres from their TV, which makes something
306           * 1 inch high virtually invisible.
307           * So we make some assumptions:
308           * - Normal size displays <= 24 inches are probably
309           *   PC monitors, with an eye-to-screen distance of
310           *   1 arm length. Under these conditions, fixed size
311           *   (DPI scaled) UI elements should be visible for most
312           *   users
313           * - Large displays > 24 inches start to encroach on
314           *   TV territory. Once we start working with TVs, we
315           *   have to consider users sitting on a couch - and
316           *   in this situation, we fall back to the age-old
317           *   standard of UI elements occupying a fixed fraction
318           *   of the display size (i.e. just look at the menu of
319           *   any console system for the past decade)
320           * - 24 -> 32 inches is a grey area, where the display
321           *   might be a monitor or a TV. Above 32 inches, a TV
322           *   is almost a certainty. So we simply lerp between
323           *   dpi scaling and pixel scaling as the display size
324           *   increases from 24 to 32 */
325          float fraction  = (display_size > 32.0f) ? 32.0f : display_size;
326          fraction       -= 24.0f;
327          fraction       /= (32.0f - 24.0f);
328 
329          scale           =   ((1.0f - fraction) * dpi_scale)
330                            + (fraction * pixel_scale);
331       }
332       else if (display_size < 12.0f)
333       {
334          /* DPI scaling also fails when using very small
335           * displays - i.e. mobile devices (tablets/phones).
336           * That 1 inch UI element is going to look pretty
337           * dumb on a 5 inch screen in landscape orientation...
338           * We're essentially in the opposite situation to the
339           * TV case above, and it turns out that a similar
340           * solution provides relief: as screen size reduces
341           * from 12 inches to zero, we lerp from dpi scaling
342           * to pixel scaling */
343          float fraction = display_size / 12.0f;
344 
345          scale          =   ((1.0f - fraction) * pixel_scale)
346                           + (fraction * dpi_scale);
347       }
348       else
349          scale          = dpi_scale;
350    }
351    /* If DPI retrieval is unsupported, all we can do
352     * is use the raw pixel scale */
353    else
354       scale             = pixel_scale;
355 
356    scale_cached         = true;
357    last_width           = width;
358    last_height          = height;
359 
360    return scale;
361 }
362 
gfx_display_get_dpi_scale(gfx_display_t * p_disp,void * settings_data,unsigned width,unsigned height)363 float gfx_display_get_dpi_scale(
364       gfx_display_t *p_disp,
365       void *settings_data,
366       unsigned width, unsigned height)
367 {
368    static unsigned last_width                          = 0;
369    static unsigned last_height                         = 0;
370    static float scale                                  = 0.0f;
371    static bool scale_cached                            = false;
372    bool scale_updated                                  = false;
373    static float last_menu_scale_factor                 = 0.0f;
374    static enum menu_driver_id_type last_menu_driver_id = MENU_DRIVER_ID_UNKNOWN;
375    static float adjusted_scale                         = 1.0f;
376    settings_t *settings                                = (settings_t*)settings_data;
377    float menu_scale_factor                             = settings->floats.menu_scale_factor;
378 
379    /* Scale is based on display metrics - these are a fixed
380     * hardware property. To minimise performance overheads
381     * we therefore only call video_context_driver_get_metrics()
382     * on first run, or when the current video resolution changes */
383    if (!scale_cached ||
384        (width  != last_width) ||
385        (height != last_height))
386    {
387       scale         = gfx_display_get_dpi_scale_internal(width, height);
388       scale_cached  = true;
389       scale_updated = true;
390       last_width    = width;
391       last_height   = height;
392    }
393 
394    /* Adjusted scale calculation may also be slow, so
395     * only update if something changes */
396    if (scale_updated ||
397        (menu_scale_factor != last_menu_scale_factor) ||
398        (p_disp->menu_driver_id != last_menu_driver_id))
399    {
400       adjusted_scale         = gfx_display_get_adjusted_scale(
401             p_disp,
402             scale, menu_scale_factor, width);
403       last_menu_scale_factor = menu_scale_factor;
404       last_menu_driver_id    = p_disp->menu_driver_id;
405    }
406 
407    return adjusted_scale;
408 }
409 
410 /* Begin scissoring operation */
gfx_display_scissor_begin(gfx_display_t * p_disp,void * userdata,unsigned video_width,unsigned video_height,int x,int y,unsigned width,unsigned height)411 void gfx_display_scissor_begin(
412       gfx_display_t *p_disp,
413       void *userdata,
414       unsigned video_width,
415       unsigned video_height,
416       int x, int y, unsigned width, unsigned height)
417 {
418    gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
419    if (dispctx && dispctx->scissor_begin)
420    {
421       if (y < 0)
422       {
423          if (height < (unsigned)(-y))
424             height = 0;
425          else
426             height += y;
427          y = 0;
428       }
429       if (x < 0)
430       {
431          if (width < (unsigned)(-x))
432             width = 0;
433          else
434             width += x;
435          x = 0;
436       }
437       if (y >= (int)video_height)
438       {
439          height = 0;
440          y = 0;
441       }
442       if (x >= (int)video_width)
443       {
444          width = 0;
445          x = 0;
446       }
447       if ((y + height) > video_height)
448          height = video_height - y;
449       if ((x + width) > video_width)
450          width = video_width - x;
451 
452       dispctx->scissor_begin(userdata,
453             video_width, video_height,
454             x, y, width, height);
455    }
456 }
457 
gfx_display_font_file(gfx_display_t * p_disp,char * fontpath,float menu_font_size,bool is_threaded)458 font_data_t *gfx_display_font_file(
459       gfx_display_t *p_disp,
460       char* fontpath, float menu_font_size, bool is_threaded)
461 {
462    font_data_t            *font_data = NULL;
463    float                  font_size  = menu_font_size;
464    gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
465 
466    if (!dispctx)
467       return NULL;
468 
469    /* Font size must be at least 2, or font_init_first()
470     * will generate a heap-buffer-overflow when using
471     * many font drivers */
472    if (font_size < 2.0f)
473       font_size = 2.0f;
474 
475    if (!dispctx->font_init_first((void**)&font_data,
476             video_driver_get_ptr(),
477             fontpath, font_size, is_threaded))
478       return NULL;
479 
480    return font_data;
481 }
482 
gfx_display_draw_bg(gfx_display_t * p_disp,gfx_display_ctx_draw_t * draw,void * userdata,bool add_opacity_to_wallpaper,float override_opacity)483 void gfx_display_draw_bg(
484       gfx_display_t *p_disp,
485       gfx_display_ctx_draw_t *draw,
486       void *userdata, bool add_opacity_to_wallpaper,
487       float override_opacity)
488 {
489    static struct video_coords coords;
490    const float           *new_vertex = NULL;
491    const float        *new_tex_coord = NULL;
492    gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
493    if (!dispctx || !draw)
494       return;
495 
496    if (draw->vertex)
497       new_vertex                     = draw->vertex;
498    else if (dispctx->get_default_vertices)
499       new_vertex                     = dispctx->get_default_vertices();
500 
501    if (draw->tex_coord)
502       new_tex_coord                  = draw->tex_coord;
503    else if (dispctx->get_default_tex_coords)
504       new_tex_coord                  = dispctx->get_default_tex_coords();
505 
506    coords.vertices                   = (unsigned)draw->vertex_count;
507    coords.vertex                     = new_vertex;
508    coords.tex_coord                  = new_tex_coord;
509    coords.lut_tex_coord              = new_tex_coord;
510    coords.color                      = (const float*)draw->color;
511 
512    draw->coords                      = &coords;
513    draw->scale_factor                = 1.0f;
514    draw->rotation                    = 0.0f;
515 
516    if (draw->texture)
517       add_opacity_to_wallpaper       = true;
518    else
519       draw->texture                  = gfx_display_white_texture;
520 
521    if (add_opacity_to_wallpaper)
522       gfx_display_set_alpha(draw->color, override_opacity);
523 
524    if (dispctx->get_default_mvp)
525       draw->matrix_data = (math_matrix_4x4*)dispctx->get_default_mvp(
526             userdata);
527 }
528 
gfx_display_draw_quad(gfx_display_t * p_disp,void * data,unsigned video_width,unsigned video_height,int x,int y,unsigned w,unsigned h,unsigned width,unsigned height,float * color)529 void gfx_display_draw_quad(
530       gfx_display_t *p_disp,
531       void *data,
532       unsigned video_width,
533       unsigned video_height,
534       int x, int y, unsigned w, unsigned h,
535       unsigned width, unsigned height,
536       float *color)
537 {
538    gfx_display_ctx_draw_t draw;
539    struct video_coords coords;
540    gfx_display_ctx_driver_t
541       *dispctx             = p_disp->dispctx;
542 
543    if (w == 0 || h == 0)
544       return;
545    if (!dispctx)
546       return;
547 
548    coords.vertices      = 4;
549    coords.vertex        = NULL;
550    coords.tex_coord     = NULL;
551    coords.lut_tex_coord = NULL;
552    coords.color         = color;
553 
554    draw.x               = x;
555    draw.y               = (int)height - y - (int)h;
556    draw.width           = w;
557    draw.height          = h;
558    draw.coords          = &coords;
559    draw.matrix_data     = NULL;
560    draw.texture         = gfx_display_white_texture;
561    draw.prim_type       = GFX_DISPLAY_PRIM_TRIANGLESTRIP;
562    draw.pipeline_id     = 0;
563    draw.scale_factor    = 1.0f;
564    draw.rotation        = 0.0f;
565 
566    if (dispctx->blend_begin)
567       dispctx->blend_begin(data);
568    if (dispctx->draw)
569       dispctx->draw(&draw, data, video_width, video_height);
570    if (dispctx->blend_end)
571       dispctx->blend_end(data);
572 }
573 
gfx_display_draw_polygon(gfx_display_t * p_disp,void * userdata,unsigned video_width,unsigned video_height,int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,unsigned width,unsigned height,float * color)574 void gfx_display_draw_polygon(
575       gfx_display_t *p_disp,
576       void *userdata,
577       unsigned video_width,
578       unsigned video_height,
579       int x1, int y1,
580       int x2, int y2,
581       int x3, int y3,
582       int x4, int y4,
583       unsigned width, unsigned height,
584       float *color)
585 {
586    float vertex[8];
587    gfx_display_ctx_draw_t draw;
588    struct video_coords coords;
589    gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
590 
591    if (width == 0 || height == 0)
592       return;
593    if (!dispctx)
594       return;
595 
596    vertex[0]             = x1 / (float)width;
597    vertex[1]             = y1 / (float)height;
598    vertex[2]             = x2 / (float)width;
599    vertex[3]             = y2 / (float)height;
600    vertex[4]             = x3 / (float)width;
601    vertex[5]             = y3 / (float)height;
602    vertex[6]             = x4 / (float)width;
603    vertex[7]             = y4 / (float)height;
604 
605    coords.vertices       = 4;
606    coords.vertex         = &vertex[0];
607    coords.tex_coord      = NULL;
608    coords.lut_tex_coord  = NULL;
609    coords.color          = color;
610 
611    draw.x                = 0;
612    draw.y                = 0;
613    draw.width            = width;
614    draw.height           = height;
615    draw.coords           = &coords;
616    draw.matrix_data      = NULL;
617    draw.texture          = gfx_display_white_texture;
618    draw.prim_type        = GFX_DISPLAY_PRIM_TRIANGLESTRIP;
619    draw.pipeline_id      = 0;
620    draw.scale_factor     = 1.0f;
621    draw.rotation         = 0.0f;
622 
623    if (dispctx->blend_begin)
624       dispctx->blend_begin(userdata);
625    if (dispctx->draw)
626       dispctx->draw(&draw, userdata, video_width, video_height);
627    if (dispctx->blend_end)
628       dispctx->blend_end(userdata);
629 }
630 
gfx_display_draw_texture(gfx_display_t * p_disp,gfx_display_ctx_driver_t * dispctx,void * userdata,unsigned video_width,unsigned video_height,int x,int y,unsigned w,unsigned h,unsigned width,unsigned height,float * color,uintptr_t texture,gfx_display_ctx_draw_t * draw)631 static void gfx_display_draw_texture(
632       gfx_display_t *p_disp,
633       gfx_display_ctx_driver_t *dispctx,
634       void *userdata,
635       unsigned video_width,
636       unsigned video_height,
637       int x, int y, unsigned w, unsigned h,
638       unsigned width, unsigned height,
639       float *color, uintptr_t texture,
640       gfx_display_ctx_draw_t *draw
641       )
642 {
643    if (w == 0 || h == 0)
644       return;
645    if (!dispctx || !dispctx->draw)
646       return;
647 
648    draw->width              = w;
649    draw->height             = h;
650    draw->prim_type          = GFX_DISPLAY_PRIM_TRIANGLESTRIP;
651    draw->pipeline_id        = 0;
652    draw->texture            = texture;
653    draw->x                  = x;
654    draw->y                  = height - y;
655 
656    dispctx->draw(draw, userdata, video_width, video_height);
657 }
658 
659 /* Draw the texture split into 9 sections, without scaling the corners.
660  * The middle sections will only scale in the X axis, and the side
661  * sections will only scale in the Y axis. */
gfx_display_draw_texture_slice(gfx_display_t * p_disp,void * userdata,unsigned video_width,unsigned video_height,int x,int y,unsigned w,unsigned h,unsigned new_w,unsigned new_h,unsigned width,unsigned height,float * color,unsigned offset,float scale_factor,uintptr_t texture)662 void gfx_display_draw_texture_slice(
663       gfx_display_t *p_disp,
664       void *userdata,
665       unsigned video_width,
666       unsigned video_height,
667       int x, int y, unsigned w, unsigned h,
668       unsigned new_w, unsigned new_h,
669       unsigned width, unsigned height,
670       float *color, unsigned offset, float scale_factor, uintptr_t texture)
671 {
672    gfx_display_ctx_draw_t draw;
673    gfx_display_ctx_rotate_draw_t rotate_draw;
674    struct video_coords coords;
675    math_matrix_4x4 mymat;
676    gfx_display_ctx_driver_t
677       *dispctx              = p_disp->dispctx;
678    float V_BL[2], V_BR[2], V_TL[2], V_TR[2], T_BL[2], T_BR[2], T_TL[2], T_TR[2];
679    /* To prevent visible seams between the corners and
680     * middle segments of the sliced texture, the texture
681     * must be scaled such that its effective size (before
682     * expansion of the middle segments) is no greater than
683     * the requested display size.
684     * > This is consequence of the way textures are rendered
685     *   in hardware...
686     * > Whenever an image is scaled, the colours at the
687     *   transparent edges get interpolated, which means
688     *   the colours of the transparent pixels themselves bleed
689     *   into the visible area.
690     * > This effectively 'blurs' anything that gets scaled
691     *   [SIDE NOTE: this causes additional issues if the transparent
692     *    pixels have the wrong colour - i.e. if they are black,
693     *    every edge gets a nasty dark border...]
694     * > This burring is a problem because (by design) the corners
695     *   of the sliced texture are drawn at native resolution,
696     *   whereas the middle segments are stretched to fit the
697     *   requested dimensions. Consequently, the corners are sharp
698     *   while the middle segments are blurred.
699     * > When *upscaling* the middle segments (i.e. display size
700     *   greater than texture size), the visible effects of this
701     *   are mostly imperceptible.
702     * > When *downscaling* them, however, the interpolation effects
703     *   completely dominate the output image - creating an ugly
704     *   transition between the corners and middle parts.
705     * > Since this is a property of hardware rendering, it is not
706     *   practical to fix this 'properly'...
707     * > However: An effective workaround is to force downscaling of
708     *   the entire texture (including corners) whenever the
709     *   requested display size is less than the texture dimensions.
710     * > This blurs the corners enough that the corner/middle
711     *   transitions are essentially invisible. */
712    float max_scale_w = (float)new_w / (float)w;
713    float max_scale_h = (float)new_h / (float)h;
714    /* Find the minimum of scale_factor, max_scale_w, max_scale_h */
715    float slice_scale = (scale_factor < max_scale_w) ?
716          (scale_factor < max_scale_h) ? scale_factor : max_scale_h :
717          (max_scale_w  < max_scale_h) ? max_scale_w  : max_scale_h;
718 
719    /* need space for the coordinates of two triangles in a strip,
720     * so 8 vertices */
721    float tex_coord[8];
722    float vert_coord[8];
723    static float colors[16] = {
724       1.0f, 1.0f, 1.0f, 1.0f,
725       1.0f, 1.0f, 1.0f, 1.0f,
726       1.0f, 1.0f, 1.0f, 1.0f,
727       1.0f, 1.0f, 1.0f, 1.0f
728    };
729 
730    /* normalized width/height of the amount to offset from the corners,
731     * for both the vertex and texture coordinates */
732    float vert_woff   = (offset * slice_scale) / (float)width;
733    float vert_hoff   = (offset * slice_scale) / (float)height;
734    float tex_woff    = offset / (float)w;
735    float tex_hoff    = offset / (float)h;
736 
737    /* the width/height of the middle sections of both the scaled and original image */
738    float vert_scaled_mid_width  = (new_w - (offset * slice_scale * 2))
739       / (float)width;
740    float vert_scaled_mid_height = (new_h - (offset * slice_scale * 2))
741       / (float)height;
742    float tex_mid_width          = (w - (offset * 2)) / (float)w;
743    float tex_mid_height         = (h - (offset * 2)) / (float)h;
744 
745    /* normalized coordinates for the start position of the image */
746    float norm_x                 = x / (float)width;
747    float norm_y                 = (height - y) / (float)height;
748 
749    if (width == 0 || height == 0)
750       return;
751    if (!dispctx || !dispctx->draw)
752       return;
753 
754    /* the four vertices of the top-left corner of the image,
755     * used as a starting point for all the other sections */
756    V_BL[0] = norm_x;
757    V_BL[1] = norm_y;
758    V_BR[0] = norm_x + vert_woff;
759    V_BR[1] = norm_y;
760    V_TL[0] = norm_x;
761    V_TL[1] = norm_y + vert_hoff;
762    V_TR[0] = norm_x + vert_woff;
763    V_TR[1] = norm_y + vert_hoff;
764    T_BL[0] = 0.0f;
765    T_BL[1] = tex_hoff;
766    T_BR[0] = tex_woff;
767    T_BR[1] = tex_hoff;
768    T_TL[0] = 0.0f;
769    T_TL[1] = 0.0f;
770    T_TR[0] = tex_woff;
771    T_TR[1] = 0.0f;
772 
773    rotate_draw.matrix       = &mymat;
774    rotate_draw.rotation     = 0.0;
775    rotate_draw.scale_x      = 1.0;
776    rotate_draw.scale_y      = 1.0;
777    rotate_draw.scale_z      = 1;
778    rotate_draw.scale_enable = true;
779    coords.vertices          = 4;
780    coords.vertex            = vert_coord;
781    coords.tex_coord         = tex_coord;
782    coords.lut_tex_coord     = NULL;
783    draw.width               = width;
784    draw.height              = height;
785    draw.coords              = &coords;
786    draw.matrix_data         = &mymat;
787    draw.prim_type           = GFX_DISPLAY_PRIM_TRIANGLESTRIP;
788    draw.pipeline_id         = 0;
789    coords.color             = (const float*)(color == NULL ? colors : color);
790 
791    gfx_display_rotate_z(p_disp, &rotate_draw, userdata);
792 
793    draw.texture             = texture;
794    draw.x                   = 0;
795    draw.y                   = 0;
796 
797    /* vertex coords are specfied bottom-up in this order: BL BR TL TR */
798    /* texture coords are specfied top-down in this order: BL BR TL TR */
799 
800    /* If someone wants to change this to not draw several times, the
801     * coordinates will need to be modified because of the triangle strip usage. */
802 
803    /* top-left corner */
804    vert_coord[0] = V_BL[0];
805    vert_coord[1] = V_BL[1];
806    vert_coord[2] = V_BR[0];
807    vert_coord[3] = V_BR[1];
808    vert_coord[4] = V_TL[0];
809    vert_coord[5] = V_TL[1];
810    vert_coord[6] = V_TR[0];
811    vert_coord[7] = V_TR[1];
812 
813    tex_coord[0] = T_BL[0];
814    tex_coord[1] = T_BL[1];
815    tex_coord[2] = T_BR[0];
816    tex_coord[3] = T_BR[1];
817    tex_coord[4] = T_TL[0];
818    tex_coord[5] = T_TL[1];
819    tex_coord[6] = T_TR[0];
820    tex_coord[7] = T_TR[1];
821 
822    dispctx->draw(&draw, userdata, video_width, video_height);
823 
824    /* top-middle section */
825    vert_coord[0] = V_BL[0] + vert_woff;
826    vert_coord[1] = V_BL[1];
827    vert_coord[2] = V_BR[0] + vert_scaled_mid_width;
828    vert_coord[3] = V_BR[1];
829    vert_coord[4] = V_TL[0] + vert_woff;
830    vert_coord[5] = V_TL[1];
831    vert_coord[6] = V_TR[0] + vert_scaled_mid_width;
832    vert_coord[7] = V_TR[1];
833 
834    tex_coord[0] = T_BL[0] + tex_woff;
835    tex_coord[1] = T_BL[1];
836    tex_coord[2] = T_BR[0] + tex_mid_width;
837    tex_coord[3] = T_BR[1];
838    tex_coord[4] = T_TL[0] + tex_woff;
839    tex_coord[5] = T_TL[1];
840    tex_coord[6] = T_TR[0] + tex_mid_width;
841    tex_coord[7] = T_TR[1];
842 
843    dispctx->draw(&draw, userdata, video_width, video_height);
844 
845    /* top-right corner */
846    vert_coord[0] = V_BL[0] + vert_woff + vert_scaled_mid_width;
847    vert_coord[1] = V_BL[1];
848    vert_coord[2] = V_BR[0] + vert_scaled_mid_width + vert_woff;
849    vert_coord[3] = V_BR[1];
850    vert_coord[4] = V_TL[0] + vert_woff + vert_scaled_mid_width;
851    vert_coord[5] = V_TL[1];
852    vert_coord[6] = V_TR[0] + vert_scaled_mid_width + vert_woff;
853    vert_coord[7] = V_TR[1];
854 
855    tex_coord[0] = T_BL[0] + tex_woff + tex_mid_width;
856    tex_coord[1] = T_BL[1];
857    tex_coord[2] = T_BR[0] + tex_mid_width + tex_woff;
858    tex_coord[3] = T_BR[1];
859    tex_coord[4] = T_TL[0] + tex_woff + tex_mid_width;
860    tex_coord[5] = T_TL[1];
861    tex_coord[6] = T_TR[0] + tex_mid_width + tex_woff;
862    tex_coord[7] = T_TR[1];
863 
864    dispctx->draw(&draw, userdata, video_width, video_height);
865 
866    /* middle-left section */
867    vert_coord[0] = V_BL[0];
868    vert_coord[1] = V_BL[1] - vert_scaled_mid_height;
869    vert_coord[2] = V_BR[0];
870    vert_coord[3] = V_BR[1] - vert_scaled_mid_height;
871    vert_coord[4] = V_TL[0];
872    vert_coord[5] = V_TL[1] - vert_hoff;
873    vert_coord[6] = V_TR[0];
874    vert_coord[7] = V_TR[1] - vert_hoff;
875 
876    tex_coord[0] = T_BL[0];
877    tex_coord[1] = T_BL[1] + tex_mid_height;
878    tex_coord[2] = T_BR[0];
879    tex_coord[3] = T_BR[1] + tex_mid_height;
880    tex_coord[4] = T_TL[0];
881    tex_coord[5] = T_TL[1] + tex_hoff;
882    tex_coord[6] = T_TR[0];
883    tex_coord[7] = T_TR[1] + tex_hoff;
884 
885    dispctx->draw(&draw, userdata, video_width, video_height);
886 
887    /* center section */
888    vert_coord[0] = V_BL[0] + vert_woff;
889    vert_coord[1] = V_BL[1] - vert_scaled_mid_height;
890    vert_coord[2] = V_BR[0] + vert_scaled_mid_width;
891    vert_coord[3] = V_BR[1] - vert_scaled_mid_height;
892    vert_coord[4] = V_TL[0] + vert_woff;
893    vert_coord[5] = V_TL[1] - vert_hoff;
894    vert_coord[6] = V_TR[0] + vert_scaled_mid_width;
895    vert_coord[7] = V_TR[1] - vert_hoff;
896 
897    tex_coord[0] = T_BL[0] + tex_woff;
898    tex_coord[1] = T_BL[1] + tex_mid_height;
899    tex_coord[2] = T_BR[0] + tex_mid_width;
900    tex_coord[3] = T_BR[1] + tex_mid_height;
901    tex_coord[4] = T_TL[0] + tex_woff;
902    tex_coord[5] = T_TL[1] + tex_hoff;
903    tex_coord[6] = T_TR[0] + tex_mid_width;
904    tex_coord[7] = T_TR[1] + tex_hoff;
905 
906    dispctx->draw(&draw, userdata, video_width, video_height);
907 
908    /* middle-right section */
909    vert_coord[0] = V_BL[0] + vert_woff + vert_scaled_mid_width;
910    vert_coord[1] = V_BL[1] - vert_scaled_mid_height;
911    vert_coord[2] = V_BR[0] + vert_woff + vert_scaled_mid_width;
912    vert_coord[3] = V_BR[1] - vert_scaled_mid_height;
913    vert_coord[4] = V_TL[0] + vert_woff + vert_scaled_mid_width;
914    vert_coord[5] = V_TL[1] - vert_hoff;
915    vert_coord[6] = V_TR[0] + vert_woff + vert_scaled_mid_width;
916    vert_coord[7] = V_TR[1] - vert_hoff;
917 
918    tex_coord[0] = T_BL[0] + tex_woff + tex_mid_width;
919    tex_coord[1] = T_BL[1] + tex_mid_height;
920    tex_coord[2] = T_BR[0] + tex_woff + tex_mid_width;
921    tex_coord[3] = T_BR[1] + tex_mid_height;
922    tex_coord[4] = T_TL[0] + tex_woff + tex_mid_width;
923    tex_coord[5] = T_TL[1] + tex_hoff;
924    tex_coord[6] = T_TR[0] + tex_woff + tex_mid_width;
925    tex_coord[7] = T_TR[1] + tex_hoff;
926 
927    dispctx->draw(&draw, userdata, video_width, video_height);
928 
929    /* bottom-left corner */
930    vert_coord[0] = V_BL[0];
931    vert_coord[1] = V_BL[1] - vert_hoff - vert_scaled_mid_height;
932    vert_coord[2] = V_BR[0];
933    vert_coord[3] = V_BR[1] - vert_hoff - vert_scaled_mid_height;
934    vert_coord[4] = V_TL[0];
935    vert_coord[5] = V_TL[1] - vert_hoff - vert_scaled_mid_height;
936    vert_coord[6] = V_TR[0];
937    vert_coord[7] = V_TR[1] - vert_hoff - vert_scaled_mid_height;
938 
939    tex_coord[0] = T_BL[0];
940    tex_coord[1] = T_BL[1] + tex_hoff + tex_mid_height;
941    tex_coord[2] = T_BR[0];
942    tex_coord[3] = T_BR[1] + tex_hoff + tex_mid_height;
943    tex_coord[4] = T_TL[0];
944    tex_coord[5] = T_TL[1] + tex_hoff + tex_mid_height;
945    tex_coord[6] = T_TR[0];
946    tex_coord[7] = T_TR[1] + tex_hoff + tex_mid_height;
947 
948    dispctx->draw(&draw, userdata, video_width, video_height);
949 
950    /* bottom-middle section */
951    vert_coord[0] = V_BL[0] + vert_woff;
952    vert_coord[1] = V_BL[1] - vert_hoff - vert_scaled_mid_height;
953    vert_coord[2] = V_BR[0] + vert_scaled_mid_width;
954    vert_coord[3] = V_BR[1] - vert_hoff - vert_scaled_mid_height;
955    vert_coord[4] = V_TL[0] + vert_woff;
956    vert_coord[5] = V_TL[1] - vert_hoff - vert_scaled_mid_height;
957    vert_coord[6] = V_TR[0] + vert_scaled_mid_width;
958    vert_coord[7] = V_TR[1] - vert_hoff - vert_scaled_mid_height;
959 
960    tex_coord[0] = T_BL[0] + tex_woff;
961    tex_coord[1] = T_BL[1] + tex_hoff + tex_mid_height;
962    tex_coord[2] = T_BR[0] + tex_mid_width;
963    tex_coord[3] = T_BR[1] + tex_hoff + tex_mid_height;
964    tex_coord[4] = T_TL[0] + tex_woff;
965    tex_coord[5] = T_TL[1] + tex_hoff + tex_mid_height;
966    tex_coord[6] = T_TR[0] + tex_mid_width;
967    tex_coord[7] = T_TR[1] + tex_hoff + tex_mid_height;
968 
969    dispctx->draw(&draw, userdata, video_width, video_height);
970 
971    /* bottom-right corner */
972    vert_coord[0] = V_BL[0] + vert_woff + vert_scaled_mid_width;
973    vert_coord[1] = V_BL[1] - vert_hoff - vert_scaled_mid_height;
974    vert_coord[2] = V_BR[0] + vert_scaled_mid_width + vert_woff;
975    vert_coord[3] = V_BR[1] - vert_hoff - vert_scaled_mid_height;
976    vert_coord[4] = V_TL[0] + vert_woff + vert_scaled_mid_width;
977    vert_coord[5] = V_TL[1] - vert_hoff - vert_scaled_mid_height;
978    vert_coord[6] = V_TR[0] + vert_scaled_mid_width + vert_woff;
979    vert_coord[7] = V_TR[1] - vert_hoff - vert_scaled_mid_height;
980 
981    tex_coord[0] = T_BL[0] + tex_woff + tex_mid_width;
982    tex_coord[1] = T_BL[1] + tex_hoff + tex_mid_height;
983    tex_coord[2] = T_BR[0] + tex_woff + tex_mid_width;
984    tex_coord[3] = T_BR[1] + tex_hoff + tex_mid_height;
985    tex_coord[4] = T_TL[0] + tex_woff + tex_mid_width;
986    tex_coord[5] = T_TL[1] + tex_hoff + tex_mid_height;
987    tex_coord[6] = T_TR[0] + tex_woff + tex_mid_width;
988    tex_coord[7] = T_TR[1] + tex_hoff + tex_mid_height;
989 
990    dispctx->draw(&draw, userdata, video_width, video_height);
991 }
992 
gfx_display_rotate_z(gfx_display_t * p_disp,gfx_display_ctx_rotate_draw_t * draw,void * data)993 void gfx_display_rotate_z(gfx_display_t *p_disp,
994       gfx_display_ctx_rotate_draw_t *draw, void *data)
995 {
996    math_matrix_4x4 matrix_rotated, matrix_scaled;
997    math_matrix_4x4                *b = NULL;
998    gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
999 
1000    if (
1001          !draw                       ||
1002          !dispctx                  ||
1003          !dispctx->get_default_mvp ||
1004          dispctx->handles_transform
1005       )
1006       return;
1007 
1008    b = (math_matrix_4x4*)dispctx->get_default_mvp(data);
1009 
1010    if (!b)
1011       return;
1012 
1013    matrix_4x4_rotate_z(matrix_rotated, draw->rotation);
1014    matrix_4x4_multiply(*draw->matrix, matrix_rotated, *b);
1015 
1016    if (!draw->scale_enable)
1017       return;
1018 
1019    matrix_4x4_scale(matrix_scaled,
1020          draw->scale_x, draw->scale_y, draw->scale_z);
1021    matrix_4x4_multiply(*draw->matrix, matrix_scaled, *draw->matrix);
1022 }
1023 
1024 /*
1025  * Draw a hardware cursor on top of the screen for the mouse.
1026  */
gfx_display_draw_cursor(gfx_display_t * p_disp,void * userdata,unsigned video_width,unsigned video_height,bool cursor_visible,float * color,float cursor_size,uintptr_t texture,float x,float y,unsigned width,unsigned height)1027 void gfx_display_draw_cursor(
1028       gfx_display_t *p_disp,
1029       void *userdata,
1030       unsigned video_width,
1031       unsigned video_height,
1032       bool cursor_visible,
1033       float *color, float cursor_size, uintptr_t texture,
1034       float x, float y, unsigned width, unsigned height)
1035 {
1036    gfx_display_ctx_draw_t draw;
1037    struct video_coords coords;
1038    gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
1039 
1040    if (!dispctx)
1041       return;
1042 
1043    coords.vertices      = 4;
1044    coords.vertex        = NULL;
1045    coords.tex_coord     = NULL;
1046    coords.lut_tex_coord = NULL;
1047    coords.color         = (const float*)color;
1048 
1049    draw.x               = x - (cursor_size / 2);
1050    draw.y               = (int)height - y - (cursor_size / 2);
1051    draw.width           = cursor_size;
1052    draw.height          = cursor_size;
1053    draw.coords          = &coords;
1054    draw.matrix_data     = NULL;
1055    draw.texture         = texture;
1056    draw.prim_type       = GFX_DISPLAY_PRIM_TRIANGLESTRIP;
1057    draw.pipeline_id     = 0;
1058    draw.scale_factor    = 1.0f;
1059    draw.rotation        = 0.0f;
1060 
1061    if (dispctx->blend_begin)
1062       dispctx->blend_begin(userdata);
1063    if (dispctx->draw)
1064       dispctx->draw(&draw, userdata, video_width, video_height);
1065    if (dispctx->blend_end)
1066       dispctx->blend_end(userdata);
1067 }
1068 
1069 /* Setup: Initializes the font associated
1070  * to the menu driver */
gfx_display_font(gfx_display_t * p_disp,enum application_special_type type,float menu_font_size,bool is_threaded)1071 font_data_t *gfx_display_font(
1072       gfx_display_t *p_disp,
1073       enum application_special_type type,
1074       float menu_font_size,
1075       bool is_threaded)
1076 {
1077    char fontpath[PATH_MAX_LENGTH];
1078 
1079    fontpath[0] = '\0';
1080 
1081    fill_pathname_application_special(
1082          fontpath, sizeof(fontpath), type);
1083 
1084    return gfx_display_font_file(p_disp, fontpath, menu_font_size, is_threaded);
1085 }
1086 
1087 /* Returns the OSK key at a given position */
gfx_display_osk_ptr_at_pos(void * data,int x,int y,unsigned width,unsigned height)1088 int gfx_display_osk_ptr_at_pos(void *data, int x, int y,
1089       unsigned width, unsigned height)
1090 {
1091    unsigned i;
1092    int ptr_width  = width / 11;
1093    int ptr_height = height / 10;
1094 
1095    if (ptr_width >= ptr_height)
1096       ptr_width = ptr_height;
1097 
1098    for (i = 0; i < 44; i++)
1099    {
1100       int line_y    = (i / 11)*height/10.0;
1101       int ptr_x     = width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width;
1102       int ptr_y     = height/2.0 + ptr_height*1.5 + line_y - ptr_height;
1103 
1104       if (x > ptr_x && x < ptr_x + ptr_width
1105        && y > ptr_y && y < ptr_y + ptr_height)
1106          return i;
1107    }
1108 
1109    return -1;
1110 }
1111 
1112 /* Get the display framebuffer's size dimensions. */
gfx_display_get_fb_size(unsigned * fb_width,unsigned * fb_height,size_t * fb_pitch)1113 void gfx_display_get_fb_size(unsigned *fb_width,
1114       unsigned *fb_height, size_t *fb_pitch)
1115 {
1116    gfx_display_t *p_disp = disp_get_ptr();
1117    *fb_width             = p_disp->framebuf_width;
1118    *fb_height            = p_disp->framebuf_height;
1119    *fb_pitch             = p_disp->framebuf_pitch;
1120 }
1121 
1122 /* Set the display framebuffer's width. */
gfx_display_set_width(unsigned width)1123 void gfx_display_set_width(unsigned width)
1124 {
1125    gfx_display_t *p_disp  = disp_get_ptr();
1126    p_disp->framebuf_width = width;
1127 }
1128 
1129 /* Set the display framebuffer's height. */
gfx_display_set_height(unsigned height)1130 void gfx_display_set_height(unsigned height)
1131 {
1132    gfx_display_t *p_disp   = disp_get_ptr();
1133    p_disp->framebuf_height = height;
1134 }
1135 
gfx_display_set_framebuffer_pitch(size_t pitch)1136 void gfx_display_set_framebuffer_pitch(size_t pitch)
1137 {
1138    gfx_display_t *p_disp   = disp_get_ptr();
1139    p_disp->framebuf_pitch = pitch;
1140 }
1141 
gfx_display_set_msg_force(bool state)1142 void gfx_display_set_msg_force(bool state)
1143 {
1144    gfx_display_t *p_disp   = disp_get_ptr();
1145    p_disp->msg_force       = state;
1146 }
1147 
gfx_display_draw_keyboard(gfx_display_t * p_disp,void * userdata,unsigned video_width,unsigned video_height,uintptr_t hover_texture,const font_data_t * font,char * grid[],unsigned id,unsigned text_color)1148 void gfx_display_draw_keyboard(
1149       gfx_display_t *p_disp,
1150       void *userdata,
1151       unsigned video_width,
1152       unsigned video_height,
1153       uintptr_t hover_texture,
1154       const font_data_t *font,
1155       char *grid[], unsigned id,
1156       unsigned text_color)
1157 {
1158    unsigned i;
1159    int ptr_width, ptr_height;
1160    gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
1161 
1162    static float white[16] =  {
1163       1.00, 1.00, 1.00, 1.00,
1164       1.00, 1.00, 1.00, 1.00,
1165       1.00, 1.00, 1.00, 1.00,
1166       1.00, 1.00, 1.00, 1.00,
1167    };
1168    static float osk_dark[16] =  {
1169       0.00, 0.00, 0.00, 0.85,
1170       0.00, 0.00, 0.00, 0.85,
1171       0.00, 0.00, 0.00, 0.85,
1172       0.00, 0.00, 0.00, 0.85,
1173    };
1174    struct video_coords coords;
1175    gfx_display_ctx_draw_t draw;
1176    math_matrix_4x4 mymat;
1177    gfx_display_ctx_rotate_draw_t rotate_draw;
1178    rotate_draw.matrix       = &mymat;
1179    rotate_draw.rotation     = 0.0;
1180    rotate_draw.scale_x      = 1.0;
1181    rotate_draw.scale_y      = 1.0;
1182    rotate_draw.scale_z      = 1;
1183    rotate_draw.scale_enable = true;
1184 
1185    coords.vertices          = 4;
1186    coords.vertex            = NULL;
1187    coords.tex_coord         = NULL;
1188    coords.lut_tex_coord     = NULL;
1189    coords.color             = (const float*)&white[0];
1190 
1191    gfx_display_draw_quad(
1192          p_disp,
1193          userdata,
1194          video_width,
1195          video_height,
1196          0,
1197          video_height / 2.0,
1198          video_width,
1199          video_height / 2.0,
1200          video_width,
1201          video_height,
1202          &osk_dark[0]);
1203 
1204    ptr_width  = video_width  / 11;
1205    ptr_height = video_height / 10;
1206 
1207    if (ptr_width >= ptr_height)
1208       ptr_width = ptr_height;
1209 
1210    gfx_display_rotate_z(p_disp, &rotate_draw, userdata);
1211 
1212    draw.coords             = &coords;
1213    draw.matrix_data        = &mymat;
1214 
1215    for (i = 0; i < 44; i++)
1216    {
1217       int line_y     = (i / 11) * video_height / 10.0;
1218       unsigned color = 0xffffffff;
1219 
1220       if (i == id)
1221       {
1222          if (dispctx && dispctx->blend_begin)
1223             dispctx->blend_begin(userdata);
1224 
1225          gfx_display_draw_texture(
1226                p_disp,
1227                dispctx,
1228                userdata,
1229                video_width,
1230                video_height,
1231                video_width / 2.0 - (11 * ptr_width) / 2.0 + (i % 11)
1232                * ptr_width,
1233                video_height / 2.0 + ptr_height * 1.5 + line_y,
1234                ptr_width, ptr_height,
1235                video_width,
1236                video_height,
1237                &white[0],
1238                hover_texture,
1239                &draw
1240                );
1241 
1242          if (dispctx && dispctx->blend_end)
1243             dispctx->blend_end(userdata);
1244 
1245          color = text_color;
1246       }
1247 
1248       gfx_display_draw_text(font, grid[i],
1249             video_width/2.0 - (11*ptr_width)/2.0 + (i % 11)
1250             * ptr_width + ptr_width/2.0,
1251             video_height / 2.0 + ptr_height + line_y + font->size / 3,
1252             video_width,
1253             video_height,
1254             color,
1255             TEXT_ALIGN_CENTER,
1256             1.0f,
1257             false, 0, false);
1258    }
1259 }
1260 
1261 /* NOTE: Reads image from file */
gfx_display_reset_textures_list(const char * texture_path,const char * iconpath,uintptr_t * item,enum texture_filter_type filter_type,unsigned * width,unsigned * height)1262 bool gfx_display_reset_textures_list(
1263       const char *texture_path, const char *iconpath,
1264       uintptr_t *item, enum texture_filter_type filter_type,
1265       unsigned *width, unsigned *height)
1266 {
1267    char texpath[PATH_MAX_LENGTH];
1268    struct texture_image ti;
1269 
1270    texpath[0]                    = '\0';
1271 
1272    ti.width                      = 0;
1273    ti.height                     = 0;
1274    ti.pixels                     = NULL;
1275    ti.supports_rgba              = video_driver_supports_rgba();
1276 
1277    if (string_is_empty(texture_path))
1278       return false;
1279 
1280    fill_pathname_join(texpath, iconpath, texture_path, sizeof(texpath));
1281 
1282    if (!image_texture_load(&ti, texpath))
1283       return false;
1284 
1285    if (width)
1286       *width = ti.width;
1287 
1288    if (height)
1289       *height = ti.height;
1290 
1291    video_driver_texture_load(&ti,
1292          filter_type, item);
1293    image_texture_free(&ti);
1294 
1295    return true;
1296 }
1297 
1298 /* Teardown; deinitializes and frees all
1299  * fonts associated to the display driver */
gfx_display_font_free(font_data_t * font)1300 void gfx_display_font_free(font_data_t *font)
1301 {
1302    font_driver_free(font);
1303 }
1304 
gfx_display_init_white_texture(uintptr_t white_texture)1305 void gfx_display_init_white_texture(uintptr_t white_texture)
1306 {
1307    struct texture_image ti;
1308    static const uint8_t white_data[] = { 0xff, 0xff, 0xff, 0xff };
1309 
1310    ti.width  = 1;
1311    ti.height = 1;
1312    ti.pixels = (uint32_t*)&white_data;
1313 
1314    video_driver_texture_load(&ti,
1315          TEXTURE_FILTER_NEAREST, &gfx_display_white_texture);
1316 }
1317 
gfx_display_free(void)1318 void gfx_display_free(void)
1319 {
1320    gfx_display_t           *p_disp   = disp_get_ptr();
1321    video_coord_array_free(&p_disp->dispca);
1322 
1323    p_disp->msg_force           = false;
1324    p_disp->header_height       = 0;
1325    p_disp->framebuf_width      = 0;
1326    p_disp->framebuf_height     = 0;
1327    p_disp->framebuf_pitch      = 0;
1328    p_disp->has_windowed        = false;
1329    p_disp->dispctx             = NULL;
1330 }
1331 
gfx_display_init(void)1332 void gfx_display_init(void)
1333 {
1334    gfx_display_t       *p_disp   = disp_get_ptr();
1335    video_coord_array_t *p_dispca = &p_disp->dispca;
1336 
1337    p_disp->has_windowed          = video_driver_has_windowed();
1338    p_dispca->allocated           =  0;
1339 }
1340 
gfx_display_driver_exists(const char * s)1341 bool gfx_display_driver_exists(const char *s)
1342 {
1343    unsigned i;
1344    for (i = 0; i < ARRAY_SIZE(gfx_display_ctx_drivers); i++)
1345    {
1346       if (string_is_equal(s, gfx_display_ctx_drivers[i]->ident))
1347          return true;
1348    }
1349 
1350    return false;
1351 }
1352 
gfx_display_init_first_driver(gfx_display_t * p_disp,bool video_is_threaded)1353 bool gfx_display_init_first_driver(gfx_display_t *p_disp,
1354       bool video_is_threaded)
1355 {
1356    unsigned i;
1357 
1358    for (i = 0; gfx_display_ctx_drivers[i]; i++)
1359    {
1360       if (!gfx_display_check_compatibility(
1361                gfx_display_ctx_drivers[i]->type,
1362                video_is_threaded))
1363          continue;
1364 
1365       RARCH_LOG("[Display]: Found display driver: \"%s\".\n",
1366             gfx_display_ctx_drivers[i]->ident);
1367       p_disp->dispctx = gfx_display_ctx_drivers[i];
1368       return true;
1369    }
1370    return false;
1371 }
1372