diff --git a/include/SDL3/SDL_pixels.h b/include/SDL3/SDL_pixels.h index fd1ed31a4a..1ac325b9ae 100644 --- a/include/SDL3/SDL_pixels.h +++ b/include/SDL3/SDL_pixels.h @@ -363,7 +363,8 @@ typedef enum SDL_PackedLayout ((((format) == SDL_PIXELFORMAT_YUY2) || \ ((format) == SDL_PIXELFORMAT_UYVY) || \ ((format) == SDL_PIXELFORMAT_YVYU) || \ - ((format) == SDL_PIXELFORMAT_P010)) ? 2 : 1) : (((format) >> 0) & 0xFF)) + ((format) == SDL_PIXELFORMAT_P010) || \ + ((format) == SDL_PIXELFORMAT_P416)) ? 2 : 1) : (((format) >> 0) & 0xFF)) /** @@ -657,22 +658,26 @@ typedef enum SDL_PixelFormat SDL_PIXELFORMAT_ABGR128_FLOAT = 0x1b608010u, /* SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYF32, SDL_ARRAYORDER_ABGR, 0, 128, 16), */ - SDL_PIXELFORMAT_YV12 = 0x32315659u, /**< Planar mode: Y + V + U (3 planes) */ + SDL_PIXELFORMAT_YV12 = 0x32315659u, /**< YUV 4:2:0 8-bit planar mode: Y + V + U (3 planes) */ /* SDL_DEFINE_PIXELFOURCC('Y', 'V', '1', '2'), */ - SDL_PIXELFORMAT_IYUV = 0x56555949u, /**< Planar mode: Y + U + V (3 planes) */ + SDL_PIXELFORMAT_IYUV = 0x56555949u, /**< YUV 4:2:0 8-bit planar mode: Y + U + V (3 planes) */ /* SDL_DEFINE_PIXELFOURCC('I', 'Y', 'U', 'V'), */ - SDL_PIXELFORMAT_YUY2 = 0x32595559u, /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */ + SDL_PIXELFORMAT_YUY2 = 0x32595559u, /**< YUV 4:2:0 8-bit packed mode: Y0+U0+Y1+V0 (1 plane) */ /* SDL_DEFINE_PIXELFOURCC('Y', 'U', 'Y', '2'), */ - SDL_PIXELFORMAT_UYVY = 0x59565955u, /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */ + SDL_PIXELFORMAT_UYVY = 0x59565955u, /**< YUV 4:2:0 8-bit packed mode: U0+Y0+V0+Y1 (1 plane) */ /* SDL_DEFINE_PIXELFOURCC('U', 'Y', 'V', 'Y'), */ - SDL_PIXELFORMAT_YVYU = 0x55595659u, /**< Packed mode: Y0+V0+Y1+U0 (1 plane) */ + SDL_PIXELFORMAT_YVYU = 0x55595659u, /**< YUV 4:2:0 8-bit packed mode: Y0+V0+Y1+U0 (1 plane) */ /* SDL_DEFINE_PIXELFOURCC('Y', 'V', 'Y', 'U'), */ - SDL_PIXELFORMAT_NV12 = 0x3231564eu, /**< Planar mode: Y + U/V interleaved (2 planes) */ + SDL_PIXELFORMAT_NV12 = 0x3231564eu, /**< YUV 4:2:0 8-bit planar mode: Y + U/V interleaved (2 planes) */ /* SDL_DEFINE_PIXELFOURCC('N', 'V', '1', '2'), */ - SDL_PIXELFORMAT_NV21 = 0x3132564eu, /**< Planar mode: Y + V/U interleaved (2 planes) */ + SDL_PIXELFORMAT_NV21 = 0x3132564eu, /**< YUV 4:2:0 8-bit planar mode: Y + V/U interleaved (2 planes) */ /* SDL_DEFINE_PIXELFOURCC('N', 'V', '2', '1'), */ - SDL_PIXELFORMAT_P010 = 0x30313050u, /**< Planar mode: Y + U/V interleaved (2 planes) */ + SDL_PIXELFORMAT_P010 = 0x30313050u, /**< YUV 4:2:0 16-bit planar mode: Y + U/V interleaved (2 planes) */ /* SDL_DEFINE_PIXELFOURCC('P', '0', '1', '0'), */ + SDL_PIXELFORMAT_P408 = 0x38303450u, /**< YUV 4:4:4 8-bit planar mode: Y + U + V (3 planes) */ + /* SDL_DEFINE_PIXELFOURCC('P', '4', '0', '8'), */ + SDL_PIXELFORMAT_P416 = 0x36313450u, /**< YUV 4:4:4 16-bit planar mode: Y + U + V (3 planes) */ + /* SDL_DEFINE_PIXELFOURCC('P', '4', '0', '8'), */ SDL_PIXELFORMAT_EXTERNAL_OES = 0x2053454fu, /**< Android video texture format */ /* SDL_DEFINE_PIXELFOURCC('O', 'E', 'S', ' ') */ diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index c11c29e71d..e9645aca93 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -2518,8 +2518,10 @@ bool SDL_UpdateYUVTexture(SDL_Texture *texture, const SDL_Rect *rect, } CHECK_PARAM(texture->format != SDL_PIXELFORMAT_YV12 && - texture->format != SDL_PIXELFORMAT_IYUV) { - return SDL_SetError("Texture format must be YV12 or IYUV"); + texture->format != SDL_PIXELFORMAT_IYUV && + texture->format != SDL_PIXELFORMAT_P408 && + texture->format != SDL_PIXELFORMAT_P416) { + return SDL_SetError("Texture format must be YV12, IYUV, P408, or P416"); } real_rect.x = 0; diff --git a/src/render/SDL_yuv_sw.c b/src/render/SDL_yuv_sw.c index 4c4b7dc987..97d8b0b59f 100644 --- a/src/render/SDL_yuv_sw.c +++ b/src/render/SDL_yuv_sw.c @@ -35,6 +35,8 @@ SDL_SW_YUVTexture *SDL_SW_CreateYUVTexture(SDL_PixelFormat format, SDL_Colorspac switch (format) { case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: + case SDL_PIXELFORMAT_P416: case SDL_PIXELFORMAT_YUY2: case SDL_PIXELFORMAT_UYVY: case SDL_PIXELFORMAT_YVYU: @@ -80,13 +82,21 @@ SDL_SW_YUVTexture *SDL_SW_CreateYUVTexture(SDL_PixelFormat format, SDL_Colorspac swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h; swdata->planes[2] = swdata->planes[1] + swdata->pitches[1] * ((h + 1) / 2); break; + case SDL_PIXELFORMAT_P408: + case SDL_PIXELFORMAT_P416: + swdata->pitches[0] = w * SDL_BYTESPERPIXEL(format); + swdata->pitches[1] = swdata->pitches[0]; + swdata->pitches[2] = swdata->pitches[1]; + swdata->planes[0] = swdata->pixels; + swdata->planes[1] = swdata->planes[0] + swdata->pitches[0] * h; + swdata->planes[2] = swdata->planes[1] + swdata->pitches[1] * h; + break; case SDL_PIXELFORMAT_YUY2: case SDL_PIXELFORMAT_UYVY: case SDL_PIXELFORMAT_YVYU: swdata->pitches[0] = ((w + 1) / 2) * 4; swdata->planes[0] = swdata->pixels; break; - case SDL_PIXELFORMAT_NV12: case SDL_PIXELFORMAT_NV21: swdata->pitches[0] = w; @@ -119,7 +129,7 @@ bool SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect, case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: if (rect->x == 0 && rect->y == 0 && - rect->w == swdata->w && rect->h == swdata->h) { + rect->w == swdata->w && rect->h == swdata->h && pitch == swdata->pitches[0]) { SDL_memcpy(swdata->pixels, pixels, (size_t)(swdata->h * swdata->w) + 2 * ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2)); } else { @@ -161,6 +171,47 @@ bool SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect, } } break; + case SDL_PIXELFORMAT_P408: + case SDL_PIXELFORMAT_P416: + if (rect->x == 0 && rect->y == 0 && + rect->w == swdata->w && rect->h == swdata->h && pitch == swdata->pitches[0]) { + SDL_memcpy(swdata->pixels, pixels, (size_t)(swdata->h * pitch * 3)); + } else { + Uint8 *src, *dst; + int row; + size_t length; + const int bpp = SDL_BYTESPERPIXEL(swdata->format); + + // Copy the Y plane + src = (Uint8 *)pixels; + dst = swdata->pixels; + dst += rect->y * swdata->pitches[0] + rect->x *bpp; + length = rect->w * bpp; + for (row = 0; row < rect->h; ++row) { + SDL_memcpy(dst, src, length); + src += pitch; + dst += swdata->pitches[0]; + } + + // Copy the next plane + dst = swdata->pixels + swdata->h * swdata->pitches[0]; + dst += rect->y * swdata->pitches[1] + rect->x * bpp; + for (row = 0; row < rect->h; ++row) { + SDL_memcpy(dst, src, length); + src += pitch; + dst += swdata->pitches[1]; + } + + // Copy the next plane + dst = swdata->pixels + swdata->h * swdata->pitches[0] + swdata->h * swdata->pitches[1]; + dst += rect->y * swdata->pitches[2] + rect->x * bpp; + for (row = 0; row < rect->h; ++row) { + SDL_memcpy(dst, src, length); + src += pitch; + dst += swdata->pitches[2]; + } + } + break; case SDL_PIXELFORMAT_YUY2: case SDL_PIXELFORMAT_UYVY: case SDL_PIXELFORMAT_YVYU: @@ -229,47 +280,72 @@ bool SDL_SW_UpdateYUVTexturePlanar(SDL_SW_YUVTexture *swdata, const SDL_Rect *re Uint8 *dst; int row; size_t length; + const int bpp = SDL_BYTESPERPIXEL(swdata->format); // Copy the Y plane src = Yplane; - dst = swdata->pixels + rect->y * swdata->w + rect->x; + dst = swdata->pixels + rect->y * swdata->pitches[0] + rect->x * bpp; length = rect->w; for (row = 0; row < rect->h; ++row) { SDL_memcpy(dst, src, length); src += Ypitch; - dst += swdata->w; + dst += swdata->pitches[0]; } // Copy the U plane src = Uplane; - if (swdata->format == SDL_PIXELFORMAT_IYUV) { - dst = swdata->pixels + swdata->h * swdata->w; + if (swdata->format == SDL_PIXELFORMAT_P408 || + swdata->format == SDL_PIXELFORMAT_P416) { + dst = swdata->pixels + swdata->h * swdata->pitches[0]; + dst += rect->y * swdata->pitches[1] + rect->x * bpp; + length = rect->w * bpp; + for (row = 0; row < rect->h; ++row) { + SDL_memcpy(dst, src, length); + src += Upitch; + dst += swdata->pitches[1]; + } } else { - dst = swdata->pixels + swdata->h * swdata->w + - ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2); - } - dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2; - length = (rect->w + 1) / 2; - for (row = 0; row < (rect->h + 1) / 2; ++row) { - SDL_memcpy(dst, src, length); - src += Upitch; - dst += (swdata->w + 1) / 2; + if (swdata->format == SDL_PIXELFORMAT_IYUV) { + dst = swdata->pixels + swdata->h * swdata->w; + } else { + dst = swdata->pixels + swdata->h * swdata->w + + ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2); + } + dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2; + length = (rect->w + 1) / 2; + for (row = 0; row < (rect->h + 1) / 2; ++row) { + SDL_memcpy(dst, src, length); + src += Upitch; + dst += (swdata->w + 1) / 2; + } } // Copy the V plane src = Vplane; - if (swdata->format == SDL_PIXELFORMAT_YV12) { - dst = swdata->pixels + swdata->h * swdata->w; + if (swdata->format == SDL_PIXELFORMAT_P408 || + swdata->format == SDL_PIXELFORMAT_P416) { + dst = swdata->pixels + swdata->h * swdata->pitches[0] + swdata->h * swdata->pitches[1]; + dst += rect->y * swdata->pitches[2] + rect->x * bpp; + length = rect->w * bpp; + for (row = 0; row < rect->h; ++row) { + SDL_memcpy(dst, src, length); + src += Vpitch; + dst += swdata->pitches[2]; + } } else { - dst = swdata->pixels + swdata->h * swdata->w + - ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2); - } - dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2; - length = (rect->w + 1) / 2; - for (row = 0; row < (rect->h + 1) / 2; ++row) { - SDL_memcpy(dst, src, length); - src += Vpitch; - dst += (swdata->w + 1) / 2; + if (swdata->format == SDL_PIXELFORMAT_YV12) { + dst = swdata->pixels + swdata->h * swdata->w; + } else { + dst = swdata->pixels + swdata->h * swdata->w + + ((swdata->h + 1) / 2) * ((swdata->w + 1) / 2); + } + dst += rect->y / 2 * ((swdata->w + 1) / 2) + rect->x / 2; + length = (rect->w + 1) / 2; + for (row = 0; row < (rect->h + 1) / 2; ++row) { + SDL_memcpy(dst, src, length); + src += Vpitch; + dst += (swdata->w + 1) / 2; + } } return true; } @@ -314,10 +390,12 @@ bool SDL_SW_LockYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect, switch (swdata->format) { case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: + case SDL_PIXELFORMAT_P416: case SDL_PIXELFORMAT_NV12: case SDL_PIXELFORMAT_NV21: if (rect && (rect->x != 0 || rect->y != 0 || rect->w != swdata->w || rect->h != swdata->h)) { - return SDL_SetError("YV12, IYUV, NV12, NV21 textures only support full surface locks"); + return SDL_SetError("YV12, IYUV, P408, P416, NV12, NV21 textures only support full surface locks"); } break; default: @@ -325,7 +403,7 @@ bool SDL_SW_LockYUVTexture(SDL_SW_YUVTexture *swdata, const SDL_Rect *rect, } if (rect) { - *pixels = swdata->planes[0] + rect->y * swdata->pitches[0] + rect->x * 2; + *pixels = swdata->planes[0] + rect->y * swdata->pitches[0] + rect->x * SDL_BYTESPERPIXEL(swdata->format); } else { *pixels = swdata->planes[0]; } diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index ed17e6c8b1..c06ec7998a 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -223,6 +223,7 @@ static D3DFORMAT PixelFormatToD3DFMT(Uint32 format) case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_NV12: case SDL_PIXELFORMAT_NV21: + case SDL_PIXELFORMAT_P408: return D3DFMT_L8; default: for (int i = 0; i < SDL_arraysize(d3d_format_map); i++) { @@ -628,17 +629,30 @@ static bool D3D_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, } #ifdef SDL_HAVE_YUV if (texturedata->yuv) { - // Skip to the correct offset into the next texture - pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); + if (texture->format == SDL_PIXELFORMAT_P408) { + // Skip to the correct offset into the next texture + pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); + if (!D3D_UpdateTextureRep(data->device, &texturedata->utexture, rect->x, rect->y, rect->w, rect->h, pixels, pitch)) { + return false; + } - if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->vtexture : &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) { - return false; - } + // Skip to the correct offset into the next texture + pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); + if (!D3D_UpdateTextureRep(data->device, &texturedata->vtexture, rect->x, rect->y, rect->w, rect->h, pixels, pitch)) { + return false; + } + } else { + // Skip to the correct offset into the next texture + pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); + if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->vtexture : &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) { + return false; + } - // Skip to the correct offset into the next texture - pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2)); - if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->utexture : &texturedata->vtexture, rect->x / 2, (rect->y + 1) / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) { - return false; + // Skip to the correct offset into the next texture + pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2)); + if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->utexture : &texturedata->vtexture, rect->x / 2, (rect->y + 1) / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) { + return false; + } } } #endif @@ -688,6 +702,23 @@ static bool D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ return false; } + texturedata->shader_params_length = 4; // The YUV shader takes 4 float4 parameters + texturedata->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); + if (texturedata->shader_params == NULL) { + return SDL_SetError("Unsupported YUV colorspace"); + } + } + if (texture->format == SDL_PIXELFORMAT_P408) { + texturedata->yuv = true; + + if (!D3D_CreateTextureRep(data->device, &texturedata->utexture, usage, texture->format, PixelFormatToD3DFMT(texture->format), texture->w, texture->h)) { + return false; + } + + if (!D3D_CreateTextureRep(data->device, &texturedata->vtexture, usage, texture->format, PixelFormatToD3DFMT(texture->format), texture->w, texture->h)) { + return false; + } + texturedata->shader_params_length = 4; // The YUV shader takes 4 float4 parameters texturedata->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); if (texturedata->shader_params == NULL) { @@ -741,11 +772,20 @@ static bool D3D_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, if (!D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch)) { return false; } - if (!D3D_UpdateTextureRep(data->device, &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch)) { - return false; - } - if (!D3D_UpdateTextureRep(data->device, &texturedata->vtexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch)) { - return false; + if (texture->format == SDL_PIXELFORMAT_P408) { + if (!D3D_UpdateTextureRep(data->device, &texturedata->utexture, rect->x, rect->y, rect->w, rect->h, Uplane, Upitch)) { + return false; + } + if (!D3D_UpdateTextureRep(data->device, &texturedata->vtexture, rect->x, rect->y, rect->w, rect->h, Vplane, Vpitch)) { + return false; + } + } else { + if (!D3D_UpdateTextureRep(data->device, &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch)) { + return false; + } + if (!D3D_UpdateTextureRep(data->device, &texturedata->vtexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch)) { + return false; + } } return true; } @@ -2005,6 +2045,7 @@ static bool D3D_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P if (caps.MaxSimultaneousTextures >= 3 && data->shaders[SHADER_YUV]) { SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408); } #endif diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index edb0be613a..607242c51e 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -266,12 +266,15 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 outpu case SDL_PIXELFORMAT_INDEX8: case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: return DXGI_FORMAT_R8_UNORM; case SDL_PIXELFORMAT_NV12: case SDL_PIXELFORMAT_NV21: return DXGI_FORMAT_NV12; case SDL_PIXELFORMAT_P010: return DXGI_FORMAT_P010; + case SDL_PIXELFORMAT_P416: + return DXGI_FORMAT_R16_UNORM; default: for (int i = 0; i < SDL_arraysize(dxgi_format_map); i++) { if (dxgi_format_map[i].sdl == format) { @@ -289,8 +292,6 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 outpu static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uint32 colorspace) { switch (format) { - case SDL_PIXELFORMAT_YV12: - case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_NV12: // For the Y texture case SDL_PIXELFORMAT_NV21: // For the Y texture return DXGI_FORMAT_R8_UNORM; @@ -1309,6 +1310,45 @@ static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD return SDL_SetError("Unsupported YUV colorspace"); } } + if (texture->format == SDL_PIXELFORMAT_P408 || + texture->format == SDL_PIXELFORMAT_P416) { + + textureData->yuv = true; + + if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_U_POINTER, &textureData->mainTextureU)) { + return false; + } + if (!textureData->mainTextureU) { + result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice, + &textureDesc, + NULL, + &textureData->mainTextureU); + if (FAILED(result)) { + return WIN_SetErrorFromHRESULT("ID3D11Device1::CreateTexture2D", result); + } + } + SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_U_POINTER, textureData->mainTextureU); + + if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_V_POINTER, &textureData->mainTextureV)) { + return false; + } + if (!textureData->mainTextureV) { + result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice, + &textureDesc, + NULL, + &textureData->mainTextureV); + if (FAILED(result)) { + return WIN_SetErrorFromHRESULT("ID3D11Device1::CreateTexture2D", result); + } + } + SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_V_POINTER, textureData->mainTextureV); + + const int bits_per_pixel = (texture->format == SDL_PIXELFORMAT_P408) ? 8 : 16; + textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel); + if (!textureData->YCbCr_matrix) { + return SDL_SetError("Unsupported YUV colorspace"); + } + } if (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21 || texture->format == SDL_PIXELFORMAT_P010) { @@ -1562,16 +1602,29 @@ static bool D3D11_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, return D3D11_UpdateTextureNV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch); } else if (textureData->yuv) { - int Ypitch = srcPitch; - int UVpitch = ((Ypitch + 1) / 2); - const Uint8 *plane0 = (const Uint8 *)srcPixels; - const Uint8 *plane1 = plane0 + rect->h * Ypitch; - const Uint8 *plane2 = plane1 + ((rect->h + 1) / 2) * UVpitch; + if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) { + const Uint8 *plane0 = (const Uint8 *)srcPixels; + const Uint8 *plane1 = plane0 + rect->h * srcPitch; + const Uint8 *plane2 = plane1 + rect->h * srcPitch; - if (texture->format == SDL_PIXELFORMAT_YV12) { - return D3D11_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane2, UVpitch, plane1, UVpitch); + if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureU, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, plane1, srcPitch)) { + return false; + } + if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureV, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, plane2, srcPitch)) { + return false; + } } else { - return D3D11_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch, plane2, UVpitch); + int Ypitch = srcPitch; + int UVpitch = ((Ypitch + 1) / 2); + const Uint8 *plane0 = (const Uint8 *)srcPixels; + const Uint8 *plane1 = plane0 + rect->h * Ypitch; + const Uint8 *plane2 = plane1 + ((rect->h + 1) / 2) * UVpitch; + + if (texture->format == SDL_PIXELFORMAT_YV12) { + return D3D11_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane2, UVpitch, plane1, UVpitch); + } else { + return D3D11_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch, plane2, UVpitch); + } } } #endif @@ -1599,11 +1652,20 @@ static bool D3D11_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTexture, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch)) { return false; } - if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureU, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch)) { - return false; - } - if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureV, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch)) { - return false; + if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) { + if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureU, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, Uplane, Upitch)) { + return false; + } + if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureV, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, Vplane, Vpitch)) { + return false; + } + } else { + if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureU, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch)) { + return false; + } + if (!D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureV, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch)) { + return false; + } } return true; } @@ -2153,6 +2215,7 @@ static void D3D11_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderC break; case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: constants->texture_type = TEXTURETYPE_YUV; constants->input_type = INPUTTYPE_SRGB; break; @@ -2168,6 +2231,10 @@ static void D3D11_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderC constants->texture_type = TEXTURETYPE_NV12; constants->input_type = INPUTTYPE_HDR10; break; + case SDL_PIXELFORMAT_P416: + constants->texture_type = TEXTURETYPE_YUV; + constants->input_type = INPUTTYPE_HDR10; + break; default: if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) { constants->texture_type = TEXTURETYPE_RGB_PIXELART; @@ -2971,9 +3038,11 @@ static bool D3D11_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P416); return true; } diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index d88903c6f6..c2671988b8 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -328,12 +328,15 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(SDL_PixelFormat format, Uin case SDL_PIXELFORMAT_INDEX8: case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: return DXGI_FORMAT_R8_UNORM; case SDL_PIXELFORMAT_NV12: case SDL_PIXELFORMAT_NV21: return DXGI_FORMAT_NV12; case SDL_PIXELFORMAT_P010: return DXGI_FORMAT_P010; + case SDL_PIXELFORMAT_P416: + return DXGI_FORMAT_R16_UNORM; default: for (int i = 0; i < SDL_arraysize(dxgi_format_map); i++) { if (dxgi_format_map[i].sdl == format) { @@ -351,9 +354,6 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(SDL_PixelFormat format, Uin static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(SDL_PixelFormat format, Uint32 colorspace) { switch (format) { - case SDL_PIXELFORMAT_INDEX8: - case SDL_PIXELFORMAT_YV12: - case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_NV12: // For the Y texture case SDL_PIXELFORMAT_NV21: // For the Y texture return DXGI_FORMAT_R8_UNORM; @@ -1740,6 +1740,55 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } } + if (texture->format == SDL_PIXELFORMAT_P408 || + texture->format == SDL_PIXELFORMAT_P416) { + textureData->yuv = true; + + if (!GetTextureProperty(create_props, SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_U_POINTER, &textureData->mainTextureU)) { + return false; + } + if (!textureData->mainTextureU) { + result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice, + &heapProps, + 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 || texture->format == SDL_PIXELFORMAT_NV21 || texture->format == SDL_PIXELFORMAT_P010) { @@ -2021,17 +2070,30 @@ static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, } #ifdef SDL_HAVE_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)) { 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) { // 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; case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: constants->texture_type = TEXTURETYPE_YUV; constants->input_type = INPUTTYPE_SRGB; break; @@ -2619,6 +2691,10 @@ static void D3D12_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderC constants->texture_type = TEXTURETYPE_NV12; constants->input_type = INPUTTYPE_HDR10; break; + case SDL_PIXELFORMAT_P416: + constants->texture_type = TEXTURETYPE_YUV; + constants->input_type = INPUTTYPE_HDR10; + break; default: if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) { constants->texture_type = TEXTURETYPE_RGB_PIXELART; @@ -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_YV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P416); return true; } diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c index a2e5035d0c..62184b2b31 100644 --- a/src/render/gpu/SDL_render_gpu.c +++ b/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_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: case SDL_PIXELFORMAT_NV12: case SDL_PIXELFORMAT_NV21: format = SDL_GPU_TEXTUREFORMAT_R8_UNORM; break; case SDL_PIXELFORMAT_P010: + case SDL_PIXELFORMAT_P416: format = SDL_GPU_TEXTUREFORMAT_R16_UNORM; break; 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 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 || texture->format == SDL_PIXELFORMAT_NV21 || 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"); } } + 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 || texture->format == SDL_PIXELFORMAT_NV21 || 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); } 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 { - 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 @@ -576,8 +624,13 @@ static bool GPU_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, SDL_GPUCommandBuffer *cbuf = renderdata->state.command_buffer; 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->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); return retval; } @@ -874,6 +927,7 @@ static void CalculateAdvancedShaderConstants(SDL_Renderer *renderer, const SDL_R break; case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: constants->texture_type = TEXTURETYPE_YUV; constants->input_type = INPUTTYPE_SRGB; break; @@ -889,6 +943,10 @@ static void CalculateAdvancedShaderConstants(SDL_Renderer *renderer, const SDL_R constants->texture_type = TEXTURETYPE_NV12; constants->input_type = INPUTTYPE_HDR10; break; + case SDL_PIXELFORMAT_P416: + constants->texture_type = TEXTURETYPE_YUV; + constants->input_type = INPUTTYPE_HDR10; + break; default: switch (texture->format) { 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_YV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P416); SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 16384); diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index 9488b4bca7..0b2ddf710d 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -752,12 +752,14 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD break; case SDL_PIXELFORMAT_INDEX8: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_NV12: case SDL_PIXELFORMAT_NV21: pixfmt = MTLPixelFormatR8Unorm; break; case SDL_PIXELFORMAT_P010: + case SDL_PIXELFORMAT_P416: pixfmt = MTLPixelFormatR16Unorm; break; case SDL_PIXELFORMAT_RGBA64_FLOAT: @@ -796,13 +798,18 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD mtltextureUv = nil; #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); 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.arrayLength = 2; } else if (texture->format == SDL_PIXELFORMAT_P010) { @@ -954,8 +961,18 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, if (texturedata.yuv) { int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0; 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 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; const int Uslice = 0; 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 if (rect->w <= 0 || rect->h <= 0) { @@ -1451,6 +1476,8 @@ static void SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderCommand break; case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: + case SDL_PIXELFORMAT_P416: constants->texture_type = TEXTURETYPE_YUV; break; 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_YV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P416); #if defined(SDL_PLATFORM_MACOS) || TARGET_OS_MACCATALYST data.mtllayer.displaySyncEnabled = NO; diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index f423367080..4ae3ec8ad4 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/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_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: case SDL_PIXELFORMAT_NV12: case SDL_PIXELFORMAT_NV21: *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 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 || texture->format == SDL_PIXELFORMAT_NV21) { // 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); } + 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 || texture->format == SDL_PIXELFORMAT_NV21) { data->nv12 = true; @@ -807,29 +841,43 @@ static bool GL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, pixels); #ifdef SDL_HAVE_YUV if (data->yuv) { - 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 * pitch); - if (texture->format == SDL_PIXELFORMAT_YV12) { - renderdata->glBindTexture(textype, data->vtexture); - } 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); 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->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 + 1) / 2) * ((pitch + 1) / 2)); - if (texture->format == SDL_PIXELFORMAT_YV12) { - renderdata->glBindTexture(textype, data->utexture); - } else { + // Skip to the correct offset into the next texture + pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); renderdata->glBindTexture(textype, data->vtexture); + renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w, rect->h, + data->format, data->formattype, pixels); + } else { + 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 * 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) { @@ -868,17 +916,29 @@ static bool GL_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, rect->h, data->format, data->formattype, 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); } @@ -1977,6 +2037,7 @@ static bool GL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Pr data->num_texture_units >= 3) { SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408); } else { SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL YUV not supported"); } diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 01e642709c..44ff3eac63 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/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 case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_P408: sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV; break; case SDL_PIXELFORMAT_NV12: @@ -1283,6 +1284,7 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, v #ifdef SDL_HAVE_YUV case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_P408: sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV; break; case SDL_PIXELFORMAT_NV12: @@ -1745,6 +1747,7 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD #ifdef SDL_HAVE_YUV case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_P408: case SDL_PIXELFORMAT_NV12: case SDL_PIXELFORMAT_NV21: #endif @@ -1783,7 +1786,7 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD data->pixel_format = format; data->pixel_type = type; #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)); #endif 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 if (data->yuv) { // 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) { // Need to add size for the U/V plane 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 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); if (data->texture_v) { 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->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)) { SDL_free(data->pixel_data); SDL_free(data); @@ -1857,7 +1873,7 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } renderdata->glActiveTexture(GL_TEXTURE1); 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)) { SDL_free(data->pixel_data); SDL_free(data); @@ -2015,37 +2031,59 @@ static bool GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, co #ifdef SDL_HAVE_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); - } - 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, 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 + 1) / 2) * ((pitch + 1) / 2)); - if (texture->format == SDL_PIXELFORMAT_YV12) { - data->glBindTexture(tdata->texture_type, tdata->texture_u); - } else { + // 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); + 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) { // Skip to the correct offset into the next texture 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->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); 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 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21); #endif diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index ad434ff672..5bfce7e38f 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -432,6 +432,8 @@ static int VULKAN_VkFormatGetNumPlanes(VkFormat vkFormat) { switch (vkFormat) { case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM: + case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM: + case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM: return 3; case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: 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: return 4; case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM: + case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM: return 1; + case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM: + return 2; case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: return (plane == 0) ? 1 : 2; 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_IYUV: 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_NV21: return VK_FORMAT_G8_B8R8_2PLANE_420_UNORM; case SDL_PIXELFORMAT_P010: return VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16; + case SDL_PIXELFORMAT_P416: + return VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM; default: for (int i = 0; i < SDL_arraysize(vk_format_map); i++) { 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 if (texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_IYUV || + texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_NV12 || 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; // 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 }; 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 samplerYcbcrConversionCreateInfo.format = textureFormat; @@ -2974,16 +2988,24 @@ static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, } else if (numPlanes == 3) { // 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 { - 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 @@ -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)) { 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)) { return false; } @@ -3450,11 +3479,13 @@ static void VULKAN_SetupShaderConstants(SDL_Renderer *renderer, const SDL_Render switch (texture->format) { case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: case SDL_PIXELFORMAT_NV12: case SDL_PIXELFORMAT_NV21: constants->input_type = INPUTTYPE_SRGB; break; case SDL_PIXELFORMAT_P010: + case SDL_PIXELFORMAT_P416: constants->input_type = INPUTTYPE_HDR10; break; default: @@ -4623,9 +4654,11 @@ static bool VULKAN_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SD if (rendererData->supportsKHRSamplerYCbCrConversion) { SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P408); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010); + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P416); } #endif diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index 444f98e50f..5655b34af9 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -215,6 +215,8 @@ const char *SDL_GetPixelFormatName(SDL_PixelFormat format) CASE(SDL_PIXELFORMAT_NV12) CASE(SDL_PIXELFORMAT_NV21) CASE(SDL_PIXELFORMAT_P010) + CASE(SDL_PIXELFORMAT_P408) + CASE(SDL_PIXELFORMAT_P416) CASE(SDL_PIXELFORMAT_EXTERNAL_OES) CASE(SDL_PIXELFORMAT_MJPG) @@ -854,7 +856,7 @@ SDL_Colorspace SDL_GetDefaultColorspaceForFormat(SDL_PixelFormat format) if (SDL_ISPIXELFORMAT_FOURCC(format)) { if (format == SDL_PIXELFORMAT_MJPG) { return SDL_COLORSPACE_SRGB; - } else if (format == SDL_PIXELFORMAT_P010) { + } else if (format == SDL_PIXELFORMAT_P010 || format == SDL_PIXELFORMAT_P416) { return SDL_COLORSPACE_HDR10; } else { return SDL_COLORSPACE_YUV_DEFAULT; diff --git a/src/video/SDL_yuv.c b/src/video/SDL_yuv.c index 6b57d54dc1..a97796f3fd 100644 --- a/src/video/SDL_yuv.c +++ b/src/video/SDL_yuv.c @@ -27,7 +27,20 @@ #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 /* @@ -39,7 +52,18 @@ bool SDL_CalculateYUVSize(SDL_PixelFormat format, int w, int h, size_t *size, si #ifdef SDL_HAVE_YUV 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; */ size_t s1; @@ -81,9 +105,11 @@ bool SDL_CalculateYUVSize(SDL_PixelFormat format, int w, int h, size_t *size, si switch (format) { 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_P408: + case SDL_PIXELFORMAT_P416: if (pitch) { - *pitch = w; + *pitch = w * SDL_BYTESPERPIXEL(format); } if (size) { @@ -187,16 +213,6 @@ static bool GetYUVConversionType(SDL_Colorspace colorspace, YCbCrType *yuv_type) 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, 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[1] = planes[0] + pitches[0] * height; 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: 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]; break; case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: + case SDL_PIXELFORMAT_P416: *y = planes[0]; *y_stride = pitches[0]; *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 || src_format == SDL_PIXELFORMAT_UYVY || src_format == SDL_PIXELFORMAT_YVYU) { @@ -583,6 +634,16 @@ static bool yuv_rgb_std( 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; } @@ -641,6 +702,29 @@ bool SDL_ConvertPixels_YUV_to_RGB(int width, int height, 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) { bool result; void *tmp; @@ -775,6 +859,7 @@ static bool SDL_ConvertPixels_XRGB8888_to_YUV(int width, int height, const void switch (dst_format) { case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_P408: case SDL_PIXELFORMAT_NV12: 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_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) { uv_skip = (uv_stride - ((width + 1) / 2) * 2); 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; + 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)) { // Y plane for (i = height; i--;) { diff --git a/src/video/yuv2rgb/yuv_rgb_internal.h b/src/video/yuv2rgb/yuv_rgb_internal.h index d5939ed651..5b487b55d3 100644 --- a/src/video/yuv2rgb/yuv_rgb_internal.h +++ b/src/video/yuv2rgb/yuv_rgb_internal.h @@ -72,8 +72,9 @@ static const RGB2YUVParam RGB2YUV[] = { /* The various layouts of YUV data we support */ #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 */ #define RGB_FORMAT_RGB565 1 @@ -82,4 +83,5 @@ static const RGB2YUVParam RGB2YUV[] = { #define RGB_FORMAT_BGRA 4 #define RGB_FORMAT_ARGB 5 #define RGB_FORMAT_ABGR 6 -#define RGB_FORMAT_XBGR2101010 7 +#define RGB_FORMAT_XBGR2101010 7 +#define RGB_FORMAT_RGB48 8 diff --git a/src/video/yuv2rgb/yuv_rgb_std.c b/src/video/yuv2rgb/yuv_rgb_std.c index 0fa900df7f..41b7017a81 100644 --- a/src/video/yuv2rgb/yuv_rgb_std.c +++ b/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 STD_FUNCTION_NAME yuv420_rgb565_std @@ -102,6 +114,26 @@ static uint16_t clamp10(int32_t v) #define RGB_FORMAT RGB_FORMAT_ABGR #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 YUV_FORMAT YUV_FORMAT_NV12 #define RGB_FORMAT RGB_FORMAT_RGB565 @@ -140,6 +172,14 @@ static uint16_t clamp10(int32_t v) #define RGB_FORMAT RGB_FORMAT_XBGR2101010 #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( uint32_t width, uint32_t height, const uint8_t *RGB, uint32_t RGB_stride, diff --git a/src/video/yuv2rgb/yuv_rgb_std.h b/src/video/yuv2rgb/yuv_rgb_std.h index c9f856ba98..5e27ecfb8b 100644 --- a/src/video/yuv2rgb/yuv_rgb_std.h +++ b/src/video/yuv2rgb/yuv_rgb_std.h @@ -93,6 +93,30 @@ void yuv422_abgr_std( uint8_t *rgb, uint32_t rgb_stride, 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( 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, @@ -135,6 +159,12 @@ void yuvp010_xbgr2101010_std( uint8_t *rgb, uint32_t rgb_stride, 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 void rgb24_yuv420_std( uint32_t width, uint32_t height, diff --git a/src/video/yuv2rgb/yuv_rgb_std_func.h b/src/video/yuv2rgb/yuv_rgb_std_func.h index 1cc4b89231..c7373afe33 100644 --- a/src/video/yuv2rgb/yuv_rgb_std_func.h +++ b/src/video/yuv2rgb/yuv_rgb_std_func.h @@ -74,6 +74,16 @@ (((Uint32)clamp10(y_tmp+r_tmp)) << 0); \ 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 #error PACK_PIXEL unimplemented #endif @@ -117,6 +127,11 @@ void STD_FUNCTION_NAME( #define uv_pixel_stride 4 #define uv_x_sample_interval 2 #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 #define y_pixel_stride 1 #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); PACK_PIXEL(rgb_ptr1); + #if uv_x_sample_interval > 1 y_tmp = (GET(y_ptr1[y_pixel_stride]-param->y_shift)*param->y_factor); PACK_PIXEL(rgb_ptr1); + #endif #if uv_y_sample_interval > 1 y_tmp = (GET(y_ptr2[0]-param->y_shift)*param->y_factor); @@ -171,12 +188,21 @@ void STD_FUNCTION_NAME( PACK_PIXEL(rgb_ptr2); #endif + #if uv_x_sample_interval > 1 y_ptr1+=2*y_pixel_stride; + #else + y_ptr1+=y_pixel_stride; + #endif #if uv_y_sample_interval > 1 y_ptr2+=2*y_pixel_stride; #endif + #if uv_x_sample_interval > 1 u_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 */ diff --git a/test/testautomation_pixels.c b/test/testautomation_pixels.c index e5ae75b52b..0ad4a646ac 100644 --- a/test/testautomation_pixels.c +++ b/test/testautomation_pixels.c @@ -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)); diff --git a/test/testyuv.c b/test/testyuv.c index 85f3ee4c8f..3cc5af8daa 100644 --- a/test/testyuv.c +++ b/test/testyuv.c @@ -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]", diff --git a/test/testyuv_cvt.c b/test/testyuv_cvt.c index a3c0f97199..d634d36111 100644 --- a/test/testyuv_cvt.c +++ b/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) { 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;