Ver Fonte

Added SDL_PIXELFORMAT_P408 and SDL_PIXELFORMAT_P416

Sam Lantinga há 19 horas atrás
pai
commit
07ecc125cf

+ 14 - 9
include/SDL3/SDL_pixels.h

@@ -363,7 +363,8 @@ typedef enum SDL_PackedLayout
         ((((format) == SDL_PIXELFORMAT_YUY2) || \
         ((((format) == SDL_PIXELFORMAT_YUY2) || \
           ((format) == SDL_PIXELFORMAT_UYVY) || \
           ((format) == SDL_PIXELFORMAT_UYVY) || \
           ((format) == SDL_PIXELFORMAT_YVYU) || \
           ((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_PIXELFORMAT_ABGR128_FLOAT = 0x1b608010u,
         /* SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF32, SDL_ARRAYORDER_ABGR, 0, 128, 16), */
         /* 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_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_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_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_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_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_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_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_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_PIXELFORMAT_EXTERNAL_OES = 0x2053454fu,     /**< Android video texture format */
         /* SDL_DEFINE_PIXELFOURCC('O', 'E', 'S', ' ') */
         /* SDL_DEFINE_PIXELFOURCC('O', 'E', 'S', ' ') */
 
 

+ 4 - 2
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 &&
     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;
     real_rect.x = 0;

+ 106 - 28
src/render/SDL_yuv_sw.c

@@ -35,6 +35,8 @@ SDL_SW_YUVTexture *SDL_SW_CreateYUVTexture(SDL_PixelFormat format, SDL_Colorspac
     switch (format) {
     switch (format) {
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
+    case SDL_PIXELFORMAT_P416:
     case SDL_PIXELFORMAT_YUY2:
     case SDL_PIXELFORMAT_YUY2:
     case SDL_PIXELFORMAT_UYVY:
     case SDL_PIXELFORMAT_UYVY:
     case SDL_PIXELFORMAT_YVYU:
     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[1] = swdata->planes[0] + swdata->pitches[0] * h;
         swdata->planes[2] = swdata->planes[1] + swdata->pitches[1] * ((h + 1) / 2);
         swdata->planes[2] = swdata->planes[1] + swdata->pitches[1] * ((h + 1) / 2);
         break;
         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_YUY2:
     case SDL_PIXELFORMAT_UYVY:
     case SDL_PIXELFORMAT_UYVY:
     case SDL_PIXELFORMAT_YVYU:
     case SDL_PIXELFORMAT_YVYU:
         swdata->pitches[0] = ((w + 1) / 2) * 4;
         swdata->pitches[0] = ((w + 1) / 2) * 4;
         swdata->planes[0] = swdata->pixels;
         swdata->planes[0] = swdata->pixels;
         break;
         break;
-
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
     case SDL_PIXELFORMAT_NV21:
         swdata->pitches[0] = w;
         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_YV12:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
         if (rect->x == 0 && rect->y == 0 &&
         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,
             SDL_memcpy(swdata->pixels, pixels,
                        (size_t)(swdata->h * swdata->w) + 2 * ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2));
                        (size_t)(swdata->h * swdata->w) + 2 * ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2));
         } else {
         } else {
@@ -161,6 +171,47 @@ bool SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
             }
             }
         }
         }
         break;
         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_YUY2:
     case SDL_PIXELFORMAT_UYVY:
     case SDL_PIXELFORMAT_UYVY:
     case SDL_PIXELFORMAT_YVYU:
     case SDL_PIXELFORMAT_YVYU:
@@ -229,47 +280,72 @@ bool SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture *swdata, const SDL_Rect *re
     Uint8 *dst;
     Uint8 *dst;
     int row;
     int row;
     size_t length;
     size_t length;
+    const int bpp = SDL_BYTESPERPIXEL(swdata->format);
 
 
     // Copy the Y plane
     // Copy the Y plane
     src = Yplane;
     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;
     length = rect->w;
     for (row = 0; row < rect->h; ++row) {
     for (row = 0; row < rect->h; ++row) {
         SDL_memcpy(dst, src, length);
         SDL_memcpy(dst, src, length);
         src += Ypitch;
         src += Ypitch;
-        dst += swdata->w;
+        dst += swdata->pitches[0];
     }
     }
 
 
     // Copy the U plane
     // Copy the U plane
     src = Uplane;
     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 {
     } 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
     // Copy the V plane
     src = Vplane;
     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 {
     } 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;
     return true;
 }
 }
@@ -314,10 +390,12 @@ bool SDL_SW_LockYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
     switch (swdata->format) {
     switch (swdata->format) {
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
+    case SDL_PIXELFORMAT_P416:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
     case SDL_PIXELFORMAT_NV21:
         if (rect && (rect->x != 0 || rect->y != 0 || rect->w != swdata->w || rect->h != swdata->h)) {
         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;
         break;
     default:
     default:
@@ -325,7 +403,7 @@ bool SDL_SW_LockYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect,
     }
     }
 
 
     if (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 {
     } else {
         *pixels = swdata->planes[0];
         *pixels = swdata->planes[0];
     }
     }

+ 55 - 14
src/render/direct3d/SDL_render_d3d.c

@@ -223,6 +223,7 @@ static D3DFORMAT PixelFormatToD3DFMT(Uint32 format)
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
     case SDL_PIXELFORMAT_NV21:
+    case SDL_PIXELFORMAT_P408:
         return D3DFMT_L8;
         return D3DFMT_L8;
     default:
     default:
         for (int i = 0; i < SDL_arraysize(d3d_format_map); i++) {
         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
 #ifdef SDL_HAVE_YUV
     if (texturedata->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
 #endif
@@ -688,6 +702,23 @@ static bool D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
             return false;
             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_length = 4; // The YUV shader takes 4 float4 parameters
         texturedata->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8);
         texturedata->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8);
         if (texturedata->shader_params == NULL) {
         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)) {
     if (!D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch)) {
         return false;
         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;
     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]) {
     if (caps.MaxSimultaneousTextures >= 3 && data->shaders[SHADER_YUV]) {
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
+        SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408);
     }
     }
 #endif
 #endif
 
 

+ 84 - 15
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_INDEX8:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
         return DXGI_FORMAT_R8_UNORM;
         return DXGI_FORMAT_R8_UNORM;
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
     case SDL_PIXELFORMAT_NV21:
         return DXGI_FORMAT_NV12;
         return DXGI_FORMAT_NV12;
     case SDL_PIXELFORMAT_P010:
     case SDL_PIXELFORMAT_P010:
         return DXGI_FORMAT_P010;
         return DXGI_FORMAT_P010;
+    case SDL_PIXELFORMAT_P416:
+        return DXGI_FORMAT_R16_UNORM;
     default:
     default:
         for (int i = 0; i < SDL_arraysize(dxgi_format_map); i++) {
         for (int i = 0; i < SDL_arraysize(dxgi_format_map); i++) {
             if (dxgi_format_map[i].sdl == format) {
             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)
 static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uint32 colorspace)
 {
 {
     switch (format) {
     switch (format) {
-    case SDL_PIXELFORMAT_YV12:
-    case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_NV12:  // For the Y texture
     case SDL_PIXELFORMAT_NV12:  // For the Y texture
     case SDL_PIXELFORMAT_NV21:  // For the Y texture
     case SDL_PIXELFORMAT_NV21:  // For the Y texture
         return DXGI_FORMAT_R8_UNORM;
         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");
             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 ||
     if (texture->format == SDL_PIXELFORMAT_NV12 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
         texture->format == SDL_PIXELFORMAT_P010) {
         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);
         return D3D11_UpdateTextureNV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch);
 
 
     } else if (textureData->yuv) {
     } 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 {
         } 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
 #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)) {
     if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTexture, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch)) {
         return false;
         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;
     return true;
 }
 }
@@ -2153,6 +2215,7 @@ static void D3D11_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderC
             break;
             break;
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_IYUV:
         case SDL_PIXELFORMAT_IYUV:
+        case SDL_PIXELFORMAT_P408:
             constants->texture_type = TEXTURETYPE_YUV;
             constants->texture_type = TEXTURETYPE_YUV;
             constants->input_type = INPUTTYPE_SRGB;
             constants->input_type = INPUTTYPE_SRGB;
             break;
             break;
@@ -2168,6 +2231,10 @@ static void D3D11_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderC
             constants->texture_type = TEXTURETYPE_NV12;
             constants->texture_type = TEXTURETYPE_NV12;
             constants->input_type = INPUTTYPE_HDR10;
             constants->input_type = INPUTTYPE_HDR10;
             break;
             break;
+        case SDL_PIXELFORMAT_P416:
+            constants->texture_type = TEXTURETYPE_YUV;
+            constants->input_type = INPUTTYPE_HDR10;
+            break;
         default:
         default:
             if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) {
             if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) {
                 constants->texture_type = TEXTURETYPE_RGB_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_INDEX8);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
+    SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
+    SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P416);
 
 
     return true;
     return true;
 }
 }

+ 95 - 17
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_INDEX8:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
         return DXGI_FORMAT_R8_UNORM;
         return DXGI_FORMAT_R8_UNORM;
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
     case SDL_PIXELFORMAT_NV21:
         return DXGI_FORMAT_NV12;
         return DXGI_FORMAT_NV12;
     case SDL_PIXELFORMAT_P010:
     case SDL_PIXELFORMAT_P010:
         return DXGI_FORMAT_P010;
         return DXGI_FORMAT_P010;
+    case SDL_PIXELFORMAT_P416:
+        return DXGI_FORMAT_R16_UNORM;
     default:
     default:
         for (int i = 0; i < SDL_arraysize(dxgi_format_map); i++) {
         for (int i = 0; i < SDL_arraysize(dxgi_format_map); i++) {
             if (dxgi_format_map[i].sdl == format) {
             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)
 static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(SDL_PixelFormat format, Uint32 colorspace)
 {
 {
     switch (format) {
     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_NV12: // For the Y texture
     case SDL_PIXELFORMAT_NV21: // For the Y texture
     case SDL_PIXELFORMAT_NV21: // For the Y texture
         return DXGI_FORMAT_R8_UNORM;
         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,
+                              D3D12_HEAP_FLAG_NONE,
+                              &textureDesc,
+                              D3D12_RESOURCE_STATE_COPY_DEST,
+                              NULL,
+                              D3D_GUID(SDL_IID_ID3D12Resource),
+                              (void **)&textureData->mainTextureU);
+            if (FAILED(result)) {
+                return WIN_SetErrorFromHRESULT("ID3D12Device::CreateCommittedResource [texture]", result);
+            }
+        }
+        textureData->mainResourceStateU = D3D12_RESOURCE_STATE_COPY_DEST;
+        SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_U_POINTER, textureData->mainTextureU);
+
+        if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_V_POINTER, &textureData->mainTextureV)) {
+            return false;
+        }
+        if (!textureData->mainTextureV) {
+            result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice,
+                              &heapProps,
+                              D3D12_HEAP_FLAG_NONE,
+                              &textureDesc,
+                              D3D12_RESOURCE_STATE_COPY_DEST,
+                              NULL,
+                              D3D_GUID(SDL_IID_ID3D12Resource),
+                              (void **)&textureData->mainTextureV);
+            if (FAILED(result)) {
+                return WIN_SetErrorFromHRESULT("ID3D12Device::CreateCommittedResource [texture]", result);
+            }
+        }
+        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_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 ||
     if (texture->format == SDL_PIXELFORMAT_NV12 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
         texture->format == SDL_PIXELFORMAT_P010) {
         texture->format == SDL_PIXELFORMAT_P010) {
@@ -2021,17 +2070,30 @@ static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
     }
     }
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     if (textureData->yuv) {
     if (textureData->yuv) {
-        // Skip to the correct offset into the next texture
-        srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch);
+        if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
+            // Skip to the correct offset into the next texture
+            srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch);
+            if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureU, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainResourceStateU)) {
+                return false;
+            }
 
 
-        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)) {
-            return false;
-        }
+            // Skip to the correct offset into the next texture
+            srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch);
+            if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureV, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainResourceStateV)) {
+                return false;
+            }
+        } else {
+            // 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)) {
+                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)) {
-            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)) {
+                return false;
+            }
         }
         }
     }
     }
 
 
@@ -2073,11 +2135,20 @@ static bool D3D12_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
     if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainResourceState)) {
     if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainResourceState)) {
         return false;
         return false;
     }
     }
-    if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch, &textureData->mainResourceStateU)) {
-        return false;
-    }
-    if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch, &textureData->mainResourceStateV)) {
-        return false;
+    if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
+        if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureU, 0, rect->x, rect->y, rect->w, rect->h, Uplane, Upitch, &textureData->mainResourceStateU)) {
+            return false;
+        }
+        if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureV, 0, rect->x, rect->y, rect->w, rect->h, Vplane, Vpitch, &textureData->mainResourceStateV)) {
+            return false;
+        }
+    } else {
+        if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureU, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch, &textureData->mainResourceStateU)) {
+            return false;
+        }
+        if (!D3D12_UpdateTextureInternal(rendererData, textureData->mainTextureV, 0, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch, &textureData->mainResourceStateV)) {
+            return false;
+        }
     }
     }
     if (textureData->mainTextureResourceView.ptr == rendererData->currentShaderResource.ptr) {
     if (textureData->mainTextureResourceView.ptr == rendererData->currentShaderResource.ptr) {
         // We'll need to rebind this resource after updating it
         // We'll need to rebind this resource after updating it
@@ -2604,6 +2675,7 @@ static void D3D12_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderC
             break;
             break;
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_IYUV:
         case SDL_PIXELFORMAT_IYUV:
+        case SDL_PIXELFORMAT_P408:
             constants->texture_type = TEXTURETYPE_YUV;
             constants->texture_type = TEXTURETYPE_YUV;
             constants->input_type = INPUTTYPE_SRGB;
             constants->input_type = INPUTTYPE_SRGB;
             break;
             break;
@@ -2619,6 +2691,10 @@ static void D3D12_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderC
             constants->texture_type = TEXTURETYPE_NV12;
             constants->texture_type = TEXTURETYPE_NV12;
             constants->input_type = INPUTTYPE_HDR10;
             constants->input_type = INPUTTYPE_HDR10;
             break;
             break;
+        case SDL_PIXELFORMAT_P416:
+            constants->texture_type = TEXTURETYPE_YUV;
+            constants->input_type = INPUTTYPE_HDR10;
+            break;
         default:
         default:
             if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) {
             if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) {
                 constants->texture_type = TEXTURETYPE_RGB_PIXELART;
                 constants->texture_type = TEXTURETYPE_RGB_PIXELART;
@@ -3566,9 +3642,11 @@ bool D3D12_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Proper
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
+    SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
+    SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P416);
 
 
     return true;
     return true;
 }
 }

+ 72 - 12
src/render/gpu/SDL_render_gpu.c

@@ -287,11 +287,13 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
     case SDL_PIXELFORMAT_INDEX8:
     case SDL_PIXELFORMAT_INDEX8:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
     case SDL_PIXELFORMAT_NV21:
         format = SDL_GPU_TEXTUREFORMAT_R8_UNORM;
         format = SDL_GPU_TEXTUREFORMAT_R8_UNORM;
         break;
         break;
     case SDL_PIXELFORMAT_P010:
     case SDL_PIXELFORMAT_P010:
+    case SDL_PIXELFORMAT_P416:
         format = SDL_GPU_TEXTUREFORMAT_R16_UNORM;
         format = SDL_GPU_TEXTUREFORMAT_R16_UNORM;
         break;
         break;
     default:
     default:
@@ -325,6 +327,11 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
             // Need to add size for the U and V planes
             // Need to add size for the U and V planes
             size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
             size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
         }
         }
+        if (texture->format == SDL_PIXELFORMAT_P408 ||
+            texture->format == SDL_PIXELFORMAT_P416) {
+            // Need to add size for the U and V planes
+            size += 2 * texture->h * data->pitch;
+        }
         if (texture->format == SDL_PIXELFORMAT_NV12 ||
         if (texture->format == SDL_PIXELFORMAT_NV12 ||
             texture->format == SDL_PIXELFORMAT_NV21 ||
             texture->format == SDL_PIXELFORMAT_NV21 ||
             texture->format == SDL_PIXELFORMAT_P010) {
             texture->format == SDL_PIXELFORMAT_P010) {
@@ -403,6 +410,38 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
             return SDL_SetError("Unsupported YUV colorspace");
             return SDL_SetError("Unsupported YUV colorspace");
         }
         }
     }
     }
+    if (texture->format == SDL_PIXELFORMAT_P408 ||
+        texture->format == SDL_PIXELFORMAT_P416) {
+        data->yuv = true;
+
+        data->textureU = SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_U_POINTER, NULL);
+        if (data->textureU) {
+            data->external_texture_u = true;
+        } else {
+            data->textureU = SDL_CreateGPUTexture(renderdata->device, &tci);
+            if (!data->textureU) {
+                return false;
+            }
+        }
+        SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_U_POINTER, data->textureU);
+
+        data->textureV = SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_V_POINTER, NULL);
+        if (data->textureV) {
+            data->external_texture_v = true;
+        } else {
+            data->textureV = SDL_CreateGPUTexture(renderdata->device, &tci);
+            if (!data->textureV) {
+                return false;
+            }
+        }
+        SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_V_POINTER, data->textureU);
+
+        const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_P408) ? 8 : 16;
+        data->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel);
+        if (!data->YCbCr_matrix) {
+            return SDL_SetError("Unsupported YUV colorspace");
+        }
+    }
     if (texture->format == SDL_PIXELFORMAT_NV12 ||
     if (texture->format == SDL_PIXELFORMAT_NV12 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
         texture->format == SDL_PIXELFORMAT_P010) {
         texture->format == SDL_PIXELFORMAT_P010) {
@@ -541,18 +580,27 @@ static bool GPU_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, cons
         retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureNV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, UVplane, UVpitch);
         retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureNV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, UVplane, UVpitch);
 
 
     } else if (data->yuv) {
     } else if (data->yuv) {
-        int Ypitch = pitch;
-        int UVpitch = ((Ypitch + 1) / 2);
-        const Uint8 *Yplane = (const Uint8 *)pixels;
-        const Uint8 *Uplane = Yplane + rect->h * Ypitch;
-        const Uint8 *Vplane = Uplane + ((rect->h + 1) / 2) * UVpitch;
+        if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
+            const Uint8 *Yplane = (const Uint8 *)pixels;
+            const Uint8 *Uplane = Yplane + rect->h * pitch;
+            const Uint8 *Vplane = Uplane + rect->h * pitch;
 
 
-        if (texture->format == SDL_PIXELFORMAT_YV12) {
-            retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, UVpitch);
-            retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, UVpitch);
+            retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x, rect->y, rect->w, rect->h, Uplane, pitch);
+            retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x, rect->y, rect->w, rect->h, Vplane, pitch);
         } else {
         } else {
-            retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, UVpitch);
-            retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, UVpitch);
+            int Ypitch = pitch;
+            int UVpitch = ((Ypitch + 1) / 2);
+            const Uint8 *Yplane = (const Uint8 *)pixels;
+            const Uint8 *Uplane = Yplane + rect->h * Ypitch;
+            const Uint8 *Vplane = Uplane + ((rect->h + 1) / 2) * UVpitch;
+
+            if (texture->format == SDL_PIXELFORMAT_YV12) {
+                retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, UVpitch);
+                retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, UVpitch);
+            } else {
+                retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, UVpitch);
+                retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, UVpitch);
+            }
         }
         }
     }
     }
 #endif
 #endif
@@ -576,8 +624,13 @@ static bool GPU_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
     SDL_GPUCommandBuffer *cbuf = renderdata->state.command_buffer;
     SDL_GPUCommandBuffer *cbuf = renderdata->state.command_buffer;
     SDL_GPUCopyPass *cpass = SDL_BeginGPUCopyPass(cbuf);
     SDL_GPUCopyPass *cpass = SDL_BeginGPUCopyPass(cbuf);
     retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->texture, bpp, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch);
     retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->texture, bpp, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch);
-    retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch);
-    retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch);
+    if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
+        retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x, rect->y, rect->w, rect->h, Uplane, Upitch);
+        retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x, rect->y, rect->w, rect->h, Vplane, Vpitch);
+    } else {
+        retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureU, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch);
+        retval &= GPU_UpdateTextureInternal(renderdata, cpass, data->textureV, bpp, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch);
+    }
     SDL_EndGPUCopyPass(cpass);
     SDL_EndGPUCopyPass(cpass);
     return retval;
     return retval;
 }
 }
@@ -874,6 +927,7 @@ static void CalculateAdvancedShaderConstants(SDL_Renderer *renderer, const SDL_R
         break;
         break;
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
         constants->texture_type = TEXTURETYPE_YUV;
         constants->texture_type = TEXTURETYPE_YUV;
         constants->input_type = INPUTTYPE_SRGB;
         constants->input_type = INPUTTYPE_SRGB;
         break;
         break;
@@ -889,6 +943,10 @@ static void CalculateAdvancedShaderConstants(SDL_Renderer *renderer, const SDL_R
         constants->texture_type = TEXTURETYPE_NV12;
         constants->texture_type = TEXTURETYPE_NV12;
         constants->input_type = INPUTTYPE_HDR10;
         constants->input_type = INPUTTYPE_HDR10;
         break;
         break;
+    case SDL_PIXELFORMAT_P416:
+        constants->texture_type = TEXTURETYPE_YUV;
+        constants->input_type = INPUTTYPE_HDR10;
+        break;
     default:
     default:
         switch (texture->format) {
         switch (texture->format) {
         case SDL_PIXELFORMAT_BGRX32:
         case SDL_PIXELFORMAT_BGRX32:
@@ -1842,9 +1900,11 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
+    SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
+    SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P416);
 
 
     SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 16384);
     SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 16384);
 
 

+ 36 - 7
src/render/metal/SDL_render_metal.m

@@ -752,12 +752,14 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
             break;
             break;
         case SDL_PIXELFORMAT_INDEX8:
         case SDL_PIXELFORMAT_INDEX8:
         case SDL_PIXELFORMAT_IYUV:
         case SDL_PIXELFORMAT_IYUV:
+        case SDL_PIXELFORMAT_P408:
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_NV12:
         case SDL_PIXELFORMAT_NV12:
         case SDL_PIXELFORMAT_NV21:
         case SDL_PIXELFORMAT_NV21:
             pixfmt = MTLPixelFormatR8Unorm;
             pixfmt = MTLPixelFormatR8Unorm;
             break;
             break;
         case SDL_PIXELFORMAT_P010:
         case SDL_PIXELFORMAT_P010:
+        case SDL_PIXELFORMAT_P416:
             pixfmt = MTLPixelFormatR16Unorm;
             pixfmt = MTLPixelFormatR16Unorm;
             break;
             break;
         case SDL_PIXELFORMAT_RGBA64_FLOAT:
         case SDL_PIXELFORMAT_RGBA64_FLOAT:
@@ -796,13 +798,18 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
 
 
         mtltextureUv = nil;
         mtltextureUv = nil;
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
-        BOOL yuv = (texture->format == SDL_PIXELFORMAT_IYUV || texture->format == SDL_PIXELFORMAT_YV12);
+        BOOL yuv = (texture->format == SDL_PIXELFORMAT_IYUV || texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416);
         BOOL nv12 = (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21 || texture->format == SDL_PIXELFORMAT_P010);
         BOOL nv12 = (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21 || texture->format == SDL_PIXELFORMAT_P010);
 
 
         if (yuv) {
         if (yuv) {
-            mtltexdesc.pixelFormat = MTLPixelFormatR8Unorm;
-            mtltexdesc.width = (texture->w + 1) / 2;
-            mtltexdesc.height = (texture->h + 1) / 2;
+			mtltexdesc.pixelFormat = pixfmt;
+            if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
+                mtltexdesc.width = texture->w;
+                mtltexdesc.height = texture->h;
+            } else {
+                mtltexdesc.width = (texture->w + 1) / 2;
+                mtltexdesc.height = (texture->h + 1) / 2;
+            }
             mtltexdesc.textureType = MTLTextureType2DArray;
             mtltexdesc.textureType = MTLTextureType2DArray;
             mtltexdesc.arrayLength = 2;
             mtltexdesc.arrayLength = 2;
         } else if (texture->format == SDL_PIXELFORMAT_P010) {
         } else if (texture->format == SDL_PIXELFORMAT_P010) {
@@ -954,8 +961,18 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
         if (texturedata.yuv) {
         if (texturedata.yuv) {
             int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0;
             int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0;
             int Vslice = texture->format == SDL_PIXELFORMAT_YV12 ? 0 : 1;
             int Vslice = texture->format == SDL_PIXELFORMAT_YV12 ? 0 : 1;
-            int UVpitch = (pitch + 1) / 2;
-            SDL_Rect UVrect = { rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2 };
+            int UVpitch;
+            SDL_Rect UVrect;
+            if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
+                UVpitch = pitch;
+                UVrect = *rect;
+            } else {
+                UVpitch = (pitch + 1) / 2;
+                UVrect.x = rect->x / 2;
+                UVrect.y = rect->y / 2;
+                UVrect.w = (rect->w + 1) / 2;
+                UVrect.h = (rect->h + 1) / 2;
+            }
 
 
             // Skip to the correct offset into the next texture
             // Skip to the correct offset into the next texture
             pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
             pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
@@ -998,7 +1015,15 @@ static bool METAL_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
         SDL3METAL_TextureData *texturedata = (__bridge SDL3METAL_TextureData *)texture->internal;
         SDL3METAL_TextureData *texturedata = (__bridge SDL3METAL_TextureData *)texture->internal;
         const int Uslice = 0;
         const int Uslice = 0;
         const int Vslice = 1;
         const int Vslice = 1;
-        SDL_Rect UVrect = { rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2 };
+        SDL_Rect UVrect;
+        if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
+            UVrect = *rect;
+        } else {
+            UVrect.x = rect->x / 2;
+            UVrect.y = rect->y / 2;
+            UVrect.w = (rect->w + 1) / 2;
+            UVrect.h = (rect->h + 1) / 2;
+        }
 
 
         // Bail out if we're supposed to update an empty rectangle
         // Bail out if we're supposed to update an empty rectangle
         if (rect->w <= 0 || rect->h <= 0) {
         if (rect->w <= 0 || rect->h <= 0) {
@@ -1451,6 +1476,8 @@ static void SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderCommand
             break;
             break;
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_IYUV:
         case SDL_PIXELFORMAT_IYUV:
+        case SDL_PIXELFORMAT_P408:
+        case SDL_PIXELFORMAT_P416:
             constants->texture_type = TEXTURETYPE_YUV;
             constants->texture_type = TEXTURETYPE_YUV;
             break;
             break;
         case SDL_PIXELFORMAT_NV12:
         case SDL_PIXELFORMAT_NV12:
@@ -2512,9 +2539,11 @@ static bool METAL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
+        SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
+        SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P416);
 
 
 #if defined(SDL_PLATFORM_MACOS) || TARGET_OS_MACCATALYST
 #if defined(SDL_PLATFORM_MACOS) || TARGET_OS_MACCATALYST
         data.mtllayer.displaySyncEnabled = NO;
         data.mtllayer.displaySyncEnabled = NO;

+ 89 - 28
src/render/opengl/SDL_render_gl.c

@@ -435,6 +435,7 @@ static bool convert_format(Uint32 pixel_format, GLint *internalFormat, GLenum *f
     case SDL_PIXELFORMAT_INDEX8:
     case SDL_PIXELFORMAT_INDEX8:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
     case SDL_PIXELFORMAT_NV21:
         *internalFormat = GL_LUMINANCE;
         *internalFormat = GL_LUMINANCE;
@@ -593,6 +594,10 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P
             // Need to add size for the U and V planes
             // Need to add size for the U and V planes
             size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
             size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
         }
         }
+        if (texture->format == SDL_PIXELFORMAT_P408) {
+            // Need to add size for the U and V planes
+            size += 2 * texture->h * data->pitch;
+        }
         if (texture->format == SDL_PIXELFORMAT_NV12 ||
         if (texture->format == SDL_PIXELFORMAT_NV12 ||
             texture->format == SDL_PIXELFORMAT_NV21) {
             texture->format == SDL_PIXELFORMAT_NV21) {
             // Need to add size for the U/V plane
             // Need to add size for the U/V plane
@@ -724,6 +729,35 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P
         SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_V_NUMBER, data->vtexture);
         SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_V_NUMBER, data->vtexture);
     }
     }
 
 
+    if (texture->format == SDL_PIXELFORMAT_P408) {
+        data->yuv = true;
+
+        data->utexture = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_U_NUMBER, 0);
+        if (data->utexture) {
+            data->utexture_external = true;
+        } else {
+            renderdata->glGenTextures(1, &data->utexture);
+        }
+        data->vtexture = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_V_NUMBER, 0);
+        if (data->vtexture) {
+            data->vtexture_external = true;
+        } else {
+            renderdata->glGenTextures(1, &data->vtexture);
+        }
+
+        renderdata->glBindTexture(textype, data->utexture);
+        renderdata->glTexImage2D(textype, 0, internalFormat, texture_w, texture_h, 0, format, type, NULL);
+        SetTextureScaleMode(renderdata, textype, texture->format, data->texture_scale_mode);
+        SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v);
+        SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_U_NUMBER, data->utexture);
+
+        renderdata->glBindTexture(textype, data->vtexture);
+        renderdata->glTexImage2D(textype, 0, internalFormat, texture_w, texture_h, 0, format, type, NULL);
+        SetTextureScaleMode(renderdata, textype, texture->format, data->texture_scale_mode);
+        SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v);
+        SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_V_NUMBER, data->vtexture);
+    }
+
     if (texture->format == SDL_PIXELFORMAT_NV12 ||
     if (texture->format == SDL_PIXELFORMAT_NV12 ||
         texture->format == SDL_PIXELFORMAT_NV21) {
         texture->format == SDL_PIXELFORMAT_NV21) {
         data->nv12 = true;
         data->nv12 = true;
@@ -807,29 +841,43 @@ static bool GL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                                 pixels);
                                 pixels);
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     if (data->yuv) {
     if (data->yuv) {
-        renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, ((pitch + 1) / 2));
+        if (texture->format == SDL_PIXELFORMAT_P408) {
+            // Skip to the correct offset into the next texture
+            pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
+            renderdata->glBindTexture(textype, data->utexture);
+            renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w, rect->h,
+                                        data->format, data->formattype, pixels);
 
 
-        // Skip to the correct offset into the next texture
-        pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
-        if (texture->format == SDL_PIXELFORMAT_YV12) {
+            // Skip to the correct offset into the next texture
+            pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
             renderdata->glBindTexture(textype, data->vtexture);
             renderdata->glBindTexture(textype, data->vtexture);
+            renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w, rect->h,
+                                        data->format, data->formattype, pixels);
         } else {
         } else {
-            renderdata->glBindTexture(textype, data->utexture);
-        }
-        renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2,
-                                    (rect->w + 1) / 2, (rect->h + 1) / 2,
-                                    data->format, data->formattype, pixels);
+            renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, ((pitch + 1) / 2));
 
 
-        // Skip to the correct offset into the next texture
-        pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2));
-        if (texture->format == SDL_PIXELFORMAT_YV12) {
-            renderdata->glBindTexture(textype, data->utexture);
-        } else {
-            renderdata->glBindTexture(textype, data->vtexture);
+            // Skip to the correct offset into the next texture
+            pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
+            if (texture->format == SDL_PIXELFORMAT_YV12) {
+                renderdata->glBindTexture(textype, data->vtexture);
+            } else {
+                renderdata->glBindTexture(textype, data->utexture);
+            }
+            renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2,
+                                        (rect->w + 1) / 2, (rect->h + 1) / 2,
+                                        data->format, data->formattype, pixels);
+
+            // Skip to the correct offset into the next texture
+            pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2));
+            if (texture->format == SDL_PIXELFORMAT_YV12) {
+                renderdata->glBindTexture(textype, data->utexture);
+            } else {
+                renderdata->glBindTexture(textype, data->vtexture);
+            }
+            renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2,
+                                        (rect->w + 1) / 2, (rect->h + 1) / 2,
+                                        data->format, data->formattype, pixels);
         }
         }
-        renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2,
-                                    (rect->w + 1) / 2, (rect->h + 1) / 2,
-                                    data->format, data->formattype, pixels);
     }
     }
 
 
     if (data->nv12) {
     if (data->nv12) {
@@ -868,17 +916,29 @@ static bool GL_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
                                 rect->h, data->format, data->formattype,
                                 rect->h, data->format, data->formattype,
                                 Yplane);
                                 Yplane);
 
 
-    renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Upitch);
-    renderdata->glBindTexture(textype, data->utexture);
-    renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2,
-                                (rect->w + 1) / 2, (rect->h + 1) / 2,
-                                data->format, data->formattype, Uplane);
+    if (texture->format == SDL_PIXELFORMAT_P408) {
+        renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Upitch);
+        renderdata->glBindTexture(textype, data->utexture);
+        renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w, rect->h,
+                                    data->format, data->formattype, Uplane);
 
 
-    renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Vpitch);
-    renderdata->glBindTexture(textype, data->vtexture);
-    renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2,
-                                (rect->w + 1) / 2, (rect->h + 1) / 2,
-                                data->format, data->formattype, Vplane);
+        renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Vpitch);
+        renderdata->glBindTexture(textype, data->vtexture);
+        renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w, rect->h,
+                                    data->format, data->formattype, Vplane);
+    } else {
+        renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Upitch);
+        renderdata->glBindTexture(textype, data->utexture);
+        renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2,
+                                    (rect->w + 1) / 2, (rect->h + 1) / 2,
+                                    data->format, data->formattype, Uplane);
+
+        renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Vpitch);
+        renderdata->glBindTexture(textype, data->vtexture);
+        renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2,
+                                    (rect->w + 1) / 2, (rect->h + 1) / 2,
+                                    data->format, data->formattype, Vplane);
+    }
 
 
     return GL_CheckError("glTexSubImage2D()", renderer);
     return GL_CheckError("glTexSubImage2D()", renderer);
 }
 }
@@ -1977,6 +2037,7 @@ static bool GL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Pr
         data->num_texture_units >= 3) {
         data->num_texture_units >= 3) {
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
+        SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408);
     } else {
     } else {
         SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL YUV not supported");
         SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL YUV not supported");
     }
     }

+ 107 - 50
src/render/opengles2/SDL_render_gles2.c

@@ -1245,6 +1245,7 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, v
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
             case SDL_PIXELFORMAT_IYUV:
             case SDL_PIXELFORMAT_IYUV:
             case SDL_PIXELFORMAT_YV12:
             case SDL_PIXELFORMAT_YV12:
+            case SDL_PIXELFORMAT_P408:
                 sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV;
                 sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV;
                 break;
                 break;
             case SDL_PIXELFORMAT_NV12:
             case SDL_PIXELFORMAT_NV12:
@@ -1283,6 +1284,7 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, v
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
         case SDL_PIXELFORMAT_IYUV:
         case SDL_PIXELFORMAT_IYUV:
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_YV12:
+        case SDL_PIXELFORMAT_P408:
             sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV;
             sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV;
             break;
             break;
         case SDL_PIXELFORMAT_NV12:
         case SDL_PIXELFORMAT_NV12:
@@ -1745,6 +1747,7 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_YV12:
+    case SDL_PIXELFORMAT_P408:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
     case SDL_PIXELFORMAT_NV21:
 #endif
 #endif
@@ -1783,7 +1786,7 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
     data->pixel_format = format;
     data->pixel_format = format;
     data->pixel_type = type;
     data->pixel_type = type;
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
-    data->yuv = ((texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12));
+    data->yuv = ((texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12) || (texture->format == SDL_PIXELFORMAT_P408));
     data->nv12 = ((texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21));
     data->nv12 = ((texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21));
 #endif
 #endif
     data->texture_scale_mode = texture->scaleMode;
     data->texture_scale_mode = texture->scaleMode;
@@ -1798,7 +1801,11 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
         if (data->yuv) {
         if (data->yuv) {
             // Need to add size for the U and V planes
             // Need to add size for the U and V planes
-            size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
+            if (texture->format == SDL_PIXELFORMAT_P408) {
+                size += 2 * texture->h * data->pitch;
+            } else {
+                size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
+            }
         } else if (data->nv12) {
         } else if (data->nv12) {
             // Need to add size for the U/V plane
             // Need to add size for the U/V plane
             size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
             size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
@@ -1821,6 +1828,15 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
 
 
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     if (data->yuv) {
     if (data->yuv) {
+        int yuv_texture_w, yuv_texture_h;
+        if (texture->format == SDL_PIXELFORMAT_P408) {
+            yuv_texture_w = texture->w;
+            yuv_texture_h = texture->h;
+        } else {
+            yuv_texture_w = (texture->w + 1) / 2;
+            yuv_texture_h = (texture->h + 1) / 2;
+        }
+
         data->texture_v = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER, 0);
         data->texture_v = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER, 0);
         if (data->texture_v) {
         if (data->texture_v) {
             data->texture_v_external = true;
             data->texture_v_external = true;
@@ -1834,7 +1850,7 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
         }
         }
         renderdata->glActiveTexture(GL_TEXTURE2);
         renderdata->glActiveTexture(GL_TEXTURE2);
         renderdata->glBindTexture(data->texture_type, data->texture_v);
         renderdata->glBindTexture(data->texture_type, data->texture_v);
-        renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL);
+        renderdata->glTexImage2D(data->texture_type, 0, format, yuv_texture_w, yuv_texture_h, 0, format, type, NULL);
         if (!GL_CheckError("glTexImage2D()", renderer)) {
         if (!GL_CheckError("glTexImage2D()", renderer)) {
             SDL_free(data->pixel_data);
             SDL_free(data->pixel_data);
             SDL_free(data);
             SDL_free(data);
@@ -1857,7 +1873,7 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
         }
         }
         renderdata->glActiveTexture(GL_TEXTURE1);
         renderdata->glActiveTexture(GL_TEXTURE1);
         renderdata->glBindTexture(data->texture_type, data->texture_u);
         renderdata->glBindTexture(data->texture_type, data->texture_u);
-        renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL);
+        renderdata->glTexImage2D(data->texture_type, 0, format, yuv_texture_w, yuv_texture_h, 0, format, type, NULL);
         if (!GL_CheckError("glTexImage2D()", renderer)) {
         if (!GL_CheckError("glTexImage2D()", renderer)) {
             SDL_free(data->pixel_data);
             SDL_free(data->pixel_data);
             SDL_free(data);
             SDL_free(data);
@@ -2015,37 +2031,59 @@ static bool GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, co
 
 
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     if (tdata->yuv) {
     if (tdata->yuv) {
-        // Skip to the correct offset into the next texture
-        pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
-        if (texture->format == SDL_PIXELFORMAT_YV12) {
-            data->glBindTexture(tdata->texture_type, tdata->texture_v);
-        } else {
+        if (texture->format == SDL_PIXELFORMAT_P408) {
+            // Skip to the correct offset into the next texture
+            pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
             data->glBindTexture(tdata->texture_type, tdata->texture_u);
             data->glBindTexture(tdata->texture_type, tdata->texture_u);
-        }
-        GLES2_TexSubImage2D(data, tdata->texture_type,
-                            rect->x / 2,
-                            rect->y / 2,
-                            (rect->w + 1) / 2,
-                            (rect->h + 1) / 2,
-                            tdata->pixel_format,
-                            tdata->pixel_type,
-                            pixels, (pitch + 1) / 2, 1);
-
-        // Skip to the correct offset into the next texture
-        pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2));
-        if (texture->format == SDL_PIXELFORMAT_YV12) {
-            data->glBindTexture(tdata->texture_type, tdata->texture_u);
-        } else {
+            GLES2_TexSubImage2D(data, tdata->texture_type,
+                                rect->x, rect->y,
+                                rect->w, rect->h,
+                                tdata->pixel_format,
+                                tdata->pixel_type,
+                                pixels, pitch, 1);
+
+            // Skip to the correct offset into the next texture
+            pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
             data->glBindTexture(tdata->texture_type, tdata->texture_v);
             data->glBindTexture(tdata->texture_type, tdata->texture_v);
+            GLES2_TexSubImage2D(data, tdata->texture_type,
+                                rect->x, rect->y,
+                                rect->w, rect->h,
+                                tdata->pixel_format,
+                                tdata->pixel_type,
+                                pixels, pitch, 1);
+        } else {
+            // Skip to the correct offset into the next texture
+            pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
+            if (texture->format == SDL_PIXELFORMAT_YV12) {
+                data->glBindTexture(tdata->texture_type, tdata->texture_v);
+            } else {
+                data->glBindTexture(tdata->texture_type, tdata->texture_u);
+            }
+            GLES2_TexSubImage2D(data, tdata->texture_type,
+                                rect->x / 2,
+                                rect->y / 2,
+                                (rect->w + 1) / 2,
+                                (rect->h + 1) / 2,
+                                tdata->pixel_format,
+                                tdata->pixel_type,
+                                pixels, (pitch + 1) / 2, 1);
+
+            // Skip to the correct offset into the next texture
+            pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2));
+            if (texture->format == SDL_PIXELFORMAT_YV12) {
+                data->glBindTexture(tdata->texture_type, tdata->texture_u);
+            } else {
+                data->glBindTexture(tdata->texture_type, tdata->texture_v);
+            }
+            GLES2_TexSubImage2D(data, tdata->texture_type,
+                                rect->x / 2,
+                                rect->y / 2,
+                                (rect->w + 1) / 2,
+                                (rect->h + 1) / 2,
+                                tdata->pixel_format,
+                                tdata->pixel_type,
+                                pixels, (pitch + 1) / 2, 1);
         }
         }
-        GLES2_TexSubImage2D(data, tdata->texture_type,
-                            rect->x / 2,
-                            rect->y / 2,
-                            (rect->w + 1) / 2,
-                            (rect->h + 1) / 2,
-                            tdata->pixel_format,
-                            tdata->pixel_type,
-                            pixels, (pitch + 1) / 2, 1);
     } else if (tdata->nv12) {
     } else if (tdata->nv12) {
         // Skip to the correct offset into the next texture
         // Skip to the correct offset into the next texture
         pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
         pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
@@ -2083,25 +2121,43 @@ static bool GLES2_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
 
 
     data->drawstate.texture = NULL; // we trash this state.
     data->drawstate.texture = NULL; // we trash this state.
 
 
-    data->glBindTexture(tdata->texture_type, tdata->texture_v);
-    GLES2_TexSubImage2D(data, tdata->texture_type,
-                        rect->x / 2,
-                        rect->y / 2,
-                        (rect->w + 1) / 2,
-                        (rect->h + 1) / 2,
-                        tdata->pixel_format,
-                        tdata->pixel_type,
-                        Vplane, Vpitch, 1);
+    if (texture->format == SDL_PIXELFORMAT_P408) {
+        data->glBindTexture(tdata->texture_type, tdata->texture_v);
+        GLES2_TexSubImage2D(data, tdata->texture_type,
+                            rect->x, rect->y,
+                            rect->w, rect->h,
+                            tdata->pixel_format,
+                            tdata->pixel_type,
+                            Vplane, Vpitch, 1);
 
 
-    data->glBindTexture(tdata->texture_type, tdata->texture_u);
-    GLES2_TexSubImage2D(data, tdata->texture_type,
-                        rect->x / 2,
-                        rect->y / 2,
-                        (rect->w + 1) / 2,
-                        (rect->h + 1) / 2,
-                        tdata->pixel_format,
-                        tdata->pixel_type,
-                        Uplane, Upitch, 1);
+        data->glBindTexture(tdata->texture_type, tdata->texture_u);
+        GLES2_TexSubImage2D(data, tdata->texture_type,
+                            rect->x, rect->y,
+                            rect->w, rect->h,
+                            tdata->pixel_format,
+                            tdata->pixel_type,
+                            Uplane, Upitch, 1);
+    } else {
+        data->glBindTexture(tdata->texture_type, tdata->texture_v);
+        GLES2_TexSubImage2D(data, tdata->texture_type,
+                            rect->x / 2,
+                            rect->y / 2,
+                            (rect->w + 1) / 2,
+                            (rect->h + 1) / 2,
+                            tdata->pixel_format,
+                            tdata->pixel_type,
+                            Vplane, Vpitch, 1);
+
+        data->glBindTexture(tdata->texture_type, tdata->texture_u);
+        GLES2_TexSubImage2D(data, tdata->texture_type,
+                            rect->x / 2,
+                            rect->y / 2,
+                            (rect->w + 1) / 2,
+                            (rect->h + 1) / 2,
+                            tdata->pixel_format,
+                            tdata->pixel_type,
+                            Uplane, Upitch, 1);
+    }
 
 
     data->glBindTexture(tdata->texture_type, tdata->texture);
     data->glBindTexture(tdata->texture_type, tdata->texture);
     GLES2_TexSubImage2D(data, tdata->texture_type,
     GLES2_TexSubImage2D(data, tdata->texture_type,
@@ -2412,6 +2468,7 @@ static bool GLES2_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
+    SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
     SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
 #endif
 #endif

+ 46 - 13
src/render/vulkan/SDL_render_vulkan.c

@@ -432,6 +432,8 @@ static int VULKAN_VkFormatGetNumPlanes(VkFormat vkFormat)
 {
 {
     switch (vkFormat) {
     switch (vkFormat) {
     case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
     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_444_UNORM:
         return 3;
         return 3;
     case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
     case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
     case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
     case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
@@ -451,7 +453,10 @@ static VkDeviceSize VULKAN_GetBytesPerPixel(VkFormat vkFormat, int plane)
     case VK_FORMAT_R16G16_UNORM:
     case VK_FORMAT_R16G16_UNORM:
         return 4;
         return 4;
     case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
     case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
+    case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
         return 1;
         return 1;
+    case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
+        return 2;
     case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
     case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
         return (plane == 0) ? 1 : 2;
         return (plane == 0) ? 1 : 2;
     case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
     case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
@@ -473,11 +478,15 @@ static VkFormat SDLPixelFormatToVkTextureFormat(SDL_PixelFormat format, Uint32 o
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
         return VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM;
         return VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM;
+    case SDL_PIXELFORMAT_P408:
+        return VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM;
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
     case SDL_PIXELFORMAT_NV21:
         return  VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
         return  VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
     case SDL_PIXELFORMAT_P010:
     case SDL_PIXELFORMAT_P010:
         return VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
         return VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
+    case SDL_PIXELFORMAT_P416:
+        return VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM;
     default:
     default:
         for (int i = 0; i < SDL_arraysize(vk_format_map); i++) {
         for (int i = 0; i < SDL_arraysize(vk_format_map); i++) {
             if (vk_format_map[i].sdl == format) {
             if (vk_format_map[i].sdl == format) {
@@ -2640,9 +2649,11 @@ static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, S
     // YUV textures must have even width and height.  Also create Ycbcr conversion
     // YUV textures must have even width and height.  Also create Ycbcr conversion
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
         texture->format == SDL_PIXELFORMAT_IYUV ||
         texture->format == SDL_PIXELFORMAT_IYUV ||
+        texture->format == SDL_PIXELFORMAT_P408 ||
         texture->format == SDL_PIXELFORMAT_NV12 ||
         texture->format == SDL_PIXELFORMAT_NV12 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
         texture->format == SDL_PIXELFORMAT_NV21 ||
-        texture->format == SDL_PIXELFORMAT_P010) {
+        texture->format == SDL_PIXELFORMAT_P010 ||
+        texture->format == SDL_PIXELFORMAT_P416) {
         const uint32_t YUV_SD_THRESHOLD = 576;
         const uint32_t YUV_SD_THRESHOLD = 576;
 
 
         // Check that we have VK_KHR_sampler_ycbcr_conversion support
         // Check that we have VK_KHR_sampler_ycbcr_conversion support
@@ -2653,9 +2664,12 @@ static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, S
         VkSamplerYcbcrConversionCreateInfoKHR samplerYcbcrConversionCreateInfo = { 0 };
         VkSamplerYcbcrConversionCreateInfoKHR samplerYcbcrConversionCreateInfo = { 0 };
         samplerYcbcrConversionCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR;
         samplerYcbcrConversionCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR;
 
 
-        // Pad width/height to multiple of 2
-        width = (width + 1) & ~1;
-        height = (height + 1) & ~1;
+        if (texture->format != SDL_PIXELFORMAT_P408 &&
+            texture->format != SDL_PIXELFORMAT_P416) {
+            // Pad width/height to multiple of 2
+            width = (width + 1) & ~1;
+            height = (height + 1) & ~1;
+        }
 
 
         // Create samplerYcbcrConversion which will be used on the VkImageView and VkSampler
         // Create samplerYcbcrConversion which will be used on the VkImageView and VkSampler
         samplerYcbcrConversionCreateInfo.format = textureFormat;
         samplerYcbcrConversionCreateInfo.format = textureFormat;
@@ -2974,16 +2988,24 @@ static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
 
 
     } else if (numPlanes == 3) {
     } else if (numPlanes == 3) {
         // YUV data
         // YUV data
-        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 VULKAN_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane2, UVpitch, plane1, UVpitch);
+            return VULKAN_UpdateTextureYUV(renderer, texture, rect, plane0, srcPitch, plane1, srcPitch, plane2, srcPitch);
         } else {
         } else {
-            return VULKAN_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 VULKAN_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane2, UVpitch, plane1, UVpitch);
+            } else {
+                return VULKAN_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch, plane2, UVpitch);
+            }
         }
         }
     }
     }
 #endif
 #endif
@@ -3010,7 +3032,14 @@ static bool VULKAN_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture
     if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainImage.imageLayout)) {
     if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainImage.imageLayout)) {
         return false;
         return false;
     }
     }
-    if (texture->format == SDL_PIXELFORMAT_YV12) {
+    if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
+        if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x, rect->y, rect->w, rect->h, Uplane, Upitch, &textureData->mainImage.imageLayout)) {
+            return false;
+        }
+        if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 2, rect->x, rect->y, rect->w, rect->h, Vplane, Vpitch, &textureData->mainImage.imageLayout)) {
+            return false;
+        }
+    } else if (texture->format == SDL_PIXELFORMAT_YV12) {
         if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch, &textureData->mainImage.imageLayout)) {
         if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch, &textureData->mainImage.imageLayout)) {
             return false;
             return false;
         }
         }
@@ -3450,11 +3479,13 @@ static void VULKAN_SetupShaderConstants(SDL_Renderer *renderer, const SDL_Render
         switch (texture->format) {
         switch (texture->format) {
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_YV12:
         case SDL_PIXELFORMAT_IYUV:
         case SDL_PIXELFORMAT_IYUV:
+        case SDL_PIXELFORMAT_P408:
         case SDL_PIXELFORMAT_NV12:
         case SDL_PIXELFORMAT_NV12:
         case SDL_PIXELFORMAT_NV21:
         case SDL_PIXELFORMAT_NV21:
             constants->input_type = INPUTTYPE_SRGB;
             constants->input_type = INPUTTYPE_SRGB;
             break;
             break;
         case SDL_PIXELFORMAT_P010:
         case SDL_PIXELFORMAT_P010:
+        case SDL_PIXELFORMAT_P416:
             constants->input_type = INPUTTYPE_HDR10;
             constants->input_type = INPUTTYPE_HDR10;
             break;
             break;
         default:
         default:
@@ -4623,9 +4654,11 @@ static bool VULKAN_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SD
     if (rendererData->supportsKHRSamplerYCbCrConversion) {
     if (rendererData->supportsKHRSamplerYCbCrConversion) {
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
+        SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
         SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
+        SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P416);
     }
     }
 #endif
 #endif
 
 

+ 3 - 1
src/video/SDL_pixels.c

@@ -215,6 +215,8 @@ const char *SDL_GetPixelFormatName(SDL_PixelFormat format)
         CASE(SDL_PIXELFORMAT_NV12)
         CASE(SDL_PIXELFORMAT_NV12)
         CASE(SDL_PIXELFORMAT_NV21)
         CASE(SDL_PIXELFORMAT_NV21)
         CASE(SDL_PIXELFORMAT_P010)
         CASE(SDL_PIXELFORMAT_P010)
+        CASE(SDL_PIXELFORMAT_P408)
+        CASE(SDL_PIXELFORMAT_P416)
         CASE(SDL_PIXELFORMAT_EXTERNAL_OES)
         CASE(SDL_PIXELFORMAT_EXTERNAL_OES)
         CASE(SDL_PIXELFORMAT_MJPG)
         CASE(SDL_PIXELFORMAT_MJPG)
 
 
@@ -854,7 +856,7 @@ SDL_Colorspace SDL_GetDefaultColorspaceForFormat(SDL_PixelFormat format)
     if (SDL_ISPIXELFORMAT_FOURCC(format)) {
     if (SDL_ISPIXELFORMAT_FOURCC(format)) {
         if (format == SDL_PIXELFORMAT_MJPG) {
         if (format == SDL_PIXELFORMAT_MJPG) {
             return SDL_COLORSPACE_SRGB;
             return SDL_COLORSPACE_SRGB;
-        } else if (format == SDL_PIXELFORMAT_P010) {
+        } else if (format == SDL_PIXELFORMAT_P010 || format == SDL_PIXELFORMAT_P416) {
             return SDL_COLORSPACE_HDR10;
             return SDL_COLORSPACE_HDR10;
         } else {
         } else {
             return SDL_COLORSPACE_YUV_DEFAULT;
             return SDL_COLORSPACE_YUV_DEFAULT;

+ 125 - 13
src/video/SDL_yuv.c

@@ -27,7 +27,20 @@
 
 
 
 
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
-static bool IsPlanar2x2Format(SDL_PixelFormat format);
+static bool IsPlanar1x1Format(SDL_PixelFormat format)
+{
+    return format == SDL_PIXELFORMAT_P408 || format == SDL_PIXELFORMAT_P416;
+}
+
+static bool IsPlanar2x2Format(SDL_PixelFormat format)
+{
+    return format == SDL_PIXELFORMAT_YV12 || format == SDL_PIXELFORMAT_IYUV || format == SDL_PIXELFORMAT_NV12 || format == SDL_PIXELFORMAT_NV21 || format == SDL_PIXELFORMAT_P010;
+}
+
+static bool IsPacked4Format(Uint32 format)
+{
+    return format == SDL_PIXELFORMAT_YUY2 || format == SDL_PIXELFORMAT_UYVY || format == SDL_PIXELFORMAT_YVYU;
+}
 #endif
 #endif
 
 
 /*
 /*
@@ -39,7 +52,18 @@ bool SDL_CalculateYUVSize(SDL_PixelFormat format, int w, int h, size_t *size, si
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     int sz_plane = 0, sz_plane_chroma = 0, sz_plane_packed = 0;
     int sz_plane = 0, sz_plane_chroma = 0, sz_plane_packed = 0;
 
 
-    if (IsPlanar2x2Format(format) == true) {
+    if (IsPlanar1x1Format(format)) {
+        /* sz_plane == w * h * bpp; */
+        size_t s1;
+        if (!SDL_size_mul_check_overflow(w, h, &s1)) {
+            return SDL_SetError("width * height would overflow");
+        }
+        if (!SDL_size_mul_check_overflow(s1, SDL_BYTESPERPIXEL(format), &s1)) {
+            return SDL_SetError("width * height * bpp would overflow");
+        }
+        sz_plane = (int)s1;
+        sz_plane_chroma = sz_plane;
+    } else if (IsPlanar2x2Format(format)) {
         {
         {
             /* sz_plane == w * h; */
             /* sz_plane == w * h; */
             size_t s1;
             size_t s1;
@@ -81,9 +105,11 @@ bool SDL_CalculateYUVSize(SDL_PixelFormat format, int w, int h, size_t *size, si
     switch (format) {
     switch (format) {
     case SDL_PIXELFORMAT_YV12: /**< Planar mode: Y + V + U  (3 planes) */
     case SDL_PIXELFORMAT_YV12: /**< Planar mode: Y + V + U  (3 planes) */
     case SDL_PIXELFORMAT_IYUV: /**< Planar mode: Y + U + V  (3 planes) */
     case SDL_PIXELFORMAT_IYUV: /**< Planar mode: Y + U + V  (3 planes) */
+    case SDL_PIXELFORMAT_P408:
+    case SDL_PIXELFORMAT_P416:
 
 
         if (pitch) {
         if (pitch) {
-            *pitch = w;
+            *pitch = w * SDL_BYTESPERPIXEL(format);
         }
         }
 
 
         if (size) {
         if (size) {
@@ -187,16 +213,6 @@ static bool GetYUVConversionType(SDL_Colorspace colorspace, YCbCrType *yuv_type)
     return SDL_SetError("Unsupported YUV colorspace");
     return SDL_SetError("Unsupported YUV colorspace");
 }
 }
 
 
-static bool IsPlanar2x2Format(SDL_PixelFormat format)
-{
-    return format == SDL_PIXELFORMAT_YV12 || format == SDL_PIXELFORMAT_IYUV || format == SDL_PIXELFORMAT_NV12 || format == SDL_PIXELFORMAT_NV21 || format == SDL_PIXELFORMAT_P010;
-}
-
-static bool IsPacked4Format(Uint32 format)
-{
-    return format == SDL_PIXELFORMAT_YUY2 || format == SDL_PIXELFORMAT_UYVY || format == SDL_PIXELFORMAT_YVYU;
-}
-
 static bool GetYUVPlanes(int width, int height, SDL_PixelFormat format, const void *yuv, int yuv_pitch,
 static bool GetYUVPlanes(int width, int height, SDL_PixelFormat format, const void *yuv, int yuv_pitch,
                          const Uint8 **y, const Uint8 **u, const Uint8 **v, Uint32 *y_stride, Uint32 *uv_stride)
                          const Uint8 **y, const Uint8 **u, const Uint8 **v, Uint32 *y_stride, Uint32 *uv_stride)
 {
 {
@@ -234,6 +250,15 @@ static bool GetYUVPlanes(int width, int height, SDL_PixelFormat format, const vo
         planes[0] = (const Uint8 *)yuv;
         planes[0] = (const Uint8 *)yuv;
         planes[1] = planes[0] + pitches[0] * height;
         planes[1] = planes[0] + pitches[0] * height;
         break;
         break;
+    case SDL_PIXELFORMAT_P408:
+    case SDL_PIXELFORMAT_P416:
+        pitches[0] = yuv_pitch;
+        pitches[1] = pitches[0];
+        pitches[2] = pitches[1];
+        planes[0] = (const Uint8 *)yuv;
+        planes[1] = planes[0] + pitches[0] * height;
+        planes[2] = planes[1] + pitches[1] * height;
+        break;
     default:
     default:
         return SDL_SetError("GetYUVPlanes(): Unsupported YUV format: %s", SDL_GetPixelFormatName(format));
         return SDL_SetError("GetYUVPlanes(): Unsupported YUV format: %s", SDL_GetPixelFormatName(format));
     }
     }
@@ -247,6 +272,8 @@ static bool GetYUVPlanes(int width, int height, SDL_PixelFormat format, const vo
         *uv_stride = pitches[1];
         *uv_stride = pitches[1];
         break;
         break;
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
+    case SDL_PIXELFORMAT_P416:
         *y = planes[0];
         *y = planes[0];
         *y_stride = pitches[0];
         *y_stride = pitches[0];
         *v = planes[2];
         *v = planes[2];
@@ -511,6 +538,30 @@ static bool yuv_rgb_std(
         }
         }
     }
     }
 
 
+    if (src_format == SDL_PIXELFORMAT_P408) {
+
+        switch (dst_format) {
+        case SDL_PIXELFORMAT_RGBX8888:
+        case SDL_PIXELFORMAT_RGBA8888:
+            yuv444_rgba_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
+            return true;
+        case SDL_PIXELFORMAT_BGRX8888:
+        case SDL_PIXELFORMAT_BGRA8888:
+            yuv444_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
+            return true;
+        case SDL_PIXELFORMAT_XRGB8888:
+        case SDL_PIXELFORMAT_ARGB8888:
+            yuv444_argb_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
+            return true;
+        case SDL_PIXELFORMAT_XBGR8888:
+        case SDL_PIXELFORMAT_ABGR8888:
+            yuv444_abgr_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
+            return true;
+        default:
+            break;
+        }
+    }
+
     if (src_format == SDL_PIXELFORMAT_YUY2 ||
     if (src_format == SDL_PIXELFORMAT_YUY2 ||
         src_format == SDL_PIXELFORMAT_UYVY ||
         src_format == SDL_PIXELFORMAT_UYVY ||
         src_format == SDL_PIXELFORMAT_YVYU) {
         src_format == SDL_PIXELFORMAT_YVYU) {
@@ -583,6 +634,16 @@ static bool yuv_rgb_std(
             break;
             break;
         }
         }
     }
     }
+
+    if (src_format == SDL_PIXELFORMAT_P416) {
+        switch (dst_format) {
+        case SDL_PIXELFORMAT_RGB48:
+            yuvp416_rgb48_std(width, height, (const uint16_t *)y, (const uint16_t *)u, (const uint16_t *)v, y_stride, uv_stride, rgb, rgb_stride, yuv_type);
+            return true;
+        default:
+            break;
+        }
+    }
     return false;
     return false;
 }
 }
 
 
@@ -641,6 +702,29 @@ bool SDL_ConvertPixels_YUV_to_RGB(int width, int height,
         return result;
         return result;
     }
     }
 
 
+    if (src_format == SDL_PIXELFORMAT_P416 && dst_format != SDL_PIXELFORMAT_RGB48) {
+        bool result;
+        void *tmp;
+        int tmp_pitch = (width * 3 * sizeof(Uint16));
+
+        tmp = SDL_malloc((size_t)tmp_pitch * height);
+        if (!tmp) {
+            return false;
+        }
+
+        // convert src/src_format to tmp/RGB48
+        result = SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, SDL_PIXELFORMAT_RGB48, src_colorspace, src_properties, tmp, tmp_pitch);
+        if (!result) {
+            SDL_free(tmp);
+            return false;
+        }
+
+        // convert tmp/RGB48 to dst/RGB
+        result = SDL_ConvertPixelsAndColorspace(width, height, SDL_PIXELFORMAT_RGB48, src_colorspace, src_properties, tmp, tmp_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
+        SDL_free(tmp);
+        return result;
+    }
+
     if (dst_format != SDL_PIXELFORMAT_ARGB8888) {
     if (dst_format != SDL_PIXELFORMAT_ARGB8888) {
         bool result;
         bool result;
         void *tmp;
         void *tmp;
@@ -775,6 +859,7 @@ static bool SDL_ConvertPixels_XRGB8888_to_YUV(int width, int height, const void
     switch (dst_format) {
     switch (dst_format) {
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
     case SDL_PIXELFORMAT_NV21:
     {
     {
@@ -847,6 +932,22 @@ static bool SDL_ConvertPixels_XRGB8888_to_YUV(int width, int height, const void
                 plane_u += uv_skip;
                 plane_u += uv_skip;
                 plane_v += uv_skip;
                 plane_v += uv_skip;
             }
             }
+        } else if (dst_format == SDL_PIXELFORMAT_P408) {
+            // Write UV planes, not interleaved
+            uv_skip = (uv_stride - width);
+            for (j = 0; j < height; j++) {
+                for (i = 0; i < width; i++) {
+                    const Uint32 p1 = ((const Uint32 *)curr_row)[i];
+                    const Uint32 r = (p1 & 0x00ff0000) >> 16;
+                    const Uint32 g = (p1 & 0x0000ff00) >> 8;
+                    const Uint32 b = (p1 & 0x000000ff);
+                    *plane_u++ = MAKE_U(r, g, b);
+                    *plane_v++ = MAKE_V(r, g, b);
+                }
+                plane_u += uv_skip;
+                plane_v += uv_skip;
+                curr_row += src_pitch;
+            }
         } else if (dst_format == SDL_PIXELFORMAT_NV12) {
         } else if (dst_format == SDL_PIXELFORMAT_NV12) {
             uv_skip = (uv_stride - ((width + 1) / 2) * 2);
             uv_skip = (uv_stride - ((width + 1) / 2) * 2);
             for (j = 0; j < height_half; j++) {
             for (j = 0; j < height_half; j++) {
@@ -1216,6 +1317,17 @@ static bool SDL_ConvertPixels_YUV_to_YUV_Copy(int width, int height, SDL_PixelFo
 {
 {
     int i;
     int i;
 
 
+    if (IsPlanar1x1Format(format)) {
+        // YUV planes
+        const size_t length = width * SDL_BYTESPERPIXEL(format);
+        for (i = height * 3; i--;) {
+            SDL_memcpy(dst, src, length);
+            src = (const Uint8 *)src + src_pitch;
+            dst = (Uint8 *)dst + dst_pitch;
+        }
+        return true;
+    }
+
     if (IsPlanar2x2Format(format)) {
     if (IsPlanar2x2Format(format)) {
         // Y plane
         // Y plane
         for (i = height; i--;) {
         for (i = height; i--;) {

+ 5 - 3
src/video/yuv2rgb/yuv_rgb_internal.h

@@ -72,8 +72,9 @@ static const RGB2YUVParam RGB2YUV[] = {
 
 
 /* The various layouts of YUV data we support */
 /* The various layouts of YUV data we support */
 #define YUV_FORMAT_420	1
 #define YUV_FORMAT_420	1
-#define YUV_FORMAT_422	2
-#define YUV_FORMAT_NV12	3
+#define YUV_FORMAT_422  2
+#define YUV_FORMAT_444  3
+#define YUV_FORMAT_NV12	4
 
 
 /* The various formats of RGB pixel that we support */
 /* The various formats of RGB pixel that we support */
 #define RGB_FORMAT_RGB565	1
 #define RGB_FORMAT_RGB565	1
@@ -82,4 +83,5 @@ static const RGB2YUVParam RGB2YUV[] = {
 #define RGB_FORMAT_BGRA		4
 #define RGB_FORMAT_BGRA		4
 #define RGB_FORMAT_ARGB		5
 #define RGB_FORMAT_ARGB		5
 #define RGB_FORMAT_ABGR		6
 #define RGB_FORMAT_ABGR		6
-#define RGB_FORMAT_XBGR2101010 7
+#define RGB_FORMAT_XBGR2101010  7
+#define RGB_FORMAT_RGB48	8

+ 40 - 0
src/video/yuv2rgb/yuv_rgb_std.c

@@ -40,6 +40,18 @@ static uint16_t clamp10(int32_t v)
     }
     }
 }
 }
 
 
+static uint16_t clamp16(int32_t v)
+{
+    v >>= PRECISION;
+    if (v < 0) {
+        return 0;
+    } else if (v > 0xffff) {
+        return 0xffff;
+    } else {
+        return (uint16_t)v;
+    }
+}
+
 #define YUV_BITS    8
 #define YUV_BITS    8
 
 
 #define STD_FUNCTION_NAME	yuv420_rgb565_std
 #define STD_FUNCTION_NAME	yuv420_rgb565_std
@@ -102,6 +114,26 @@ static uint16_t clamp10(int32_t v)
 #define RGB_FORMAT			RGB_FORMAT_ABGR
 #define RGB_FORMAT			RGB_FORMAT_ABGR
 #include "yuv_rgb_std_func.h"
 #include "yuv_rgb_std_func.h"
 
 
+#define STD_FUNCTION_NAME	yuv444_rgba_std
+#define YUV_FORMAT			YUV_FORMAT_444
+#define RGB_FORMAT			RGB_FORMAT_RGBA
+#include "yuv_rgb_std_func.h"
+
+#define STD_FUNCTION_NAME	yuv444_bgra_std
+#define YUV_FORMAT			YUV_FORMAT_444
+#define RGB_FORMAT			RGB_FORMAT_BGRA
+#include "yuv_rgb_std_func.h"
+
+#define STD_FUNCTION_NAME	yuv444_argb_std
+#define YUV_FORMAT			YUV_FORMAT_444
+#define RGB_FORMAT			RGB_FORMAT_ARGB
+#include "yuv_rgb_std_func.h"
+
+#define STD_FUNCTION_NAME	yuv444_abgr_std
+#define YUV_FORMAT			YUV_FORMAT_444
+#define RGB_FORMAT			RGB_FORMAT_ABGR
+#include "yuv_rgb_std_func.h"
+
 #define STD_FUNCTION_NAME	yuvnv12_rgb565_std
 #define STD_FUNCTION_NAME	yuvnv12_rgb565_std
 #define YUV_FORMAT			YUV_FORMAT_NV12
 #define YUV_FORMAT			YUV_FORMAT_NV12
 #define RGB_FORMAT			RGB_FORMAT_RGB565
 #define RGB_FORMAT			RGB_FORMAT_RGB565
@@ -140,6 +172,14 @@ static uint16_t clamp10(int32_t v)
 #define RGB_FORMAT			RGB_FORMAT_XBGR2101010
 #define RGB_FORMAT			RGB_FORMAT_XBGR2101010
 #include "yuv_rgb_std_func.h"
 #include "yuv_rgb_std_func.h"
 
 
+#undef YUV_BITS
+#define YUV_BITS    16
+
+#define STD_FUNCTION_NAME	yuvp416_rgb48_std
+#define YUV_FORMAT			YUV_FORMAT_444
+#define RGB_FORMAT			RGB_FORMAT_RGB48
+#include "yuv_rgb_std_func.h"
+
 void rgb24_yuv420_std(
 void rgb24_yuv420_std(
         uint32_t width, uint32_t height,
         uint32_t width, uint32_t height,
         const uint8_t *RGB, uint32_t RGB_stride,
         const uint8_t *RGB, uint32_t RGB_stride,

+ 30 - 0
src/video/yuv2rgb/yuv_rgb_std.h

@@ -93,6 +93,30 @@ void yuv422_abgr_std(
         uint8_t *rgb, uint32_t rgb_stride,
         uint8_t *rgb, uint32_t rgb_stride,
         YCbCrType yuv_type);
         YCbCrType yuv_type);
 
 
+void yuv444_rgba_std(
+        uint32_t width, uint32_t height,
+        const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride,
+        uint8_t *rgb, uint32_t rgb_stride,
+        YCbCrType yuv_type);
+
+void yuv444_bgra_std(
+        uint32_t width, uint32_t height,
+        const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride,
+        uint8_t *rgb, uint32_t rgb_stride,
+        YCbCrType yuv_type);
+
+void yuv444_argb_std(
+        uint32_t width, uint32_t height,
+        const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride,
+        uint8_t *rgb, uint32_t rgb_stride,
+        YCbCrType yuv_type);
+
+void yuv444_abgr_std(
+        uint32_t width, uint32_t height,
+        const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride,
+        uint8_t *rgb, uint32_t rgb_stride,
+        YCbCrType yuv_type);
+
 void yuvnv12_rgb565_std(
 void yuvnv12_rgb565_std(
         uint32_t width, uint32_t height,
         uint32_t width, uint32_t height,
         const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride,
         const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride,
@@ -135,6 +159,12 @@ void yuvp010_xbgr2101010_std(
         uint8_t *rgb, uint32_t rgb_stride,
         uint8_t *rgb, uint32_t rgb_stride,
         YCbCrType yuv_type);
         YCbCrType yuv_type);
 
 
+void yuvp416_rgb48_std(
+        uint32_t width, uint32_t height,
+        const uint16_t *y, const uint16_t *u, const uint16_t *v, uint32_t y_stride, uint32_t uv_stride,
+        uint8_t *rgb, uint32_t rgb_stride,
+        YCbCrType yuv_type);
+
 // rgb to yuv, standard c implementation
 // rgb to yuv, standard c implementation
 void rgb24_yuv420_std(
 void rgb24_yuv420_std(
         uint32_t width, uint32_t height,
         uint32_t width, uint32_t height,

+ 26 - 0
src/video/yuv2rgb/yuv_rgb_std_func.h

@@ -74,6 +74,16 @@
 		(((Uint32)clamp10(y_tmp+r_tmp)) << 0); \
 		(((Uint32)clamp10(y_tmp+r_tmp)) << 0); \
 	rgb_ptr += 4
 	rgb_ptr += 4
 
 
+#elif RGB_FORMAT == RGB_FORMAT_RGB48
+
+#define PACK_PIXEL(rgb_ptr) \
+	*(Uint16 *)rgb_ptr = clamp16(y_tmp + r_tmp); \
+	rgb_ptr += sizeof(Uint16);				   \
+	*(Uint16 *)rgb_ptr = clamp16(y_tmp + g_tmp); \
+	rgb_ptr += sizeof(Uint16);				   \
+	*(Uint16 *)rgb_ptr = clamp16(y_tmp + b_tmp); \
+	rgb_ptr += sizeof(Uint16)
+
 #else
 #else
 #error PACK_PIXEL unimplemented
 #error PACK_PIXEL unimplemented
 #endif
 #endif
@@ -117,6 +127,11 @@ void STD_FUNCTION_NAME(
 	#define uv_pixel_stride 4
 	#define uv_pixel_stride 4
 	#define uv_x_sample_interval 2
 	#define uv_x_sample_interval 2
 	#define uv_y_sample_interval 1
 	#define uv_y_sample_interval 1
+#elif YUV_FORMAT == YUV_FORMAT_444
+	#define y_pixel_stride 1
+	#define uv_pixel_stride 1
+	#define uv_x_sample_interval 1
+	#define uv_y_sample_interval 1
 #elif YUV_FORMAT == YUV_FORMAT_NV12
 #elif YUV_FORMAT == YUV_FORMAT_NV12
 	#define y_pixel_stride 1
 	#define y_pixel_stride 1
 	#define uv_pixel_stride 2
 	#define uv_pixel_stride 2
@@ -160,8 +175,10 @@ void STD_FUNCTION_NAME(
 			int32_t y_tmp = (GET(y_ptr1[0]-param->y_shift)*param->y_factor);
 			int32_t y_tmp = (GET(y_ptr1[0]-param->y_shift)*param->y_factor);
 			PACK_PIXEL(rgb_ptr1);
 			PACK_PIXEL(rgb_ptr1);
 
 
+			#if uv_x_sample_interval > 1
 			y_tmp = (GET(y_ptr1[y_pixel_stride]-param->y_shift)*param->y_factor);
 			y_tmp = (GET(y_ptr1[y_pixel_stride]-param->y_shift)*param->y_factor);
 			PACK_PIXEL(rgb_ptr1);
 			PACK_PIXEL(rgb_ptr1);
+			#endif
 
 
 			#if uv_y_sample_interval > 1
 			#if uv_y_sample_interval > 1
 			y_tmp = (GET(y_ptr2[0]-param->y_shift)*param->y_factor);
 			y_tmp = (GET(y_ptr2[0]-param->y_shift)*param->y_factor);
@@ -171,12 +188,21 @@ void STD_FUNCTION_NAME(
 			PACK_PIXEL(rgb_ptr2);
 			PACK_PIXEL(rgb_ptr2);
 			#endif
 			#endif
 
 
+			#if uv_x_sample_interval > 1
 			y_ptr1+=2*y_pixel_stride;
 			y_ptr1+=2*y_pixel_stride;
+			#else
+			y_ptr1+=y_pixel_stride;
+			#endif
 			#if uv_y_sample_interval > 1
 			#if uv_y_sample_interval > 1
 			y_ptr2+=2*y_pixel_stride;
 			y_ptr2+=2*y_pixel_stride;
 			#endif
 			#endif
+			#if uv_x_sample_interval > 1
 			u_ptr+=2*uv_pixel_stride/uv_x_sample_interval;
 			u_ptr+=2*uv_pixel_stride/uv_x_sample_interval;
 			v_ptr+=2*uv_pixel_stride/uv_x_sample_interval;
 			v_ptr+=2*uv_pixel_stride/uv_x_sample_interval;
+			#else
+			u_ptr+=uv_pixel_stride/uv_x_sample_interval;
+			v_ptr+=uv_pixel_stride/uv_x_sample_interval;
+			#endif
 		}
 		}
 
 
 		/* Catch the last pixel, if needed */
 		/* Catch the last pixel, if needed */

+ 20 - 2
test/testautomation_pixels.c

@@ -51,7 +51,8 @@ static const SDL_PixelFormat g_AllFormats[] = {
     SDL_PIXELFORMAT_UYVY,
     SDL_PIXELFORMAT_UYVY,
     SDL_PIXELFORMAT_YVYU,
     SDL_PIXELFORMAT_YVYU,
     SDL_PIXELFORMAT_NV12,
     SDL_PIXELFORMAT_NV12,
-    SDL_PIXELFORMAT_NV21
+    SDL_PIXELFORMAT_NV21,
+    SDL_PIXELFORMAT_P408
 };
 };
 static const int g_numAllFormats = SDL_arraysize(g_AllFormats);
 static const int g_numAllFormats = SDL_arraysize(g_AllFormats);
 
 
@@ -98,7 +99,8 @@ static const char *g_AllFormatsVerbose[] = {
     "SDL_PIXELFORMAT_UYVY",
     "SDL_PIXELFORMAT_UYVY",
     "SDL_PIXELFORMAT_YVYU",
     "SDL_PIXELFORMAT_YVYU",
     "SDL_PIXELFORMAT_NV12",
     "SDL_PIXELFORMAT_NV12",
-    "SDL_PIXELFORMAT_NV21"
+    "SDL_PIXELFORMAT_NV21",
+    "SDL_PIXELFORMAT_P408"
 };
 };
 
 
 static const SDL_PixelFormat g_AllLargeFormats[] = {
 static const SDL_PixelFormat g_AllLargeFormats[] = {
@@ -630,6 +632,22 @@ SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL
 SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_P010));
 SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_P010));
 SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_P010));
 SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_P010));
 SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_P010));
 SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P010_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_P010));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P408_FORMAT, SDL_PIXELFORMAT_P408 == SDL_DEFINE_PIXELFOURCC('P', '4', '0', '8'));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P408_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_P408));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P408_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_P408));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P408_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_P408));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P408_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_P408));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P408_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_P408));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P408_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_P408));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P408_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_P408));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P416_FORMAT, SDL_PIXELFORMAT_P416 == SDL_DEFINE_PIXELFOURCC('P', '4', '1', '6'));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P416_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_P416));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P416_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_P416));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P416_PACKED, !SDL_ISPIXELFORMAT_PACKED(SDL_PIXELFORMAT_P416));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P416_ARRAY, !SDL_ISPIXELFORMAT_ARRAY(SDL_PIXELFORMAT_P416));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P416_10BIT, !SDL_ISPIXELFORMAT_10BIT(SDL_PIXELFORMAT_P416));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P416_FLOAT, !SDL_ISPIXELFORMAT_FLOAT(SDL_PIXELFORMAT_P416));
+SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_P416_ALPHA, !SDL_ISPIXELFORMAT_ALPHA(SDL_PIXELFORMAT_P416));
 SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_FORMAT, SDL_PIXELFORMAT_EXTERNAL_OES == SDL_DEFINE_PIXELFOURCC('O', 'E', 'S', ' '));
 SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_FORMAT, SDL_PIXELFORMAT_EXTERNAL_OES == SDL_DEFINE_PIXELFOURCC('O', 'E', 'S', ' '));
 SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_EXTERNAL_OES));
 SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_EXTERNAL_OES));
 SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_EXTERNAL_OES));
 SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_EXTERNAL_OES));

+ 81 - 12
test/testyuv.c

@@ -15,8 +15,8 @@
 #include "testyuv_cvt.h"
 #include "testyuv_cvt.h"
 #include "testutils.h"
 #include "testutils.h"
 
 
-/* 422 (YUY2, etc) and P010 formats are the largest */
-#define MAX_YUV_SURFACE_SIZE(W, H, P) ((H + 1) * ((W + 1) + P) * 4)
+/* 422 (YUY2, etc) and P416 formats are the largest */
+#define MAX_YUV_SURFACE_SIZE(W, H, P) ((H + 1) * ((W + 1) + P) * 3 * 2)
 
 
 /* Return true if the YUV format is packed pixels */
 /* Return true if the YUV format is packed pixels */
 static bool is_packed_yuv_format(Uint32 format)
 static bool is_packed_yuv_format(Uint32 format)
@@ -109,6 +109,7 @@ static bool run_automated_tests(int pattern_size, int extra_pitch)
     const Uint32 formats[] = {
     const Uint32 formats[] = {
         SDL_PIXELFORMAT_YV12,
         SDL_PIXELFORMAT_YV12,
         SDL_PIXELFORMAT_IYUV,
         SDL_PIXELFORMAT_IYUV,
+        SDL_PIXELFORMAT_P408,
         SDL_PIXELFORMAT_NV12,
         SDL_PIXELFORMAT_NV12,
         SDL_PIXELFORMAT_NV21,
         SDL_PIXELFORMAT_NV21,
         SDL_PIXELFORMAT_YUY2,
         SDL_PIXELFORMAT_YUY2,
@@ -164,6 +165,11 @@ static bool run_automated_tests(int pattern_size, int extra_pitch)
     /* Verify conversion between YUV formats */
     /* Verify conversion between YUV formats */
     for (i = 0; i < SDL_arraysize(formats); ++i) {
     for (i = 0; i < SDL_arraysize(formats); ++i) {
         for (j = 0; j < SDL_arraysize(formats); ++j) {
         for (j = 0; j < SDL_arraysize(formats); ++j) {
+            if (formats[i] != formats[j] && (formats[i] == SDL_PIXELFORMAT_P408 || formats[j] == SDL_PIXELFORMAT_P408)) {
+                // Converting between 444 and 420 formats is lossy and not currently supported
+                continue;
+            }
+
             yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch;
             yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch;
             yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch;
             yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch;
             if (!SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch)) {
             if (!SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch)) {
@@ -189,6 +195,11 @@ static bool run_automated_tests(int pattern_size, int extra_pitch)
                 continue;
                 continue;
             }
             }
 
 
+            if (formats[i] != formats[j] && (formats[i] == SDL_PIXELFORMAT_P408 || formats[j] == SDL_PIXELFORMAT_P408)) {
+                // Converting between 444 and 420 formats is lossy and not currently supported
+                continue;
+            }
+
             yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch;
             yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch;
             yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch;
             yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch;
             if (!SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch)) {
             if (!SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch)) {
@@ -370,16 +381,16 @@ static bool create_textures(SDL_Renderer *renderer, SDL_Surface *original, SDL_P
     int pitch;
     int pitch;
     SDL_Surface *converted = NULL;
     SDL_Surface *converted = NULL;
     bool result = false;
     bool result = false;
+    size_t max_size;
 
 
     YUV_CONVERSION_MODE yuv_mode = GetYUVConversionModeForResolution(original->w, original->h);
     YUV_CONVERSION_MODE yuv_mode = GetYUVConversionModeForResolution(original->w, original->h);
     if (yuv_mode == YUV_CONVERSION_BT2020) {
     if (yuv_mode == YUV_CONVERSION_BT2020) {
-        yuv_format = SDL_PIXELFORMAT_P010;
-        rgb_format = SDL_PIXELFORMAT_XBGR2101010;
         rgb_colorspace = SDL_COLORSPACE_HDR10;
         rgb_colorspace = SDL_COLORSPACE_HDR10;
     }
     }
     yuv_colorspace = GetColorspaceForYUVConversionMode(yuv_mode);
     yuv_colorspace = GetColorspaceForYUVConversionMode(yuv_mode);
 
 
-    raw_yuv = SDL_calloc(1, MAX_YUV_SURFACE_SIZE(original->w, original->h, 0));
+    max_size = MAX_YUV_SURFACE_SIZE(original->w, original->h, 0);
+    raw_yuv = SDL_calloc(1, max_size);
     ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h, yuv_mode, monochrome, luminance);
     ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h, yuv_mode, monochrome, luminance);
     pitch = CalculateYUVPitch(yuv_format, original->w);
     pitch = CalculateYUVPitch(yuv_format, original->w);
 
 
@@ -458,6 +469,53 @@ static bool create_textures(SDL_Renderer *renderer, SDL_Surface *original, SDL_P
         SDL_free(plane0);
         SDL_free(plane0);
         SDL_free(plane1);
         SDL_free(plane1);
         SDL_free(plane2);
         SDL_free(plane2);
+    } else if (planar && (yuv_format == SDL_PIXELFORMAT_P408 || yuv_format == SDL_PIXELFORMAT_P416)) {
+        const int rows = original->h;
+        const Uint8 *src_plane0 = (const Uint8 *)raw_yuv;
+        const Uint8 *src_plane1 = src_plane0 + rows * pitch;
+        const Uint8 *src_plane2 = src_plane1 + rows * pitch;
+        const int Ypitch = pitch + 37;
+        const int UVpitch = Ypitch;
+        Uint8 *plane0 = (Uint8 *)SDL_calloc(1, rows * Ypitch);
+        Uint8 *plane1 = (Uint8 *)SDL_calloc(1, rows * UVpitch);
+        Uint8 *plane2 = (Uint8 *)SDL_calloc(1, rows * UVpitch);
+        int row;
+        const Uint8 *src;
+        Uint8 *dst;
+
+        if (!plane0 || !plane1 || !plane0) {
+            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create YUV planes: %s", SDL_GetError());
+            goto done;
+        }
+
+        src = src_plane0;
+        dst = plane0;
+        for (row = 0; row < rows; ++row) {
+            SDL_memcpy(dst, src, pitch);
+            src += pitch;
+            dst += Ypitch;
+        }
+
+        src = src_plane1;
+        dst = plane1;
+        for (row = 0; row < rows; ++row) {
+            SDL_memcpy(dst, src, pitch);
+            src += pitch;
+            dst += UVpitch;
+        }
+
+        src = src_plane2;
+        dst = plane2;
+        for (row = 0; row < rows; ++row) {
+            SDL_memcpy(dst, src, pitch);
+            src += pitch;
+            dst += UVpitch;
+        }
+
+        SDL_UpdateYUVTexture(output[2], NULL, plane0, Ypitch, plane1, UVpitch, plane2, UVpitch);
+        SDL_free(plane0);
+        SDL_free(plane1);
+        SDL_free(plane2);
     } else if (planar && (yuv_format == SDL_PIXELFORMAT_NV12 || yuv_format == SDL_PIXELFORMAT_NV21 || yuv_format == SDL_PIXELFORMAT_P010)) {
     } else if (planar && (yuv_format == SDL_PIXELFORMAT_NV12 || yuv_format == SDL_PIXELFORMAT_NV21 || yuv_format == SDL_PIXELFORMAT_P010)) {
         const int Yrows = original->h;
         const int Yrows = original->h;
         const int UVrows = ((original->h + 1) / 2);
         const int UVrows = ((original->h + 1) / 2);
@@ -586,6 +644,7 @@ static bool run_all_format_test(SDL_Window *window, const char *requested_render
     const SDL_PixelFormat yuv_formats[] = {
     const SDL_PixelFormat yuv_formats[] = {
         SDL_PIXELFORMAT_YV12,
         SDL_PIXELFORMAT_YV12,
         SDL_PIXELFORMAT_IYUV,
         SDL_PIXELFORMAT_IYUV,
+        SDL_PIXELFORMAT_P408,
         SDL_PIXELFORMAT_YUY2,
         SDL_PIXELFORMAT_YUY2,
         SDL_PIXELFORMAT_UYVY,
         SDL_PIXELFORMAT_UYVY,
         SDL_PIXELFORMAT_YVYU,
         SDL_PIXELFORMAT_YVYU,
@@ -676,6 +735,7 @@ static bool run_interactive(SDL_Window *window, const char *renderer_name, SDL_S
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s", SDL_GetError());
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s", SDL_GetError());
         return false;
         return false;
     }
     }
+    renderer_name = SDL_GetRendererName(renderer);
 
 
     SDL_Texture *output[3];
     SDL_Texture *output[3];
     if (!create_textures(renderer, original, yuv_format, rgb_format, planar, monochrome, luminance, output)) {
     if (!create_textures(renderer, original, yuv_format, rgb_format, planar, monochrome, luminance, output)) {
@@ -695,7 +755,6 @@ static bool run_interactive(SDL_Window *window, const char *renderer_name, SDL_S
         break;
         break;
     case YUV_CONVERSION_BT2020:
     case YUV_CONVERSION_BT2020:
         yuv_mode_name = "BT.2020";
         yuv_mode_name = "BT.2020";
-        yuv_format = SDL_PIXELFORMAT_P010;
         break;
         break;
     default:
     default:
         yuv_mode_name = "UNKNOWN";
         yuv_mode_name = "UNKNOWN";
@@ -745,7 +804,7 @@ static bool run_interactive(SDL_Window *window, const char *renderer_name, SDL_S
         if (current == 0) {
         if (current == 0) {
             SDLTest_DrawString(renderer, 4, 4, titles[current]);
             SDLTest_DrawString(renderer, 4, 4, titles[current]);
         } else {
         } else {
-            if (SDL_snprintf(title, sizeof(title), "%s %s %s", titles[current], yuv_format_name, yuv_mode_name) > 0) {
+            if (SDL_snprintf(title, sizeof(title), "%s %s %s %s", titles[current], yuv_format_name, yuv_mode_name, renderer_name) > 0) {
                 SDLTest_DrawString(renderer, 4, 4, title);
                 SDLTest_DrawString(renderer, 4, 4, title);
             }
             }
         }
         }
@@ -838,9 +897,6 @@ int main(int argc, char **argv)
             } else if (SDL_strcmp(argv[i], "--bt709") == 0) {
             } else if (SDL_strcmp(argv[i], "--bt709") == 0) {
                 SetYUVConversionMode(YUV_CONVERSION_BT709);
                 SetYUVConversionMode(YUV_CONVERSION_BT709);
                 consumed = 1;
                 consumed = 1;
-            } else if (SDL_strcmp(argv[i], "--bt2020") == 0) {
-                SetYUVConversionMode(YUV_CONVERSION_BT2020);
-                consumed = 1;
             } else if (SDL_strcmp(argv[i], "--auto") == 0) {
             } else if (SDL_strcmp(argv[i], "--auto") == 0) {
                 SetYUVConversionMode(YUV_CONVERSION_AUTOMATIC);
                 SetYUVConversionMode(YUV_CONVERSION_AUTOMATIC);
                 consumed = 1;
                 consumed = 1;
@@ -850,6 +906,9 @@ int main(int argc, char **argv)
             } else if (SDL_strcmp(argv[i], "--iyuv") == 0) {
             } else if (SDL_strcmp(argv[i], "--iyuv") == 0) {
                 yuv_format = SDL_PIXELFORMAT_IYUV;
                 yuv_format = SDL_PIXELFORMAT_IYUV;
                 consumed = 1;
                 consumed = 1;
+            } else if (SDL_strcmp(argv[i], "--p408") == 0) {
+                yuv_format = SDL_PIXELFORMAT_P408;
+                consumed = 1;
             } else if (SDL_strcmp(argv[i], "--yuy2") == 0) {
             } else if (SDL_strcmp(argv[i], "--yuy2") == 0) {
                 yuv_format = SDL_PIXELFORMAT_YUY2;
                 yuv_format = SDL_PIXELFORMAT_YUY2;
                 consumed = 1;
                 consumed = 1;
@@ -865,6 +924,16 @@ int main(int argc, char **argv)
             } else if (SDL_strcmp(argv[i], "--nv21") == 0) {
             } else if (SDL_strcmp(argv[i], "--nv21") == 0) {
                 yuv_format = SDL_PIXELFORMAT_NV21;
                 yuv_format = SDL_PIXELFORMAT_NV21;
                 consumed = 1;
                 consumed = 1;
+            } else if (SDL_strcmp(argv[i], "--p010") == 0) {
+                yuv_format = SDL_PIXELFORMAT_P010;
+                rgb_format = SDL_PIXELFORMAT_XBGR2101010;
+                SetYUVConversionMode(YUV_CONVERSION_BT2020);
+                consumed = 1;
+            } else if (SDL_strcmp(argv[i], "--p416") == 0) {
+                yuv_format = SDL_PIXELFORMAT_P416;
+                rgb_format = SDL_PIXELFORMAT_XBGR2101010;
+                SetYUVConversionMode(YUV_CONVERSION_BT2020);
+                consumed = 1;
             } else if (SDL_strcmp(argv[i], "--rgb555") == 0) {
             } else if (SDL_strcmp(argv[i], "--rgb555") == 0) {
                 rgb_format = SDL_PIXELFORMAT_XRGB1555;
                 rgb_format = SDL_PIXELFORMAT_XRGB1555;
                 consumed = 1;
                 consumed = 1;
@@ -911,8 +980,8 @@ int main(int argc, char **argv)
         }
         }
         if (consumed <= 0) {
         if (consumed <= 0) {
             static const char *options[] = {
             static const char *options[] = {
-                "[--jpeg|--bt601|--bt709|--bt2020|--auto]",
-                "[--yv12|--iyuv|--yuy2|--uyvy|--yvyu|--nv12|--nv21]",
+                "[--jpeg|--bt601|--bt709|--auto]",
+                "[--yv12|--iyuv|--p408|--yuy2|--uyvy|--yvyu|--nv12|--nv21|--p010|--p416]",
                 "[--rgb555|--rgb565|--rgb24|--argb|--abgr|--rgba|--bgra]",
                 "[--rgb555|--rgb565|--rgb24|--argb|--abgr|--rgba|--bgra]",
                 "[--monochrome] [--luminance N%] [--planar]",
                 "[--monochrome] [--luminance N%] [--planar]",
                 "[--automated] [--colorspace-test] [--renderer NAME]",
                 "[--automated] [--colorspace-test] [--renderer NAME]",

+ 53 - 0
test/testyuv_cvt.c

@@ -222,6 +222,53 @@ static void RGBtoYUV(const Uint8 *rgb, int rgb_bits, int *yuv, int yuv_bits, YUV
     }
     }
 }
 }
 
 
+static void ConvertRGBtoPlanar1x1(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance)
+{
+    int x, y;
+    int yuv[3];
+    Uint8 *Y, *U, *V;
+    const Uint8 *rgb;
+    int rgb_row_advance = (pitch - w * 3);
+    int yuv_bits;
+    int yuv_bytes_per_pixel = SDL_BYTESPERPIXEL(format);
+
+    rgb = src;
+    Y = out;
+    U = (Y + h * w * yuv_bytes_per_pixel);
+    V = (U + h * w * yuv_bytes_per_pixel);
+    switch (format) {
+    case SDL_PIXELFORMAT_P408:
+        yuv_bits = 8;
+        break;
+    case SDL_PIXELFORMAT_P416:
+        yuv_bits = 16;
+        break;
+    default:
+        SDL_assert(!"Unsupported planar YUV format");
+        return;
+    }
+
+    for (y = 0; y < h; ++y) {
+        for (x = 0; x < w; ++x) {
+            RGBtoYUV(rgb, 8, yuv, yuv_bits, mode, monochrome, luminance);
+            rgb += 3;
+            if (format == SDL_PIXELFORMAT_P408) {
+                *Y = (Uint8)yuv[0];
+                *U = (Uint8)yuv[1];
+                *V = (Uint8)yuv[2];
+            } else {
+                *(Uint16 *)Y = (Uint16)yuv[0];
+                *(Uint16 *)U = (Uint16)yuv[1];
+                *(Uint16 *)V = (Uint16)yuv[2];
+            }
+            Y += yuv_bytes_per_pixel;
+            U += yuv_bytes_per_pixel;
+            V += yuv_bytes_per_pixel;
+        }
+        rgb += rgb_row_advance;
+    }
+}
+
 static void ConvertRGBtoPlanar2x2(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance)
 static void ConvertRGBtoPlanar2x2(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance)
 {
 {
     int x, y;
     int x, y;
@@ -517,6 +564,10 @@ static void ConvertRGBtoPacked4(Uint32 format, Uint8 *src, int pitch, Uint8 *out
 bool ConvertRGBtoYUV(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance)
 bool ConvertRGBtoYUV(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance)
 {
 {
     switch (format) {
     switch (format) {
+    case SDL_PIXELFORMAT_P408:
+    case SDL_PIXELFORMAT_P416:
+        ConvertRGBtoPlanar1x1(format, src, pitch, out, w, h, mode, monochrome, luminance);
+        return true;
     case SDL_PIXELFORMAT_P010:
     case SDL_PIXELFORMAT_P010:
         ConvertRGBtoPlanar2x2_P010(format, src, pitch, out, w, h, mode, monochrome, luminance);
         ConvertRGBtoPlanar2x2_P010(format, src, pitch, out, w, h, mode, monochrome, luminance);
         return true;
         return true;
@@ -540,9 +591,11 @@ int CalculateYUVPitch(Uint32 format, int width)
 {
 {
     switch (format) {
     switch (format) {
     case SDL_PIXELFORMAT_P010:
     case SDL_PIXELFORMAT_P010:
+    case SDL_PIXELFORMAT_P416:
         return width * 2;
         return width * 2;
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_YV12:
     case SDL_PIXELFORMAT_IYUV:
     case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_P408:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV12:
     case SDL_PIXELFORMAT_NV21:
     case SDL_PIXELFORMAT_NV21:
         return width;
         return width;