sdl12-compat: Implemented YUV overlays.

From 27eec98b5e3d89cbb592176c67eeb7519a356f70 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Wed, 28 Apr 2021 16:02:19 -0400
Subject: [PATCH] Implemented YUV overlays.

---
 src/SDL12_compat.c | 162 ++++++++++++++++++++++++++++++++++++++++-----
 src/SDL20_syms.h   |   2 +
 2 files changed, 149 insertions(+), 15 deletions(-)

diff --git a/src/SDL12_compat.c b/src/SDL12_compat.c
index 3201069..932e2b3 100644
--- a/src/SDL12_compat.c
+++ b/src/SDL12_compat.c
@@ -218,6 +218,12 @@ typedef struct SDL12_Surface
     int refcount;
 } SDL12_Surface;
 
+#define SDL12_YV12_OVERLAY 0x32315659
+#define SDL12_IYUV_OVERLAY 0x56555949
+#define SDL12_YUY2_OVERLAY 0x32595559
+#define SDL12_UYVY_OVERLAY 0x59565955
+#define SDL12_YVYU_OVERLAY 0x55595659
+
 typedef struct SDL12_Overlay
 {
     Uint32 format;
@@ -4176,38 +4182,164 @@ SDL_GetWMInfo(SDL_SysWMinfo * info)
     return 0; /* some programs only test against 0, not -1 */
 }
 
+
+typedef struct SDL12_YUVData
+{
+    SDL_Texture *texture20;
+    Uint8 *pixelbuf;
+    Uint8 *pixels[3];
+    Uint16 pitches[3];
+} SDL12_YUVData;
+
 DECLSPEC SDL12_Overlay * SDLCALL
-SDL_CreateYUVOverlay(int w, int h, Uint32 format, SDL12_Surface *display)
+SDL_CreateYUVOverlay(int w, int h, Uint32 format12, SDL12_Surface *display12)
 {
-    FIXME("write me");
-    SDL20_Unsupported();
-    return NULL;
+    /* SDL 1.2 has you pass the screen surface in here, but it doesn't check that it's _actually_ the screen surface,
+       which implies you should be able to blit YUV overlays to other surfaces too...but then SDL_DisplayYUVOverlay
+       always uses the screen surface unconditionally. As such, and because overlays were sort of hostile to software
+       rendering anyhow, we always make an SDL_Texture in here and draw over the screen pixels, in hopes that the GPU
+       gives us a boost here. */
+
+    SDL12_Overlay *retval = NULL;
+    SDL12_YUVData *hwdata = NULL;
+    Uint32 format20 = 0;
+
+    if (display12 != VideoSurface12) {  /* SDL 1.2 doesn't check this, but it seems irresponsible not to. */
+        SDL20_SetError("YUV overlays are only supported on the screen surface");
+        return NULL;
+    }
+
+    if (display12->flags & SDL12_OPENGL) {
+        SDL20_SetError("YUV overlays are not supported in OpenGL mode");
+        return NULL;
+    }
+
+    SDL_assert(VideoRenderer20 != NULL);
+
+    switch (format12) {
+        #define SUPPORTED_YUV_FORMAT(x) case SDL12_##x##_OVERLAY: format20 = SDL_PIXELFORMAT_##x; break
+        SUPPORTED_YUV_FORMAT(YV12);
+        SUPPORTED_YUV_FORMAT(IYUV);
+        SUPPORTED_YUV_FORMAT(YUY2);
+        SUPPORTED_YUV_FORMAT(UYVY);
+        SUPPORTED_YUV_FORMAT(YVYU);
+        #undef SUPPORTED_YUV_FORMAT
+        default: SDL20_SetError("Unsupported YUV format"); return NULL;
+    }
+
+    retval = (SDL12_Overlay *) SDL20_calloc(1, sizeof (SDL12_Overlay) + sizeof (SDL12_YUVData));
+    if (!retval) {
+        SDL20_OutOfMemory();
+        return NULL;
+    }
+
+    hwdata = (SDL12_YUVData *) (retval + 1);
+    hwdata->pixelbuf = (Uint8 *) SDL_calloc(1, (w * 2) * h);
+    if (!hwdata->pixelbuf) {
+        SDL20_free(retval);
+        SDL20_OutOfMemory();
+        return NULL;
+    }
+
+    hwdata->pixels[0] = hwdata->pixelbuf;
+    if ((format12 == SDL12_YV12_OVERLAY) || (format12 == SDL12_IYUV_OVERLAY)) {
+        retval->planes = 3;
+        hwdata->pitches[0] = w;
+        hwdata->pitches[1] = hwdata->pitches[2] = w / 2;
+        hwdata->pixels[1] = hwdata->pixels[0] + (w * h);
+        hwdata->pixels[2] = hwdata->pixels[1] + ((w / 2) * h);
+    } else {
+        retval->planes = 1;
+        hwdata->pitches[0] = w * 2;
+    }
+
+    hwdata->texture20 = SDL20_CreateTexture(VideoRenderer20, format20, SDL_TEXTUREACCESS_STREAMING, w, h);
+    if (!hwdata->texture20) {
+        SDL20_free(hwdata->pixelbuf);
+        SDL20_free(retval);
+        return NULL;
+    }
+
+    retval->format = format12;
+    retval->w = w;
+    retval->h = h;
+    retval->hwfuncs = (void *) 0x1;  /* in case it's important for this to be non-NULL. */
+    retval->hwdata = hwdata;
+    retval->hw_overlay = 1;
+    retval->pitches = hwdata->pitches;
+
+    return retval;
 }
 
 DECLSPEC int SDLCALL
-SDL_LockYUVOverlay(SDL12_Overlay * overlay)
+SDL_LockYUVOverlay(SDL12_Overlay *overlay12)
 {
-    FIXME("write me");
-    return SDL20_Unsupported();
+    if (!overlay12) {
+        return SDL20_InvalidParamError("overlay");
+    }
+
+    overlay12->pixels = ((SDL12_YUVData *) overlay12->hwdata)->pixels;
+    return 0;  /* success */
 }
 
 DECLSPEC void SDLCALL
-SDL_UnlockYUVOverlay(SDL12_Overlay * overlay)
-{
-    FIXME("write me");
+SDL_UnlockYUVOverlay(SDL12_Overlay *overlay12)
+{
+    if (overlay12) {
+        const SDL_Rect rect = { 0, 0, overlay12->w, overlay12->h };
+        SDL12_YUVData *hwdata = (SDL12_YUVData *) overlay12->hwdata;
+        if (overlay12->format == SDL12_IYUV_OVERLAY) {
+            SDL20_UpdateYUVTexture(hwdata->texture20, &rect,
+                                 hwdata->pixels[0], hwdata->pitches[0],
+                                 hwdata->pixels[1], hwdata->pitches[1],
+                                 hwdata->pixels[2], hwdata->pitches[2]);
+        } else if (overlay12->format == SDL12_YV12_OVERLAY) {
+            SDL20_UpdateYUVTexture(hwdata->texture20, &rect,
+                                 hwdata->pixels[0], hwdata->pitches[0],
+                                 hwdata->pixels[2], hwdata->pitches[2],
+                                 hwdata->pixels[1], hwdata->pitches[1]);
+        } else {
+            SDL20_UpdateTexture(hwdata->texture20, &rect, hwdata->pixels[0], hwdata->pitches[0]);
+        }
+        overlay12->pixels = NULL;
+    }
 }
 
 DECLSPEC int SDLCALL
-SDL_DisplayYUVOverlay(SDL12_Overlay * overlay, SDL12_Rect * dstrect12)
+SDL_DisplayYUVOverlay(SDL12_Overlay *overlay12, SDL12_Rect *dstrect12)
 {
-    FIXME("write me");
-    return SDL20_Unsupported();
+    SDL12_YUVData *hwdata;
+    SDL_Rect dstrect20;
+
+    if (!overlay12) {
+        return SDL20_InvalidParamError("overlay");
+    } else if (!dstrect12) {
+        return SDL20_InvalidParamError("dstrect");
+    } else if (!VideoRenderer20) {
+        return SDL20_SetError("No software screen surface available");
+    }
+
+    hwdata = (SDL12_YUVData *) overlay12->hwdata;
+
+    SDL20_RenderClear(VideoRenderer20);
+    SDL20_RenderCopy(VideoRenderer20, VideoTexture20, NULL, NULL);
+    SDL20_RenderCopy(VideoRenderer20, hwdata->texture20, NULL, Rect12to20(dstrect12, &dstrect20));
+    SDL20_RenderPresent(VideoRenderer20);
+    VideoSurfaceLastPresentTicks = SDL20_GetTicks();
+    VideoSurfacePresentTicks = 0;
+
+    return 0;
 }
 
 DECLSPEC void SDLCALL
-SDL_FreeYUVOverlay(SDL12_Overlay * overlay)
+SDL_FreeYUVOverlay(SDL12_Overlay *overlay12)
 {
-    FIXME("write me");
+    if (overlay12) {
+        SDL12_YUVData *hwdata = (SDL12_YUVData *) overlay12->hwdata;
+        SDL20_DestroyTexture(hwdata->texture20);
+        SDL20_free(hwdata->pixelbuf);
+        SDL20_free(overlay12);
+    }
 }
 
 DECLSPEC void * SDLCALL
diff --git a/src/SDL20_syms.h b/src/SDL20_syms.h
index 8663122..64b61b3 100644
--- a/src/SDL20_syms.h
+++ b/src/SDL20_syms.h
@@ -281,6 +281,8 @@ SDL20_SYM(int,GetRendererInfo,(SDL_Renderer *a, SDL_RendererInfo *b),(a,b),retur
 SDL20_SYM(SDL_Texture *,CreateTexture,(SDL_Renderer *a, Uint32 b, int c, int d, int e),(a,b,c,d,e),return)
 SDL20_SYM(int,LockTexture,(SDL_Texture *a, const SDL_Rect *b, void **c, int *d),(a,b,c,d),return)
 SDL20_SYM(void,UnlockTexture,(SDL_Texture *a),(a),)
+SDL20_SYM(int,UpdateTexture,(SDL_Texture *a, const SDL_Rect *b, const void *c, int d),(a,b,c,d),return)
+SDL20_SYM(int,UpdateYUVTexture,(SDL_Texture *a, const SDL_Rect *b, const Uint8 *c, int d, const Uint8 *e, int f, const Uint8 *g, int h),(a,b,c,d,e,f,g,h),return)
 SDL20_SYM(int,RenderSetLogicalSize,(SDL_Renderer *a, int b, int c),(a,b,c),return)
 SDL20_SYM(int,SetRenderDrawColor,(SDL_Renderer *a, Uint8 b, Uint8 c, Uint8 d, Uint8 e),(a,b,c,d,e),return)
 SDL20_SYM(int,RenderClear,(SDL_Renderer *a),(a),return)