7#if defined(SDL_LIBRETRO_IMPLEMENTATION) && !defined(SDL_LIBRETRO_VIDEO_IMPL_ONCE)
8#define SDL_LIBRETRO_VIDEO_IMPL_ONCE
14static SDL_PixelFormat SDL_Libretro_GetTextureFormat(
enum retro_pixel_format fmt) {
16 case RETRO_PIXEL_FORMAT_XRGB8888:
return SDL_PIXELFORMAT_XRGB8888;
17 case RETRO_PIXEL_FORMAT_RGB565:
return SDL_PIXELFORMAT_RGB565;
18 case RETRO_PIXEL_FORMAT_0RGB1555:
return SDL_PIXELFORMAT_XRGB1555;
19 default:
return SDL_PIXELFORMAT_RGB565;
29static void SDL_Libretro_ReleaseSoftwareFramebuffer(SDL_Libretro* lr) {
30 if (!lr->core.softwareFramebufferPixels) {
33 if (lr->core.texture) {
34 SDL_UnlockTexture(lr->core.texture);
36 lr->core.softwareFramebufferPixels = NULL;
43static bool SDL_Libretro_InitVideo(SDL_Libretro* lr) {
44 if (!lr || !lr->renderer)
return false;
47 SDL_Libretro_CloseVideo(lr);
50 if (lr->core.width == 0 || lr->core.height == 0) {
52 lr->core.height = 240;
56 lr->core.texture = SDL_CreateTexture(lr->renderer, SDL_Libretro_GetTextureFormat(lr->core.pixelFormat), SDL_TEXTUREACCESS_STREAMING, lr->core.width, lr->core.height);
58 if (!lr->core.texture) {
59 SDL_SetError(
"[SDL_Libretro] Failed to create texture: %s", SDL_GetError());
62 lr->core.videoReinitPending =
false;
64 SDL_SetTextureScaleMode(lr->core.texture, lr->scaleMode);
72static void SDL_Libretro_CloseVideo(SDL_Libretro* lr) {
75 SDL_Libretro_ReleaseSoftwareFramebuffer(lr);
76 if (lr->core.texture) {
77 SDL_DestroyTexture(lr->core.texture);
78 lr->core.texture = NULL;
82static void SDL_Libretro_VideoRefresh(
const void* data,
unsigned width,
unsigned height,
size_t pitch) {
87 if (lr->core.softwareFramebufferPixels) {
88 void* swfb = lr->core.softwareFramebufferPixels;
89 SDL_Libretro_ReleaseSoftwareFramebuffer(lr);
90 if (data == swfb)
return;
96 if (data == RETRO_HW_FRAME_BUFFER_VALID)
return;
99 if (width != lr->core.width || height != lr->core.height) {
100 lr->core.width = width;
101 lr->core.height = height;
102 if (!SDL_Libretro_InitVideo(lr))
return;
106 if (!lr->core.texture)
return;
111 if (!SDL_LockTexture(lr->core.texture, NULL, &pixels, &lockPitch)) {
116 const Uint8* src = (
const Uint8*)data;
117 Uint8* dst = (Uint8*)pixels;
118 size_t rowBytes = pitch < (size_t)lockPitch ? pitch : (size_t)lockPitch;
119 if (pitch == (
size_t)lockPitch) {
121 SDL_memcpy(dst, src, rowBytes * height);
124 for (
size_t y = 0; y < height; y++) {
125 SDL_memcpy(dst + y * lockPitch, src + y * pitch, rowBytes);
129 SDL_UnlockTexture(lr->core.texture);
142 if (!lr || !renderer) {
143 SDL_SetError(
"[SDL_Libretro] Invalid context");
147 bool changed = (lr->renderer != renderer);
148 lr->renderer = renderer;
149 lr->window = SDL_GetRenderWindow(renderer);
155 if (lr->core.gameLoaded && (!lr->core.texture || changed)) {
156 return SDL_Libretro_InitVideo(lr);
169 return lr ? lr->renderer : NULL;
186 return (lr && lr->core.texture) ? lr->core.texture : NULL;
202 if (!lr || !lr->core.texture || !lr->renderer)
return NULL;
206 SDL_Renderer* renderer = lr->renderer;
207 int w = (int)lr->core.width;
208 int h = (int)lr->core.height;
210 SDL_Texture* target = SDL_CreateTexture(renderer, SDL_Libretro_GetTextureFormat(lr->core.pixelFormat), SDL_TEXTUREACCESS_TARGET, w, h);
211 if (!target)
return NULL;
213 SDL_Texture* prevTarget = SDL_GetRenderTarget(renderer);
214 SDL_Surface* out = NULL;
215 if (SDL_SetRenderTarget(renderer, target)) {
217 if (SDL_RenderTexture(renderer, lr->core.texture, NULL, NULL)) {
218 out = SDL_RenderReadPixels(renderer, NULL);
220 SDL_SetRenderTarget(renderer, prevTarget);
223 SDL_DestroyTexture(target);
238 if (screenshot == NULL) {
241 bool saved = SDL_SavePNG(screenshot, path != NULL ? path :
"screenshot.png");
242 SDL_DestroySurface(screenshot);
254static void SDL_Libretro_FitRect(
const SDL_Libretro* lr, SDL_FRect* rect,
bool rotated) {
255 SDL_FRect avail = *rect;
261 float srcAspect = lr->core.aspectRatio;
262 if (srcAspect <= 0.0f && lr->core.width > 0 && lr->core.height > 0) {
263 srcAspect = (float)lr->core.width / (
float)lr->core.height;
267 float aspect = (rotated && srcAspect > 0.0f) ? 1.0f / srcAspect : srcAspect;
269 if (aspect > avail.w / avail.h) {
270 rect->h = avail.w / aspect;
271 rect->y = avail.y + (avail.h - rect->h) * 0.5f;
274 rect->w = avail.h * aspect;
275 rect->x = avail.x + (avail.w - rect->w) * 0.5f;
282 float coreW = rotated ? (float)lr->core.height : (float)lr->core.width;
283 float coreH = rotated ? (float)lr->core.width : (float)lr->core.height;
284 int scale = SDL_max(1, SDL_min((
int)(rect->w / coreW), (
int)(rect->h / coreH)));
285 float w = coreW * (float)scale;
286 float h = coreH * (float)scale;
287 rect->x += (rect->w - w) * 0.5f;
288 rect->y += (rect->h - h) * 0.5f;
305 if (!lr || !renderer)
return false;
311 if (!lr->core.texture)
return false;
315 lr->core.renderDstRect = *dstRect;
321 SDL_RendererLogicalPresentation mode = SDL_LOGICAL_PRESENTATION_DISABLED;
322 if (!SDL_GetRenderLogicalPresentation(renderer, &w, &h, &mode) ||
323 mode == SDL_LOGICAL_PRESENTATION_DISABLED) {
324 if (!SDL_GetCurrentRenderOutputSize(renderer, &w, &h))
return false;
326 lr->core.renderDstRect = (SDL_FRect){0.0f, 0.0f, (float)w, (
float)h};
330 bool rotated = (lr->core.rotation & 1) != 0;
333 SDL_Libretro_FitRect(lr, &lr->core.renderDstRect, rotated);
337 double angle = -(lr->core.rotation * 90.0);
340 SDL_FRect dst = lr->core.renderDstRect;
341 dst.w = lr->core.renderDstRect.h;
342 dst.h = lr->core.renderDstRect.w;
343 dst.x = lr->core.renderDstRect.x + (lr->core.renderDstRect.w - dst.w) * 0.5f;
344 dst.y = lr->core.renderDstRect.y + (lr->core.renderDstRect.h - dst.h) * 0.5f;
345 return SDL_RenderTextureRotated(renderer, lr->core.texture, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
348 return SDL_RenderTextureRotated(renderer, lr->core.texture, NULL, &lr->core.renderDstRect, angle, NULL, SDL_FLIP_NONE);
359 if (w) *w = lr ? (int)lr->core.width : 0;
360 if (h) *h = lr ? (int)lr->core.height : 0;
372 return lr ? lr->core.aspectRatio : 0.0f;
382 return lr ? lr->core.fps : 0.0;
394 return lr ? lr->core.rotation * 90 : 0;
408 if (!lr)
return false;
410 return SDL_InvalidParamError(
"mode");
439 if (!lr)
return false;
440 bool valid = (mode == SDL_SCALEMODE_NEAREST || mode == SDL_SCALEMODE_LINEAR);
441#if SDL_VERSION_ATLEAST(3, 4, 0)
442 if (mode == SDL_SCALEMODE_PIXELART) valid =
true;
445 return SDL_InvalidParamError(
"mode");
447 lr->scaleMode = mode;
448 if (lr->core.texture) {
449 SDL_SetTextureScaleMode(lr->core.texture, mode);
463 return lr ? lr->scaleMode : SDL_SCALEMODE_NEAREST;
bool SDL_Libretro_Render(SDL_Libretro *lr, SDL_Renderer *renderer, const SDL_FRect *dstRect)
Render the libretro context in the given renderer.
SDL_ScaleMode SDL_Libretro_GetScaleMode(const SDL_Libretro *lr)
Gets the texture scale mode used when the libretro frame is scaled.
SDL_LibretroFitMode SDL_Libretro_GetFitMode(const SDL_Libretro *lr)
Gets how the frame is fit into the destination rectangle when rendered.
bool SDL_Libretro_SaveScreenshot(const SDL_Libretro *lr, const char *path)
Saves a PNG screenshot of the current frame.
SDL_Surface * SDL_Libretro_CreateSurface(const SDL_Libretro *lr)
Creates a new surface from the current libretro context.
int SDL_Libretro_GetRotation(const SDL_Libretro *lr)
Gets the screen rotation requested by the core.
SDL_LibretroFitMode
When rendering the libretro context, determine how to display within the destination.
double SDL_Libretro_GetFPS(const SDL_Libretro *lr)
Gets the frame rate the core expects to run at.
bool SDL_Libretro_SetFitMode(SDL_Libretro *lr, SDL_LibretroFitMode mode)
Sets how the frame is fit into the destination rectangle when rendered.
struct SDL_Libretro SDL_Libretro
A libretro frontend context: one loaded core, its game, and the settings that carry across cores.
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 height of the core's current video frame.
bool SDL_Libretro_SetRenderer(SDL_Libretro *lr, SDL_Renderer *renderer)
Set the renderer the libretro context draws into.
SDL_Texture * SDL_Libretro_GetTexture(const SDL_Libretro *lr)
Get the streaming texture that holds the core's most recent video frame.
bool SDL_Libretro_SetScaleMode(SDL_Libretro *lr, SDL_ScaleMode mode)
Sets the texture scale mode used when the libretro frame is scaled.
@ SDL_LIBRETRO_FIT_STRETCH
Ignore the aspect ratio, and stretch the image to match the destination rectangle.
@ SDL_LIBRETRO_FIT_FIRST
The default fit mode to fit within the destination.
@ SDL_LIBRETRO_FIT_INTEGER
Keep the same aspect ratio, while keeping integer scaling within the confines of the destination.
@ SDL_LIBRETRO_FIT_COUNT
The number of possible fit modes.
@ SDL_LIBRETRO_FIT_ASPECT
Keep the same aspect ratio, and fit within the confines of the destination.