SDL: Added SDL_PIXELFORMAT_I0FL

From 8f9738372c21d299b7e0a5dd7b5853ccca500269 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 9 Sep 2026 09:12:35 -0700
Subject: [PATCH] Added SDL_PIXELFORMAT_I0FL

---
 include/SDL3/SDL_pixels.h                |  3 +
 src/render/SDL_render.c                  |  3 +-
 src/render/SDL_yuv_sw.c                  | 49 +++++++------
 src/render/direct3d11/SDL_render_d3d11.c | 25 +++----
 src/render/direct3d12/SDL_render_d3d12.c | 30 ++++----
 src/render/gpu/SDL_render_gpu.c          | 27 +++----
 src/render/metal/SDL_render_metal.m      |  7 +-
 src/render/vulkan/SDL_render_vulkan.c    |  9 ++-
 src/video/SDL_pixels.c                   |  3 +-
 src/video/SDL_yuv.c                      | 44 ++++++++---
 src/video/yuv2rgb/yuv_rgb_std.c          |  5 ++
 src/video/yuv2rgb/yuv_rgb_std.h          |  6 ++
 test/testautomation_pixels.c             |  8 ++
 test/testyuv.c                           | 11 ++-
 test/testyuv_cvt.c                       | 93 ++++++++++++++----------
 15 files changed, 196 insertions(+), 127 deletions(-)

diff --git a/include/SDL3/SDL_pixels.h b/include/SDL3/SDL_pixels.h
index 94bd873c6154f..8da15e860ae51 100644
--- a/include/SDL3/SDL_pixels.h
+++ b/include/SDL3/SDL_pixels.h
@@ -364,6 +364,7 @@ typedef enum SDL_PackedLayout
           ((format) == SDL_PIXELFORMAT_UYVY) || \
           ((format) == SDL_PIXELFORMAT_YVYU) || \
           ((format) == SDL_PIXELFORMAT_P010) || \
+          ((format) == SDL_PIXELFORMAT_I0FL) || \
           ((format) == SDL_PIXELFORMAT_I4FL)) ? 2 : 1) : (((format) >> 0) & 0xFF))
 
 
@@ -676,6 +677,8 @@ typedef enum SDL_PixelFormat
         /* SDL_DEFINE_PIXELFOURCC('I', '4', '4', '4'), */
     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_I0FL = 0x4c463049u,      /**< YUV 4:2:0 16-bit planar mode: Y + U + V  (3 planes) */
+        /* SDL_DEFINE_PIXELFOURCC('I', '0', 'F', 'L'), */
     SDL_PIXELFORMAT_I4FL = 0x4c463449u,      /**< YUV 4:4:4 16-bit planar mode: Y + U + V  (3 planes) */
         /* SDL_DEFINE_PIXELFOURCC('I', '4', 'F', 'L'), */
     SDL_PIXELFORMAT_EXTERNAL_OES = 0x2053454fu,     /**< Android video texture format */
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index bd0c2eb6fc782..b17ec729efbec 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -2498,8 +2498,9 @@ bool SDL_UpdateYUVTexture(SDL_Texture *texture, const SDL_Rect *rect,
     CHECK_PARAM(texture->format != SDL_PIXELFORMAT_YV12 &&
                 texture->format != SDL_PIXELFORMAT_IYUV &&
                 texture->format != SDL_PIXELFORMAT_I444 &&
+                texture->format != SDL_PIXELFORMAT_I0FL &&
                 texture->format != SDL_PIXELFORMAT_I4FL) {
-        return SDL_SetError("Texture format must be YV12, IYUV, I444, or I4FL");
+        return SDL_SetError("Texture format must be YV12, IYUV, I444, I0FL, or I4FL");
     }
 
     real_rect.x = 0;
diff --git a/src/render/SDL_yuv_sw.c b/src/render/SDL_yuv_sw.c
index 02fb98afa92fb..9bb35eedd038d 100644
--- a/src/render/SDL_yuv_sw.c
+++ b/src/render/SDL_yuv_sw.c
@@ -36,6 +36,7 @@ SDL_SW_YUVTexture *SDL_SW_CreateYUVTexture(SDL_PixelFormat format, SDL_Colorspac
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_I444:
+    case SDL_PIXELFORMAT_I0FL:
     case SDL_PIXELFORMAT_I4FL:
     case SDL_PIXELFORMAT_YUY2:
     case SDL_PIXELFORMAT_UYVY:
@@ -75,9 +76,10 @@ SDL_SW_YUVTexture *SDL_SW_CreateYUVTexture(SDL_PixelFormat format, SDL_Colorspac
     switch (format) {
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
-        swdata->pitches[0] = w;
-        swdata->pitches[1] = (swdata->pitches[0] + 1) / 2;
-        swdata->pitches[2] = (swdata->pitches[0] + 1) / 2;
+    case SDL_PIXELFORMAT_I0FL:
+        swdata->pitches[0] = w * SDL_BYTESPERPIXEL(format);
+        swdata->pitches[1] = ((w + 1) / 2) * SDL_BYTESPERPIXEL(format);
+        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 + 1) / 2);
@@ -128,6 +130,7 @@ bool SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
     switch (swdata->format) {
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_I0FL:
         if (rect->x == 0 && rect->y == 0 &&
             rect->w == swdata->w && rect->h == swdata->h && pitch == swdata->pitches[0]) {
             SDL_memcpy(swdata->pixels, pixels,
@@ -136,26 +139,27 @@ bool SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
             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 + rect->y * swdata->w + rect->x;
-            length = rect->w;
+            length = rect->w * bpp;
             for (row = 0; row < rect->h; ++row) {
                 SDL_memcpy(dst, src, length);
                 src += pitch;
-                dst += swdata->w;
+                dst += swdata->pitches[0];
             }
 
             // Copy the next plane
             src = (Uint8 *)pixels + rect->h * pitch;
             dst = swdata->pixels + swdata->h * swdata->w;
             dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2;
-            length = (rect->w + 1) / 2;
+            length = ((rect->w + 1) / 2) * bpp;
             for (row = 0; row < (rect->h + 1) / 2; ++row) {
                 SDL_memcpy(dst, src, length);
                 src += (pitch + 1) / 2;
-                dst += (swdata->w + 1) / 2;
+                dst += swdata->pitches[1];
             }
 
             // Copy the next plane
@@ -163,11 +167,11 @@ bool SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
             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;
+            length = ((rect->w + 1) / 2) * bpp;
             for (row = 0; row < (rect->h + 1) / 2; ++row) {
                 SDL_memcpy(dst, src, length);
                 src += (pitch + 1) / 2;
-                dst += (swdata->w + 1) / 2;
+                dst += swdata->pitches[2];
             }
         }
         break;
@@ -285,7 +289,7 @@ bool SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture *swdata, const SDL_Rect *re
     // Copy the Y plane
     src = Yplane;
     dst = swdata->pixels + rect->y * swdata->pitches[0] + rect->x * bpp;
-    length = rect->w;
+    length = rect->w * bpp;
     for (row = 0; row < rect->h; ++row) {
         SDL_memcpy(dst, src, length);
         src += Ypitch;
@@ -305,18 +309,18 @@ bool SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture *swdata, const SDL_Rect *re
             dst += swdata->pitches[1];
         }
     } else {
-        if (swdata->format == SDL_PIXELFORMAT_IYUV) {
-            dst = swdata->pixels + swdata->h * swdata->w;
+        if (swdata->format == SDL_PIXELFORMAT_IYUV ||
+            swdata->format == SDL_PIXELFORMAT_I0FL) {
+            dst = swdata->pixels + swdata->h * swdata->pitches[0];
         } else {
-            dst = swdata->pixels + swdata->h * swdata->w +
-                  ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
+            dst = swdata->pixels + swdata->h * swdata->pitches[0] + ((swdata->h + 1) / 2) * swdata->pitches[1];
         }
-        dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2;
-        length = (rect->w + 1) / 2;
+        dst += rect->y / 2 * ((swdata->w + 1) / 2) * bpp + (rect->x / 2) * bpp;
+        length = ((rect->w + 1) / 2) * bpp;
         for (row = 0; row < (rect->h + 1) / 2; ++row) {
             SDL_memcpy(dst, src, length);
             src += Upitch;
-            dst += (swdata->w + 1) / 2;
+            dst += swdata->pitches[1];
         }
     }
 
@@ -334,17 +338,16 @@ bool SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture *swdata, const SDL_Rect *re
         }
     } else {
         if (swdata->format == SDL_PIXELFORMAT_YV12) {
-            dst = swdata->pixels + swdata->h * swdata->w;
+            dst = swdata->pixels + swdata->h * swdata->pitches[0];
         } else {
-            dst = swdata->pixels + swdata->h * swdata->w +
-                  ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2);
+            dst = swdata->pixels + swdata->h * swdata->pitches[0] + ((swdata->h + 1) / 2) * swdata->pitches[1];
         }
-        dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2;
-        length = (rect->w + 1) / 2;
+        dst += rect->y / 2 * ((swdata->w + 1) / 2) * bpp + (rect->x / 2) * bpp;
+        length = ((rect->w + 1) / 2) * bpp;
         for (row = 0; row < (rect->h + 1) / 2; ++row) {
             SDL_memcpy(dst, src, length);
             src += Vpitch;
-            dst += (swdata->w + 1) / 2;
+            dst += swdata->pitches[2];
         }
     }
     return true;
diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c
index 6a541a36fed3e..2b6b339286891 100644
--- a/src/render/direct3d11/SDL_render_d3d11.c
+++ b/src/render/direct3d11/SDL_render_d3d11.c
@@ -273,6 +273,7 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 outpu
         return DXGI_FORMAT_NV12;
     case SDL_PIXELFORMAT_P010:
         return DXGI_FORMAT_P010;
+    case SDL_PIXELFORMAT_I0FL:
     case SDL_PIXELFORMAT_I4FL:
         return DXGI_FORMAT_R16_UNORM;
     default:
@@ -1272,7 +1273,8 @@ static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
 
 #ifdef SDL_HAVE_YUV
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
-        texture->format == SDL_PIXELFORMAT_IYUV) {
+        texture->format == SDL_PIXELFORMAT_IYUV ||
+        texture->format == SDL_PIXELFORMAT_I0FL) {
         textureData->yuv = true;
 
         textureDesc.Width = (textureDesc.Width + 1) / 2;
@@ -1306,14 +1308,14 @@ static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
         }
         SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_V_POINTER, textureData->mainTextureV);
 
-        textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8);
+        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_I0FL) ? 16 : 8;
+        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_I444 ||
         texture->format == SDL_PIXELFORMAT_I4FL) {
-
         textureData->yuv = true;
 
         if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_U_POINTER, &textureData->mainTextureU)) {
@@ -1344,7 +1346,7 @@ static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
         }
         SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_V_POINTER, textureData->mainTextureV);
 
-        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_I444) ? 8 : 16;
+        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_I4FL) ? 16 : 8;
         textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel);
         if (!textureData->YCbCr_matrix) {
             return SDL_SetError("Unsupported YUV colorspace");
@@ -1353,18 +1355,9 @@ static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
     if (texture->format == SDL_PIXELFORMAT_NV12 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
         texture->format == SDL_PIXELFORMAT_P010) {
-        int bits_per_pixel;
-
         textureData->nv12 = true;
 
-        switch (texture->format) {
-        case SDL_PIXELFORMAT_P010:
-            bits_per_pixel = 10;
-            break;
-        default:
-            bits_per_pixel = 8;
-            break;
-        }
+        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_P010) ? 10 : 8;
         textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel);
         if (!textureData->YCbCr_matrix) {
             return SDL_SetError("Unsupported YUV colorspace");
@@ -1616,7 +1609,7 @@ static bool D3D11_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
             }
         } else {
             int Ypitch = srcPitch;
-            int UVpitch = ((Ypitch + 1) / 2);
+            int UVpitch = ((Ypitch + 1 * SDL_BYTESPERPIXEL(texture->format)) / 2);
             const Uint8 *plane0 = (const Uint8 *)srcPixels;
             const Uint8 *plane1 = plane0 + rect->h * Ypitch;
             const Uint8 *plane2 = plane1 + ((rect->h + 1) / 2) * UVpitch;
@@ -2232,6 +2225,7 @@ static void D3D11_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderC
             constants->texture_type = TEXTURETYPE_NV12;
             constants->input_type = INPUTTYPE_HDR10;
             break;
+        case SDL_PIXELFORMAT_I0FL:
         case SDL_PIXELFORMAT_I4FL:
             constants->texture_type = TEXTURETYPE_YUV;
             constants->input_type = INPUTTYPE_HDR10;
@@ -3079,6 +3073,7 @@ static bool D3D11_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
+    SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_I0FL);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_I4FL);
 
     return true;
diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c
index daff2f13a9a81..9e529ce5beb1f 100644
--- a/src/render/direct3d12/SDL_render_d3d12.c
+++ b/src/render/direct3d12/SDL_render_d3d12.c
@@ -335,6 +335,7 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(SDL_PixelFormat format, Uin
         return DXGI_FORMAT_NV12;
     case SDL_PIXELFORMAT_P010:
         return DXGI_FORMAT_P010;
+    case SDL_PIXELFORMAT_I0FL:
     case SDL_PIXELFORMAT_I4FL:
         return DXGI_FORMAT_R16_UNORM;
     default:
@@ -1691,7 +1692,8 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
 
 #ifdef SDL_HAVE_YUV
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
-        texture->format == SDL_PIXELFORMAT_IYUV) {
+        texture->format == SDL_PIXELFORMAT_IYUV ||
+        texture->format == SDL_PIXELFORMAT_I0FL) {
         textureData->yuv = true;
 
         textureDesc.Width = (textureDesc.Width + 1) / 2;
@@ -1735,7 +1737,8 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
         textureData->mainResourceStateV = D3D12_RESOURCE_STATE_COPY_DEST;
         SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_V_POINTER, textureData->mainTextureV);
 
-        textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8);
+        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_I0FL) ? 16 : 8;
+        textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel);
         if (!textureData->YCbCr_matrix) {
             return SDL_SetError("Unsupported YUV colorspace");
         }
@@ -1783,7 +1786,7 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
         textureData->mainResourceStateV = D3D12_RESOURCE_STATE_COPY_DEST;
         SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_V_POINTER, textureData->mainTextureV);
 
-        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_I444) ? 8 : 16;
+        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_I4FL) ? 16 : 8;
         textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel);
         if (!textureData->YCbCr_matrix) {
             return SDL_SetError("Unsupported YUV colorspace");
@@ -1793,18 +1796,9 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
     if (texture->format == SDL_PIXELFORMAT_NV12 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
         texture->format == SDL_PIXELFORMAT_P010) {
-        int bits_per_pixel;
-
         textureData->nv12 = true;
 
-        switch (texture->format) {
-        case SDL_PIXELFORMAT_P010:
-            bits_per_pixel = 10;
-            break;
-        default:
-            bits_per_pixel = 8;
-            break;
-        }
+        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_P010) ? 10 : 8;
         textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel);
         if (!textureData->YCbCr_matrix) {
             return SDL_SetError("Unsupported YUV colorspace");
@@ -2084,15 +2078,17 @@ static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                 return false;
             }
         } else {
+            const int bpp = SDL_BYTESPERPIXEL(texture->format);
+
             // Skip to the correct offset into the next texture
             srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch);
-            if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureV : textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateV : &textureData->mainResourceStateU)) {
+            if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureV : textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1 * bpp) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateV : &textureData->mainResourceStateU)) {
                 return false;
             }
 
             // Skip to the correct offset into the next texture
-            srcPixels = (const void *)((const Uint8 *)srcPixels + ((rect->h + 1) / 2) * ((srcPitch + 1) / 2));
-            if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureU : textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateU : &textureData->mainResourceStateV)) {
+            srcPixels = (const void *)((const Uint8 *)srcPixels + ((rect->h + 1) / 2) * ((srcPitch + 1 * bpp) / 2));
+            if (!D3D12_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureU : textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1 * bpp) / 2, texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainResourceStateU : &textureData->mainResourceStateV)) {
                 return false;
             }
         }
@@ -2692,6 +2688,7 @@ static void D3D12_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderC
             constants->texture_type = TEXTURETYPE_NV12;
             constants->input_type = INPUTTYPE_HDR10;
             break;
+        case SDL_PIXELFORMAT_I0FL:
         case SDL_PIXELFORMAT_I4FL:
             constants->texture_type = TEXTURETYPE_YUV;
             constants->input_type = INPUTTYPE_HDR10;
@@ -3685,6 +3682,7 @@ bool D3D12_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Proper
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
+    SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_I0FL);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_I4FL);
 
     return true;
diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c
index db065138f668e..d54243478cba8 100644
--- a/src/render/gpu/SDL_render_gpu.c
+++ b/src/render/gpu/SDL_render_gpu.c
@@ -294,6 +294,7 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
         format = SDL_GPU_TEXTUREFORMAT_R8_UNORM;
         break;
     case SDL_PIXELFORMAT_P010:
+    case SDL_PIXELFORMAT_I0FL:
     case SDL_PIXELFORMAT_I4FL:
         format = SDL_GPU_TEXTUREFORMAT_R16_UNORM;
         break;
@@ -324,7 +325,8 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
         data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
         size = (size_t)texture->h * data->pitch;
         if (texture->format == SDL_PIXELFORMAT_YV12 ||
-            texture->format == SDL_PIXELFORMAT_IYUV) {
+            texture->format == SDL_PIXELFORMAT_IYUV ||
+            texture->format == SDL_PIXELFORMAT_I0FL) {
             // Need to add size for the U and V planes
             size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
         }
@@ -378,7 +380,8 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
 
 #ifdef SDL_HAVE_YUV
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
-        texture->format == SDL_PIXELFORMAT_IYUV) {
+        texture->format == SDL_PIXELFORMAT_IYUV ||
+        texture->format == SDL_PIXELFORMAT_I0FL) {
         data->yuv = true;
 
         tci.width = (tci.width + 1) / 2;
@@ -406,7 +409,8 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
         }
         SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_V_POINTER, data->textureV);
 
-        data->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8);
+        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_I0FL) ? 16 : 8;
+        data->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel);
         if (!data->YCbCr_matrix) {
             return SDL_SetError("Unsupported YUV colorspace");
         }
@@ -437,7 +441,7 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
         }
         SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_V_POINTER, data->textureU);
 
-        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_I444) ? 8 : 16;
+        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_I4FL) ? 16 : 8;
         data->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel);
         if (!data->YCbCr_matrix) {
             return SDL_SetError("Unsupported YUV colorspace");
@@ -446,8 +450,6 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
     if (texture->format == SDL_PIXELFORMAT_NV12 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
         texture->format == SDL_PIXELFORMAT_P010) {
-        int bits_per_pixel;
-
         data->nv12 = true;
 
         data->textureNV = SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_UV_POINTER, NULL);
@@ -469,14 +471,7 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
         }
         SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_UV_POINTER, data->textureNV);
 
-        switch (texture->format) {
-        case SDL_PIXELFORMAT_P010:
-            bits_per_pixel = 10;
-            break;
-        default:
-            bits_per_pixel = 8;
-            break;
-        }
+        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_P010) ? 10 : 8;
         data->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel);
         if (!data->YCbCr_matrix) {
             return SDL_SetError("Unsupported YUV colorspace");
@@ -590,7 +585,7 @@ static bool GPU_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, cons
             retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x, rect->y, rect->w, rect->h, Vplane, pitch);
         } else {
             int Ypitch = pitch;
-            int UVpitch = ((Ypitch + 1) / 2);
+            int UVpitch = ((Ypitch + 1 * SDL_BYTESPERPIXEL(texture->format)) / 2);
             const Uint8 *Yplane = (const Uint8 *)pixels;
             const Uint8 *Uplane = Yplane + rect->h * Ypitch;
             const Uint8 *Vplane = Uplane + ((rect->h + 1) / 2) * UVpitch;
@@ -944,6 +939,7 @@ static void CalculateAdvancedShaderConstants(SDL_Renderer *renderer, const SDL_R
         constants->texture_type = TEXTURETYPE_NV12;
         constants->input_type = INPUTTYPE_HDR10;
         break;
+    case SDL_PIXELFORMAT_I0FL:
     case SDL_PIXELFORMAT_I4FL:
         constants->texture_type = TEXTURETYPE_YUV;
         constants->input_type = INPUTTYPE_HDR10;
@@ -1912,6 +1908,7 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
+    SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_I0FL);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_I4FL);
 
     SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 16384);
diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m
index 14b9853e3fd9b..39eae3a36dac3 100644
--- a/src/render/metal/SDL_render_metal.m
+++ b/src/render/metal/SDL_render_metal.m
@@ -762,6 +762,7 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
             pixfmt = MTLPixelFormatR8Unorm;
             break;
         case SDL_PIXELFORMAT_P010:
+        case SDL_PIXELFORMAT_I0FL:
         case SDL_PIXELFORMAT_I4FL:
             pixfmt = MTLPixelFormatR16Unorm;
             break;
@@ -806,7 +807,7 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
         SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_METAL_TEXTURE_POINTER, (__bridge void *)mtltexture);
 
 #ifdef SDL_HAVE_YUV
-        BOOL yuv = (texture->format == SDL_PIXELFORMAT_IYUV || texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_I444 || texture->format == SDL_PIXELFORMAT_I4FL);
+        BOOL yuv = (texture->format == SDL_PIXELFORMAT_IYUV || texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_I444 || texture->format == SDL_PIXELFORMAT_I0FL || texture->format == SDL_PIXELFORMAT_I4FL);
         BOOL nv12 = (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21 || texture->format == SDL_PIXELFORMAT_P010);
 
         if (yuv) {
@@ -1008,7 +1009,7 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                 UVpitch = pitch;
                 UVrect = *rect;
             } else {
-                UVpitch = (pitch + 1) / 2;
+                UVpitch = (pitch + 1 * SDL_BYTESPERPIXEL(texture->format)) / 2;
                 UVrect.x = rect->x / 2;
                 UVrect.y = rect->y / 2;
                 UVrect.w = (rect->w + 1) / 2;
@@ -1517,6 +1518,7 @@ static void SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderCommand
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_IYUV:
         case SDL_PIXELFORMAT_I444:
+        case SDL_PIXELFORMAT_I0FL:
         case SDL_PIXELFORMAT_I4FL:
             constants->texture_type = TEXTURETYPE_YUV;
             break;
@@ -2622,6 +2624,7 @@ in case we want to use it later (recreating the renderer)
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
+        SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_I0FL);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_I4FL);
 
 #if defined(SDL_PLATFORM_MACOS) || TARGET_OS_MACCATALYST
diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c
index 80086fb271523..9f3993a1b321d 100644
--- a/src/render/vulkan/SDL_render_vulkan.c
+++ b/src/render/vulkan/SDL_render_vulkan.c
@@ -433,6 +433,7 @@ static int VULKAN_VkFormatGetNumPlanes(VkFormat vkFormat)
     switch (vkFormat) {
     case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
     case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
+    case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
     case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
         return 3;
     case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
@@ -455,6 +456,7 @@ static VkDeviceSize VULKAN_GetBytesPerPixel(VkFormat vkFormat, int plane)
     case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
     case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
         return 1;
+    case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
     case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
         return 2;
     case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
@@ -485,6 +487,8 @@ static VkFormat SDLPixelFormatToVkTextureFormat(SDL_PixelFormat format, Uint32 o
         return  VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
     case SDL_PIXELFORMAT_P010:
         return VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
+    case SDL_PIXELFORMAT_I0FL:
+        return VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM;
     case SDL_PIXELFORMAT_I4FL:
         return VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM;
     default:
@@ -2665,6 +2669,7 @@ static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, S
         texture->format == SDL_PIXELFORMAT_NV12 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
         texture->format == SDL_PIXELFORMAT_P010 ||
+        texture->format == SDL_PIXELFORMAT_I0FL ||
         texture->format == SDL_PIXELFORMAT_I4FL) {
         const uint32_t YUV_SD_THRESHOLD = 576;
 
@@ -3008,7 +3013,7 @@ static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
             return VULKAN_UpdateTextureYUV(renderer, texture, rect, plane0, srcPitch, plane1, srcPitch, plane2, srcPitch);
         } else {
             int Ypitch = srcPitch;
-            int UVpitch = ((Ypitch + 1) / 2);
+            int UVpitch = ((Ypitch + 1 * SDL_BYTESPERPIXEL(texture->format)) / 2);
             const Uint8 *plane0 = (const Uint8 *)srcPixels;
             const Uint8 *plane1 = plane0 + rect->h * Ypitch;
             const Uint8 *plane2 = plane1 + ((rect->h + 1) / 2) * UVpitch;
@@ -3497,6 +3502,7 @@ static void VULKAN_SetupShaderConstants(SDL_Renderer *renderer, const SDL_Render
             constants->input_type = INPUTTYPE_SRGB;
             break;
         case SDL_PIXELFORMAT_P010:
+        case SDL_PIXELFORMAT_I0FL:
         case SDL_PIXELFORMAT_I4FL:
             constants->input_type = INPUTTYPE_HDR10;
             break;
@@ -4722,6 +4728,7 @@ static bool VULKAN_CreateRenderer(SDL_Renderer *renderer, SDL_Window *wi

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