RetroArch/gfx/sdl_gfx.c

375 lines
9.3 KiB
C
Raw Normal View History

2012-04-21 21:13:50 +00:00
/* RetroArch - A frontend for libretro.
2013-01-01 00:37:37 +00:00
* Copyright (C) 2010-2013 - Hans-Kristian Arntzen
2011-04-21 01:23:44 +00:00
*
2012-04-21 21:13:50 +00:00
* RetroArch is free software: you can redistribute it and/or modify it under the terms
2011-04-21 01:23:44 +00:00
* 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.
*
2012-04-21 21:13:50 +00:00
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
2011-04-21 01:23:44 +00:00
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
2012-04-21 21:31:57 +00:00
* You should have received a copy of the GNU General Public License along with RetroArch.
2011-04-21 01:23:44 +00:00
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "SDL.h"
2011-12-24 23:59:46 +00:00
#include "../driver.h"
2011-04-21 01:23:44 +00:00
#include <stdlib.h>
#include <string.h>
2011-12-24 23:59:46 +00:00
#include "../general.h"
#include "scaler/scaler.h"
2011-04-21 01:39:03 +00:00
#include "gfx_common.h"
#include "gfx_context.h"
#include "fonts/fonts.h"
2012-09-26 13:52:25 +00:00
#ifdef HAVE_X11
#include "context/x11_common.h"
#endif
2011-04-22 01:13:09 +00:00
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
2012-09-30 09:26:26 +00:00
#include "SDL/SDL_syswm.h"
2011-12-24 12:46:12 +00:00
typedef struct sdl_video
2011-04-21 01:23:44 +00:00
{
2012-11-20 22:35:40 +00:00
SDL_Surface *screen;
2011-04-21 01:23:44 +00:00
bool quitting;
2012-12-14 21:33:04 +00:00
void *font;
const font_renderer_driver_t *font_driver;
2011-09-06 16:32:40 +00:00
uint8_t font_r;
uint8_t font_g;
uint8_t font_b;
struct scaler_ctx scaler;
unsigned last_width;
unsigned last_height;
2011-12-24 12:46:12 +00:00
} sdl_video_t;
2011-04-21 01:23:44 +00:00
static void sdl_gfx_free(void *data)
{
2011-12-24 12:46:12 +00:00
sdl_video_t *vid = (sdl_video_t*)data;
2011-04-21 01:23:44 +00:00
if (!vid)
return;
SDL_QuitSubSystem(SDL_INIT_VIDEO);
2011-04-22 01:13:09 +00:00
if (vid->font)
vid->font_driver->free(vid->font);
2011-04-22 01:13:09 +00:00
scaler_ctx_gen_reset(&vid->scaler);
2011-04-21 01:23:44 +00:00
free(vid);
}
2011-04-22 01:13:09 +00:00
static void sdl_init_font(sdl_video_t *vid, const char *font_path, unsigned font_size)
{
2011-11-09 23:15:41 +00:00
if (!g_settings.video.font_enable)
return;
if (font_renderer_create_default(&vid->font_driver, &vid->font))
2011-04-22 01:13:09 +00:00
{
2011-09-06 16:32:40 +00:00
int r = g_settings.video.msg_color_r * 255;
int g = g_settings.video.msg_color_g * 255;
int b = g_settings.video.msg_color_b * 255;
r = r < 0 ? 0 : (r > 255 ? 255 : r);
g = g < 0 ? 0 : (g > 255 ? 255 : g);
b = b < 0 ? 0 : (b > 255 ? 255 : b);
vid->font_r = r;
vid->font_g = g;
vid->font_b = b;
2011-04-22 01:13:09 +00:00
}
2011-10-06 19:12:11 +00:00
else
RARCH_LOG("Could not initialize fonts.\n");
2011-04-22 01:13:09 +00:00
}
2012-11-20 22:35:40 +00:00
static void sdl_render_msg(sdl_video_t *vid, SDL_Surface *buffer,
const char *msg, unsigned width, unsigned height, const SDL_PixelFormat *fmt)
2011-04-22 01:13:09 +00:00
{
2013-10-22 19:26:33 +00:00
int x, y;
if (!vid->font)
return;
2011-04-22 01:13:09 +00:00
struct font_output_list out;
vid->font_driver->render_msg(vid->font, msg, &out);
2011-04-22 01:13:09 +00:00
struct font_output *head = out.head;
int msg_base_x = g_settings.video.msg_pos_x * width;
int msg_base_y = (1.0 - g_settings.video.msg_pos_y) * height;
2011-04-22 01:13:09 +00:00
unsigned rshift = fmt->Rshift;
unsigned gshift = fmt->Gshift;
unsigned bshift = fmt->Bshift;
for (; head; head = head->next)
2011-04-22 01:13:09 +00:00
{
int base_x = msg_base_x + head->off_x;
int base_y = msg_base_y - head->off_y - head->height;
int glyph_width = head->width;
int glyph_height = head->height;
const uint8_t *src = head->output;
if (base_x < 0)
{
src -= base_x;
glyph_width += base_x;
base_x = 0;
}
if (base_y < 0)
{
src -= base_y * (int)head->pitch;
glyph_height += base_y;
base_y = 0;
}
int max_width = width - base_x;
int max_height = height - base_y;
if (max_width <= 0 || max_height <= 0)
continue;
if (glyph_width > max_width)
glyph_width = max_width;
if (glyph_height > max_height)
glyph_height = max_height;
uint32_t *out = (uint32_t*)buffer->pixels + base_y * (buffer->pitch >> 2) + base_x;
2013-10-22 19:26:33 +00:00
for (y = 0; y < glyph_height; y++, src += head->pitch, out += buffer->pitch >> 2)
2011-04-22 01:13:09 +00:00
{
2013-10-22 19:26:33 +00:00
for (x = 0; x < glyph_width; x++)
2011-04-22 01:13:09 +00:00
{
unsigned blend = src[x];
unsigned out_pix = out[x];
unsigned r = (out_pix >> rshift) & 0xff;
unsigned g = (out_pix >> gshift) & 0xff;
unsigned b = (out_pix >> bshift) & 0xff;
unsigned out_r = (r * (256 - blend) + vid->font_r * blend) >> 8;
unsigned out_g = (g * (256 - blend) + vid->font_g * blend) >> 8;
unsigned out_b = (b * (256 - blend) + vid->font_b * blend) >> 8;
out[x] = (out_r << rshift) | (out_g << gshift) | (out_b << bshift);
2011-04-22 01:13:09 +00:00
}
}
}
vid->font_driver->free_output(vid->font, &out);
2011-04-22 01:13:09 +00:00
}
2012-09-28 20:38:42 +00:00
static void sdl_gfx_set_handles(void)
{
// SysWMinfo headers are broken on OSX. :(
#if defined(_WIN32)
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (SDL_GetWMInfo(&info) == 1)
{
driver.display_type = RARCH_DISPLAY_WIN32;
driver.video_display = 0;
driver.video_window = (uintptr_t)info.window;
}
#elif defined(HAVE_X11)
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (SDL_GetWMInfo(&info) == 1)
{
driver.display_type = RARCH_DISPLAY_X11;
driver.video_display = (uintptr_t)info.info.x11.display;
driver.video_window = (uintptr_t)info.info.x11.window;
}
#endif
}
2011-11-02 18:31:36 +00:00
static void *sdl_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data)
2011-04-21 01:23:44 +00:00
{
#ifdef _WIN32
gfx_set_dwm();
#endif
#ifdef HAVE_X11
XInitThreads();
#endif
2011-04-21 01:23:44 +00:00
SDL_InitSubSystem(SDL_INIT_VIDEO);
2011-12-24 12:46:12 +00:00
sdl_video_t *vid = (sdl_video_t*)calloc(1, sizeof(*vid));
2011-04-21 01:23:44 +00:00
if (!vid)
return NULL;
const SDL_VideoInfo *video_info = SDL_GetVideoInfo();
2012-04-21 21:25:32 +00:00
rarch_assert(video_info);
2011-04-21 01:23:44 +00:00
unsigned full_x = video_info->current_w;
unsigned full_y = video_info->current_h;
2012-04-21 21:25:32 +00:00
RARCH_LOG("Detecting desktop resolution %ux%u.\n", full_x, full_y);
2011-04-21 01:23:44 +00:00
2012-09-28 20:38:42 +00:00
void *sdl_input = NULL;
2011-12-24 12:46:12 +00:00
if (!video->fullscreen)
2012-04-21 21:25:32 +00:00
RARCH_LOG("Creating window @ %ux%u\n", video->width, video->height);
vid->screen = SDL_SetVideoMode(video->width, video->height, 32,
SDL_HWSURFACE | SDL_HWACCEL | SDL_DOUBLEBUF | (video->fullscreen ? SDL_FULLSCREEN : 0));
2011-04-23 16:06:48 +00:00
2012-11-20 22:35:40 +00:00
// We assume that SDL chooses ARGB8888.
// Assuming this simplifies the driver *a ton*.
2011-04-21 01:23:44 +00:00
if (!vid->screen)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to init SDL surface: %s\n", SDL_GetError());
2011-04-21 01:23:44 +00:00
goto error;
}
if (video->fullscreen)
SDL_ShowCursor(SDL_DISABLE);
2012-09-28 20:38:42 +00:00
sdl_gfx_set_handles();
if (input && input_data)
2011-04-21 01:23:44 +00:00
{
sdl_input = input_sdl.init();
if (sdl_input)
{
*input = &input_sdl;
*input_data = sdl_input;
}
else
{
*input = NULL;
*input_data = NULL;
}
2011-12-14 15:37:31 +00:00
}
2011-04-21 01:23:44 +00:00
2011-04-22 01:13:09 +00:00
sdl_init_font(vid, g_settings.video.font_path, g_settings.video.font_size);
vid->scaler.scaler_type = video->smooth ? SCALER_TYPE_BILINEAR : SCALER_TYPE_POINT;
2012-11-20 22:35:40 +00:00
vid->scaler.in_fmt = video->rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565;
vid->scaler.out_fmt = SCALER_FMT_ARGB8888;
2011-04-21 01:23:44 +00:00
return vid;
error:
sdl_gfx_free(vid);
return NULL;
}
static void check_window(sdl_video_t *vid)
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
vid->quitting = true;
break;
default:
break;
}
}
}
2011-11-02 18:31:36 +00:00
static bool sdl_gfx_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
2011-04-21 01:23:44 +00:00
{
if (!frame)
return true;
2011-12-24 12:46:12 +00:00
sdl_video_t *vid = (sdl_video_t*)data;
2011-04-21 01:23:44 +00:00
2012-11-20 22:35:40 +00:00
vid->scaler.in_stride = pitch;
if (width != vid->last_width || height != vid->last_height)
{
vid->scaler.in_width = width;
vid->scaler.in_height = height;
vid->scaler.out_width = vid->screen->w;
vid->scaler.out_height = vid->screen->h;
vid->scaler.out_stride = vid->screen->pitch;
scaler_ctx_gen_filter(&vid->scaler);
vid->last_width = width;
vid->last_height = height;
}
2011-04-21 01:23:44 +00:00
if (SDL_MUSTLOCK(vid->screen))
SDL_LockSurface(vid->screen);
2011-04-21 01:23:44 +00:00
static retro_perf_counter_t sdl_scale = { "sdl_scale", 0, 0, 0, false };
rarch_perf_init(&sdl_scale, g_settings.perfcounter_enable);
rarch_perf_start(&sdl_scale, g_settings.perfcounter_enable);
2012-11-20 22:35:40 +00:00
scaler_ctx_scale(&vid->scaler, vid->screen->pixels, frame);
rarch_perf_stop(&sdl_scale, g_settings.perfcounter_enable);
2012-11-20 22:35:40 +00:00
if (msg)
sdl_render_msg(vid, vid->screen, msg, vid->screen->w, vid->screen->h, vid->screen->format);
2011-04-21 01:23:44 +00:00
if (SDL_MUSTLOCK(vid->screen))
SDL_UnlockSurface(vid->screen);
2011-04-21 01:23:44 +00:00
char buf[128];
if (gfx_get_fps(buf, sizeof(buf), NULL, 0))
2011-04-21 01:39:03 +00:00
SDL_WM_SetCaption(buf, NULL);
SDL_Flip(vid->screen);
g_extern.frame_count++;
2011-04-21 01:39:03 +00:00
2011-04-21 01:23:44 +00:00
return true;
}
static void sdl_gfx_set_nonblock_state(void *data, bool state)
{
(void)data; // Can SDL even do this?
(void)state;
}
static bool sdl_gfx_alive(void *data)
{
2011-12-24 12:46:12 +00:00
sdl_video_t *vid = (sdl_video_t*)data;
2011-10-18 17:52:43 +00:00
check_window(vid);
2011-04-21 01:23:44 +00:00
return !vid->quitting;
}
static bool sdl_gfx_focus(void *data)
{
(void)data;
return (SDL_GetAppState() & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) == (SDL_APPINPUTFOCUS | SDL_APPACTIVE);
}
static void sdl_gfx_viewport_info(void *data, struct rarch_viewport *vp)
{
sdl_video_t *vid = (sdl_video_t*)data;
vp->x = vp->y = 0;
2013-01-11 15:23:04 +00:00
vp->width = vp->full_width = vid->screen->w;
vp->height = vp->full_height = vid->screen->h;
}
2011-04-21 01:23:44 +00:00
const video_driver_t video_sdl = {
2011-12-24 12:46:12 +00:00
sdl_gfx_init,
sdl_gfx_frame,
sdl_gfx_set_nonblock_state,
sdl_gfx_alive,
sdl_gfx_focus,
NULL,
sdl_gfx_free,
"sdl",
#ifdef HAVE_MENU
2013-03-24 01:24:53 +00:00
NULL,
#endif
NULL,
sdl_gfx_viewport_info,
2011-04-21 01:23:44 +00:00
};