Added SDL_PIXELFORMAT_P408 and SDL_PIXELFORMAT_P416
Some checks are pending
Build (All) / Create test plan (push) Waiting to run
Build (All) / level1 (push) Blocked by required conditions
Build (All) / level2 (push) Blocked by required conditions

This commit is contained in:
Sam Lantinga 2026-06-22 12:42:45 -07:00
parent 0bb539314d
commit 07ecc125cf
20 changed files with 1091 additions and 226 deletions

View file

@ -51,7 +51,8 @@ static const SDL_PixelFormat g_AllFormats[] = {
SDL_PIXELFORMAT_UYVY,
SDL_PIXELFORMAT_YVYU,
SDL_PIXELFORMAT_NV12,
SDL_PIXELFORMAT_NV21
SDL_PIXELFORMAT_NV21,
SDL_PIXELFORMAT_P408
};
static const int g_numAllFormats = SDL_arraysize(g_AllFormats);
@ -98,7 +99,8 @@ static const char *g_AllFormatsVerbose[] = {
"SDL_PIXELFORMAT_UYVY",
"SDL_PIXELFORMAT_YVYU",
"SDL_PIXELFORMAT_NV12",
"SDL_PIXELFORMAT_NV21"
"SDL_PIXELFORMAT_NV21",
"SDL_PIXELFORMAT_P408"
};
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_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_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_FOURCC, SDL_ISPIXELFORMAT_FOURCC(SDL_PIXELFORMAT_EXTERNAL_OES));
SDL_COMPILE_TIME_ASSERT(SDL_PIXELFORMAT_EXTERNAL_OES_INDEXED, !SDL_ISPIXELFORMAT_INDEXED(SDL_PIXELFORMAT_EXTERNAL_OES));

View file

@ -15,8 +15,8 @@
#include "testyuv_cvt.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 */
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[] = {
SDL_PIXELFORMAT_YV12,
SDL_PIXELFORMAT_IYUV,
SDL_PIXELFORMAT_P408,
SDL_PIXELFORMAT_NV12,
SDL_PIXELFORMAT_NV21,
SDL_PIXELFORMAT_YUY2,
@ -164,6 +165,11 @@ static bool run_automated_tests(int pattern_size, int extra_pitch)
/* Verify conversion between YUV formats */
for (i = 0; i < SDL_arraysize(formats); ++i) {
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;
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)) {
@ -189,6 +195,11 @@ static bool run_automated_tests(int pattern_size, int extra_pitch)
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;
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)) {
@ -370,16 +381,16 @@ static bool create_textures(SDL_Renderer *renderer, SDL_Surface *original, SDL_P
int pitch;
SDL_Surface *converted = NULL;
bool result = false;
size_t max_size;
YUV_CONVERSION_MODE yuv_mode = GetYUVConversionModeForResolution(original->w, original->h);
if (yuv_mode == YUV_CONVERSION_BT2020) {
yuv_format = SDL_PIXELFORMAT_P010;
rgb_format = SDL_PIXELFORMAT_XBGR2101010;
rgb_colorspace = SDL_COLORSPACE_HDR10;
}
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);
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(plane1);
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)) {
const int Yrows = original->h;
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[] = {
SDL_PIXELFORMAT_YV12,
SDL_PIXELFORMAT_IYUV,
SDL_PIXELFORMAT_P408,
SDL_PIXELFORMAT_YUY2,
SDL_PIXELFORMAT_UYVY,
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());
return false;
}
renderer_name = SDL_GetRendererName(renderer);
SDL_Texture *output[3];
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;
case YUV_CONVERSION_BT2020:
yuv_mode_name = "BT.2020";
yuv_format = SDL_PIXELFORMAT_P010;
break;
default:
yuv_mode_name = "UNKNOWN";
@ -745,7 +804,7 @@ static bool run_interactive(SDL_Window *window, const char *renderer_name, SDL_S
if (current == 0) {
SDLTest_DrawString(renderer, 4, 4, titles[current]);
} 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);
}
}
@ -838,9 +897,6 @@ int main(int argc, char **argv)
} else if (SDL_strcmp(argv[i], "--bt709") == 0) {
SetYUVConversionMode(YUV_CONVERSION_BT709);
consumed = 1;
} else if (SDL_strcmp(argv[i], "--bt2020") == 0) {
SetYUVConversionMode(YUV_CONVERSION_BT2020);
consumed = 1;
} else if (SDL_strcmp(argv[i], "--auto") == 0) {
SetYUVConversionMode(YUV_CONVERSION_AUTOMATIC);
consumed = 1;
@ -850,6 +906,9 @@ int main(int argc, char **argv)
} else if (SDL_strcmp(argv[i], "--iyuv") == 0) {
yuv_format = SDL_PIXELFORMAT_IYUV;
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) {
yuv_format = SDL_PIXELFORMAT_YUY2;
consumed = 1;
@ -865,6 +924,16 @@ int main(int argc, char **argv)
} else if (SDL_strcmp(argv[i], "--nv21") == 0) {
yuv_format = SDL_PIXELFORMAT_NV21;
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) {
rgb_format = SDL_PIXELFORMAT_XRGB1555;
consumed = 1;
@ -911,8 +980,8 @@ int main(int argc, char **argv)
}
if (consumed <= 0) {
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]",
"[--monochrome] [--luminance N%] [--planar]",
"[--automated] [--colorspace-test] [--renderer NAME]",

View file

@ -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)
{
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)
{
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:
ConvertRGBtoPlanar2x2_P010(format, src, pitch, out, w, h, mode, monochrome, luminance);
return true;
@ -540,9 +591,11 @@ int CalculateYUVPitch(Uint32 format, int width)
{
switch (format) {
case SDL_PIXELFORMAT_P010:
case SDL_PIXELFORMAT_P416:
return width * 2;
case SDL_PIXELFORMAT_YV12:
case SDL_PIXELFORMAT_IYUV:
case SDL_PIXELFORMAT_P408:
case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21:
return width;