SDL: Added SDL_PIXELFORMAT_P408 and SDL_PIXELFORMAT_P416

From 07ecc125cf34cc0f1787c13c50176b0730aecbc5 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 22 Jun 2026 12:42:45 -0700
Subject: [PATCH] Added SDL_PIXELFORMAT_P408 and SDL_PIXELFORMAT_P416

---
 include/SDL3/SDL_pixels.h                |  23 ++--
 src/render/SDL_render.c                  |   6 +-
 src/render/SDL_yuv_sw.c                  | 134 +++++++++++++++----
 src/render/direct3d/SDL_render_d3d.c     |  69 ++++++++--
 src/render/direct3d11/SDL_render_d3d11.c |  99 +++++++++++---
 src/render/direct3d12/SDL_render_d3d12.c | 112 +++++++++++++---
 src/render/gpu/SDL_render_gpu.c          |  84 ++++++++++--
 src/render/metal/SDL_render_metal.m      |  43 ++++++-
 src/render/opengl/SDL_render_gl.c        | 117 +++++++++++++----
 src/render/opengles2/SDL_render_gles2.c  | 157 +++++++++++++++--------
 src/render/vulkan/SDL_render_vulkan.c    |  59 +++++++--
 src/video/SDL_pixels.c                   |   4 +-
 src/video/SDL_yuv.c                      | 138 ++++++++++++++++++--
 src/video/yuv2rgb/yuv_rgb_internal.h     |   8 +-
 src/video/yuv2rgb/yuv_rgb_std.c          |  40 ++++++
 src/video/yuv2rgb/yuv_rgb_std.h          |  30 +++++
 src/video/yuv2rgb/yuv_rgb_std_func.h     |  26 ++++
 test/testautomation_pixels.c             |  22 +++-
 test/testyuv.c                           |  93 ++++++++++++--
 test/testyuv_cvt.c                       |  53 ++++++++
 20 files changed, 1091 insertions(+), 226 deletions(-)

diff --git a/include/SDL3/SDL_pixels.h b/include/SDL3/SDL_pixels.h
index fd1ed31a4a266..1ac325b9aed7c 100644
--- a/include/SDL3/SDL_pixels.h
+++ b/include/SDL3/SDL_pixels.h
@@ -363,7 +363,8 @@ typedef enum SDL_PackedLayout
         ((((format) == SDL_PIXELFORMAT_YUY2) || \
           ((format) == SDL_PIXELFORMAT_UYVY) || \
           ((format) == SDL_PIXELFORMAT_YVYU) || \
-          ((format) == SDL_PIXELFORMAT_P010)) ? 2 : 1) : (((format) >> 0) & 0xFF))
+          ((format) == SDL_PIXELFORMAT_P010) || \
+          ((format) == SDL_PIXELFORMAT_P416)) ? 2 : 1) : (((format) >> 0) & 0xFF))
 
 
 /**
@@ -657,22 +658,26 @@ typedef enum SDL_PixelFormat
     SDL_PIXELFORMAT_ABGR128_FLOAT = 0x1b608010u,
         /* SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF32, SDL_ARRAYORDER_ABGR, 0, 128, 16), */
 
-    SDL_PIXELFORMAT_YV12 = 0x32315659u,      /**< Planar mode: Y + V + U  (3 planes) */
+    SDL_PIXELFORMAT_YV12 = 0x32315659u,      /**< YUV 4:2:0 8-bit planar mode: Y + V + U  (3 planes) */
         /* SDL_DEFINE_PIXELFOURCC('Y', 'V', '1', '2'), */
-    SDL_PIXELFORMAT_IYUV = 0x56555949u,      /**< Planar mode: Y + U + V  (3 planes) */
+    SDL_PIXELFORMAT_IYUV = 0x56555949u,      /**< YUV 4:2:0 8-bit planar mode: Y + U + V  (3 planes) */
         /* SDL_DEFINE_PIXELFOURCC('I', 'Y', 'U', 'V'), */
-    SDL_PIXELFORMAT_YUY2 = 0x32595559u,      /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */
+    SDL_PIXELFORMAT_YUY2 = 0x32595559u,      /**< YUV 4:2:0 8-bit packed mode: Y0+U0+Y1+V0 (1 plane) */
         /* SDL_DEFINE_PIXELFOURCC('Y', 'U', 'Y', '2'), */
-    SDL_PIXELFORMAT_UYVY = 0x59565955u,      /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */
+    SDL_PIXELFORMAT_UYVY = 0x59565955u,      /**< YUV 4:2:0 8-bit packed mode: U0+Y0+V0+Y1 (1 plane) */
         /* SDL_DEFINE_PIXELFOURCC('U', 'Y', 'V', 'Y'), */
-    SDL_PIXELFORMAT_YVYU = 0x55595659u,      /**< Packed mode: Y0+V0+Y1+U0 (1 plane) */
+    SDL_PIXELFORMAT_YVYU = 0x55595659u,      /**< YUV 4:2:0 8-bit packed mode: Y0+V0+Y1+U0 (1 plane) */
         /* SDL_DEFINE_PIXELFOURCC('Y', 'V', 'Y', 'U'), */
-    SDL_PIXELFORMAT_NV12 = 0x3231564eu,      /**< Planar mode: Y + U/V interleaved  (2 planes) */
+    SDL_PIXELFORMAT_NV12 = 0x3231564eu,      /**< YUV 4:2:0 8-bit planar mode: Y + U/V interleaved  (2 planes) */
         /* SDL_DEFINE_PIXELFOURCC('N', 'V', '1', '2'), */
-    SDL_PIXELFORMAT_NV21 = 0x3132564eu,      /**< Planar mode: Y + V/U interleaved  (2 planes) */
+    SDL_PIXELFORMAT_NV21 = 0x3132564eu,      /**< YUV 4:2:0 8-bit planar mode: Y + V/U interleaved  (2 planes) */
         /* SDL_DEFINE_PIXELFOURCC('N', 'V', '2', '1'), */
-    SDL_PIXELFORMAT_P010 = 0x30313050u,      /**< Planar mode: Y + U/V interleaved  (2 planes) */
+    SDL_PIXELFORMAT_P010 = 0x30313050u,      /**< YUV 4:2:0 16-bit planar mode: Y + U/V interleaved  (2 planes) */
         /* SDL_DEFINE_PIXELFOURCC('P', '0', '1', '0'), */
+    SDL_PIXELFORMAT_P408 = 0x38303450u,      /**< YUV 4:4:4 8-bit planar mode: Y + U + V  (3 planes) */
+        /* SDL_DEFINE_PIXELFOURCC('P', '4', '0', '8'), */
+    SDL_PIXELFORMAT_P416 = 0x36313450u,      /**< YUV 4:4:4 16-bit planar mode: Y + U + V  (3 planes) */
+        /* SDL_DEFINE_PIXELFOURCC('P', '4', '0', '8'), */
     SDL_PIXELFORMAT_EXTERNAL_OES = 0x2053454fu,     /**< Android video texture format */
         /* SDL_DEFINE_PIXELFOURCC('O', 'E', 'S', ' ') */
 
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index c11c29e71d306..e9645aca93d94 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -2518,8 +2518,10 @@ bool SDL_UpdateYUVTexture(SDL_Texture *texture, const SDL_Rect *rect,
     }
 
     CHECK_PARAM(texture->format != SDL_PIXELFORMAT_YV12 &&
-                texture->format != SDL_PIXELFORMAT_IYUV) {
-        return SDL_SetError("Texture format must be YV12 or IYUV");
+                texture->format != SDL_PIXELFORMAT_IYUV &&
+                texture->format != SDL_PIXELFORMAT_P408 &&
+                texture->format != SDL_PIXELFORMAT_P416) {
+        return SDL_SetError("Texture format must be YV12, IYUV, P408, or P416");
     }
 
     real_rect.x = 0;
diff --git a/src/render/SDL_yuv_sw.c b/src/render/SDL_yuv_sw.c
index 4c4b7dc987345..97d8b0b59fd6b 100644
--- a/src/render/SDL_yuv_sw.c
+++ b/src/render/SDL_yuv_sw.c
@@ -35,6 +35,8 @@ SDL_SW_YUVTexture *SDL_SW_CreateYUVTexture(SDL_PixelFormat format, SDL_Colorspac
     switch (format) {
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
+    case SDL_PIXELFORMAT_P416:
     case SDL_PIXELFORMAT_YUY2:
     case SDL_PIXELFORMAT_UYVY:
     case SDL_PIXELFORMAT_YVYU:
@@ -80,13 +82,21 @@ SDL_SW_YUVTexture *SDL_SW_CreateYUVTexture(SDL_PixelFormat format, SDL_Colorspac
         swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h;
         swdata->planes[2] = swdata->planes[1] + swdata->pitches[1] * ((h + 1) / 2);
         break;
+    case SDL_PIXELFORMAT_P408:
+    case SDL_PIXELFORMAT_P416:
+        swdata->pitches[0] = w * SDL_BYTESPERPIXEL(format);
+        swdata->pitches[1] = swdata->pitches[0];
+        swdata->pitches[2] = swdata->pitches[1];
+        swdata->planes[0] = swdata->pixels;
+        swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h;
+        swdata->planes[2] = swdata->planes[1] + swdata->pitches[1] * h;
+        break;
     case SDL_PIXELFORMAT_YUY2:
     case SDL_PIXELFORMAT_UYVY:
     case SDL_PIXELFORMAT_YVYU:
         swdata->pitches[0] = ((w + 1) / 2) * 4;
         swdata->planes[0] = swdata->pixels;
         break;
-
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
         swdata->pitches[0] = w;
@@ -119,7 +129,7 @@ bool SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
         if (rect->x == 0 && rect->y == 0 &&
-            rect->w == swdata->w && rect->h == swdata->h) {
+            rect->w == swdata->w && rect->h == swdata->h && pitch == swdata->pitches[0]) {
             SDL_memcpy(swdata->pixels, pixels,
                        (size_t)(swdata->h * swdata->w) + 2 * ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2));
         } else {
@@ -161,6 +171,47 @@ bool SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
             }
         }
         break;
+    case SDL_PIXELFORMAT_P408:
+    case SDL_PIXELFORMAT_P416:
+        if (rect->x == 0 && rect->y == 0 &&
+            rect->w == swdata->w && rect->h == swdata->h && pitch == swdata->pitches[0]) {
+            SDL_memcpy(swdata->pixels, pixels, (size_t)(swdata->h * pitch * 3));
+        } else {
+            Uint8 *src, *dst;
+            int row;
+            size_t length;
+            const int bpp = SDL_BYTESPERPIXEL(swdata->format);
+
+            // Copy the Y plane
+            src = (Uint8 *)pixels;
+            dst = swdata->pixels;
+            dst += rect->y * swdata->pitches[0] + rect->x *bpp;
+            length = rect->w * bpp;
+            for (row = 0; row < rect->h; ++row) {
+                SDL_memcpy(dst, src, length);
+                src += pitch;
+                dst += swdata->pitches[0];
+            }
+
+            // Copy the next plane
+            dst = swdata->pixels + swdata->h * swdata->pitches[0];
+            dst += rect->y * swdata->pitches[1] + rect->x * bpp;
+            for (row = 0; row < rect->h; ++row) {
+                SDL_memcpy(dst, src, length);
+                src += pitch;
+                dst += swdata->pitches[1];
+            }
+
+            // Copy the next plane
+            dst = swdata->pixels + swdata->h * swdata->pitches[0] + swdata->h * swdata->pitches[1];
+            dst += rect->y * swdata->pitches[2] + rect->x * bpp;
+            for (row = 0; row < rect->h; ++row) {
+                SDL_memcpy(dst, src, length);
+                src += pitch;
+                dst += swdata->pitches[2];
+            }
+        }
+        break;
     case SDL_PIXELFORMAT_YUY2:
     case SDL_PIXELFORMAT_UYVY:
     case SDL_PIXELFORMAT_YVYU:
@@ -229,47 +280,72 @@ bool SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture *swdata, const SDL_Rect *re
     Uint8 *dst;
     int row;
     size_t length;
+    const int bpp = SDL_BYTESPERPIXEL(swdata->format);
 
     // Copy the Y plane
     src = Yplane;
-    dst = swdata->pixels + rect->y * swdata->w + rect->x;
+    dst = swdata->pixels + rect->y * swdata->pitches[0] + rect->x * bpp;
     length = rect->w;
     for (row = 0; row < rect->h; ++row) {
         SDL_memcpy(dst, src, length);
         src += Ypitch;
-        dst += swdata->w;
+        dst += swdata->pitches[0];
     }
 
     // Copy the U plane
     src = Uplane;
-    if (swdata->format == SDL_PIXELFORMAT_IYUV) {
-        dst = swdata->pixels + swdata->h * swdata->w;
+    if (swdata->format == SDL_PIXELFORMAT_P408 ||
+        swdata->format == SDL_PIXELFORMAT_P416) {
+        dst = swdata->pixels + swdata->h * swdata->pitches[0];
+        dst += rect->y * swdata->pitches[1] + rect->x * bpp;
+        length = rect->w * bpp;
+        for (row = 0; row < rect->h; ++row) {
+            SDL_memcpy(dst, src, length);
+            src += Upitch;
+            dst += swdata->pitches[1];
+        }
     } else {
-        dst = swdata->pixels + swdata->h * swdata->w +
-              ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
-    }
-    dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2;
-    length = (rect->w + 1) / 2;
-    for (row = 0; row < (rect->h + 1) / 2; ++row) {
-        SDL_memcpy(dst, src, length);
-        src += Upitch;
-        dst += (swdata->w + 1) / 2;
+        if (swdata->format == SDL_PIXELFORMAT_IYUV) {
+            dst = swdata->pixels + swdata->h * swdata->w;
+        } else {
+            dst = swdata->pixels + swdata->h * swdata->w +
+                  ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
+        }
+        dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2;
+        length = (rect->w + 1) / 2;
+        for (row = 0; row < (rect->h + 1) / 2; ++row) {
+            SDL_memcpy(dst, src, length);
+            src += Upitch;
+            dst += (swdata->w + 1) / 2;
+        }
     }
 
     // Copy the V plane
     src = Vplane;
-    if (swdata->format == SDL_PIXELFORMAT_YV12) {
-        dst = swdata->pixels + swdata->h * swdata->w;
+    if (swdata->format == SDL_PIXELFORMAT_P408 ||
+        swdata->format == SDL_PIXELFORMAT_P416) {
+        dst = swdata->pixels + swdata->h * swdata->pitches[0] + swdata->h * swdata->pitches[1];
+        dst += rect->y * swdata->pitches[2] + rect->x * bpp;
+        length = rect->w * bpp;
+        for (row = 0; row < rect->h; ++row) {
+            SDL_memcpy(dst, src, length);
+            src += Vpitch;
+            dst += swdata->pitches[2];
+        }
     } else {
-        dst = swdata->pixels + swdata->h * swdata->w +
-              ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
-    }
-    dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2;
-    length = (rect->w + 1) / 2;
-    for (row = 0; row < (rect->h + 1) / 2; ++row) {
-        SDL_memcpy(dst, src, length);
-        src += Vpitch;
-        dst += (swdata->w + 1) / 2;
+        if (swdata->format == SDL_PIXELFORMAT_YV12) {
+            dst = swdata->pixels + swdata->h * swdata->w;
+        } else {
+            dst = swdata->pixels + swdata->h * swdata->w +
+                  ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
+        }
+        dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2;
+        length = (rect->w + 1) / 2;
+        for (row = 0; row < (rect->h + 1) / 2; ++row) {
+            SDL_memcpy(dst, src, length);
+            src += Vpitch;
+            dst += (swdata->w + 1) / 2;
+        }
     }
     return true;
 }
@@ -314,10 +390,12 @@ bool SDL_SW_LockYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
     switch (swdata->format) {
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
+    case SDL_PIXELFORMAT_P416:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
         if (rect && (rect->x != 0 || rect->y != 0 || rect->w != swdata->w || rect->h != swdata->h)) {
-            return SDL_SetError("YV12, IYUV, NV12, NV21 textures only support full surface locks");
+            return SDL_SetError("YV12, IYUV, P408, P416, NV12, NV21 textures only support full surface locks");
         }
         break;
     default:
@@ -325,7 +403,7 @@ bool SDL_SW_LockYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
     }
 
     if (rect) {
-        *pixels = swdata->planes[0] + rect->y * swdata->pitches[0] + rect->x * 2;
+        *pixels = swdata->planes[0] + rect->y * swdata->pitches[0] + rect->x * SDL_BYTESPERPIXEL(swdata->format);
     } else {
         *pixels = swdata->planes[0];
     }
diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c
index ed17e6c8b1d3c..c06ec7998a1da 100644
--- a/src/render/direct3d/SDL_render_d3d.c
+++ b/src/render/direct3d/SDL_render_d3d.c
@@ -223,6 +223,7 @@ static D3DFORMAT PixelFormatToD3DFMT(Uint32 format)
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
+    case SDL_PIXELFORMAT_P408:
         return D3DFMT_L8;
     default:
         for (int i = 0; i < SDL_arraysize(d3d_format_map); i++) {
@@ -628,17 +629,30 @@ static bool D3D_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
     }
 #ifdef SDL_HAVE_YUV
     if (texturedata->yuv) {
-        // Skip to the correct offset into the next texture
-        pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
+        if (texture->format == SDL_PIXELFORMAT_P408) {
+            // Skip to the correct offset into the next texture
+            pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
+            if (!D3D_UpdateTextureRep(data->device, &texturedata->utexture, rect->x, rect->y, rect->w, rect->h, pixels, pitch)) {
+                return false;
+            }
 
-        if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->vtexture : &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) {
-            return false;
-        }
+            // Skip to the correct offset into the next texture
+            pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
+            if (!D3D_UpdateTextureRep(data->device, &texturedata->vtexture, rect->x, rect->y, rect->w, rect->h, pixels, pitch)) {
+                return false;
+            }
+        } else {
+            // Skip to the correct offset into the next texture
+            pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
+            if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->vtexture : &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) {
+                return false;
+            }
 
-        // Skip to the correct offset into the next texture
-        pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2));
-        if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->utexture : &texturedata->vtexture, rect->x / 2, (rect->y + 1) / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) {
-            return false;
+            // Skip to the correct offset into the next texture
+            pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2));
+            if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->utexture : &texturedata->vtexture, rect->x / 2, (rect->y + 1) / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) {
+                return false;
+            }
         }
     }
 #endif
@@ -688,6 +702,23 @@ static bool D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
             return false;
         }
 
+        texturedata->shader_params_length = 4; // The YUV shader takes 4 float4 parameters
+        texturedata->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8);
+        if (texturedata->shader_params == NULL) {
+            return SDL_SetError("Unsupported YUV colorspace");
+        }
+    }
+    if (texture->format == SDL_PIXELFORMAT_P408) {
+        texturedata->yuv = true;
+
+        if (!D3D_CreateTextureRep(data->device, &texturedata->utexture, usage, texture->format, PixelFormatToD3DFMT(texture->format), texture->w, texture->h)) {
+            return false;
+        }
+
+        if (!D3D_CreateTextureRep(data->device, &texturedata->vtexture, usage, texture->format, PixelFormatToD3DFMT(texture->format), texture->w, texture->h)) {
+            return false;
+        }
+
         texturedata->shader_params_length = 4; // The YUV shader takes 4 float4 parameters
         texturedata->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8);
         if (texturedata->shader_params == NULL) {
@@ -741,11 +772,20 @@ static bool D3D_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
     if (!D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch)) {
         return false;
     }
-    if (!D3D_UpdateTextureRep(data->device, &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch)) {
-        return false;
-    }
-    if (!D3D_UpdateTextureRep(data->device, &texturedata->vtexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch)) {
-        return false;
+    if (texture->format == SDL_PIXELFORMAT_P408) {
+        if (!D3D_UpdateTextureRep(data->device, &texturedata->utexture, rect->x, rect->y, rect->w, rect->h, Uplane, Upitch)) {
+            return false;
+        }
+        if (!D3D_UpdateTextureRep(data->device, &texturedata->vtexture, rect->x, rect->y, rect->w, rect->h, Vplane, Vpitch)) {
+            return false;
+        }
+    } else {
+        if (!D3D_UpdateTextureRep(data->device, &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch)) {
+            return false;
+        }
+        if (!D3D_UpdateTextureRep(data->device, &texturedata->vtexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch)) {
+            return false;
+        }
     }
     return true;
 }
@@ -2005,6 +2045,7 @@ static bool D3D_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P
     if (caps.MaxSimultaneousTextures >= 3 && data->shaders[SHADER_YUV]) {
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
+        SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408);
     }
 #endif
 
diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c
index edb0be613ae93..607242c51efce 100644
--- a/src/render/direct3d11/SDL_render_d3d11.c
+++ b/src/render/direct3d11/SDL_render_d3d11.c
@@ -266,12 +266,15 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 outpu
     case SDL_PIXELFORMAT_INDEX8:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
         return DXGI_FORMAT_R8_UNORM;
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
         return DXGI_FORMAT_NV12;
     case SDL_PIXELFORMAT_P010:
         return DXGI_FORMAT_P010;
+    case SDL_PIXELFORMAT_P416:
+        return DXGI_FORMAT_R16_UNORM;
     default:
         for (int i = 0; i < SDL_arraysize(dxgi_format_map); i++) {
             if (dxgi_format_map[i].sdl == format) {
@@ -289,8 +292,6 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 outpu
 static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uint32 colorspace)
 {
     switch (format) {
-    case SDL_PIXELFORMAT_YV12:
-    case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_NV12:  // For the Y texture
     case SDL_PIXELFORMAT_NV21:  // For the Y texture
         return DXGI_FORMAT_R8_UNORM;
@@ -1309,6 +1310,45 @@ static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
             return SDL_SetError("Unsupported YUV colorspace");
         }
     }
+    if (texture->format == SDL_PIXELFORMAT_P408 ||
+        texture->format == SDL_PIXELFORMAT_P416) {
+
+        textureData->yuv = true;
+
+        if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_U_POINTER, &textureData->mainTextureU)) {
+            return false;
+        }
+        if (!textureData->mainTextureU) {
+            result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
+                                                  &textureDesc,
+                                                  NULL,
+                                                  &textureData->mainTextureU);
+            if (FAILED(result)) {
+                return WIN_SetErrorFromHRESULT("ID3D11Device1::CreateTexture2D", result);
+            }
+        }
+        SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_U_POINTER, textureData->mainTextureU);
+
+        if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_V_POINTER, &textureData->mainTextureV)) {
+            return false;
+        }
+        if (!textureData->mainTextureV) {
+            result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
+                                                  &textureDesc,
+                                                  NULL,
+                                                  &textureData->mainTextureV);
+            if (FAILED(result)) {
+                return WIN_SetErrorFromHRESULT("ID3D11Device1::CreateTexture2D", result);
+            }
+        }
+        SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_V_POINTER, textureData->mainTextureV);
+
+        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_P408) ? 8 : 16;
+        textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel);
+        if (!textureData->YCbCr_matrix) {
+            return SDL_SetError("Unsupported YUV colorspace");
+        }
+    }
     if (texture->format == SDL_PIXELFORMAT_NV12 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
         texture->format == SDL_PIXELFORMAT_P010) {
@@ -1562,16 +1602,29 @@ static bool D3D11_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
         return D3D11_UpdateTextureNV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch);
 
     } else if (textureData->yuv) {
-        int Ypitch = srcPitch;
-        int UVpitch = ((Ypitch + 1) / 2);
-        const Uint8 *plane0 = (const Uint8 *)srcPixels;
-        const Uint8 *plane1 = plane0 + rect->h * Ypitch;
-        const Uint8 *plane2 = plane1 + ((rect->h + 1) / 2) * UVpitch;
+        if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
+            const Uint8 *plane0 = (const Uint8 *)srcPixels;
+            const Uint8 *plane1 = plane0 + rect->h * srcPitch;
+            const Uint8 *plane2 = plane1 + rect->h * srcPitch;
 
-        if (texture->format == SDL_PIXELFORMAT_YV12) {
-            return D3D11_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane2, UVpitch, plane1, UVpitch);
+            if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureU, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, plane1, srcPitch)) {
+                return false;
+            }
+            if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureV, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, plane2, srcPitch)) {
+                return false;
+            }
         } else {
-            return D3D11_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch, plane2, UVpitch);
+            int Ypitch = srcPitch;
+            int UVpitch = ((Ypitch + 1) / 2);
+            const Uint8 *plane0 = (const Uint8 *)srcPixels;
+            const Uint8 *plane1 = plane0 + rect->h * Ypitch;
+            const Uint8 *plane2 = plane1 + ((rect->h + 1) / 2) * UVpitch;
+
+            if (texture->format == SDL_PIXELFORMAT_YV12) {
+                return D3D11_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane2, UVpitch, plane1, UVpitch);
+            } else {
+                return D3D11_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch, plane2, UVpitch);
+            }
         }
     }
 #endif
@@ -1599,11 +1652,20 @@ static bool D3D11_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
     if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTexture, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch)) {
         return false;
     }
-    if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureU, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch)) {
-        return false;
-    }
-    if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureV, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch)) {
-        return false;
+    if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
+        if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureU, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, Uplane, Upitch)) {
+            return false;
+        }
+        if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureV, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, Vplane, Vpitch)) {
+            return false;
+        }
+    } else {
+        if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureU, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch)) {
+            return false;
+        }
+        if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureV, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch)) {
+            return false;
+        }
     }
     return true;
 }
@@ -2153,6 +2215,7 @@ static void D3D11_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderC
             break;
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_IYUV:
+        case SDL_PIXELFORMAT_P408:
             constants->texture_type = TEXTURETYPE_YUV;
             constants->input_type = INPUTTYPE_SRGB;
             break;
@@ -2168,6 +2231,10 @@ static void D3D11_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderC
             constants->texture_type = TEXTURETYPE_NV12;
             constants->input_type = INPUTTYPE_HDR10;
             break;
+        case SDL_PIXELFORMAT_P416:
+            constants->texture_type = TEXTURETYPE_YUV;
+            constants->input_type = INPUTTYPE_HDR10;
+            break;
         default:
             if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) {
                 constants->texture_type = TEXTURETYPE_RGB_PIXELART;
@@ -2971,9 +3038,11 @@ static bool D3D11_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
+    SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
+    SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P416);
 
     return true;
 }
diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c
index d88903c6f6266..c2671988b8a19 100644
--- a/src/render/direct3d12/SDL_render_d3d12.c
+++ b/src/render/direct3d12/SDL_render_d3d12.c
@@ -328,12 +328,15 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(SDL_PixelFormat format, Uin
     case SDL_PIXELFORMAT_INDEX8:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
         return DXGI_FORMAT_R8_UNORM;
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
         return DXGI_FORMAT_NV12;
     case SDL_PIXELFORMAT_P010:
         return DXGI_FORMAT_P010;
+    case SDL_PIXELFORMAT_P416:
+        return DXGI_FORMAT_R16_UNORM;
     default:
         for (int i = 0; i < SDL_arraysize(dxgi_format_map); i++) {
             if (dxgi_format_map[i].sdl == format) {
@@ -351,9 +354,6 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(SDL_PixelFormat format, Uin
 static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(SDL_PixelFormat format, Uint32 colorspace)
 {
     switch (format) {
-    case SDL_PIXELFORMAT_INDEX8:
-    case SDL_PIXELFORMAT_YV12:
-    case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_NV12: // For the Y texture
     case SDL_PIXELFORMAT_NV21: // For the Y texture
         return DXGI_FORMAT_R8_UNORM;
@@ -1740,6 +1740,55 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
         }
     }
 
+    if (texture->format == SDL_PIXELFORMAT_P408 ||
+        texture->format == SDL_PIXELFORMAT_P416) {
+        textureData->yuv = true;
+
+        if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_U_POINTER, &textureData->mainTextureU)) {
+            return false;
+        }
+        if (!textureData->mainTextureU) {
+            result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice,
+                              &heapProps,
+                              D3

(Patch may be truncated, please check the link at the top of this post.)