7 #if defined(SDL_LIBRETRO_IMPLEMENTATION) && !defined(SDL_LIBRETRO_VIDEO_IMPL_ONCE)
8#define SDL_LIBRETRO_VIDEO_IMPL_ONCE
15static SDL_PixelFormat SDL_Libretro_GetTextureFormat(
enum retro_pixel_format fmt) {
17 case RETRO_PIXEL_FORMAT_XRGB8888:
return SDL_PIXELFORMAT_XRGB8888;
18 case RETRO_PIXEL_FORMAT_RGB565:
return SDL_PIXELFORMAT_RGB565;
19 case RETRO_PIXEL_FORMAT_0RGB1555:
return SDL_PIXELFORMAT_XRGB1555;
20 default:
return SDL_PIXELFORMAT_RGB565;
31static void SDL_Libretro_ReleaseSoftwareFramebuffer(SDL_Libretro* lr) {
32 if (!lr->core.softwareFramebufferPixels) {
35 if (lr->core.texture) {
36 SDL_UnlockTexture(lr->core.texture);
38 lr->core.softwareFramebufferPixels = NULL;
46static bool SDL_Libretro_InitVideo(SDL_Libretro* lr) {
47 if (!lr || !lr->renderer)
return false;
50 SDL_Libretro_CloseVideo(lr);
53 if (lr->core.width == 0 || lr->core.height == 0) {
55 lr->core.height = 240;
59 lr->core.texture = SDL_CreateTexture(lr->renderer,
60 SDL_Libretro_GetTextureFormat(lr->core.pixelFormat),
61 SDL_TEXTUREACCESS_STREAMING,
62 lr->core.width, lr->core.height);
64 if (!lr->core.texture) {
65 SDL_SetError(
"[SDL_Libretro] Failed to create texture: %s", SDL_GetError());
68 lr->core.videoReinitPending =
false;
71 #if SDL_VERSION_ATLEAST(3, 4, 0)
72 if (lr->core.textureScaleMode == SDL_SCALEMODE_NEAREST)
73 lr->core.textureScaleMode = SDL_SCALEMODE_PIXELART;
75 SDL_SetTextureScaleMode(lr->core.texture, lr->core.textureScaleMode);
84static void SDL_Libretro_CloseVideo(SDL_Libretro* lr) {
87 if (lr->core.texture) {
88 SDL_Libretro_ReleaseSoftwareFramebuffer(lr);
89 SDL_DestroyTexture(lr->core.texture);
90 lr->core.texture = NULL;
94static void SDL_Libretro_VideoRefresh(
const void* data,
unsigned width,
unsigned height,
size_t pitch) {
95 SDL_Libretro* lr = SDL_Libretro_active;
99 if (lr->core.softwareFramebufferPixels) {
100 void* swfb = lr->core.softwareFramebufferPixels;
101 SDL_Libretro_ReleaseSoftwareFramebuffer(lr);
102 if (data == swfb)
return;
108 if (data == RETRO_HW_FRAME_BUFFER_VALID)
return;
111 if (width != lr->core.width || height != lr->core.height) {
112 lr->core.width = width;
113 lr->core.height = height;
114 if (!SDL_Libretro_InitVideo(lr))
return;
118 if (!lr->core.texture)
return;
123 if (!SDL_LockTexture(lr->core.texture, NULL, &pixels, &lockPitch)) {
128 const Uint8* src = (
const Uint8*)data;
129 Uint8* dst = (Uint8*)pixels;
130 size_t rowBytes = pitch < (size_t)lockPitch ? pitch : (size_t)lockPitch;
131 if (pitch == (
size_t)lockPitch) {
133 SDL_memcpy(dst, src, rowBytes * height);
135 for (
size_t y = 0; y < height; y++) {
136 SDL_memcpy(dst + y * lockPitch, src + y * pitch, rowBytes);
140 SDL_UnlockTexture(lr->core.texture);
153 if (!lr || !renderer) {
154 SDL_SetError(
"[SDL_Libretro] Invalid context");
158 bool changed = (lr->renderer != renderer);
159 lr->renderer = renderer;
160 lr->window = SDL_GetRenderWindow(renderer);
166 if (lr->core.gameLoaded && (!lr->core.texture || changed)) {
167 return SDL_Libretro_InitVideo(lr);
180 return lr ? lr->renderer : NULL;
183SDL_Texture* SDL_Libretro_GetTexture(
const SDL_Libretro* lr) {
184 return (lr && lr->core.texture) ? lr->core.texture : NULL;
193 if (!lr || !lr->core.texture)
return NULL;
197 if (!SDL_LockTexture(lr->core.texture, NULL, &pixels, &pitch))
return NULL;
199 SDL_PixelFormat fmt = SDL_Libretro_GetTextureFormat(lr->core.pixelFormat);
200 SDL_Surface* ref = SDL_CreateSurfaceFrom((
int)lr->core.width, (
int)lr->core.height, fmt, pixels, pitch);
201 SDL_Surface* out = NULL;
203 out = SDL_CreateSurface((
int)lr->core.width, (
int)lr->core.height, fmt);
204 if (out) SDL_BlitSurface(ref, NULL, out, NULL);
205 SDL_DestroySurface(ref);
208 SDL_UnlockTexture(lr->core.texture);
220static void SDL_Libretro_FitRect(
const SDL_Libretro* lr, SDL_FRect* rect,
bool rotated) {
221 SDL_FRect avail = *rect;
225 float srcAspect = lr->core.aspectRatio;
226 if (srcAspect <= 0.0f && lr->core.width > 0 && lr->core.height > 0) {
227 srcAspect = (float)lr->core.width / (
float)lr->core.height;
231 float aspect = (rotated && srcAspect > 0.0f) ? 1.0f / srcAspect : srcAspect;
233 if (aspect > avail.w / avail.h) {
234 rect->h = avail.w / aspect;
235 rect->y = avail.y + (avail.h - rect->h) * 0.5f;
237 rect->w = avail.h * aspect;
238 rect->x = avail.x + (avail.w - rect->w) * 0.5f;
245 float coreW = rotated ? (float)lr->core.height : (float)lr->core.width;
246 float coreH = rotated ? (float)lr->core.width : (float)lr->core.height;
247 int scale = SDL_max(1, SDL_min((
int)(rect->w / coreW), (
int)(rect->h / coreH)));
248 float w = coreW * (float)scale;
249 float h = coreH * (float)scale;
250 rect->x += (rect->w - w) * 0.5f;
251 rect->y += (rect->h - h) * 0.5f;
269 if (!lr || !renderer)
return false;
275 if (!lr->core.texture)
return false;
279 lr->core.renderDstRect = *dstRect;
282 if (!SDL_GetRenderOutputSize(renderer, &w, &h))
return false;
283 lr->core.renderDstRect = (SDL_FRect){ 0.0f, 0.0f, (float)w, (
float)h };
287 bool rotated = (lr->core.rotation & 1) != 0;
290 SDL_Libretro_FitRect(lr, &lr->core.renderDstRect, rotated);
294 double angle = -(lr->core.rotation * 90.0);
297 SDL_FRect dst = lr->core.renderDstRect;
298 dst.w = lr->core.renderDstRect.h;
299 dst.h = lr->core.renderDstRect.w;
300 dst.x = lr->core.renderDstRect.x + (lr->core.renderDstRect.w - dst.w) * 0.5f;
301 dst.y = lr->core.renderDstRect.y + (lr->core.renderDstRect.h - dst.h) * 0.5f;
302 return SDL_RenderTextureRotated(renderer, lr->core.texture, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
305 return SDL_RenderTextureRotated(renderer, lr->core.texture, NULL, &lr->core.renderDstRect, angle, NULL, SDL_FLIP_NONE);
312 if (w) *w = lr ? (int)lr->core.width : 0;
313 if (h) *h = lr ? (int)lr->core.height : 0;
320 return lr ? lr->core.aspectRatio : 0.0f;
323double SDL_Libretro_GetFPS(
const SDL_Libretro* lr) {
324 return lr ? lr->core.fps : 0.0;
327int SDL_Libretro_GetRotation(
const SDL_Libretro* lr) {
328 return lr ? lr->core.rotation * 90 : 0;
335 if (lr && lr->fitMode != mode) {
341 return lr ? lr->fitMode : SDL_LIBRETRO_FIT_ASPECT;
bool SDL_Libretro_Render(SDL_Renderer *renderer, SDL_Libretro *lr, const SDL_FRect *dstRect)
Render the libretro context in the given renderer.
SDL_Surface * SDL_Libretro_CreateSurface(const SDL_Libretro *lr)
Creates a new surface from the current libretro context.
SDL_LibretroFitMode
When rendering the libretro context, determine how to display within the destination.
float SDL_Libretro_GetAspectRatio(const SDL_Libretro *lr)
Gets the aspect ratio for the libretro context.
SDL_Renderer * SDL_Libretro_GetRenderer(const SDL_Libretro *lr)
Get the renderer the context draws into.
void SDL_Libretro_GetSize(const SDL_Libretro *lr, int *w, int *h)
Gets the width and heigh tof the given libretro context.
bool SDL_Libretro_SetRenderer(SDL_Libretro *lr, SDL_Renderer *renderer)
Set the renderer the libretro context draws into.
void SDL_Libretro_SetFitMode(SDL_Libretro *lr, SDL_LibretroFitMode mode)
Sets the desired scale mode for the libretro context when it's displayed.
@ SDL_LIBRETRO_FIT_STRETCH
Keep the same aspect ratio, while keeping integer scaling within the confines of the destination.
@ SDL_LIBRETRO_FIT_INTEGER
Keep the same aspect ratio, and fit within the confines of the destination.