diff --git a/gfx/gfx_display.c b/gfx/gfx_display.c index f507fe4e20..a466693a7e 100644 --- a/gfx/gfx_display.c +++ b/gfx/gfx_display.c @@ -1,1808 +1,1808 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * Copyright (C) 2011-2017 - Daniel De Matteis - * Copyright (C) 2016-2019 - Brad Parker - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ -#include "gfx_display.h" -#include "gfx_animation.h" - -#include "video_coord_array.h" -#include "../configuration.h" -#include "../verbosity.h" - -#define PARTICLES_COUNT 100 - -/* Number of pixels corner-to-corner on a 1080p - * display: - * > sqrt((1920 * 1920) + (1080 * 1080)) - * Note: This is a double, so no suffix */ -#define DIAGONAL_PIXELS_1080P 2202.90717008229831581901 - -/* Standard reference DPI value, used when determining - * DPI-aware menu scaling factors */ -#define REFERENCE_DPI 96.0f - -/* 'OZONE_SIDEBAR_WIDTH' must be kept in sync - * with ozone menu driver metrics */ -#define OZONE_SIDEBAR_WIDTH 408 - -static float osk_dark[16] = { - 0.00, 0.00, 0.00, 0.85, - 0.00, 0.00, 0.00, 0.85, - 0.00, 0.00, 0.00, 0.85, - 0.00, 0.00, 0.00, 0.85, -}; - -uintptr_t gfx_display_white_texture; - -static bool gfx_display_has_windowed = false; - -/* Width, height and pitch of the menu framebuffer */ -static unsigned gfx_display_framebuf_width = 0; -static unsigned gfx_display_framebuf_height = 0; -static size_t gfx_display_framebuf_pitch = 0; - -/* Height of the menu display header */ -static unsigned gfx_display_header_height = 0; - -static bool gfx_display_msg_force = false; -static bool gfx_display_framebuf_dirty = false; - -static enum menu_driver_id_type menu_driver_id = MENU_DRIVER_ID_UNKNOWN; - -static video_coord_array_t menu_disp_ca; - -static void *gfx_display_null_get_default_mvp(video_frame_info_t *video_info) { return NULL; } -static void gfx_display_null_blend_begin(video_frame_info_t *video_info) { } -static void gfx_display_null_blend_end(video_frame_info_t *video_info) { } -static void gfx_display_null_draw(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info) { } -static void gfx_display_null_draw_pipeline(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info) { } -static void gfx_display_null_viewport(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info) { } -static void gfx_display_null_restore_clear_color(void) { } -static void gfx_display_null_clear_color(gfx_display_ctx_clearcolor_t *clearcolor, video_frame_info_t *video_info) { } - -static bool gfx_display_null_font_init_first( - void **font_handle, void *video_data, - const char *font_path, float font_size, - bool is_threaded) -{ - font_data_t **handle = (font_data_t**)font_handle; - if ((*handle = font_driver_init_first(video_data, - font_path, font_size, true, - is_threaded, - FONT_DRIVER_RENDER_DONT_CARE))) - return true; - return false; -} - -static const float *gfx_display_null_get_default_vertices(void) -{ - static float dummy[16] = {0.0f}; - return &dummy[0]; -} - -static const float *gfx_display_null_get_default_tex_coords(void) -{ - static float dummy[16] = {0.0f}; - return &dummy[0]; -} - -gfx_display_ctx_driver_t gfx_display_ctx_null = { - gfx_display_null_draw, - gfx_display_null_draw_pipeline, - gfx_display_null_viewport, - gfx_display_null_blend_begin, - gfx_display_null_blend_end, - gfx_display_null_restore_clear_color, - gfx_display_null_clear_color, - gfx_display_null_get_default_mvp, - gfx_display_null_get_default_vertices, - gfx_display_null_get_default_tex_coords, - gfx_display_null_font_init_first, - GFX_VIDEO_DRIVER_GENERIC, - "null", - false, - NULL, - NULL -}; - -/* Menu display drivers */ -static gfx_display_ctx_driver_t *gfx_display_ctx_drivers[] = { -#ifdef HAVE_D3D8 - &gfx_display_ctx_d3d8, -#endif -#ifdef HAVE_D3D9 - &gfx_display_ctx_d3d9, -#endif -#ifdef HAVE_D3D10 - &gfx_display_ctx_d3d10, -#endif -#ifdef HAVE_D3D11 - &gfx_display_ctx_d3d11, -#endif -#ifdef HAVE_D3D12 - &gfx_display_ctx_d3d12, -#endif -#ifdef HAVE_OPENGL - &gfx_display_ctx_gl, -#endif -#ifdef HAVE_OPENGL1 - &gfx_display_ctx_gl1, -#endif -#ifdef HAVE_OPENGL_CORE - &gfx_display_ctx_gl_core, -#endif -#ifdef HAVE_VULKAN - &gfx_display_ctx_vulkan, -#endif -#ifdef HAVE_METAL - &gfx_display_ctx_metal, -#endif -#ifdef HAVE_VITA2D - &gfx_display_ctx_vita2d, -#endif -#ifdef _3DS - &gfx_display_ctx_ctr, -#endif -#ifdef WIIU - &gfx_display_ctx_wiiu, -#endif -#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__) -#ifdef HAVE_GDI - &gfx_display_ctx_gdi, -#endif -#endif - &gfx_display_ctx_null, - NULL, -}; - -static gfx_display_ctx_driver_t *menu_disp = NULL; - -static INLINE float gfx_display_scalef(float val, - float oldmin, float oldmax, float newmin, float newmax) -{ - return (((val - oldmin) * (newmax - newmin)) / (oldmax - oldmin)) + newmin; -} - -static INLINE float gfx_display_randf(float min, float max) -{ - return (rand() * ((max - min) / RAND_MAX)) + min; -} - -void gfx_display_set_driver_id(enum menu_driver_id_type type) -{ - menu_driver_id = type; -} - -enum menu_driver_id_type gfx_display_get_driver_id(void) -{ - return menu_driver_id; -} - -static float gfx_display_get_adjusted_scale_internal( - float base_scale, float scale_factor, unsigned width) -{ - /* Apply user-set scaling factor */ - float adjusted_scale = base_scale * scale_factor; - - /* Ozone has a capped scale factor */ - adjusted_scale = (menu_driver_id == MENU_DRIVER_ID_OZONE) ? - (((float)OZONE_SIDEBAR_WIDTH * adjusted_scale) > ((float)width * 0.3333333f) ? - ((float)width * 0.3333333f / (float)OZONE_SIDEBAR_WIDTH) : adjusted_scale) : - adjusted_scale; - - /* Ensure final scale is 'sane' */ - return (adjusted_scale > 0.0001f) ? adjusted_scale : 1.0f; -} - -float gfx_display_get_dpi_scale_internal(unsigned width, unsigned height) -{ - static unsigned last_width = 0; - static unsigned last_height = 0; - static float scale = 0.0f; - static bool scale_cached = false; - float diagonal_pixels; - float pixel_scale; - float dpi; - gfx_ctx_metrics_t metrics; - - if (scale_cached && - (width == last_width) && - (height == last_height)) - return scale; - - /* Determine the diagonal 'size' of the display - * (or window) in terms of pixels */ - diagonal_pixels = (float)sqrt( - (double)((width * width) + (height * height))); - - /* TODO/FIXME: On Mac, calling video_context_driver_get_metrics() - * here causes RetroArch to crash (EXC_BAD_ACCESS). This is - * unfortunate, and needs to be fixed at the gfx context driver - * level. Until this is done, all we can do is fallback to using - * the old legacy 'magic number' scaling on Mac platforms. */ -#if defined(HAVE_COCOA) || defined(HAVE_COCOA_METAL) - if (true) - { - scale = (diagonal_pixels / 6.5f) / 212.0f; - scale_cached = true; - last_width = width; - last_height = height; - return scale; - } -#endif - - /* Get pixel scale relative to baseline 1080p display */ - pixel_scale = diagonal_pixels / DIAGONAL_PIXELS_1080P; - - /* Attempt to get display DPI */ - metrics.type = DISPLAY_METRIC_DPI; - metrics.value = &dpi; - - if (video_context_driver_get_metrics(&metrics) && (dpi > 0.0f)) - { - float display_size; - float dpi_scale; - -#if defined(ANDROID) || defined(HAVE_COCOATOUCH) - /* Android/iOS devices tell complete lies when - * reporting DPI values. From the Android devices - * I've had access to, the DPI is generally - * overestimated by 17%. All we can do is apply - * a blind correction factor... */ - dpi = dpi * 0.83f; -#endif - - /* Note: If we are running in windowed mode, this - * 'display size' is actually the window size - which - * kinda makes a mess of everything. Since we cannot - * get fullscreen resolution when running in windowed - * mode, there is nothing we can do about this. So just - * treat the window as a display, and hope for the best... */ - display_size = diagonal_pixels / dpi; - dpi_scale = dpi / REFERENCE_DPI; - - /* Note: We have tried leveraging every possible metric - * (and numerous studies on TV/monitor/mobile device - * usage habits) to determine an appropriate auto scaling - * factor. *None of these 'smart'/technical methods work - * consistently in the real world* - there is simply too - * much variance. - * So instead we have implemented a very fuzzy/loose - * method which is crude as can be, but actually has - * some semblance of usability... */ - - if (display_size > 24.0f) - { - /* DPI scaling fails miserably when using large - * displays. Having a UI element that's 1 inch high - * on all screens might seem like a good idea - until - * you realise that a HTPC user is probably sitting - * several metres from their TV, which makes something - * 1 inch high virtually invisible. - * So we make some assumptions: - * - Normal size displays <= 24 inches are probably - * PC monitors, with an eye-to-screen distance of - * 1 arm length. Under these conditions, fixed size - * (DPI scaled) UI elements should be visible for most - * users - * - Large displays > 24 inches start to encroach on - * TV territory. Once we start working with TVs, we - * have to consider users sitting on a couch - and - * in this situation, we fall back to the age-old - * standard of UI elements occupying a fixed fraction - * of the display size (i.e. just look at the menu of - * any console system for the past decade) - * - 24 -> 32 inches is a grey area, where the display - * might be a monitor or a TV. Above 32 inches, a TV - * is almost a certainty. So we simply lerp between - * dpi scaling and pixel scaling as the display size - * increases from 24 to 32 */ - float fraction = (display_size > 32.0f) ? 32.0f : display_size; - fraction = fraction - 24.0f; - fraction = fraction / (32.0f - 24.0f); - - scale = ((1.0f - fraction) * dpi_scale) + (fraction * pixel_scale); - } - else if (display_size < 12.0f) - { - /* DPI scaling also fails when using very small - * displays - i.e. mobile devices (tablets/phones). - * That 1 inch UI element is going to look pretty - * dumb on a 5 inch screen in landscape orientation... - * We're essentially in the opposite situation to the - * TV case above, and it turns out that a similar - * solution provides relief: as screen size reduces - * from 12 inches to zero, we lerp from dpi scaling - * to pixel scaling */ - float fraction = display_size / 12.0f; - - scale = ((1.0f - fraction) * pixel_scale) + (fraction * dpi_scale); - } - else - scale = dpi_scale; - } - /* If DPI retrieval is unsupported, all we can do - * is use the raw pixel scale */ - else - scale = pixel_scale; - - scale_cached = true; - last_width = width; - last_height = height; - - return scale; -} - -float gfx_display_get_dpi_scale(unsigned width, unsigned height) -{ - static unsigned last_width = 0; - static unsigned last_height = 0; - static float scale = 0.0f; - static bool scale_cached = false; - bool scale_updated = false; - static float last_menu_scale_factor = 0.0f; - static enum menu_driver_id_type last_menu_driver_id = MENU_DRIVER_ID_UNKNOWN; - static float adjusted_scale = 1.0f; - settings_t *settings = config_get_ptr(); - float menu_scale_factor = settings->floats.menu_scale_factor; - - /* Scale is based on display metrics - these are a fixed - * hardware property. To minimise performance overheads - * we therefore only call video_context_driver_get_metrics() - * on first run, or when the current video resolution changes */ - if (!scale_cached || - (width != last_width) || - (height != last_height)) - { - scale = gfx_display_get_dpi_scale_internal(width, height); - scale_cached = true; - scale_updated = true; - last_width = width; - last_height = height; - } - - /* Adjusted scale calculation may also be slow, so - * only update if something changes */ - if (scale_updated || - (menu_scale_factor != last_menu_scale_factor) || - (menu_driver_id != last_menu_driver_id)) - { - adjusted_scale = gfx_display_get_adjusted_scale_internal(scale, menu_scale_factor, width); - last_menu_scale_factor = menu_scale_factor; - last_menu_driver_id = menu_driver_id; - } - - return adjusted_scale; -} - -float gfx_display_get_widget_dpi_scale(unsigned width, unsigned height) -{ - static unsigned last_width = 0; - static unsigned last_height = 0; - static float scale = 0.0f; - static bool scale_cached = false; - bool scale_updated = false; - static float last_menu_scale_factor = 0.0f; - static enum menu_driver_id_type last_menu_driver_id = MENU_DRIVER_ID_UNKNOWN; - static float adjusted_scale = 1.0f; - settings_t *settings = config_get_ptr(); - - /* When using RGUI, settings->floats.menu_scale_factor - * is ignored - * > If we are not using a widget scale factor override, - * just set menu_scale_factor to 1.0 */ - float menu_scale_factor = - settings->bools.menu_widget_scale_auto ? - ((menu_driver_id == MENU_DRIVER_ID_RGUI) ? - 1.0f : settings->floats.menu_scale_factor) : - settings->floats.menu_widget_scale_factor; - - /* Scale is based on display metrics - these are a fixed - * hardware property. To minimise performance overheads - * we therefore only call video_context_driver_get_metrics() - * on first run, or when the current video resolution changes */ - if (!scale_cached || - (width != last_width) || - (height != last_height)) - { - scale = gfx_display_get_dpi_scale_internal(width, height); - scale_cached = true; - scale_updated = true; - last_width = width; - last_height = height; - } - - /* Adjusted scale calculation may also be slow, so - * only update if something changes */ - if (scale_updated || - (menu_scale_factor != last_menu_scale_factor) || - (menu_driver_id != last_menu_driver_id)) - { - adjusted_scale = gfx_display_get_adjusted_scale_internal(scale, menu_scale_factor, width); - last_menu_scale_factor = menu_scale_factor; - last_menu_driver_id = menu_driver_id; - } - - return adjusted_scale; -} - -float gfx_display_get_widget_pixel_scale(unsigned width, unsigned height) -{ - static unsigned last_width = 0; - static unsigned last_height = 0; - static float scale = 0.0f; - static bool scale_cached = false; - bool scale_updated = false; - static float last_menu_scale_factor = 0.0f; - static enum menu_driver_id_type last_menu_driver_id = MENU_DRIVER_ID_UNKNOWN; - static float adjusted_scale = 1.0f; - settings_t *settings = config_get_ptr(); - - /* When using RGUI, settings->floats.menu_scale_factor - * is ignored - * > If we are not using a widget scale factor override, - * just set menu_scale_factor to 1.0 */ - float menu_scale_factor = - settings->bools.menu_widget_scale_auto ? - ((menu_driver_id == MENU_DRIVER_ID_RGUI) ? - 1.0f : settings->floats.menu_scale_factor) : - settings->floats.menu_widget_scale_factor; - - /* We need to perform a square root here, which - * can be slow on some platforms (not *slow*, but - * it involves enough work that it's worth trying - * to optimise). We therefore cache the pixel scale, - * and only update on first run or when the video - * size changes */ - if (!scale_cached || - (width != last_width) || - (height != last_height)) - { - /* Baseline reference is a 1080p display */ - scale = (float)( - sqrt((double)((width * width) + (height * height))) / - DIAGONAL_PIXELS_1080P); - - scale_cached = true; - scale_updated = true; - last_width = width; - last_height = height; - } - - /* Adjusted scale calculation may also be slow, so - * only update if something changes */ - if (scale_updated || - (menu_scale_factor != last_menu_scale_factor) || - (menu_driver_id != last_menu_driver_id)) - { - adjusted_scale = gfx_display_get_adjusted_scale_internal(scale, menu_scale_factor, width); - last_menu_scale_factor = menu_scale_factor; - last_menu_driver_id = menu_driver_id; - } - - return adjusted_scale; -} - -/* Ugh... Since we must now have independent scale - * factors for menus and widgets, and most of the internal - * scaling variables are cached/static, a huge amount of - * code duplication is required for the pixel_scale and - * dpi_scale functions. A necessary evil, I suppose... */ - -#if 0 -static float gfx_display_get_pixel_scale(unsigned width, unsigned height) -{ - static unsigned last_width = 0; - static unsigned last_height = 0; - static float scale = 0.0f; - static bool scale_cached = false; - bool scale_updated = false; - static float last_menu_scale_factor = 0.0f; - static enum menu_driver_id_type last_menu_driver_id = MENU_DRIVER_ID_UNKNOWN; - static float adjusted_scale = 1.0f; - settings_t *settings = config_get_ptr(); - float menu_scale_factor = settings->floats.menu_scale_factor; - - /* We need to perform a square root here, which - * can be slow on some platforms (not *slow*, but - * it involves enough work that it's worth trying - * to optimise). We therefore cache the pixel scale, - * and only update on first run or when the video - * size changes */ - if (!scale_cached || - (width != last_width) || - (height != last_height)) - { - /* Baseline reference is a 1080p display */ - scale = (float)( - sqrt((double)((width * width) + (height * height))) / - DIAGONAL_PIXELS_1080P); - - scale_cached = true; - scale_updated = true; - last_width = width; - last_height = height; - } - - /* Adjusted scale calculation may also be slow, so - * only update if something changes */ - if (scale_updated || - (menu_scale_factor != last_menu_scale_factor) || - (menu_driver_id != last_menu_driver_id)) - { - adjusted_scale = gfx_display_get_adjusted_scale_internal( - scale, menu_scale_factor, width); - last_menu_scale_factor = menu_scale_factor; - last_menu_driver_id = menu_driver_id; - } - - return adjusted_scale; -} -#endif - - -/* Check if the current menu driver is compatible - * with your video driver. */ -static bool gfx_display_check_compatibility( - enum gfx_display_driver_type type, - bool video_is_threaded) -{ - const char *video_driver = video_driver_get_ident(); - - switch (type) - { - case GFX_VIDEO_DRIVER_GENERIC: - return true; - case GFX_VIDEO_DRIVER_OPENGL: - if (string_is_equal(video_driver, "gl")) - return true; - break; - case GFX_VIDEO_DRIVER_OPENGL1: - if (string_is_equal(video_driver, "gl1")) - return true; - break; - case GFX_VIDEO_DRIVER_OPENGL_CORE: - if (string_is_equal(video_driver, "glcore")) - return true; - break; - case GFX_VIDEO_DRIVER_VULKAN: - if (string_is_equal(video_driver, "vulkan")) - return true; - break; - case GFX_VIDEO_DRIVER_METAL: - if (string_is_equal(video_driver, "metal")) - return true; - break; - case GFX_VIDEO_DRIVER_DIRECT3D8: - if (string_is_equal(video_driver, "d3d8")) - return true; - break; - case GFX_VIDEO_DRIVER_DIRECT3D9: - if (string_is_equal(video_driver, "d3d9")) - return true; - break; - case GFX_VIDEO_DRIVER_DIRECT3D10: - if (string_is_equal(video_driver, "d3d10")) - return true; - break; - case GFX_VIDEO_DRIVER_DIRECT3D11: - if (string_is_equal(video_driver, "d3d11")) - return true; - break; - case GFX_VIDEO_DRIVER_DIRECT3D12: - if (string_is_equal(video_driver, "d3d12")) - return true; - break; - case GFX_VIDEO_DRIVER_VITA2D: - if (string_is_equal(video_driver, "vita2d")) - return true; - break; - case GFX_VIDEO_DRIVER_CTR: - if (string_is_equal(video_driver, "ctr")) - return true; - break; - case GFX_VIDEO_DRIVER_WIIU: - if (string_is_equal(video_driver, "gx2")) - return true; - break; - case GFX_VIDEO_DRIVER_GDI: - if (string_is_equal(video_driver, "gdi")) - return true; - break; - case GFX_VIDEO_DRIVER_SWITCH: - if (string_is_equal(video_driver, "switch")) - return true; - break; - } - - return false; -} - -/* Reset the menu's coordinate array vertices. - * NOTE: Not every menu driver uses this. */ -void gfx_display_coords_array_reset(void) -{ - menu_disp_ca.coords.vertices = 0; -} - -video_coord_array_t *gfx_display_get_coords_array(void) -{ - return &menu_disp_ca; -} - - -/* Begin blending operation */ -void gfx_display_blend_begin(video_frame_info_t *video_info) -{ - if (menu_disp && menu_disp->blend_begin) - menu_disp->blend_begin(video_info); -} - -/* End blending operation */ -void gfx_display_blend_end(video_frame_info_t *video_info) -{ - if (menu_disp && menu_disp->blend_end) - menu_disp->blend_end(video_info); -} - -/* Begin scissoring operation */ -void gfx_display_scissor_begin(video_frame_info_t *video_info, int x, int y, unsigned width, unsigned height) -{ - if (menu_disp && menu_disp->scissor_begin) - { - if (y < 0) - { - if (height < (unsigned)(-y)) - height = 0; - else - height += y; - y = 0; - } - if (x < 0) - { - if (width < (unsigned)(-x)) - width = 0; - else - width += x; - x = 0; - } - if (y >= (int)video_info->height) - { - height = 0; - y = 0; - } - if (x >= (int)video_info->width) - { - width = 0; - x = 0; - } - if ((y + height) > video_info->height) - height = video_info->height - y; - if ((x + width) > video_info->width) - width = video_info->width - x; - - menu_disp->scissor_begin(video_info, x, y, width, height); - } -} - -/* End scissoring operation */ -void gfx_display_scissor_end(video_frame_info_t *video_info) -{ - if (menu_disp && menu_disp->scissor_end) - menu_disp->scissor_end(video_info); -} - -font_data_t *gfx_display_font_file( - char* fontpath, float menu_font_size, bool is_threaded) -{ - font_data_t *font_data = NULL; - - if (!menu_disp) - return NULL; - - if (!menu_disp->font_init_first((void**)&font_data, - video_driver_get_ptr(false), - fontpath, menu_font_size, is_threaded)) - return NULL; - - return font_data; -} - -bool gfx_display_restore_clear_color(void) -{ - if (!menu_disp || !menu_disp->restore_clear_color) - return false; - menu_disp->restore_clear_color(); - return true; -} - -void gfx_display_clear_color(gfx_display_ctx_clearcolor_t *color, - video_frame_info_t *video_info) -{ - if (menu_disp && menu_disp->clear_color) - menu_disp->clear_color(color, video_info); -} - -void gfx_display_draw(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info) -{ - if (!menu_disp || !draw || !menu_disp->draw) - return; - - if (draw->height <= 0) - return; - if (draw->width <= 0) - return; - menu_disp->draw(draw, video_info); -} - -void gfx_display_draw_blend(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info) -{ - if (!menu_disp || !draw || !menu_disp->draw) - return; - - if (draw->height <= 0) - return; - if (draw->width <= 0) - return; - gfx_display_blend_begin(video_info); - menu_disp->draw(draw, video_info); - gfx_display_blend_end(video_info); -} - -void gfx_display_draw_pipeline(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info) -{ - if (menu_disp && draw && menu_disp->draw_pipeline) - menu_disp->draw_pipeline(draw, video_info); -} - -void gfx_display_draw_bg(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info, bool add_opacity_to_wallpaper, - float override_opacity) -{ - static struct video_coords coords; - const float *new_vertex = NULL; - const float *new_tex_coord = NULL; - if (!menu_disp || !draw) - return; - - new_vertex = draw->vertex; - new_tex_coord = draw->tex_coord; - - if (!new_vertex) - new_vertex = menu_disp->get_default_vertices(); - if (!new_tex_coord) - new_tex_coord = menu_disp->get_default_tex_coords(); - - coords.vertices = (unsigned)draw->vertex_count; - coords.vertex = new_vertex; - coords.tex_coord = new_tex_coord; - coords.lut_tex_coord = new_tex_coord; - coords.color = (const float*)draw->color; - - draw->coords = &coords; - draw->scale_factor = 1.0f; - draw->rotation = 0.0f; - - if (draw->texture) - add_opacity_to_wallpaper = true; - - if (add_opacity_to_wallpaper) - gfx_display_set_alpha(draw->color, override_opacity); - - if (!draw->texture) - draw->texture = gfx_display_white_texture; - - if (menu_disp && menu_disp->get_default_mvp) - draw->matrix_data = (math_matrix_4x4*)menu_disp->get_default_mvp(video_info); -} - -void gfx_display_draw_gradient(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info) -{ - draw->texture = 0; - draw->x = 0; - draw->y = 0; - - gfx_display_draw_bg(draw, video_info, false, - video_info->menu_wallpaper_opacity); - gfx_display_draw(draw, video_info); -} - -void gfx_display_draw_quad( - video_frame_info_t *video_info, - int x, int y, unsigned w, unsigned h, - unsigned width, unsigned height, - float *color) -{ - gfx_display_ctx_draw_t draw; - struct video_coords coords; - - coords.vertices = 4; - coords.vertex = NULL; - coords.tex_coord = NULL; - coords.lut_tex_coord = NULL; - coords.color = color; - - if (menu_disp && menu_disp->blend_begin) - menu_disp->blend_begin(video_info); - - draw.x = x; - draw.y = (int)height - y - (int)h; - draw.width = w; - draw.height = h; - draw.coords = &coords; - draw.matrix_data = NULL; - draw.texture = gfx_display_white_texture; - draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; - draw.pipeline.id = 0; - draw.scale_factor = 1.0f; - draw.rotation = 0.0f; - - gfx_display_draw(&draw, video_info); - - if (menu_disp && menu_disp->blend_end) - menu_disp->blend_end(video_info); -} - -void gfx_display_draw_polygon( - video_frame_info_t *video_info, - int x1, int y1, - int x2, int y2, - int x3, int y3, - int x4, int y4, - unsigned width, unsigned height, - float *color) -{ - gfx_display_ctx_draw_t draw; - struct video_coords coords; - - float vertex[8]; - - vertex[0] = x1 / (float)width; - vertex[1] = y1 / (float)height; - vertex[2] = x2 / (float)width; - vertex[3] = y2 / (float)height; - vertex[4] = x3 / (float)width; - vertex[5] = y3 / (float)height; - vertex[6] = x4 / (float)width; - vertex[7] = y4 / (float)height; - - coords.vertices = 4; - coords.vertex = &vertex[0]; - coords.tex_coord = NULL; - coords.lut_tex_coord = NULL; - coords.color = color; - - if (menu_disp && menu_disp->blend_begin) - menu_disp->blend_begin(video_info); - - draw.x = 0; - draw.y = 0; - draw.width = width; - draw.height = height; - draw.coords = &coords; - draw.matrix_data = NULL; - draw.texture = gfx_display_white_texture; - draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; - draw.pipeline.id = 0; - draw.scale_factor = 1.0f; - draw.rotation = 0.0f; - - gfx_display_draw(&draw, video_info); - - if (menu_disp && menu_disp->blend_end) - menu_disp->blend_end(video_info); -} - -void gfx_display_draw_texture( - video_frame_info_t *video_info, - int x, int y, unsigned w, unsigned h, - unsigned width, unsigned height, - float *color, uintptr_t texture) -{ - gfx_display_ctx_draw_t draw; - gfx_display_ctx_rotate_draw_t rotate_draw; - struct video_coords coords; - math_matrix_4x4 mymat; - rotate_draw.matrix = &mymat; - rotate_draw.rotation = 0.0; - rotate_draw.scale_x = 1.0; - rotate_draw.scale_y = 1.0; - rotate_draw.scale_z = 1; - rotate_draw.scale_enable = true; - coords.vertices = 4; - coords.vertex = NULL; - coords.tex_coord = NULL; - coords.lut_tex_coord = NULL; - draw.width = w; - draw.height = h; - draw.coords = &coords; - draw.matrix_data = &mymat; - draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; - draw.pipeline.id = 0; - coords.color = (const float*)color; - - gfx_display_rotate_z(&rotate_draw, video_info); - - draw.texture = texture; - draw.x = x; - draw.y = height - y; - gfx_display_draw(&draw, video_info); -} - -/* Draw the texture split into 9 sections, without scaling the corners. - * The middle sections will only scale in the X axis, and the side - * sections will only scale in the Y axis. */ -void gfx_display_draw_texture_slice( - video_frame_info_t *video_info, - 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) -{ - gfx_display_ctx_draw_t draw; - gfx_display_ctx_rotate_draw_t rotate_draw; - struct video_coords coords; - math_matrix_4x4 mymat; - unsigned i; - 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]; - - /* need space for the coordinates of two triangles in a strip, so 8 vertices */ - float *tex_coord = (float*)malloc(8 * sizeof(float)); - float *vert_coord = (float*)malloc(8 * sizeof(float)); - float *colors = (float*)malloc(16 * sizeof(float)); - - /* normalized width/height of the amount to offset from the corners, - * for both the vertex and texture coordinates */ - float vert_woff = (offset * scale_factor) / (float)width; - float vert_hoff = (offset * scale_factor) / (float)height; - float tex_woff = offset / (float)w; - float tex_hoff = offset / (float)h; - - /* the width/height of the middle sections of both the scaled and original image */ - float vert_scaled_mid_width = (new_w - (offset * scale_factor * 2)) / (float)width; - float vert_scaled_mid_height = (new_h - (offset * scale_factor * 2)) / (float)height; - float tex_mid_width = (w - (offset * 2)) / (float)w; - float tex_mid_height = (h - (offset * 2)) / (float)h; - - /* normalized coordinates for the start position of the image */ - float norm_x = x / (float)width; - float norm_y = (height - y) / (float)height; - - /* the four vertices of the top-left corner of the image, - * used as a starting point for all the other sections */ - V_BL[0] = norm_x; - V_BL[1] = norm_y; - V_BR[0] = norm_x + vert_woff; - V_BR[1] = norm_y; - V_TL[0] = norm_x; - V_TL[1] = norm_y + vert_hoff; - V_TR[0] = norm_x + vert_woff; - V_TR[1] = norm_y + vert_hoff; - T_BL[0] = 0.0f; - T_BL[1] = tex_hoff; - T_BR[0] = tex_woff; - T_BR[1] = tex_hoff; - T_TL[0] = 0.0f; - T_TL[1] = 0.0f; - T_TR[0] = tex_woff; - T_TR[1] = 0.0f; - - for (i = 0; i < (16 * sizeof(float)) / sizeof(colors[0]); i++) - colors[i] = 1.0f; - - rotate_draw.matrix = &mymat; - rotate_draw.rotation = 0.0; - rotate_draw.scale_x = 1.0; - rotate_draw.scale_y = 1.0; - rotate_draw.scale_z = 1; - rotate_draw.scale_enable = true; - coords.vertices = 4; - coords.vertex = vert_coord; - coords.tex_coord = tex_coord; - coords.lut_tex_coord = NULL; - draw.width = width; - draw.height = height; - draw.coords = &coords; - draw.matrix_data = &mymat; - draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; - draw.pipeline.id = 0; - coords.color = (const float*)(color == NULL ? colors : color); - - gfx_display_rotate_z(&rotate_draw, video_info); - - draw.texture = texture; - draw.x = 0; - draw.y = 0; - - /* vertex coords are specfied bottom-up in this order: BL BR TL TR */ - /* texture coords are specfied top-down in this order: BL BR TL TR */ - - /* If someone wants to change this to not draw several times, the - * coordinates will need to be modified because of the triangle strip usage. */ - - /* top-left corner */ - vert_coord[0] = V_BL[0]; - vert_coord[1] = V_BL[1]; - vert_coord[2] = V_BR[0]; - vert_coord[3] = V_BR[1]; - vert_coord[4] = V_TL[0]; - vert_coord[5] = V_TL[1]; - vert_coord[6] = V_TR[0]; - vert_coord[7] = V_TR[1]; - - tex_coord[0] = T_BL[0]; - tex_coord[1] = T_BL[1]; - tex_coord[2] = T_BR[0]; - tex_coord[3] = T_BR[1]; - tex_coord[4] = T_TL[0]; - tex_coord[5] = T_TL[1]; - tex_coord[6] = T_TR[0]; - tex_coord[7] = T_TR[1]; - - gfx_display_draw(&draw, video_info); - - /* top-middle section */ - vert_coord[0] = V_BL[0] + vert_woff; - vert_coord[1] = V_BL[1]; - vert_coord[2] = V_BR[0] + vert_scaled_mid_width; - vert_coord[3] = V_BR[1]; - vert_coord[4] = V_TL[0] + vert_woff; - vert_coord[5] = V_TL[1]; - vert_coord[6] = V_TR[0] + vert_scaled_mid_width; - vert_coord[7] = V_TR[1]; - - tex_coord[0] = T_BL[0] + tex_woff; - tex_coord[1] = T_BL[1]; - tex_coord[2] = T_BR[0] + tex_mid_width; - tex_coord[3] = T_BR[1]; - tex_coord[4] = T_TL[0] + tex_woff; - tex_coord[5] = T_TL[1]; - tex_coord[6] = T_TR[0] + tex_mid_width; - tex_coord[7] = T_TR[1]; - - gfx_display_draw(&draw, video_info); - - /* top-right corner */ - vert_coord[0] = V_BL[0] + vert_woff + vert_scaled_mid_width; - vert_coord[1] = V_BL[1]; - vert_coord[2] = V_BR[0] + vert_scaled_mid_width + vert_woff; - vert_coord[3] = V_BR[1]; - vert_coord[4] = V_TL[0] + vert_woff + vert_scaled_mid_width; - vert_coord[5] = V_TL[1]; - vert_coord[6] = V_TR[0] + vert_scaled_mid_width + vert_woff; - vert_coord[7] = V_TR[1]; - - tex_coord[0] = T_BL[0] + tex_woff + tex_mid_width; - tex_coord[1] = T_BL[1]; - tex_coord[2] = T_BR[0] + tex_mid_width + tex_woff; - tex_coord[3] = T_BR[1]; - tex_coord[4] = T_TL[0] + tex_woff + tex_mid_width; - tex_coord[5] = T_TL[1]; - tex_coord[6] = T_TR[0] + tex_mid_width + tex_woff; - tex_coord[7] = T_TR[1]; - - gfx_display_draw(&draw, video_info); - - /* middle-left section */ - vert_coord[0] = V_BL[0]; - vert_coord[1] = V_BL[1] - vert_scaled_mid_height; - vert_coord[2] = V_BR[0]; - vert_coord[3] = V_BR[1] - vert_scaled_mid_height; - vert_coord[4] = V_TL[0]; - vert_coord[5] = V_TL[1] - vert_hoff; - vert_coord[6] = V_TR[0]; - vert_coord[7] = V_TR[1] - vert_hoff; - - tex_coord[0] = T_BL[0]; - tex_coord[1] = T_BL[1] + tex_mid_height; - tex_coord[2] = T_BR[0]; - tex_coord[3] = T_BR[1] + tex_mid_height; - tex_coord[4] = T_TL[0]; - tex_coord[5] = T_TL[1] + tex_hoff; - tex_coord[6] = T_TR[0]; - tex_coord[7] = T_TR[1] + tex_hoff; - - gfx_display_draw(&draw, video_info); - - /* center section */ - vert_coord[0] = V_BL[0] + vert_woff; - vert_coord[1] = V_BL[1] - vert_scaled_mid_height; - vert_coord[2] = V_BR[0] + vert_scaled_mid_width; - vert_coord[3] = V_BR[1] - vert_scaled_mid_height; - vert_coord[4] = V_TL[0] + vert_woff; - vert_coord[5] = V_TL[1] - vert_hoff; - vert_coord[6] = V_TR[0] + vert_scaled_mid_width; - vert_coord[7] = V_TR[1] - vert_hoff; - - tex_coord[0] = T_BL[0] + tex_woff; - tex_coord[1] = T_BL[1] + tex_mid_height; - tex_coord[2] = T_BR[0] + tex_mid_width; - tex_coord[3] = T_BR[1] + tex_mid_height; - tex_coord[4] = T_TL[0] + tex_woff; - tex_coord[5] = T_TL[1] + tex_hoff; - tex_coord[6] = T_TR[0] + tex_mid_width; - tex_coord[7] = T_TR[1] + tex_hoff; - - gfx_display_draw(&draw, video_info); - - /* middle-right section */ - vert_coord[0] = V_BL[0] + vert_woff + vert_scaled_mid_width; - vert_coord[1] = V_BL[1] - vert_scaled_mid_height; - vert_coord[2] = V_BR[0] + vert_woff + vert_scaled_mid_width; - vert_coord[3] = V_BR[1] - vert_scaled_mid_height; - vert_coord[4] = V_TL[0] + vert_woff + vert_scaled_mid_width; - vert_coord[5] = V_TL[1] - vert_hoff; - vert_coord[6] = V_TR[0] + vert_woff + vert_scaled_mid_width; - vert_coord[7] = V_TR[1] - vert_hoff; - - tex_coord[0] = T_BL[0] + tex_woff + tex_mid_width; - tex_coord[1] = T_BL[1] + tex_mid_height; - tex_coord[2] = T_BR[0] + tex_woff + tex_mid_width; - tex_coord[3] = T_BR[1] + tex_mid_height; - tex_coord[4] = T_TL[0] + tex_woff + tex_mid_width; - tex_coord[5] = T_TL[1] + tex_hoff; - tex_coord[6] = T_TR[0] + tex_woff + tex_mid_width; - tex_coord[7] = T_TR[1] + tex_hoff; - - gfx_display_draw(&draw, video_info); - - /* bottom-left corner */ - vert_coord[0] = V_BL[0]; - vert_coord[1] = V_BL[1] - vert_hoff - vert_scaled_mid_height; - vert_coord[2] = V_BR[0]; - vert_coord[3] = V_BR[1] - vert_hoff - vert_scaled_mid_height; - vert_coord[4] = V_TL[0]; - vert_coord[5] = V_TL[1] - vert_hoff - vert_scaled_mid_height; - vert_coord[6] = V_TR[0]; - vert_coord[7] = V_TR[1] - vert_hoff - vert_scaled_mid_height; - - tex_coord[0] = T_BL[0]; - tex_coord[1] = T_BL[1] + tex_hoff + tex_mid_height; - tex_coord[2] = T_BR[0]; - tex_coord[3] = T_BR[1] + tex_hoff + tex_mid_height; - tex_coord[4] = T_TL[0]; - tex_coord[5] = T_TL[1] + tex_hoff + tex_mid_height; - tex_coord[6] = T_TR[0]; - tex_coord[7] = T_TR[1] + tex_hoff + tex_mid_height; - - gfx_display_draw(&draw, video_info); - - /* bottom-middle section */ - vert_coord[0] = V_BL[0] + vert_woff; - vert_coord[1] = V_BL[1] - vert_hoff - vert_scaled_mid_height; - vert_coord[2] = V_BR[0] + vert_scaled_mid_width; - vert_coord[3] = V_BR[1] - vert_hoff - vert_scaled_mid_height; - vert_coord[4] = V_TL[0] + vert_woff; - vert_coord[5] = V_TL[1] - vert_hoff - vert_scaled_mid_height; - vert_coord[6] = V_TR[0] + vert_scaled_mid_width; - vert_coord[7] = V_TR[1] - vert_hoff - vert_scaled_mid_height; - - tex_coord[0] = T_BL[0] + tex_woff; - tex_coord[1] = T_BL[1] + tex_hoff + tex_mid_height; - tex_coord[2] = T_BR[0] + tex_mid_width; - tex_coord[3] = T_BR[1] + tex_hoff + tex_mid_height; - tex_coord[4] = T_TL[0] + tex_woff; - tex_coord[5] = T_TL[1] + tex_hoff + tex_mid_height; - tex_coord[6] = T_TR[0] + tex_mid_width; - tex_coord[7] = T_TR[1] + tex_hoff + tex_mid_height; - - gfx_display_draw(&draw, video_info); - - /* bottom-right corner */ - vert_coord[0] = V_BL[0] + vert_woff + vert_scaled_mid_width; - vert_coord[1] = V_BL[1] - vert_hoff - vert_scaled_mid_height; - vert_coord[2] = V_BR[0] + vert_scaled_mid_width + vert_woff; - vert_coord[3] = V_BR[1] - vert_hoff - vert_scaled_mid_height; - vert_coord[4] = V_TL[0] + vert_woff + vert_scaled_mid_width; - vert_coord[5] = V_TL[1] - vert_hoff - vert_scaled_mid_height; - vert_coord[6] = V_TR[0] + vert_scaled_mid_width + vert_woff; - vert_coord[7] = V_TR[1] - vert_hoff - vert_scaled_mid_height; - - tex_coord[0] = T_BL[0] + tex_woff + tex_mid_width; - tex_coord[1] = T_BL[1] + tex_hoff + tex_mid_height; - tex_coord[2] = T_BR[0] + tex_woff + tex_mid_width; - tex_coord[3] = T_BR[1] + tex_hoff + tex_mid_height; - tex_coord[4] = T_TL[0] + tex_woff + tex_mid_width; - tex_coord[5] = T_TL[1] + tex_hoff + tex_mid_height; - tex_coord[6] = T_TR[0] + tex_woff + tex_mid_width; - tex_coord[7] = T_TR[1] + tex_hoff + tex_mid_height; - - gfx_display_draw(&draw, video_info); - - free(colors); - free(vert_coord); - free(tex_coord); -} - -void gfx_display_rotate_z(gfx_display_ctx_rotate_draw_t *draw, - video_frame_info_t *video_info) -{ - math_matrix_4x4 matrix_rotated, matrix_scaled; - math_matrix_4x4 *b = NULL; - - if ( - !draw || - !menu_disp || - !menu_disp->get_default_mvp || - menu_disp->handles_transform - ) - return; - - b = (math_matrix_4x4*)menu_disp->get_default_mvp(video_info); - - if (!b) - return; - - matrix_4x4_rotate_z(matrix_rotated, draw->rotation); - matrix_4x4_multiply(*draw->matrix, matrix_rotated, *b); - - if (!draw->scale_enable) - return; - - matrix_4x4_scale(matrix_scaled, - draw->scale_x, draw->scale_y, draw->scale_z); - matrix_4x4_multiply(*draw->matrix, matrix_scaled, *draw->matrix); -} - -/* - * Draw a hardware cursor on top of the screen for the mouse. - */ -void gfx_display_draw_cursor( - video_frame_info_t *video_info, - float *color, float cursor_size, uintptr_t texture, - float x, float y, unsigned width, unsigned height) -{ - gfx_display_ctx_draw_t draw; - struct video_coords coords; - bool cursor_visible = video_info->fullscreen; - if (!video_info->menu_mouse_enable || !cursor_visible) - return; - - coords.vertices = 4; - coords.vertex = NULL; - coords.tex_coord = NULL; - coords.lut_tex_coord = NULL; - coords.color = (const float*)color; - - if (menu_disp && menu_disp->blend_begin) - menu_disp->blend_begin(video_info); - - draw.x = x - (cursor_size / 2); - draw.y = (int)height - y - (cursor_size / 2); - draw.width = cursor_size; - draw.height = cursor_size; - draw.coords = &coords; - draw.matrix_data = NULL; - draw.texture = texture; - draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; - draw.pipeline.id = 0; - - gfx_display_draw(&draw, video_info); - - if (menu_disp && menu_disp->blend_end) - menu_disp->blend_end(video_info); -} - -void gfx_display_push_quad( - unsigned width, unsigned height, - const float *colors, int x1, int y1, - int x2, int y2) -{ - float vertex[8]; - video_coords_t coords; - const float *coord_draw_ptr = NULL; - video_coord_array_t *ca = &menu_disp_ca; - - vertex[0] = x1 / (float)width; - vertex[1] = y1 / (float)height; - vertex[2] = x2 / (float)width; - vertex[3] = y1 / (float)height; - vertex[4] = x1 / (float)width; - vertex[5] = y2 / (float)height; - vertex[6] = x2 / (float)width; - vertex[7] = y2 / (float)height; - - if (menu_disp && menu_disp->get_default_tex_coords) - coord_draw_ptr = menu_disp->get_default_tex_coords(); - - coords.color = colors; - coords.vertex = vertex; - coords.tex_coord = coord_draw_ptr; - coords.lut_tex_coord = coord_draw_ptr; - coords.vertices = 3; - - video_coord_array_append(ca, &coords, 3); - - coords.color += 4; - coords.vertex += 2; - coords.tex_coord += 2; - coords.lut_tex_coord += 2; - - video_coord_array_append(ca, &coords, 3); -} - -void gfx_display_snow( - int16_t pointer_x, - int16_t pointer_y, - int width, int height) -{ - struct display_particle - { - float x, y; - float xspeed, yspeed; - float alpha; - bool alive; - }; - static struct display_particle particles[PARTICLES_COUNT] = {{0}}; - static int timeout = 0; - unsigned i, max_gen = 2; - - for (i = 0; i < PARTICLES_COUNT; ++i) - { - struct display_particle *p = (struct display_particle*)&particles[i]; - - if (p->alive) - { -#if 0 - menu_input_pointer_t pointer; - menu_input_get_pointer_state(&pointer); -#endif - - p->y += p->yspeed; - p->x += gfx_display_scalef( - pointer_x, 0, width, -0.3, 0.3); - p->x += p->xspeed; - - p->alive = p->y >= 0 && p->y < height - && p->x >= 0 && p->x < width; - } - else if (max_gen > 0 && timeout <= 0) - { - p->xspeed = gfx_display_randf(-0.2, 0.2); - p->yspeed = gfx_display_randf(1, 2); - p->y = 0; - p->x = rand() % width; - p->alpha = (float)rand() / (float)RAND_MAX; - p->alive = true; - - max_gen--; - } - } - - if (max_gen == 0) - timeout = 3; - else - timeout--; - - for (i = 0; i < PARTICLES_COUNT; ++i) - { - unsigned j; - float alpha, colors[16]; - struct display_particle *p = &particles[i]; - - if (!p->alive) - continue; - - alpha = gfx_display_randf(0, 100) > 90 ? - p->alpha/2 : p->alpha; - - for (j = 0; j < 16; j++) - { - colors[j] = 1; - if (j == 3 || j == 7 || j == 11 || j == 15) - colors[j] = alpha; - } - - gfx_display_push_quad(width, height, - colors, p->x-2, p->y-2, p->x+2, p->y+2); - - j++; - } -} - -/* Setup: Initializes the font associated - * to the menu driver */ -font_data_t *gfx_display_font( - enum application_special_type type, - float menu_font_size, - bool is_threaded) -{ - char fontpath[PATH_MAX_LENGTH]; - - if (!menu_disp) - return NULL; - - fontpath[0] = '\0'; - - fill_pathname_application_special( - fontpath, sizeof(fontpath), type); - - return gfx_display_font_file(fontpath, menu_font_size, is_threaded); -} - -/* Returns the OSK key at a given position */ -int gfx_display_osk_ptr_at_pos(void *data, int x, int y, - unsigned width, unsigned height) -{ - unsigned i; - int ptr_width = width / 11; - int ptr_height = height / 10; - - if (ptr_width >= ptr_height) - ptr_width = ptr_height; - - for (i = 0; i < 44; i++) - { - int line_y = (i / 11)*height/10.0; - int ptr_x = width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width; - int ptr_y = height/2.0 + ptr_height*1.5 + line_y - ptr_height; - - if (x > ptr_x && x < ptr_x + ptr_width - && y > ptr_y && y < ptr_y + ptr_height) - return i; - } - - return -1; -} - -/* Get the menu framebuffer's size dimensions. */ -void gfx_display_get_fb_size(unsigned *fb_width, - unsigned *fb_height, size_t *fb_pitch) -{ - *fb_width = gfx_display_framebuf_width; - *fb_height = gfx_display_framebuf_height; - *fb_pitch = gfx_display_framebuf_pitch; -} - -/* Set the menu framebuffer's width. */ -void gfx_display_set_width(unsigned width) -{ - gfx_display_framebuf_width = width; -} - -/* Set the menu framebuffer's height. */ -void gfx_display_set_height(unsigned height) -{ - gfx_display_framebuf_height = height; -} - -void gfx_display_set_header_height(unsigned height) -{ - gfx_display_header_height = height; -} - -unsigned gfx_display_get_header_height(void) -{ - return gfx_display_header_height; -} - -size_t gfx_display_get_framebuffer_pitch(void) -{ - return gfx_display_framebuf_pitch; -} - -void gfx_display_set_framebuffer_pitch(size_t pitch) -{ - gfx_display_framebuf_pitch = pitch; -} - -bool gfx_display_get_msg_force(void) -{ - return gfx_display_msg_force; -} - -void gfx_display_set_msg_force(bool state) -{ - gfx_display_msg_force = state; -} - -/* Returns true if an animation is still active or - * when the menu framebuffer still is dirty and - * therefore it still needs to be rendered onscreen. - * - * This function can be used for optimization purposes - * so that we don't have to render the menu graphics per-frame - * unless a change has happened. - * */ -bool gfx_display_get_update_pending(void) -{ - if (gfx_animation_is_active() || gfx_display_framebuf_dirty) - return true; - return false; -} - -void gfx_display_set_viewport(unsigned width, unsigned height) -{ - video_driver_set_viewport(width, height, true, false); -} - -void gfx_display_unset_viewport(unsigned width, unsigned height) -{ - video_driver_set_viewport(width, height, false, true); -} - -/* Checks if the menu framebuffer has its 'dirty flag' set. This - * means that the current contents of the framebuffer has changed - * and that it has to be rendered to the screen. */ -bool gfx_display_get_framebuffer_dirty_flag(void) -{ - return gfx_display_framebuf_dirty; -} - -/* Set the menu framebuffer's 'dirty flag'. */ -void gfx_display_set_framebuffer_dirty_flag(void) -{ - gfx_display_framebuf_dirty = true; -} - -/* Unset the menu framebufer's 'dirty flag'. */ -void gfx_display_unset_framebuffer_dirty_flag(void) -{ - gfx_display_framebuf_dirty = false; -} - -void gfx_display_draw_keyboard( - uintptr_t hover_texture, - const font_data_t *font, - video_frame_info_t *video_info, - char *grid[], unsigned id, - unsigned text_color) -{ - unsigned i; - int ptr_width, ptr_height; - unsigned width = video_info->width; - unsigned height = video_info->height; - - float white[16]= { - 1.00, 1.00, 1.00, 1.00, - 1.00, 1.00, 1.00, 1.00, - 1.00, 1.00, 1.00, 1.00, - 1.00, 1.00, 1.00, 1.00, - }; - - gfx_display_draw_quad( - video_info, - 0, height/2.0, width, height/2.0, - width, height, - &osk_dark[0]); - - ptr_width = width / 11; - ptr_height = height / 10; - - if (ptr_width >= ptr_height) - ptr_width = ptr_height; - - for (i = 0; i < 44; i++) - { - int line_y = (i / 11) * height / 10.0; - unsigned color = 0xffffffff; - - if (i == id) - { - gfx_display_blend_begin(video_info); - - gfx_display_draw_texture( - video_info, - width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width, - height/2.0 + ptr_height*1.5 + line_y, - ptr_width, ptr_height, - width, height, - &white[0], - hover_texture); - - gfx_display_blend_end(video_info); - - color = text_color; - } - - gfx_display_draw_text(font, grid[i], - width/2.0 - (11*ptr_width)/2.0 + (i % 11) - * ptr_width + ptr_width/2.0, - height/2.0 + ptr_height + line_y + font->size / 3, - width, height, color, TEXT_ALIGN_CENTER, 1.0f, - false, 0, false); - } -} - -/* Draw text on top of the screen. - */ -void gfx_display_draw_text( - const font_data_t *font, const char *text, - float x, float y, int width, int height, - uint32_t color, enum text_alignment text_align, - float scale, bool shadows_enable, float shadow_offset, - bool draw_outside) -{ - struct font_params params; - - if ((color & 0x000000FF) == 0) - return; - - /* Don't draw outside of the screen */ - if (!draw_outside && - ((x < -64 || x > width + 64) - || (y < -64 || y > height + 64)) - ) - return; - - params.x = x / width; - params.y = 1.0f - y / height; - params.scale = scale; - params.drop_mod = 0.0f; - params.drop_x = 0.0f; - params.drop_y = 0.0f; - params.color = color; - params.full_screen = true; - params.text_align = text_align; - - if (shadows_enable) - { - params.drop_x = shadow_offset; - params.drop_y = -shadow_offset; - params.drop_alpha = 0.35f; - } - - video_driver_set_osd_msg(text, ¶ms, (void*)font); -} - -bool 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) -{ - struct texture_image ti; - char texpath[PATH_MAX_LENGTH] = {0}; - - ti.width = 0; - ti.height = 0; - ti.pixels = NULL; - ti.supports_rgba = video_driver_supports_rgba(); - - if (string_is_empty(texture_path)) - return false; - - fill_pathname_join(texpath, iconpath, texture_path, sizeof(texpath)); - - if (!path_is_valid(texpath)) - return false; - - if (!image_texture_load(&ti, texpath)) - return false; - - if (width) - *width = ti.width; - - if (height) - *height = ti.height; - - video_driver_texture_load(&ti, - filter_type, item); - image_texture_free(&ti); - - return true; -} - - -bool gfx_display_reset_textures_list_buffer( - uintptr_t *item, enum texture_filter_type filter_type, - void* buffer, unsigned buffer_len, enum image_type_enum image_type, - unsigned *width, unsigned *height) -{ - struct texture_image ti; - - ti.width = 0; - ti.height = 0; - ti.pixels = NULL; - ti.supports_rgba = video_driver_supports_rgba(); - - if (!image_texture_load_buffer(&ti, image_type, buffer, buffer_len)) - return false; - - if (width) - *width = ti.width; - - if (height) - *height = ti.height; - - /* if the poke interface doesn't support texture load then return false */ - if (!video_driver_texture_load(&ti, filter_type, item)) - return false; - image_texture_free(&ti); - return true; -} - -/* Teardown; deinitializes and frees all - * fonts associated to the menu driver */ -void gfx_display_font_free(font_data_t *font) -{ - font_driver_free(font); -} - - -void gfx_display_allocate_white_texture(void) -{ - struct texture_image ti; - static const uint8_t white_data[] = { 0xff, 0xff, 0xff, 0xff }; - - ti.width = 1; - ti.height = 1; - ti.pixels = (uint32_t*)&white_data; - - if (gfx_display_white_texture) - video_driver_texture_unload(&gfx_display_white_texture); - - video_driver_texture_load(&ti, - TEXTURE_FILTER_NEAREST, &gfx_display_white_texture); -} - - -void gfx_display_free(void) -{ - video_coord_array_free(&menu_disp_ca); - gfx_animation_ctl(MENU_ANIMATION_CTL_DEINIT, NULL); - - gfx_display_msg_force = false; - gfx_display_header_height = 0; - gfx_display_framebuf_width = 0; - gfx_display_framebuf_height = 0; - gfx_display_framebuf_pitch = 0; - menu_disp = NULL; - gfx_display_has_windowed = false; -} - -void gfx_display_init(void) -{ - gfx_display_has_windowed = video_driver_has_windowed(); - menu_disp_ca.allocated = 0; -} - - -bool gfx_display_driver_exists(const char *s) -{ - unsigned i; - for (i = 0; i < ARRAY_SIZE(gfx_display_ctx_drivers); i++) - { - if (string_is_equal(s, gfx_display_ctx_drivers[i]->ident)) - return true; - } - - return false; -} - -bool gfx_display_init_first_driver(bool video_is_threaded) -{ - unsigned i; - - for (i = 0; gfx_display_ctx_drivers[i]; i++) - { - if (!gfx_display_check_compatibility( - gfx_display_ctx_drivers[i]->type, - video_is_threaded)) - continue; - - RARCH_LOG("[Menu]: Found menu display driver: \"%s\".\n", - gfx_display_ctx_drivers[i]->ident); - menu_disp = gfx_display_ctx_drivers[i]; - return true; - } - return false; -} +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2019 - Brad Parker + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ +#include "gfx_display.h" +#include "gfx_animation.h" + +#include "video_coord_array.h" +#include "../configuration.h" +#include "../verbosity.h" + +#define PARTICLES_COUNT 100 + +/* Number of pixels corner-to-corner on a 1080p + * display: + * > sqrt((1920 * 1920) + (1080 * 1080)) + * Note: This is a double, so no suffix */ +#define DIAGONAL_PIXELS_1080P 2202.90717008229831581901 + +/* Standard reference DPI value, used when determining + * DPI-aware menu scaling factors */ +#define REFERENCE_DPI 96.0f + +/* 'OZONE_SIDEBAR_WIDTH' must be kept in sync + * with ozone menu driver metrics */ +#define OZONE_SIDEBAR_WIDTH 408 + +static float osk_dark[16] = { + 0.00, 0.00, 0.00, 0.85, + 0.00, 0.00, 0.00, 0.85, + 0.00, 0.00, 0.00, 0.85, + 0.00, 0.00, 0.00, 0.85, +}; + +uintptr_t gfx_display_white_texture; + +static bool gfx_display_has_windowed = false; + +/* Width, height and pitch of the menu framebuffer */ +static unsigned gfx_display_framebuf_width = 0; +static unsigned gfx_display_framebuf_height = 0; +static size_t gfx_display_framebuf_pitch = 0; + +/* Height of the menu display header */ +static unsigned gfx_display_header_height = 0; + +static bool gfx_display_msg_force = false; +static bool gfx_display_framebuf_dirty = false; + +static enum menu_driver_id_type menu_driver_id = MENU_DRIVER_ID_UNKNOWN; + +static video_coord_array_t menu_disp_ca; + +static void *gfx_display_null_get_default_mvp(video_frame_info_t *video_info) { return NULL; } +static void gfx_display_null_blend_begin(video_frame_info_t *video_info) { } +static void gfx_display_null_blend_end(video_frame_info_t *video_info) { } +static void gfx_display_null_draw(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info) { } +static void gfx_display_null_draw_pipeline(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info) { } +static void gfx_display_null_viewport(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info) { } +static void gfx_display_null_restore_clear_color(void) { } +static void gfx_display_null_clear_color(gfx_display_ctx_clearcolor_t *clearcolor, video_frame_info_t *video_info) { } + +static bool gfx_display_null_font_init_first( + void **font_handle, void *video_data, + const char *font_path, float font_size, + bool is_threaded) +{ + font_data_t **handle = (font_data_t**)font_handle; + if ((*handle = font_driver_init_first(video_data, + font_path, font_size, true, + is_threaded, + FONT_DRIVER_RENDER_DONT_CARE))) + return true; + return false; +} + +static const float *gfx_display_null_get_default_vertices(void) +{ + static float dummy[16] = {0.0f}; + return &dummy[0]; +} + +static const float *gfx_display_null_get_default_tex_coords(void) +{ + static float dummy[16] = {0.0f}; + return &dummy[0]; +} + +gfx_display_ctx_driver_t gfx_display_ctx_null = { + gfx_display_null_draw, + gfx_display_null_draw_pipeline, + gfx_display_null_viewport, + gfx_display_null_blend_begin, + gfx_display_null_blend_end, + gfx_display_null_restore_clear_color, + gfx_display_null_clear_color, + gfx_display_null_get_default_mvp, + gfx_display_null_get_default_vertices, + gfx_display_null_get_default_tex_coords, + gfx_display_null_font_init_first, + GFX_VIDEO_DRIVER_GENERIC, + "null", + false, + NULL, + NULL +}; + +/* Menu display drivers */ +static gfx_display_ctx_driver_t *gfx_display_ctx_drivers[] = { +#ifdef HAVE_D3D8 + &gfx_display_ctx_d3d8, +#endif +#ifdef HAVE_D3D9 + &gfx_display_ctx_d3d9, +#endif +#ifdef HAVE_D3D10 + &gfx_display_ctx_d3d10, +#endif +#ifdef HAVE_D3D11 + &gfx_display_ctx_d3d11, +#endif +#ifdef HAVE_D3D12 + &gfx_display_ctx_d3d12, +#endif +#ifdef HAVE_OPENGL + &gfx_display_ctx_gl, +#endif +#ifdef HAVE_OPENGL1 + &gfx_display_ctx_gl1, +#endif +#ifdef HAVE_OPENGL_CORE + &gfx_display_ctx_gl_core, +#endif +#ifdef HAVE_VULKAN + &gfx_display_ctx_vulkan, +#endif +#ifdef HAVE_METAL + &gfx_display_ctx_metal, +#endif +#ifdef HAVE_VITA2D + &gfx_display_ctx_vita2d, +#endif +#ifdef _3DS + &gfx_display_ctx_ctr, +#endif +#ifdef WIIU + &gfx_display_ctx_wiiu, +#endif +#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__) +#ifdef HAVE_GDI + &gfx_display_ctx_gdi, +#endif +#endif + &gfx_display_ctx_null, + NULL, +}; + +static gfx_display_ctx_driver_t *menu_disp = NULL; + +static INLINE float gfx_display_scalef(float val, + float oldmin, float oldmax, float newmin, float newmax) +{ + return (((val - oldmin) * (newmax - newmin)) / (oldmax - oldmin)) + newmin; +} + +static INLINE float gfx_display_randf(float min, float max) +{ + return (rand() * ((max - min) / RAND_MAX)) + min; +} + +void gfx_display_set_driver_id(enum menu_driver_id_type type) +{ + menu_driver_id = type; +} + +enum menu_driver_id_type gfx_display_get_driver_id(void) +{ + return menu_driver_id; +} + +static float gfx_display_get_adjusted_scale_internal( + float base_scale, float scale_factor, unsigned width) +{ + /* Apply user-set scaling factor */ + float adjusted_scale = base_scale * scale_factor; + + /* Ozone has a capped scale factor */ + adjusted_scale = (menu_driver_id == MENU_DRIVER_ID_OZONE) ? + (((float)OZONE_SIDEBAR_WIDTH * adjusted_scale) > ((float)width * 0.3333333f) ? + ((float)width * 0.3333333f / (float)OZONE_SIDEBAR_WIDTH) : adjusted_scale) : + adjusted_scale; + + /* Ensure final scale is 'sane' */ + return (adjusted_scale > 0.0001f) ? adjusted_scale : 1.0f; +} + +float gfx_display_get_dpi_scale_internal(unsigned width, unsigned height) +{ + static unsigned last_width = 0; + static unsigned last_height = 0; + static float scale = 0.0f; + static bool scale_cached = false; + float diagonal_pixels; + float pixel_scale; + float dpi; + gfx_ctx_metrics_t metrics; + + if (scale_cached && + (width == last_width) && + (height == last_height)) + return scale; + + /* Determine the diagonal 'size' of the display + * (or window) in terms of pixels */ + diagonal_pixels = (float)sqrt( + (double)((width * width) + (height * height))); + + /* TODO/FIXME: On Mac, calling video_context_driver_get_metrics() + * here causes RetroArch to crash (EXC_BAD_ACCESS). This is + * unfortunate, and needs to be fixed at the gfx context driver + * level. Until this is done, all we can do is fallback to using + * the old legacy 'magic number' scaling on Mac platforms. */ +#if defined(HAVE_COCOA) || defined(HAVE_COCOA_METAL) + if (true) + { + scale = (diagonal_pixels / 6.5f) / 212.0f; + scale_cached = true; + last_width = width; + last_height = height; + return scale; + } +#endif + + /* Get pixel scale relative to baseline 1080p display */ + pixel_scale = diagonal_pixels / DIAGONAL_PIXELS_1080P; + + /* Attempt to get display DPI */ + metrics.type = DISPLAY_METRIC_DPI; + metrics.value = &dpi; + + if (video_context_driver_get_metrics(&metrics) && (dpi > 0.0f)) + { + float display_size; + float dpi_scale; + +#if defined(ANDROID) || defined(HAVE_COCOATOUCH) + /* Android/iOS devices tell complete lies when + * reporting DPI values. From the Android devices + * I've had access to, the DPI is generally + * overestimated by 17%. All we can do is apply + * a blind correction factor... */ + dpi = dpi * 0.83f; +#endif + + /* Note: If we are running in windowed mode, this + * 'display size' is actually the window size - which + * kinda makes a mess of everything. Since we cannot + * get fullscreen resolution when running in windowed + * mode, there is nothing we can do about this. So just + * treat the window as a display, and hope for the best... */ + display_size = diagonal_pixels / dpi; + dpi_scale = dpi / REFERENCE_DPI; + + /* Note: We have tried leveraging every possible metric + * (and numerous studies on TV/monitor/mobile device + * usage habits) to determine an appropriate auto scaling + * factor. *None of these 'smart'/technical methods work + * consistently in the real world* - there is simply too + * much variance. + * So instead we have implemented a very fuzzy/loose + * method which is crude as can be, but actually has + * some semblance of usability... */ + + if (display_size > 24.0f) + { + /* DPI scaling fails miserably when using large + * displays. Having a UI element that's 1 inch high + * on all screens might seem like a good idea - until + * you realise that a HTPC user is probably sitting + * several metres from their TV, which makes something + * 1 inch high virtually invisible. + * So we make some assumptions: + * - Normal size displays <= 24 inches are probably + * PC monitors, with an eye-to-screen distance of + * 1 arm length. Under these conditions, fixed size + * (DPI scaled) UI elements should be visible for most + * users + * - Large displays > 24 inches start to encroach on + * TV territory. Once we start working with TVs, we + * have to consider users sitting on a couch - and + * in this situation, we fall back to the age-old + * standard of UI elements occupying a fixed fraction + * of the display size (i.e. just look at the menu of + * any console system for the past decade) + * - 24 -> 32 inches is a grey area, where the display + * might be a monitor or a TV. Above 32 inches, a TV + * is almost a certainty. So we simply lerp between + * dpi scaling and pixel scaling as the display size + * increases from 24 to 32 */ + float fraction = (display_size > 32.0f) ? 32.0f : display_size; + fraction = fraction - 24.0f; + fraction = fraction / (32.0f - 24.0f); + + scale = ((1.0f - fraction) * dpi_scale) + (fraction * pixel_scale); + } + else if (display_size < 12.0f) + { + /* DPI scaling also fails when using very small + * displays - i.e. mobile devices (tablets/phones). + * That 1 inch UI element is going to look pretty + * dumb on a 5 inch screen in landscape orientation... + * We're essentially in the opposite situation to the + * TV case above, and it turns out that a similar + * solution provides relief: as screen size reduces + * from 12 inches to zero, we lerp from dpi scaling + * to pixel scaling */ + float fraction = display_size / 12.0f; + + scale = ((1.0f - fraction) * pixel_scale) + (fraction * dpi_scale); + } + else + scale = dpi_scale; + } + /* If DPI retrieval is unsupported, all we can do + * is use the raw pixel scale */ + else + scale = pixel_scale; + + scale_cached = true; + last_width = width; + last_height = height; + + return scale; +} + +float gfx_display_get_dpi_scale(unsigned width, unsigned height) +{ + static unsigned last_width = 0; + static unsigned last_height = 0; + static float scale = 0.0f; + static bool scale_cached = false; + bool scale_updated = false; + static float last_menu_scale_factor = 0.0f; + static enum menu_driver_id_type last_menu_driver_id = MENU_DRIVER_ID_UNKNOWN; + static float adjusted_scale = 1.0f; + settings_t *settings = config_get_ptr(); + float menu_scale_factor = settings->floats.menu_scale_factor; + + /* Scale is based on display metrics - these are a fixed + * hardware property. To minimise performance overheads + * we therefore only call video_context_driver_get_metrics() + * on first run, or when the current video resolution changes */ + if (!scale_cached || + (width != last_width) || + (height != last_height)) + { + scale = gfx_display_get_dpi_scale_internal(width, height); + scale_cached = true; + scale_updated = true; + last_width = width; + last_height = height; + } + + /* Adjusted scale calculation may also be slow, so + * only update if something changes */ + if (scale_updated || + (menu_scale_factor != last_menu_scale_factor) || + (menu_driver_id != last_menu_driver_id)) + { + adjusted_scale = gfx_display_get_adjusted_scale_internal(scale, menu_scale_factor, width); + last_menu_scale_factor = menu_scale_factor; + last_menu_driver_id = menu_driver_id; + } + + return adjusted_scale; +} + +float gfx_display_get_widget_dpi_scale(unsigned width, unsigned height) +{ + static unsigned last_width = 0; + static unsigned last_height = 0; + static float scale = 0.0f; + static bool scale_cached = false; + bool scale_updated = false; + static float last_menu_scale_factor = 0.0f; + static enum menu_driver_id_type last_menu_driver_id = MENU_DRIVER_ID_UNKNOWN; + static float adjusted_scale = 1.0f; + settings_t *settings = config_get_ptr(); + + /* When using RGUI, settings->floats.menu_scale_factor + * is ignored + * > If we are not using a widget scale factor override, + * just set menu_scale_factor to 1.0 */ + float menu_scale_factor = + settings->bools.menu_widget_scale_auto ? + ((menu_driver_id == MENU_DRIVER_ID_RGUI) ? + 1.0f : settings->floats.menu_scale_factor) : + settings->floats.menu_widget_scale_factor; + + /* Scale is based on display metrics - these are a fixed + * hardware property. To minimise performance overheads + * we therefore only call video_context_driver_get_metrics() + * on first run, or when the current video resolution changes */ + if (!scale_cached || + (width != last_width) || + (height != last_height)) + { + scale = gfx_display_get_dpi_scale_internal(width, height); + scale_cached = true; + scale_updated = true; + last_width = width; + last_height = height; + } + + /* Adjusted scale calculation may also be slow, so + * only update if something changes */ + if (scale_updated || + (menu_scale_factor != last_menu_scale_factor) || + (menu_driver_id != last_menu_driver_id)) + { + adjusted_scale = gfx_display_get_adjusted_scale_internal(scale, menu_scale_factor, width); + last_menu_scale_factor = menu_scale_factor; + last_menu_driver_id = menu_driver_id; + } + + return adjusted_scale; +} + +float gfx_display_get_widget_pixel_scale(unsigned width, unsigned height) +{ + static unsigned last_width = 0; + static unsigned last_height = 0; + static float scale = 0.0f; + static bool scale_cached = false; + bool scale_updated = false; + static float last_menu_scale_factor = 0.0f; + static enum menu_driver_id_type last_menu_driver_id = MENU_DRIVER_ID_UNKNOWN; + static float adjusted_scale = 1.0f; + settings_t *settings = config_get_ptr(); + + /* When using RGUI, settings->floats.menu_scale_factor + * is ignored + * > If we are not using a widget scale factor override, + * just set menu_scale_factor to 1.0 */ + float menu_scale_factor = + settings->bools.menu_widget_scale_auto ? + ((menu_driver_id == MENU_DRIVER_ID_RGUI) ? + 1.0f : settings->floats.menu_scale_factor) : + settings->floats.menu_widget_scale_factor; + + /* We need to perform a square root here, which + * can be slow on some platforms (not *slow*, but + * it involves enough work that it's worth trying + * to optimise). We therefore cache the pixel scale, + * and only update on first run or when the video + * size changes */ + if (!scale_cached || + (width != last_width) || + (height != last_height)) + { + /* Baseline reference is a 1080p display */ + scale = (float)( + sqrt((double)((width * width) + (height * height))) / + DIAGONAL_PIXELS_1080P); + + scale_cached = true; + scale_updated = true; + last_width = width; + last_height = height; + } + + /* Adjusted scale calculation may also be slow, so + * only update if something changes */ + if (scale_updated || + (menu_scale_factor != last_menu_scale_factor) || + (menu_driver_id != last_menu_driver_id)) + { + adjusted_scale = gfx_display_get_adjusted_scale_internal(scale, menu_scale_factor, width); + last_menu_scale_factor = menu_scale_factor; + last_menu_driver_id = menu_driver_id; + } + + return adjusted_scale; +} + +/* Ugh... Since we must now have independent scale + * factors for menus and widgets, and most of the internal + * scaling variables are cached/static, a huge amount of + * code duplication is required for the pixel_scale and + * dpi_scale functions. A necessary evil, I suppose... */ + +#if 0 +static float gfx_display_get_pixel_scale(unsigned width, unsigned height) +{ + static unsigned last_width = 0; + static unsigned last_height = 0; + static float scale = 0.0f; + static bool scale_cached = false; + bool scale_updated = false; + static float last_menu_scale_factor = 0.0f; + static enum menu_driver_id_type last_menu_driver_id = MENU_DRIVER_ID_UNKNOWN; + static float adjusted_scale = 1.0f; + settings_t *settings = config_get_ptr(); + float menu_scale_factor = settings->floats.menu_scale_factor; + + /* We need to perform a square root here, which + * can be slow on some platforms (not *slow*, but + * it involves enough work that it's worth trying + * to optimise). We therefore cache the pixel scale, + * and only update on first run or when the video + * size changes */ + if (!scale_cached || + (width != last_width) || + (height != last_height)) + { + /* Baseline reference is a 1080p display */ + scale = (float)( + sqrt((double)((width * width) + (height * height))) / + DIAGONAL_PIXELS_1080P); + + scale_cached = true; + scale_updated = true; + last_width = width; + last_height = height; + } + + /* Adjusted scale calculation may also be slow, so + * only update if something changes */ + if (scale_updated || + (menu_scale_factor != last_menu_scale_factor) || + (menu_driver_id != last_menu_driver_id)) + { + adjusted_scale = gfx_display_get_adjusted_scale_internal( + scale, menu_scale_factor, width); + last_menu_scale_factor = menu_scale_factor; + last_menu_driver_id = menu_driver_id; + } + + return adjusted_scale; +} +#endif + + +/* Check if the current menu driver is compatible + * with your video driver. */ +static bool gfx_display_check_compatibility( + enum gfx_display_driver_type type, + bool video_is_threaded) +{ + const char *video_driver = video_driver_get_ident(); + + switch (type) + { + case GFX_VIDEO_DRIVER_GENERIC: + return true; + case GFX_VIDEO_DRIVER_OPENGL: + if (string_is_equal(video_driver, "gl")) + return true; + break; + case GFX_VIDEO_DRIVER_OPENGL1: + if (string_is_equal(video_driver, "gl1")) + return true; + break; + case GFX_VIDEO_DRIVER_OPENGL_CORE: + if (string_is_equal(video_driver, "glcore")) + return true; + break; + case GFX_VIDEO_DRIVER_VULKAN: + if (string_is_equal(video_driver, "vulkan")) + return true; + break; + case GFX_VIDEO_DRIVER_METAL: + if (string_is_equal(video_driver, "metal")) + return true; + break; + case GFX_VIDEO_DRIVER_DIRECT3D8: + if (string_is_equal(video_driver, "d3d8")) + return true; + break; + case GFX_VIDEO_DRIVER_DIRECT3D9: + if (string_is_equal(video_driver, "d3d9")) + return true; + break; + case GFX_VIDEO_DRIVER_DIRECT3D10: + if (string_is_equal(video_driver, "d3d10")) + return true; + break; + case GFX_VIDEO_DRIVER_DIRECT3D11: + if (string_is_equal(video_driver, "d3d11")) + return true; + break; + case GFX_VIDEO_DRIVER_DIRECT3D12: + if (string_is_equal(video_driver, "d3d12")) + return true; + break; + case GFX_VIDEO_DRIVER_VITA2D: + if (string_is_equal(video_driver, "vita2d")) + return true; + break; + case GFX_VIDEO_DRIVER_CTR: + if (string_is_equal(video_driver, "ctr")) + return true; + break; + case GFX_VIDEO_DRIVER_WIIU: + if (string_is_equal(video_driver, "gx2")) + return true; + break; + case GFX_VIDEO_DRIVER_GDI: + if (string_is_equal(video_driver, "gdi")) + return true; + break; + case GFX_VIDEO_DRIVER_SWITCH: + if (string_is_equal(video_driver, "switch")) + return true; + break; + } + + return false; +} + +/* Reset the menu's coordinate array vertices. + * NOTE: Not every menu driver uses this. */ +void gfx_display_coords_array_reset(void) +{ + menu_disp_ca.coords.vertices = 0; +} + +video_coord_array_t *gfx_display_get_coords_array(void) +{ + return &menu_disp_ca; +} + + +/* Begin blending operation */ +void gfx_display_blend_begin(video_frame_info_t *video_info) +{ + if (menu_disp && menu_disp->blend_begin) + menu_disp->blend_begin(video_info); +} + +/* End blending operation */ +void gfx_display_blend_end(video_frame_info_t *video_info) +{ + if (menu_disp && menu_disp->blend_end) + menu_disp->blend_end(video_info); +} + +/* Begin scissoring operation */ +void gfx_display_scissor_begin(video_frame_info_t *video_info, int x, int y, unsigned width, unsigned height) +{ + if (menu_disp && menu_disp->scissor_begin) + { + if (y < 0) + { + if (height < (unsigned)(-y)) + height = 0; + else + height += y; + y = 0; + } + if (x < 0) + { + if (width < (unsigned)(-x)) + width = 0; + else + width += x; + x = 0; + } + if (y >= (int)video_info->height) + { + height = 0; + y = 0; + } + if (x >= (int)video_info->width) + { + width = 0; + x = 0; + } + if ((y + height) > video_info->height) + height = video_info->height - y; + if ((x + width) > video_info->width) + width = video_info->width - x; + + menu_disp->scissor_begin(video_info, x, y, width, height); + } +} + +/* End scissoring operation */ +void gfx_display_scissor_end(video_frame_info_t *video_info) +{ + if (menu_disp && menu_disp->scissor_end) + menu_disp->scissor_end(video_info); +} + +font_data_t *gfx_display_font_file( + char* fontpath, float menu_font_size, bool is_threaded) +{ + font_data_t *font_data = NULL; + + if (!menu_disp) + return NULL; + + if (!menu_disp->font_init_first((void**)&font_data, + video_driver_get_ptr(false), + fontpath, menu_font_size, is_threaded)) + return NULL; + + return font_data; +} + +bool gfx_display_restore_clear_color(void) +{ + if (!menu_disp || !menu_disp->restore_clear_color) + return false; + menu_disp->restore_clear_color(); + return true; +} + +void gfx_display_clear_color(gfx_display_ctx_clearcolor_t *color, + video_frame_info_t *video_info) +{ + if (menu_disp && menu_disp->clear_color) + menu_disp->clear_color(color, video_info); +} + +void gfx_display_draw(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info) +{ + if (!menu_disp || !draw || !menu_disp->draw) + return; + + if (draw->height <= 0) + return; + if (draw->width <= 0) + return; + menu_disp->draw(draw, video_info); +} + +void gfx_display_draw_blend(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info) +{ + if (!menu_disp || !draw || !menu_disp->draw) + return; + + if (draw->height <= 0) + return; + if (draw->width <= 0) + return; + gfx_display_blend_begin(video_info); + menu_disp->draw(draw, video_info); + gfx_display_blend_end(video_info); +} + +void gfx_display_draw_pipeline(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info) +{ + if (menu_disp && draw && menu_disp->draw_pipeline) + menu_disp->draw_pipeline(draw, video_info); +} + +void gfx_display_draw_bg(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info, bool add_opacity_to_wallpaper, + float override_opacity) +{ + static struct video_coords coords; + const float *new_vertex = NULL; + const float *new_tex_coord = NULL; + if (!menu_disp || !draw) + return; + + new_vertex = draw->vertex; + new_tex_coord = draw->tex_coord; + + if (!new_vertex) + new_vertex = menu_disp->get_default_vertices(); + if (!new_tex_coord) + new_tex_coord = menu_disp->get_default_tex_coords(); + + coords.vertices = (unsigned)draw->vertex_count; + coords.vertex = new_vertex; + coords.tex_coord = new_tex_coord; + coords.lut_tex_coord = new_tex_coord; + coords.color = (const float*)draw->color; + + draw->coords = &coords; + draw->scale_factor = 1.0f; + draw->rotation = 0.0f; + + if (draw->texture) + add_opacity_to_wallpaper = true; + + if (add_opacity_to_wallpaper) + gfx_display_set_alpha(draw->color, override_opacity); + + if (!draw->texture) + draw->texture = gfx_display_white_texture; + + if (menu_disp && menu_disp->get_default_mvp) + draw->matrix_data = (math_matrix_4x4*)menu_disp->get_default_mvp(video_info); +} + +void gfx_display_draw_gradient(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info) +{ + draw->texture = 0; + draw->x = 0; + draw->y = 0; + + gfx_display_draw_bg(draw, video_info, false, + video_info->menu_wallpaper_opacity); + gfx_display_draw(draw, video_info); +} + +void gfx_display_draw_quad( + video_frame_info_t *video_info, + int x, int y, unsigned w, unsigned h, + unsigned width, unsigned height, + float *color) +{ + gfx_display_ctx_draw_t draw; + struct video_coords coords; + + coords.vertices = 4; + coords.vertex = NULL; + coords.tex_coord = NULL; + coords.lut_tex_coord = NULL; + coords.color = color; + + if (menu_disp && menu_disp->blend_begin) + menu_disp->blend_begin(video_info); + + draw.x = x; + draw.y = (int)height - y - (int)h; + draw.width = w; + draw.height = h; + draw.coords = &coords; + draw.matrix_data = NULL; + draw.texture = gfx_display_white_texture; + draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; + draw.pipeline.id = 0; + draw.scale_factor = 1.0f; + draw.rotation = 0.0f; + + gfx_display_draw(&draw, video_info); + + if (menu_disp && menu_disp->blend_end) + menu_disp->blend_end(video_info); +} + +void gfx_display_draw_polygon( + video_frame_info_t *video_info, + int x1, int y1, + int x2, int y2, + int x3, int y3, + int x4, int y4, + unsigned width, unsigned height, + float *color) +{ + gfx_display_ctx_draw_t draw; + struct video_coords coords; + + float vertex[8]; + + vertex[0] = x1 / (float)width; + vertex[1] = y1 / (float)height; + vertex[2] = x2 / (float)width; + vertex[3] = y2 / (float)height; + vertex[4] = x3 / (float)width; + vertex[5] = y3 / (float)height; + vertex[6] = x4 / (float)width; + vertex[7] = y4 / (float)height; + + coords.vertices = 4; + coords.vertex = &vertex[0]; + coords.tex_coord = NULL; + coords.lut_tex_coord = NULL; + coords.color = color; + + if (menu_disp && menu_disp->blend_begin) + menu_disp->blend_begin(video_info); + + draw.x = 0; + draw.y = 0; + draw.width = width; + draw.height = height; + draw.coords = &coords; + draw.matrix_data = NULL; + draw.texture = gfx_display_white_texture; + draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; + draw.pipeline.id = 0; + draw.scale_factor = 1.0f; + draw.rotation = 0.0f; + + gfx_display_draw(&draw, video_info); + + if (menu_disp && menu_disp->blend_end) + menu_disp->blend_end(video_info); +} + +void gfx_display_draw_texture( + video_frame_info_t *video_info, + int x, int y, unsigned w, unsigned h, + unsigned width, unsigned height, + float *color, uintptr_t texture) +{ + gfx_display_ctx_draw_t draw; + gfx_display_ctx_rotate_draw_t rotate_draw; + struct video_coords coords; + math_matrix_4x4 mymat; + rotate_draw.matrix = &mymat; + rotate_draw.rotation = 0.0; + rotate_draw.scale_x = 1.0; + rotate_draw.scale_y = 1.0; + rotate_draw.scale_z = 1; + rotate_draw.scale_enable = true; + coords.vertices = 4; + coords.vertex = NULL; + coords.tex_coord = NULL; + coords.lut_tex_coord = NULL; + draw.width = w; + draw.height = h; + draw.coords = &coords; + draw.matrix_data = &mymat; + draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; + draw.pipeline.id = 0; + coords.color = (const float*)color; + + gfx_display_rotate_z(&rotate_draw, video_info); + + draw.texture = texture; + draw.x = x; + draw.y = height - y; + gfx_display_draw(&draw, video_info); +} + +/* Draw the texture split into 9 sections, without scaling the corners. + * The middle sections will only scale in the X axis, and the side + * sections will only scale in the Y axis. */ +void gfx_display_draw_texture_slice( + video_frame_info_t *video_info, + 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) +{ + gfx_display_ctx_draw_t draw; + gfx_display_ctx_rotate_draw_t rotate_draw; + struct video_coords coords; + math_matrix_4x4 mymat; + unsigned i; + 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]; + + /* need space for the coordinates of two triangles in a strip, so 8 vertices */ + float *tex_coord = (float*)malloc(8 * sizeof(float)); + float *vert_coord = (float*)malloc(8 * sizeof(float)); + float *colors = (float*)malloc(16 * sizeof(float)); + + /* normalized width/height of the amount to offset from the corners, + * for both the vertex and texture coordinates */ + float vert_woff = (offset * scale_factor) / (float)width; + float vert_hoff = (offset * scale_factor) / (float)height; + float tex_woff = offset / (float)w; + float tex_hoff = offset / (float)h; + + /* the width/height of the middle sections of both the scaled and original image */ + float vert_scaled_mid_width = (new_w - (offset * scale_factor * 2)) / (float)width; + float vert_scaled_mid_height = (new_h - (offset * scale_factor * 2)) / (float)height; + float tex_mid_width = (w - (offset * 2)) / (float)w; + float tex_mid_height = (h - (offset * 2)) / (float)h; + + /* normalized coordinates for the start position of the image */ + float norm_x = x / (float)width; + float norm_y = (height - y) / (float)height; + + /* the four vertices of the top-left corner of the image, + * used as a starting point for all the other sections */ + V_BL[0] = norm_x; + V_BL[1] = norm_y; + V_BR[0] = norm_x + vert_woff; + V_BR[1] = norm_y; + V_TL[0] = norm_x; + V_TL[1] = norm_y + vert_hoff; + V_TR[0] = norm_x + vert_woff; + V_TR[1] = norm_y + vert_hoff; + T_BL[0] = 0.0f; + T_BL[1] = tex_hoff; + T_BR[0] = tex_woff; + T_BR[1] = tex_hoff; + T_TL[0] = 0.0f; + T_TL[1] = 0.0f; + T_TR[0] = tex_woff; + T_TR[1] = 0.0f; + + for (i = 0; i < (16 * sizeof(float)) / sizeof(colors[0]); i++) + colors[i] = 1.0f; + + rotate_draw.matrix = &mymat; + rotate_draw.rotation = 0.0; + rotate_draw.scale_x = 1.0; + rotate_draw.scale_y = 1.0; + rotate_draw.scale_z = 1; + rotate_draw.scale_enable = true; + coords.vertices = 4; + coords.vertex = vert_coord; + coords.tex_coord = tex_coord; + coords.lut_tex_coord = NULL; + draw.width = width; + draw.height = height; + draw.coords = &coords; + draw.matrix_data = &mymat; + draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; + draw.pipeline.id = 0; + coords.color = (const float*)(color == NULL ? colors : color); + + gfx_display_rotate_z(&rotate_draw, video_info); + + draw.texture = texture; + draw.x = 0; + draw.y = 0; + + /* vertex coords are specfied bottom-up in this order: BL BR TL TR */ + /* texture coords are specfied top-down in this order: BL BR TL TR */ + + /* If someone wants to change this to not draw several times, the + * coordinates will need to be modified because of the triangle strip usage. */ + + /* top-left corner */ + vert_coord[0] = V_BL[0]; + vert_coord[1] = V_BL[1]; + vert_coord[2] = V_BR[0]; + vert_coord[3] = V_BR[1]; + vert_coord[4] = V_TL[0]; + vert_coord[5] = V_TL[1]; + vert_coord[6] = V_TR[0]; + vert_coord[7] = V_TR[1]; + + tex_coord[0] = T_BL[0]; + tex_coord[1] = T_BL[1]; + tex_coord[2] = T_BR[0]; + tex_coord[3] = T_BR[1]; + tex_coord[4] = T_TL[0]; + tex_coord[5] = T_TL[1]; + tex_coord[6] = T_TR[0]; + tex_coord[7] = T_TR[1]; + + gfx_display_draw(&draw, video_info); + + /* top-middle section */ + vert_coord[0] = V_BL[0] + vert_woff; + vert_coord[1] = V_BL[1]; + vert_coord[2] = V_BR[0] + vert_scaled_mid_width; + vert_coord[3] = V_BR[1]; + vert_coord[4] = V_TL[0] + vert_woff; + vert_coord[5] = V_TL[1]; + vert_coord[6] = V_TR[0] + vert_scaled_mid_width; + vert_coord[7] = V_TR[1]; + + tex_coord[0] = T_BL[0] + tex_woff; + tex_coord[1] = T_BL[1]; + tex_coord[2] = T_BR[0] + tex_mid_width; + tex_coord[3] = T_BR[1]; + tex_coord[4] = T_TL[0] + tex_woff; + tex_coord[5] = T_TL[1]; + tex_coord[6] = T_TR[0] + tex_mid_width; + tex_coord[7] = T_TR[1]; + + gfx_display_draw(&draw, video_info); + + /* top-right corner */ + vert_coord[0] = V_BL[0] + vert_woff + vert_scaled_mid_width; + vert_coord[1] = V_BL[1]; + vert_coord[2] = V_BR[0] + vert_scaled_mid_width + vert_woff; + vert_coord[3] = V_BR[1]; + vert_coord[4] = V_TL[0] + vert_woff + vert_scaled_mid_width; + vert_coord[5] = V_TL[1]; + vert_coord[6] = V_TR[0] + vert_scaled_mid_width + vert_woff; + vert_coord[7] = V_TR[1]; + + tex_coord[0] = T_BL[0] + tex_woff + tex_mid_width; + tex_coord[1] = T_BL[1]; + tex_coord[2] = T_BR[0] + tex_mid_width + tex_woff; + tex_coord[3] = T_BR[1]; + tex_coord[4] = T_TL[0] + tex_woff + tex_mid_width; + tex_coord[5] = T_TL[1]; + tex_coord[6] = T_TR[0] + tex_mid_width + tex_woff; + tex_coord[7] = T_TR[1]; + + gfx_display_draw(&draw, video_info); + + /* middle-left section */ + vert_coord[0] = V_BL[0]; + vert_coord[1] = V_BL[1] - vert_scaled_mid_height; + vert_coord[2] = V_BR[0]; + vert_coord[3] = V_BR[1] - vert_scaled_mid_height; + vert_coord[4] = V_TL[0]; + vert_coord[5] = V_TL[1] - vert_hoff; + vert_coord[6] = V_TR[0]; + vert_coord[7] = V_TR[1] - vert_hoff; + + tex_coord[0] = T_BL[0]; + tex_coord[1] = T_BL[1] + tex_mid_height; + tex_coord[2] = T_BR[0]; + tex_coord[3] = T_BR[1] + tex_mid_height; + tex_coord[4] = T_TL[0]; + tex_coord[5] = T_TL[1] + tex_hoff; + tex_coord[6] = T_TR[0]; + tex_coord[7] = T_TR[1] + tex_hoff; + + gfx_display_draw(&draw, video_info); + + /* center section */ + vert_coord[0] = V_BL[0] + vert_woff; + vert_coord[1] = V_BL[1] - vert_scaled_mid_height; + vert_coord[2] = V_BR[0] + vert_scaled_mid_width; + vert_coord[3] = V_BR[1] - vert_scaled_mid_height; + vert_coord[4] = V_TL[0] + vert_woff; + vert_coord[5] = V_TL[1] - vert_hoff; + vert_coord[6] = V_TR[0] + vert_scaled_mid_width; + vert_coord[7] = V_TR[1] - vert_hoff; + + tex_coord[0] = T_BL[0] + tex_woff; + tex_coord[1] = T_BL[1] + tex_mid_height; + tex_coord[2] = T_BR[0] + tex_mid_width; + tex_coord[3] = T_BR[1] + tex_mid_height; + tex_coord[4] = T_TL[0] + tex_woff; + tex_coord[5] = T_TL[1] + tex_hoff; + tex_coord[6] = T_TR[0] + tex_mid_width; + tex_coord[7] = T_TR[1] + tex_hoff; + + gfx_display_draw(&draw, video_info); + + /* middle-right section */ + vert_coord[0] = V_BL[0] + vert_woff + vert_scaled_mid_width; + vert_coord[1] = V_BL[1] - vert_scaled_mid_height; + vert_coord[2] = V_BR[0] + vert_woff + vert_scaled_mid_width; + vert_coord[3] = V_BR[1] - vert_scaled_mid_height; + vert_coord[4] = V_TL[0] + vert_woff + vert_scaled_mid_width; + vert_coord[5] = V_TL[1] - vert_hoff; + vert_coord[6] = V_TR[0] + vert_woff + vert_scaled_mid_width; + vert_coord[7] = V_TR[1] - vert_hoff; + + tex_coord[0] = T_BL[0] + tex_woff + tex_mid_width; + tex_coord[1] = T_BL[1] + tex_mid_height; + tex_coord[2] = T_BR[0] + tex_woff + tex_mid_width; + tex_coord[3] = T_BR[1] + tex_mid_height; + tex_coord[4] = T_TL[0] + tex_woff + tex_mid_width; + tex_coord[5] = T_TL[1] + tex_hoff; + tex_coord[6] = T_TR[0] + tex_woff + tex_mid_width; + tex_coord[7] = T_TR[1] + tex_hoff; + + gfx_display_draw(&draw, video_info); + + /* bottom-left corner */ + vert_coord[0] = V_BL[0]; + vert_coord[1] = V_BL[1] - vert_hoff - vert_scaled_mid_height; + vert_coord[2] = V_BR[0]; + vert_coord[3] = V_BR[1] - vert_hoff - vert_scaled_mid_height; + vert_coord[4] = V_TL[0]; + vert_coord[5] = V_TL[1] - vert_hoff - vert_scaled_mid_height; + vert_coord[6] = V_TR[0]; + vert_coord[7] = V_TR[1] - vert_hoff - vert_scaled_mid_height; + + tex_coord[0] = T_BL[0]; + tex_coord[1] = T_BL[1] + tex_hoff + tex_mid_height; + tex_coord[2] = T_BR[0]; + tex_coord[3] = T_BR[1] + tex_hoff + tex_mid_height; + tex_coord[4] = T_TL[0]; + tex_coord[5] = T_TL[1] + tex_hoff + tex_mid_height; + tex_coord[6] = T_TR[0]; + tex_coord[7] = T_TR[1] + tex_hoff + tex_mid_height; + + gfx_display_draw(&draw, video_info); + + /* bottom-middle section */ + vert_coord[0] = V_BL[0] + vert_woff; + vert_coord[1] = V_BL[1] - vert_hoff - vert_scaled_mid_height; + vert_coord[2] = V_BR[0] + vert_scaled_mid_width; + vert_coord[3] = V_BR[1] - vert_hoff - vert_scaled_mid_height; + vert_coord[4] = V_TL[0] + vert_woff; + vert_coord[5] = V_TL[1] - vert_hoff - vert_scaled_mid_height; + vert_coord[6] = V_TR[0] + vert_scaled_mid_width; + vert_coord[7] = V_TR[1] - vert_hoff - vert_scaled_mid_height; + + tex_coord[0] = T_BL[0] + tex_woff; + tex_coord[1] = T_BL[1] + tex_hoff + tex_mid_height; + tex_coord[2] = T_BR[0] + tex_mid_width; + tex_coord[3] = T_BR[1] + tex_hoff + tex_mid_height; + tex_coord[4] = T_TL[0] + tex_woff; + tex_coord[5] = T_TL[1] + tex_hoff + tex_mid_height; + tex_coord[6] = T_TR[0] + tex_mid_width; + tex_coord[7] = T_TR[1] + tex_hoff + tex_mid_height; + + gfx_display_draw(&draw, video_info); + + /* bottom-right corner */ + vert_coord[0] = V_BL[0] + vert_woff + vert_scaled_mid_width; + vert_coord[1] = V_BL[1] - vert_hoff - vert_scaled_mid_height; + vert_coord[2] = V_BR[0] + vert_scaled_mid_width + vert_woff; + vert_coord[3] = V_BR[1] - vert_hoff - vert_scaled_mid_height; + vert_coord[4] = V_TL[0] + vert_woff + vert_scaled_mid_width; + vert_coord[5] = V_TL[1] - vert_hoff - vert_scaled_mid_height; + vert_coord[6] = V_TR[0] + vert_scaled_mid_width + vert_woff; + vert_coord[7] = V_TR[1] - vert_hoff - vert_scaled_mid_height; + + tex_coord[0] = T_BL[0] + tex_woff + tex_mid_width; + tex_coord[1] = T_BL[1] + tex_hoff + tex_mid_height; + tex_coord[2] = T_BR[0] + tex_woff + tex_mid_width; + tex_coord[3] = T_BR[1] + tex_hoff + tex_mid_height; + tex_coord[4] = T_TL[0] + tex_woff + tex_mid_width; + tex_coord[5] = T_TL[1] + tex_hoff + tex_mid_height; + tex_coord[6] = T_TR[0] + tex_woff + tex_mid_width; + tex_coord[7] = T_TR[1] + tex_hoff + tex_mid_height; + + gfx_display_draw(&draw, video_info); + + free(colors); + free(vert_coord); + free(tex_coord); +} + +void gfx_display_rotate_z(gfx_display_ctx_rotate_draw_t *draw, + video_frame_info_t *video_info) +{ + math_matrix_4x4 matrix_rotated, matrix_scaled; + math_matrix_4x4 *b = NULL; + + if ( + !draw || + !menu_disp || + !menu_disp->get_default_mvp || + menu_disp->handles_transform + ) + return; + + b = (math_matrix_4x4*)menu_disp->get_default_mvp(video_info); + + if (!b) + return; + + matrix_4x4_rotate_z(matrix_rotated, draw->rotation); + matrix_4x4_multiply(*draw->matrix, matrix_rotated, *b); + + if (!draw->scale_enable) + return; + + matrix_4x4_scale(matrix_scaled, + draw->scale_x, draw->scale_y, draw->scale_z); + matrix_4x4_multiply(*draw->matrix, matrix_scaled, *draw->matrix); +} + +/* + * Draw a hardware cursor on top of the screen for the mouse. + */ +void gfx_display_draw_cursor( + video_frame_info_t *video_info, + float *color, float cursor_size, uintptr_t texture, + float x, float y, unsigned width, unsigned height) +{ + gfx_display_ctx_draw_t draw; + struct video_coords coords; + bool cursor_visible = video_info->fullscreen; + if (!video_info->menu_mouse_enable || !cursor_visible) + return; + + coords.vertices = 4; + coords.vertex = NULL; + coords.tex_coord = NULL; + coords.lut_tex_coord = NULL; + coords.color = (const float*)color; + + if (menu_disp && menu_disp->blend_begin) + menu_disp->blend_begin(video_info); + + draw.x = x - (cursor_size / 2); + draw.y = (int)height - y - (cursor_size / 2); + draw.width = cursor_size; + draw.height = cursor_size; + draw.coords = &coords; + draw.matrix_data = NULL; + draw.texture = texture; + draw.prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; + draw.pipeline.id = 0; + + gfx_display_draw(&draw, video_info); + + if (menu_disp && menu_disp->blend_end) + menu_disp->blend_end(video_info); +} + +void gfx_display_push_quad( + unsigned width, unsigned height, + const float *colors, int x1, int y1, + int x2, int y2) +{ + float vertex[8]; + video_coords_t coords; + const float *coord_draw_ptr = NULL; + video_coord_array_t *ca = &menu_disp_ca; + + vertex[0] = x1 / (float)width; + vertex[1] = y1 / (float)height; + vertex[2] = x2 / (float)width; + vertex[3] = y1 / (float)height; + vertex[4] = x1 / (float)width; + vertex[5] = y2 / (float)height; + vertex[6] = x2 / (float)width; + vertex[7] = y2 / (float)height; + + if (menu_disp && menu_disp->get_default_tex_coords) + coord_draw_ptr = menu_disp->get_default_tex_coords(); + + coords.color = colors; + coords.vertex = vertex; + coords.tex_coord = coord_draw_ptr; + coords.lut_tex_coord = coord_draw_ptr; + coords.vertices = 3; + + video_coord_array_append(ca, &coords, 3); + + coords.color += 4; + coords.vertex += 2; + coords.tex_coord += 2; + coords.lut_tex_coord += 2; + + video_coord_array_append(ca, &coords, 3); +} + +void gfx_display_snow( + int16_t pointer_x, + int16_t pointer_y, + int width, int height) +{ + struct display_particle + { + float x, y; + float xspeed, yspeed; + float alpha; + bool alive; + }; + static struct display_particle particles[PARTICLES_COUNT] = {{0}}; + static int timeout = 0; + unsigned i, max_gen = 2; + + for (i = 0; i < PARTICLES_COUNT; ++i) + { + struct display_particle *p = (struct display_particle*)&particles[i]; + + if (p->alive) + { +#if 0 + menu_input_pointer_t pointer; + menu_input_get_pointer_state(&pointer); +#endif + + p->y += p->yspeed; + p->x += gfx_display_scalef( + pointer_x, 0, width, -0.3, 0.3); + p->x += p->xspeed; + + p->alive = p->y >= 0 && p->y < height + && p->x >= 0 && p->x < width; + } + else if (max_gen > 0 && timeout <= 0) + { + p->xspeed = gfx_display_randf(-0.2, 0.2); + p->yspeed = gfx_display_randf(1, 2); + p->y = 0; + p->x = rand() % width; + p->alpha = (float)rand() / (float)RAND_MAX; + p->alive = true; + + max_gen--; + } + } + + if (max_gen == 0) + timeout = 3; + else + timeout--; + + for (i = 0; i < PARTICLES_COUNT; ++i) + { + unsigned j; + float alpha, colors[16]; + struct display_particle *p = &particles[i]; + + if (!p->alive) + continue; + + alpha = gfx_display_randf(0, 100) > 90 ? + p->alpha/2 : p->alpha; + + for (j = 0; j < 16; j++) + { + colors[j] = 1; + if (j == 3 || j == 7 || j == 11 || j == 15) + colors[j] = alpha; + } + + gfx_display_push_quad(width, height, + colors, p->x-2, p->y-2, p->x+2, p->y+2); + + j++; + } +} + +/* Setup: Initializes the font associated + * to the menu driver */ +font_data_t *gfx_display_font( + enum application_special_type type, + float menu_font_size, + bool is_threaded) +{ + char fontpath[PATH_MAX_LENGTH]; + + if (!menu_disp) + return NULL; + + fontpath[0] = '\0'; + + fill_pathname_application_special( + fontpath, sizeof(fontpath), type); + + return gfx_display_font_file(fontpath, menu_font_size, is_threaded); +} + +/* Returns the OSK key at a given position */ +int gfx_display_osk_ptr_at_pos(void *data, int x, int y, + unsigned width, unsigned height) +{ + unsigned i; + int ptr_width = width / 11; + int ptr_height = height / 10; + + if (ptr_width >= ptr_height) + ptr_width = ptr_height; + + for (i = 0; i < 44; i++) + { + int line_y = (i / 11)*height/10.0; + int ptr_x = width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width; + int ptr_y = height/2.0 + ptr_height*1.5 + line_y - ptr_height; + + if (x > ptr_x && x < ptr_x + ptr_width + && y > ptr_y && y < ptr_y + ptr_height) + return i; + } + + return -1; +} + +/* Get the menu framebuffer's size dimensions. */ +void gfx_display_get_fb_size(unsigned *fb_width, + unsigned *fb_height, size_t *fb_pitch) +{ + *fb_width = gfx_display_framebuf_width; + *fb_height = gfx_display_framebuf_height; + *fb_pitch = gfx_display_framebuf_pitch; +} + +/* Set the menu framebuffer's width. */ +void gfx_display_set_width(unsigned width) +{ + gfx_display_framebuf_width = width; +} + +/* Set the menu framebuffer's height. */ +void gfx_display_set_height(unsigned height) +{ + gfx_display_framebuf_height = height; +} + +void gfx_display_set_header_height(unsigned height) +{ + gfx_display_header_height = height; +} + +unsigned gfx_display_get_header_height(void) +{ + return gfx_display_header_height; +} + +size_t gfx_display_get_framebuffer_pitch(void) +{ + return gfx_display_framebuf_pitch; +} + +void gfx_display_set_framebuffer_pitch(size_t pitch) +{ + gfx_display_framebuf_pitch = pitch; +} + +bool gfx_display_get_msg_force(void) +{ + return gfx_display_msg_force; +} + +void gfx_display_set_msg_force(bool state) +{ + gfx_display_msg_force = state; +} + +/* Returns true if an animation is still active or + * when the menu framebuffer still is dirty and + * therefore it still needs to be rendered onscreen. + * + * This function can be used for optimization purposes + * so that we don't have to render the menu graphics per-frame + * unless a change has happened. + * */ +bool gfx_display_get_update_pending(void) +{ + if (gfx_animation_is_active() || gfx_display_framebuf_dirty) + return true; + return false; +} + +void gfx_display_set_viewport(unsigned width, unsigned height) +{ + video_driver_set_viewport(width, height, true, false); +} + +void gfx_display_unset_viewport(unsigned width, unsigned height) +{ + video_driver_set_viewport(width, height, false, true); +} + +/* Checks if the menu framebuffer has its 'dirty flag' set. This + * means that the current contents of the framebuffer has changed + * and that it has to be rendered to the screen. */ +bool gfx_display_get_framebuffer_dirty_flag(void) +{ + return gfx_display_framebuf_dirty; +} + +/* Set the menu framebuffer's 'dirty flag'. */ +void gfx_display_set_framebuffer_dirty_flag(void) +{ + gfx_display_framebuf_dirty = true; +} + +/* Unset the menu framebufer's 'dirty flag'. */ +void gfx_display_unset_framebuffer_dirty_flag(void) +{ + gfx_display_framebuf_dirty = false; +} + +void gfx_display_draw_keyboard( + uintptr_t hover_texture, + const font_data_t *font, + video_frame_info_t *video_info, + char *grid[], unsigned id, + unsigned text_color) +{ + unsigned i; + int ptr_width, ptr_height; + unsigned width = video_info->width; + unsigned height = video_info->height; + + float white[16]= { + 1.00, 1.00, 1.00, 1.00, + 1.00, 1.00, 1.00, 1.00, + 1.00, 1.00, 1.00, 1.00, + 1.00, 1.00, 1.00, 1.00, + }; + + gfx_display_draw_quad( + video_info, + 0, height/2.0, width, height/2.0, + width, height, + &osk_dark[0]); + + ptr_width = width / 11; + ptr_height = height / 10; + + if (ptr_width >= ptr_height) + ptr_width = ptr_height; + + for (i = 0; i < 44; i++) + { + int line_y = (i / 11) * height / 10.0; + unsigned color = 0xffffffff; + + if (i == id) + { + gfx_display_blend_begin(video_info); + + gfx_display_draw_texture( + video_info, + width/2.0 - (11*ptr_width)/2.0 + (i % 11) * ptr_width, + height/2.0 + ptr_height*1.5 + line_y, + ptr_width, ptr_height, + width, height, + &white[0], + hover_texture); + + gfx_display_blend_end(video_info); + + color = text_color; + } + + gfx_display_draw_text(font, grid[i], + width/2.0 - (11*ptr_width)/2.0 + (i % 11) + * ptr_width + ptr_width/2.0, + height/2.0 + ptr_height + line_y + font->size / 3, + width, height, color, TEXT_ALIGN_CENTER, 1.0f, + false, 0, false); + } +} + +/* Draw text on top of the screen. + */ +void gfx_display_draw_text( + const font_data_t *font, const char *text, + float x, float y, int width, int height, + uint32_t color, enum text_alignment text_align, + float scale, bool shadows_enable, float shadow_offset, + bool draw_outside) +{ + struct font_params params; + + if ((color & 0x000000FF) == 0) + return; + + /* Don't draw outside of the screen */ + if (!draw_outside && + ((x < -64 || x > width + 64) + || (y < -64 || y > height + 64)) + ) + return; + + params.x = x / width; + params.y = 1.0f - y / height; + params.scale = scale; + params.drop_mod = 0.0f; + params.drop_x = 0.0f; + params.drop_y = 0.0f; + params.color = color; + params.full_screen = true; + params.text_align = text_align; + + if (shadows_enable) + { + params.drop_x = shadow_offset; + params.drop_y = -shadow_offset; + params.drop_alpha = 0.35f; + } + + video_driver_set_osd_msg(text, ¶ms, (void*)font); +} + +bool 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) +{ + struct texture_image ti; + char texpath[PATH_MAX_LENGTH] = {0}; + + ti.width = 0; + ti.height = 0; + ti.pixels = NULL; + ti.supports_rgba = video_driver_supports_rgba(); + + if (string_is_empty(texture_path)) + return false; + + fill_pathname_join(texpath, iconpath, texture_path, sizeof(texpath)); + + if (!path_is_valid(texpath)) + return false; + + if (!image_texture_load(&ti, texpath)) + return false; + + if (width) + *width = ti.width; + + if (height) + *height = ti.height; + + video_driver_texture_load(&ti, + filter_type, item); + image_texture_free(&ti); + + return true; +} + + +bool gfx_display_reset_textures_list_buffer( + uintptr_t *item, enum texture_filter_type filter_type, + void* buffer, unsigned buffer_len, enum image_type_enum image_type, + unsigned *width, unsigned *height) +{ + struct texture_image ti; + + ti.width = 0; + ti.height = 0; + ti.pixels = NULL; + ti.supports_rgba = video_driver_supports_rgba(); + + if (!image_texture_load_buffer(&ti, image_type, buffer, buffer_len)) + return false; + + if (width) + *width = ti.width; + + if (height) + *height = ti.height; + + /* if the poke interface doesn't support texture load then return false */ + if (!video_driver_texture_load(&ti, filter_type, item)) + return false; + image_texture_free(&ti); + return true; +} + +/* Teardown; deinitializes and frees all + * fonts associated to the menu driver */ +void gfx_display_font_free(font_data_t *font) +{ + font_driver_free(font); +} + + +void gfx_display_allocate_white_texture(void) +{ + struct texture_image ti; + static const uint8_t white_data[] = { 0xff, 0xff, 0xff, 0xff }; + + ti.width = 1; + ti.height = 1; + ti.pixels = (uint32_t*)&white_data; + + if (gfx_display_white_texture) + video_driver_texture_unload(&gfx_display_white_texture); + + video_driver_texture_load(&ti, + TEXTURE_FILTER_NEAREST, &gfx_display_white_texture); +} + + +void gfx_display_free(void) +{ + video_coord_array_free(&menu_disp_ca); + gfx_animation_ctl(MENU_ANIMATION_CTL_DEINIT, NULL); + + gfx_display_msg_force = false; + gfx_display_header_height = 0; + gfx_display_framebuf_width = 0; + gfx_display_framebuf_height = 0; + gfx_display_framebuf_pitch = 0; + menu_disp = NULL; + gfx_display_has_windowed = false; +} + +void gfx_display_init(void) +{ + gfx_display_has_windowed = video_driver_has_windowed(); + menu_disp_ca.allocated = 0; +} + + +bool gfx_display_driver_exists(const char *s) +{ + unsigned i; + for (i = 0; i < ARRAY_SIZE(gfx_display_ctx_drivers); i++) + { + if (string_is_equal(s, gfx_display_ctx_drivers[i]->ident)) + return true; + } + + return false; +} + +bool gfx_display_init_first_driver(bool video_is_threaded) +{ + unsigned i; + + for (i = 0; gfx_display_ctx_drivers[i]; i++) + { + if (!gfx_display_check_compatibility( + gfx_display_ctx_drivers[i]->type, + video_is_threaded)) + continue; + + RARCH_LOG("[Menu]: Found menu display driver: \"%s\".\n", + gfx_display_ctx_drivers[i]->ident); + menu_disp = gfx_display_ctx_drivers[i]; + return true; + } + return false; +} diff --git a/gfx/gfx_display.h b/gfx/gfx_display.h index 61916f4b0d..e9ce317738 100644 --- a/gfx/gfx_display.h +++ b/gfx/gfx_display.h @@ -1,364 +1,364 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * Copyright (C) 2011-2017 - Daniel De Matteis - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#ifndef __GFX_DISPLAY_H__ -#define __GFX_DISPLAY_H__ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "../retroarch.h" -#include "../file_path_special.h" -#include "../gfx/font_driver.h" - -RETRO_BEGIN_DECLS - -#define COLOR_TEXT_ALPHA(color, alpha) (color & 0xFFFFFF00) | alpha - -#define HEX_R(hex) ((hex >> 16) & 0xFF) * (1.0f / 255.0f) -#define HEX_G(hex) ((hex >> 8 ) & 0xFF) * (1.0f / 255.0f) -#define HEX_B(hex) ((hex >> 0 ) & 0xFF) * (1.0f / 255.0f) - -#define COLOR_HEX_TO_FLOAT(hex, alpha) { \ - HEX_R(hex), HEX_G(hex), HEX_B(hex), alpha, \ - HEX_R(hex), HEX_G(hex), HEX_B(hex), alpha, \ - HEX_R(hex), HEX_G(hex), HEX_B(hex), alpha, \ - HEX_R(hex), HEX_G(hex), HEX_B(hex), alpha \ -} - -enum menu_driver_id_type -{ - MENU_DRIVER_ID_UNKNOWN = 0, - MENU_DRIVER_ID_RGUI, - MENU_DRIVER_ID_OZONE, - MENU_DRIVER_ID_GLUI, - MENU_DRIVER_ID_XMB, - MENU_DRIVER_ID_XUI, - MENU_DRIVER_ID_STRIPES -}; - - -enum gfx_display_prim_type -{ - GFX_DISPLAY_PRIM_NONE = 0, - GFX_DISPLAY_PRIM_TRIANGLESTRIP, - GFX_DISPLAY_PRIM_TRIANGLES -}; - -enum gfx_display_driver_type -{ - GFX_VIDEO_DRIVER_GENERIC = 0, - GFX_VIDEO_DRIVER_OPENGL, - GFX_VIDEO_DRIVER_OPENGL1, - GFX_VIDEO_DRIVER_OPENGL_CORE, - GFX_VIDEO_DRIVER_VULKAN, - GFX_VIDEO_DRIVER_METAL, - GFX_VIDEO_DRIVER_DIRECT3D8, - GFX_VIDEO_DRIVER_DIRECT3D9, - GFX_VIDEO_DRIVER_DIRECT3D10, - GFX_VIDEO_DRIVER_DIRECT3D11, - GFX_VIDEO_DRIVER_DIRECT3D12, - GFX_VIDEO_DRIVER_VITA2D, - GFX_VIDEO_DRIVER_CTR, - GFX_VIDEO_DRIVER_WIIU, - GFX_VIDEO_DRIVER_GDI, - GFX_VIDEO_DRIVER_SWITCH -}; - -typedef struct gfx_display_ctx_clearcolor -{ - float r; - float g; - float b; - float a; -} gfx_display_ctx_clearcolor_t; - -typedef struct gfx_display_frame_info -{ - bool shadows_enable; -} gfx_display_frame_info_t; - -typedef struct gfx_display_ctx_draw gfx_display_ctx_draw_t; - - -typedef struct gfx_display_ctx_driver -{ - /* Draw graphics to the screen. */ - void (*draw)(gfx_display_ctx_draw_t *draw, video_frame_info_t *video_info); - /* Draw one of the menu pipeline shaders. */ - void (*draw_pipeline)(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info); - void (*viewport)(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info); - /* Start blending operation. */ - void (*blend_begin)(video_frame_info_t *video_info); - /* Finish blending operation. */ - void (*blend_end)(video_frame_info_t *video_info); - /* Set the clear color back to its default values. */ - void (*restore_clear_color)(void); - /* Set the color to be used when clearing the screen */ - void (*clear_color)(gfx_display_ctx_clearcolor_t *clearcolor, - video_frame_info_t *video_info); - /* Get the default Model-View-Projection matrix */ - void *(*get_default_mvp)(video_frame_info_t *video_info); - /* Get the default vertices matrix */ - const float *(*get_default_vertices)(void); - /* Get the default texture coordinates matrix */ - const float *(*get_default_tex_coords)(void); - /* Initialize the first compatible font driver for this menu driver. */ - bool (*font_init_first)( - void **font_handle, void *video_data, - const char *font_path, float font_size, - bool is_threaded); - enum gfx_display_driver_type type; - const char *ident; - bool handles_transform; - /* Enables and disables scissoring */ - void (*scissor_begin)(video_frame_info_t *video_info, int x, int y, unsigned width, unsigned height); - void (*scissor_end)(video_frame_info_t *video_info); -} gfx_display_ctx_driver_t; - -struct gfx_display_ctx_draw -{ - float x; - float y; - float *color; - const float *vertex; - const float *tex_coord; - unsigned width; - unsigned height; - uintptr_t texture; - size_t vertex_count; - struct video_coords *coords; - void *matrix_data; - enum gfx_display_prim_type prim_type; - struct - { - unsigned id; - const void *backend_data; - size_t backend_data_size; - bool active; - } pipeline; - float rotation; - float scale_factor; -}; - -typedef struct gfx_display_ctx_rotate_draw -{ - bool scale_enable; - float rotation; - float scale_x; - float scale_y; - float scale_z; - math_matrix_4x4 *matrix; -} gfx_display_ctx_rotate_draw_t; - -typedef struct gfx_display_ctx_coord_draw -{ - const float *ptr; -} gfx_display_ctx_coord_draw_t; - -typedef struct gfx_display_ctx_datetime -{ - char *s; - size_t len; - unsigned time_mode; -} gfx_display_ctx_datetime_t; - -typedef struct gfx_display_ctx_powerstate -{ - char *s; - size_t len; - unsigned percent; - bool battery_enabled; - bool charging; -} gfx_display_ctx_powerstate_t; - -#define gfx_display_set_alpha(color, alpha_value) (color[3] = color[7] = color[11] = color[15] = (alpha_value)) - -void gfx_display_free(void); - -void gfx_display_init(void); - -void gfx_display_blend_begin(video_frame_info_t *video_info); - -void gfx_display_blend_end(video_frame_info_t *video_info); - -void gfx_display_push_quad( - unsigned width, unsigned height, - const float *colors, int x1, int y1, - int x2, int y2); - -void gfx_display_snow( - int16_t pointer_x, - int16_t pointer_y, - int width, int height); - -void gfx_display_draw_cursor( - video_frame_info_t *video_info, - float *color, float cursor_size, uintptr_t texture, - float x, float y, unsigned width, unsigned height); - -void gfx_display_draw_text( - const font_data_t *font, const char *text, - float x, float y, int width, int height, - uint32_t color, enum text_alignment text_align, - float scale_factor, bool shadows_enable, float shadow_offset, - bool draw_outside); - -font_data_t *gfx_display_font( - enum application_special_type type, - float font_size, - bool video_is_threaded); - - -void gfx_display_scissor_begin(video_frame_info_t *video_info, int x, int y, unsigned width, unsigned height); -void gfx_display_scissor_end(video_frame_info_t *video_info); - -void gfx_display_font_free(font_data_t *font); - -void gfx_display_coords_array_reset(void); -video_coord_array_t *gfx_display_get_coords_array(void); - -void gfx_display_set_width(unsigned width); -void gfx_display_get_fb_size(unsigned *fb_width, unsigned *fb_height, - size_t *fb_pitch); -void gfx_display_set_height(unsigned height); -void gfx_display_set_header_height(unsigned height); -unsigned gfx_display_get_header_height(void); -size_t gfx_display_get_framebuffer_pitch(void); -void gfx_display_set_framebuffer_pitch(size_t pitch); - -bool gfx_display_get_msg_force(void); -void gfx_display_set_msg_force(bool state); -bool gfx_display_get_update_pending(void); -void gfx_display_set_viewport(unsigned width, unsigned height); -void gfx_display_unset_viewport(unsigned width, unsigned height); -bool gfx_display_get_framebuffer_dirty_flag(void); -void gfx_display_set_framebuffer_dirty_flag(void); -void gfx_display_unset_framebuffer_dirty_flag(void); -bool gfx_display_init_first_driver(bool video_is_threaded); -bool gfx_display_restore_clear_color(void); -void gfx_display_clear_color(gfx_display_ctx_clearcolor_t *color, - video_frame_info_t *video_info); -void gfx_display_draw(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info); -void gfx_display_draw_blend(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info); -void gfx_display_draw_keyboard( - uintptr_t hover_texture, - const font_data_t *font, - video_frame_info_t *video_info, - char *grid[], unsigned id, - unsigned text_color); - -void gfx_display_draw_pipeline(gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info); -void gfx_display_draw_bg( - gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info, - bool add_opacity, float opacity_override); -void gfx_display_draw_gradient( - gfx_display_ctx_draw_t *draw, - video_frame_info_t *video_info); -void gfx_display_draw_quad( - video_frame_info_t *video_info, - int x, int y, unsigned w, unsigned h, - unsigned width, unsigned height, - float *color); -void gfx_display_draw_polygon( - video_frame_info_t *video_info, - int x1, int y1, - int x2, int y2, - int x3, int y3, - int x4, int y4, - unsigned width, unsigned height, - float *color); -void gfx_display_draw_texture( - video_frame_info_t *video_info, - int x, int y, unsigned w, unsigned h, - unsigned width, unsigned height, - float *color, uintptr_t texture); -void gfx_display_draw_texture_slice( - video_frame_info_t *video_info, - 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); - -void gfx_display_rotate_z(gfx_display_ctx_rotate_draw_t *draw, - video_frame_info_t *video_info); - -font_data_t *gfx_display_font_file(char* fontpath, float font_size, bool is_threaded); - -bool 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); - -bool gfx_display_reset_textures_list_buffer( - uintptr_t *item, enum texture_filter_type filter_type, - void* buffer, unsigned buffer_len, enum image_type_enum image_type, - unsigned *width, unsigned *height); - -/* Returns the OSK key at a given position */ -int gfx_display_osk_ptr_at_pos(void *data, int x, int y, - unsigned width, unsigned height); - -enum menu_driver_id_type gfx_display_get_driver_id(void); - -void gfx_display_set_driver_id(enum menu_driver_id_type type); - -float gfx_display_get_dpi_scale(unsigned width, unsigned height); - -float gfx_display_get_widget_dpi_scale(unsigned width, unsigned height); - -float gfx_display_get_widget_pixel_scale(unsigned width, unsigned height); - -void gfx_display_allocate_white_texture(void); - -bool gfx_display_driver_exists(const char *s); - -bool gfx_display_init_first_driver(bool video_is_threaded); - -extern uintptr_t gfx_display_white_texture; - -extern gfx_display_ctx_driver_t gfx_display_ctx_gl; -extern gfx_display_ctx_driver_t gfx_display_ctx_gl_core; -extern gfx_display_ctx_driver_t gfx_display_ctx_gl1; -extern gfx_display_ctx_driver_t gfx_display_ctx_vulkan; -extern gfx_display_ctx_driver_t gfx_display_ctx_metal; -extern gfx_display_ctx_driver_t gfx_display_ctx_d3d8; -extern gfx_display_ctx_driver_t gfx_display_ctx_d3d9; -extern gfx_display_ctx_driver_t gfx_display_ctx_d3d10; -extern gfx_display_ctx_driver_t gfx_display_ctx_d3d11; -extern gfx_display_ctx_driver_t gfx_display_ctx_d3d12; -extern gfx_display_ctx_driver_t gfx_display_ctx_vita2d; -extern gfx_display_ctx_driver_t gfx_display_ctx_ctr; -extern gfx_display_ctx_driver_t gfx_display_ctx_wiiu; -extern gfx_display_ctx_driver_t gfx_display_ctx_gdi; -extern gfx_display_ctx_driver_t gfx_display_ctx_switch; - -RETRO_END_DECLS - -#endif +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __GFX_DISPLAY_H__ +#define __GFX_DISPLAY_H__ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../retroarch.h" +#include "../file_path_special.h" +#include "../gfx/font_driver.h" + +RETRO_BEGIN_DECLS + +#define COLOR_TEXT_ALPHA(color, alpha) (color & 0xFFFFFF00) | alpha + +#define HEX_R(hex) ((hex >> 16) & 0xFF) * (1.0f / 255.0f) +#define HEX_G(hex) ((hex >> 8 ) & 0xFF) * (1.0f / 255.0f) +#define HEX_B(hex) ((hex >> 0 ) & 0xFF) * (1.0f / 255.0f) + +#define COLOR_HEX_TO_FLOAT(hex, alpha) { \ + HEX_R(hex), HEX_G(hex), HEX_B(hex), alpha, \ + HEX_R(hex), HEX_G(hex), HEX_B(hex), alpha, \ + HEX_R(hex), HEX_G(hex), HEX_B(hex), alpha, \ + HEX_R(hex), HEX_G(hex), HEX_B(hex), alpha \ +} + +enum menu_driver_id_type +{ + MENU_DRIVER_ID_UNKNOWN = 0, + MENU_DRIVER_ID_RGUI, + MENU_DRIVER_ID_OZONE, + MENU_DRIVER_ID_GLUI, + MENU_DRIVER_ID_XMB, + MENU_DRIVER_ID_XUI, + MENU_DRIVER_ID_STRIPES +}; + + +enum gfx_display_prim_type +{ + GFX_DISPLAY_PRIM_NONE = 0, + GFX_DISPLAY_PRIM_TRIANGLESTRIP, + GFX_DISPLAY_PRIM_TRIANGLES +}; + +enum gfx_display_driver_type +{ + GFX_VIDEO_DRIVER_GENERIC = 0, + GFX_VIDEO_DRIVER_OPENGL, + GFX_VIDEO_DRIVER_OPENGL1, + GFX_VIDEO_DRIVER_OPENGL_CORE, + GFX_VIDEO_DRIVER_VULKAN, + GFX_VIDEO_DRIVER_METAL, + GFX_VIDEO_DRIVER_DIRECT3D8, + GFX_VIDEO_DRIVER_DIRECT3D9, + GFX_VIDEO_DRIVER_DIRECT3D10, + GFX_VIDEO_DRIVER_DIRECT3D11, + GFX_VIDEO_DRIVER_DIRECT3D12, + GFX_VIDEO_DRIVER_VITA2D, + GFX_VIDEO_DRIVER_CTR, + GFX_VIDEO_DRIVER_WIIU, + GFX_VIDEO_DRIVER_GDI, + GFX_VIDEO_DRIVER_SWITCH +}; + +typedef struct gfx_display_ctx_clearcolor +{ + float r; + float g; + float b; + float a; +} gfx_display_ctx_clearcolor_t; + +typedef struct gfx_display_frame_info +{ + bool shadows_enable; +} gfx_display_frame_info_t; + +typedef struct gfx_display_ctx_draw gfx_display_ctx_draw_t; + + +typedef struct gfx_display_ctx_driver +{ + /* Draw graphics to the screen. */ + void (*draw)(gfx_display_ctx_draw_t *draw, video_frame_info_t *video_info); + /* Draw one of the menu pipeline shaders. */ + void (*draw_pipeline)(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info); + void (*viewport)(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info); + /* Start blending operation. */ + void (*blend_begin)(video_frame_info_t *video_info); + /* Finish blending operation. */ + void (*blend_end)(video_frame_info_t *video_info); + /* Set the clear color back to its default values. */ + void (*restore_clear_color)(void); + /* Set the color to be used when clearing the screen */ + void (*clear_color)(gfx_display_ctx_clearcolor_t *clearcolor, + video_frame_info_t *video_info); + /* Get the default Model-View-Projection matrix */ + void *(*get_default_mvp)(video_frame_info_t *video_info); + /* Get the default vertices matrix */ + const float *(*get_default_vertices)(void); + /* Get the default texture coordinates matrix */ + const float *(*get_default_tex_coords)(void); + /* Initialize the first compatible font driver for this menu driver. */ + bool (*font_init_first)( + void **font_handle, void *video_data, + const char *font_path, float font_size, + bool is_threaded); + enum gfx_display_driver_type type; + const char *ident; + bool handles_transform; + /* Enables and disables scissoring */ + void (*scissor_begin)(video_frame_info_t *video_info, int x, int y, unsigned width, unsigned height); + void (*scissor_end)(video_frame_info_t *video_info); +} gfx_display_ctx_driver_t; + +struct gfx_display_ctx_draw +{ + float x; + float y; + float *color; + const float *vertex; + const float *tex_coord; + unsigned width; + unsigned height; + uintptr_t texture; + size_t vertex_count; + struct video_coords *coords; + void *matrix_data; + enum gfx_display_prim_type prim_type; + struct + { + unsigned id; + const void *backend_data; + size_t backend_data_size; + bool active; + } pipeline; + float rotation; + float scale_factor; +}; + +typedef struct gfx_display_ctx_rotate_draw +{ + bool scale_enable; + float rotation; + float scale_x; + float scale_y; + float scale_z; + math_matrix_4x4 *matrix; +} gfx_display_ctx_rotate_draw_t; + +typedef struct gfx_display_ctx_coord_draw +{ + const float *ptr; +} gfx_display_ctx_coord_draw_t; + +typedef struct gfx_display_ctx_datetime +{ + char *s; + size_t len; + unsigned time_mode; +} gfx_display_ctx_datetime_t; + +typedef struct gfx_display_ctx_powerstate +{ + char *s; + size_t len; + unsigned percent; + bool battery_enabled; + bool charging; +} gfx_display_ctx_powerstate_t; + +#define gfx_display_set_alpha(color, alpha_value) (color[3] = color[7] = color[11] = color[15] = (alpha_value)) + +void gfx_display_free(void); + +void gfx_display_init(void); + +void gfx_display_blend_begin(video_frame_info_t *video_info); + +void gfx_display_blend_end(video_frame_info_t *video_info); + +void gfx_display_push_quad( + unsigned width, unsigned height, + const float *colors, int x1, int y1, + int x2, int y2); + +void gfx_display_snow( + int16_t pointer_x, + int16_t pointer_y, + int width, int height); + +void gfx_display_draw_cursor( + video_frame_info_t *video_info, + float *color, float cursor_size, uintptr_t texture, + float x, float y, unsigned width, unsigned height); + +void gfx_display_draw_text( + const font_data_t *font, const char *text, + float x, float y, int width, int height, + uint32_t color, enum text_alignment text_align, + float scale_factor, bool shadows_enable, float shadow_offset, + bool draw_outside); + +font_data_t *gfx_display_font( + enum application_special_type type, + float font_size, + bool video_is_threaded); + + +void gfx_display_scissor_begin(video_frame_info_t *video_info, int x, int y, unsigned width, unsigned height); +void gfx_display_scissor_end(video_frame_info_t *video_info); + +void gfx_display_font_free(font_data_t *font); + +void gfx_display_coords_array_reset(void); +video_coord_array_t *gfx_display_get_coords_array(void); + +void gfx_display_set_width(unsigned width); +void gfx_display_get_fb_size(unsigned *fb_width, unsigned *fb_height, + size_t *fb_pitch); +void gfx_display_set_height(unsigned height); +void gfx_display_set_header_height(unsigned height); +unsigned gfx_display_get_header_height(void); +size_t gfx_display_get_framebuffer_pitch(void); +void gfx_display_set_framebuffer_pitch(size_t pitch); + +bool gfx_display_get_msg_force(void); +void gfx_display_set_msg_force(bool state); +bool gfx_display_get_update_pending(void); +void gfx_display_set_viewport(unsigned width, unsigned height); +void gfx_display_unset_viewport(unsigned width, unsigned height); +bool gfx_display_get_framebuffer_dirty_flag(void); +void gfx_display_set_framebuffer_dirty_flag(void); +void gfx_display_unset_framebuffer_dirty_flag(void); +bool gfx_display_init_first_driver(bool video_is_threaded); +bool gfx_display_restore_clear_color(void); +void gfx_display_clear_color(gfx_display_ctx_clearcolor_t *color, + video_frame_info_t *video_info); +void gfx_display_draw(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info); +void gfx_display_draw_blend(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info); +void gfx_display_draw_keyboard( + uintptr_t hover_texture, + const font_data_t *font, + video_frame_info_t *video_info, + char *grid[], unsigned id, + unsigned text_color); + +void gfx_display_draw_pipeline(gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info); +void gfx_display_draw_bg( + gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info, + bool add_opacity, float opacity_override); +void gfx_display_draw_gradient( + gfx_display_ctx_draw_t *draw, + video_frame_info_t *video_info); +void gfx_display_draw_quad( + video_frame_info_t *video_info, + int x, int y, unsigned w, unsigned h, + unsigned width, unsigned height, + float *color); +void gfx_display_draw_polygon( + video_frame_info_t *video_info, + int x1, int y1, + int x2, int y2, + int x3, int y3, + int x4, int y4, + unsigned width, unsigned height, + float *color); +void gfx_display_draw_texture( + video_frame_info_t *video_info, + int x, int y, unsigned w, unsigned h, + unsigned width, unsigned height, + float *color, uintptr_t texture); +void gfx_display_draw_texture_slice( + video_frame_info_t *video_info, + 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); + +void gfx_display_rotate_z(gfx_display_ctx_rotate_draw_t *draw, + video_frame_info_t *video_info); + +font_data_t *gfx_display_font_file(char* fontpath, float font_size, bool is_threaded); + +bool 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); + +bool gfx_display_reset_textures_list_buffer( + uintptr_t *item, enum texture_filter_type filter_type, + void* buffer, unsigned buffer_len, enum image_type_enum image_type, + unsigned *width, unsigned *height); + +/* Returns the OSK key at a given position */ +int gfx_display_osk_ptr_at_pos(void *data, int x, int y, + unsigned width, unsigned height); + +enum menu_driver_id_type gfx_display_get_driver_id(void); + +void gfx_display_set_driver_id(enum menu_driver_id_type type); + +float gfx_display_get_dpi_scale(unsigned width, unsigned height); + +float gfx_display_get_widget_dpi_scale(unsigned width, unsigned height); + +float gfx_display_get_widget_pixel_scale(unsigned width, unsigned height); + +void gfx_display_allocate_white_texture(void); + +bool gfx_display_driver_exists(const char *s); + +bool gfx_display_init_first_driver(bool video_is_threaded); + +extern uintptr_t gfx_display_white_texture; + +extern gfx_display_ctx_driver_t gfx_display_ctx_gl; +extern gfx_display_ctx_driver_t gfx_display_ctx_gl_core; +extern gfx_display_ctx_driver_t gfx_display_ctx_gl1; +extern gfx_display_ctx_driver_t gfx_display_ctx_vulkan; +extern gfx_display_ctx_driver_t gfx_display_ctx_metal; +extern gfx_display_ctx_driver_t gfx_display_ctx_d3d8; +extern gfx_display_ctx_driver_t gfx_display_ctx_d3d9; +extern gfx_display_ctx_driver_t gfx_display_ctx_d3d10; +extern gfx_display_ctx_driver_t gfx_display_ctx_d3d11; +extern gfx_display_ctx_driver_t gfx_display_ctx_d3d12; +extern gfx_display_ctx_driver_t gfx_display_ctx_vita2d; +extern gfx_display_ctx_driver_t gfx_display_ctx_ctr; +extern gfx_display_ctx_driver_t gfx_display_ctx_wiiu; +extern gfx_display_ctx_driver_t gfx_display_ctx_gdi; +extern gfx_display_ctx_driver_t gfx_display_ctx_switch; + +RETRO_END_DECLS + +#endif