diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index 6ff737b39c..d0381b9ba0 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -558,7 +558,7 @@ static int D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P } texturedata->shader = SHADER_YUV; - texturedata->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace); + texturedata->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); if (texturedata->shader_params == NULL) { return SDL_SetError("Unsupported YUV colorspace"); } diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index e720143e3f..f83ef3fe63 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -1297,7 +1297,7 @@ static int D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_V_POINTER, textureData->mainTextureV); textureData->shader = SHADER_YUV; - textureData->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace); + textureData->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); if (!textureData->shader_params) { return SDL_SetError("Unsupported YUV colorspace"); } @@ -1306,6 +1306,8 @@ static int D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL texture->format == SDL_PIXELFORMAT_NV21 || texture->format == SDL_PIXELFORMAT_P010 || texture->format == SDL_PIXELFORMAT_P016) { + int bits_per_pixel; + textureData->nv12 = SDL_TRUE; switch (texture->format) { @@ -1328,7 +1330,18 @@ static int D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL /* This should never happen because of the check above */ return SDL_SetError("Unsupported YUV colorspace"); } - textureData->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace); + switch (texture->format) { + case SDL_PIXELFORMAT_P010: + bits_per_pixel = 10; + break; + case SDL_PIXELFORMAT_P016: + bits_per_pixel = 16; + break; + default: + bits_per_pixel = 8; + break; + } + textureData->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel); if (!textureData->shader_params) { return SDL_SetError("Unsupported YUV colorspace"); } diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index afd25c9cdf..c1ae85ba54 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -1641,7 +1641,7 @@ static int D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_V_POINTER, textureData->mainTextureV); textureData->shader = SHADER_YUV; - textureData->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace); + textureData->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); if (!textureData->shader_params) { return SDL_SetError("Unsupported YUV colorspace"); } @@ -1652,7 +1652,7 @@ static int D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL textureData->nv12 = SDL_TRUE; textureData->shader = (texture->format == SDL_PIXELFORMAT_NV12 ? SHADER_NV12 : SHADER_NV21); - textureData->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace); + textureData->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); if (!textureData->shader_params) { return SDL_SetError("Unsupported YUV colorspace"); } diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index d3bb3f1506..4effe602db 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -1822,10 +1822,10 @@ static SDL_Renderer *METAL_CreateRenderer(SDL_Window *window, SDL_PropertiesID c constantdata = [mtlbufconstantstaging contents]; SDL_memcpy(constantdata + CONSTANTS_OFFSET_IDENTITY, identitytransform, sizeof(identitytransform)); SDL_memcpy(constantdata + CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, halfpixeltransform, sizeof(halfpixeltransform)); - SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT601_LIMITED, SDL_GetYCbCRtoRGBConversionMatrix(SDL_COLORSPACE_BT601_LIMITED), YCbCr_shader_matrix_size); - SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT601_FULL, SDL_GetYCbCRtoRGBConversionMatrix(SDL_COLORSPACE_BT601_FULL), YCbCr_shader_matrix_size); - SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT709_LIMITED, SDL_GetYCbCRtoRGBConversionMatrix(SDL_COLORSPACE_BT709_LIMITED), YCbCr_shader_matrix_size); - SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT709_FULL, SDL_GetYCbCRtoRGBConversionMatrix(SDL_COLORSPACE_BT709_FULL), YCbCr_shader_matrix_size); + SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT601_LIMITED, SDL_GetYCbCRtoRGBConversionMatrix(SDL_COLORSPACE_BT601_LIMITED, 0, 0, 8), YCbCr_shader_matrix_size); + SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT601_FULL, SDL_GetYCbCRtoRGBConversionMatrix(SDL_COLORSPACE_BT601_FULL, 0, 0, 8), YCbCr_shader_matrix_size); + SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT709_LIMITED, SDL_GetYCbCRtoRGBConversionMatrix(SDL_COLORSPACE_BT709_LIMITED, 0, 0, 8), YCbCr_shader_matrix_size); + SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT709_FULL, SDL_GetYCbCRtoRGBConversionMatrix(SDL_COLORSPACE_BT709_FULL, 0, 0, 8), YCbCr_shader_matrix_size); mtlbufquadindicesstaging = [data.mtldevice newBufferWithLength:indicessize options:MTLResourceStorageModeShared]; diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index bfda77082f..72dfb25619 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -680,7 +680,7 @@ static int GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Pr data->shader = SHADER_NV21_RA; } } - data->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace); + data->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); if (!data->shader_params) { return SDL_SetError("Unsupported YUV colorspace"); } diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 7d07d02dd3..f528b5439a 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -629,7 +629,7 @@ static int GLES2_SelectProgram(GLES2_RenderData *data, GLES2_ImageSource source, #if SDL_HAVE_YUV case GLES2_IMAGESOURCE_TEXTURE_YUV: ftype = GLES2_SHADER_FRAGMENT_TEXTURE_YUV; - shader_params = SDL_GetYCbCRtoRGBConversionMatrix(colorspace); + shader_params = SDL_GetYCbCRtoRGBConversionMatrix(colorspace, 0, 0, 8); if (!shader_params) { SDL_SetError("Unsupported YUV colorspace"); goto fault; @@ -641,7 +641,7 @@ static int GLES2_SelectProgram(GLES2_RenderData *data, GLES2_ImageSource source, } else { ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV12_RA; } - shader_params = SDL_GetYCbCRtoRGBConversionMatrix(colorspace); + shader_params = SDL_GetYCbCRtoRGBConversionMatrix(colorspace, 0, 0, 8); if (!shader_params) { SDL_SetError("Unsupported YUV colorspace"); goto fault; @@ -653,7 +653,7 @@ static int GLES2_SelectProgram(GLES2_RenderData *data, GLES2_ImageSource source, } else { ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV21_RA; } - shader_params = SDL_GetYCbCRtoRGBConversionMatrix(colorspace); + shader_params = SDL_GetYCbCRtoRGBConversionMatrix(colorspace, 0, 0, 8); if (!shader_params) { SDL_SetError("Unsupported YUV colorspace"); goto fault; @@ -1548,7 +1548,7 @@ static int GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL } SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_U_NUMBER, data->texture_u); - if (!SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace)) { + if (!SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8)) { return SDL_SetError("Unsupported YUV colorspace"); } } else if (data->nv12) { @@ -1573,7 +1573,7 @@ static int GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL } SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_UV_NUMBER, data->texture_u); - if (!SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace)) { + if (!SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8)) { return SDL_SetError("Unsupported YUV colorspace"); } } diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index 5202949ab6..b1b9461952 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -764,88 +764,126 @@ float SDL_PQfromNits(float v) return SDL_powf(num / den, m2); } -const float *SDL_GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace) +/* This is a helpful tool for deriving these: + * https://kdashg.github.io/misc/colors/from-coeffs.html + */ +static const float mat_BT601_Limited_8bit[] = { + -0.0627451017f, -0.501960814f, -0.501960814f, 0.0f, /* offset */ + 1.1644f, 0.0000f, 1.5960f, 0.0f, /* Rcoeff */ + 1.1644f, -0.3918f, -0.8130f, 0.0f, /* Gcoeff */ + 1.1644f, 2.0172f, 0.0000f, 0.0f, /* Bcoeff */ +}; + +static const float mat_BT601_Full_8bit[] = { + 0.0f, -0.501960814f, -0.501960814f, 0.0f, /* offset */ + 1.0000f, 0.0000f, 1.4020f, 0.0f, /* Rcoeff */ + 1.0000f, -0.3441f, -0.7141f, 0.0f, /* Gcoeff */ + 1.0000f, 1.7720f, 0.0000f, 0.0f, /* Bcoeff */ +}; + +static const float mat_BT709_Limited_8bit[] = { + -0.0627451017f, -0.501960814f, -0.501960814f, 0.0f, /* offset */ + 1.1644f, 0.0000f, 1.7927f, 0.0f, /* Rcoeff */ + 1.1644f, -0.2132f, -0.5329f, 0.0f, /* Gcoeff */ + 1.1644f, 2.1124f, 0.0000f, 0.0f, /* Bcoeff */ +}; + +static const float mat_BT709_Full_8bit[] = { + 0.0f, -0.501960814f, -0.501960814f, 0.0f, /* offset */ + 1.0000f, 0.0000f, 1.5748f, 0.0f, /* Rcoeff */ + 1.0000f, -0.1873f, -0.4681f, 0.0f, /* Gcoeff */ + 1.0000f, 1.8556f, 0.0000f, 0.0f, /* Bcoeff */ +}; + +static const float mat_BT2020_Limited_10bit[] = { + -0.062561095f, -0.500488759f, -0.500488759f, 0.0f, /* offset */ + 1.1678f, 0.0000f, 1.6836f, 0.0f, /* Rcoeff */ + 1.1678f, -0.1879f, -0.6523f, 0.0f, /* Gcoeff */ + 1.1678f, 2.1481f, 0.0000f, 0.0f, /* Bcoeff */ +}; + +static const float mat_BT2020_Full_10bit[] = { + 0.0f, -0.500488759f, -0.500488759f, 0.0f, /* offset */ + 1.0000f, 0.0000f, 1.4760f, 0.0f, /* Rcoeff */ + 1.0000f, -0.1647f, -0.5719f, 0.0f, /* Gcoeff */ + 1.0000f, 1.8832f, 0.0000f, 0.0f, /* Bcoeff */ +}; + +static const float *SDL_GetBT601ConversionMatrix( SDL_Colorspace colorspace ) { - /* This is a helpful tool for deriving these: - * https://kdashg.github.io/misc/colors/from-coeffs.html - */ - static const float mat_BT601_Limited_8bit[] = { - -0.0627451017f, -0.501960814f, -0.501960814f, 0.0f, /* offset */ - 1.1644f, 0.0000f, 1.5960f, 0.0f, /* Rcoeff */ - 1.1644f, -0.3918f, -0.8130f, 0.0f, /* Gcoeff */ - 1.1644f, 2.0172f, 0.0000f, 0.0f, /* Bcoeff */ - }; + switch (SDL_COLORSPACERANGE(colorspace)) { + case SDL_COLOR_RANGE_LIMITED: + case SDL_COLOR_RANGE_UNKNOWN: + return mat_BT601_Limited_8bit; + break; + case SDL_COLOR_RANGE_FULL: + return mat_BT601_Full_8bit; + break; + default: + break; + } + return NULL; +} - static const float mat_BT601_Full_8bit[] = { - 0.0f, -0.501960814f, -0.501960814f, 0.0f, /* offset */ - 1.0000f, 0.0000f, 1.4020f, 0.0f, /* Rcoeff */ - 1.0000f, -0.3441f, -0.7141f, 0.0f, /* Gcoeff */ - 1.0000f, 1.7720f, 0.0000f, 0.0f, /* Bcoeff */ - }; +static const float *SDL_GetBT709ConversionMatrix(SDL_Colorspace colorspace) +{ + switch (SDL_COLORSPACERANGE(colorspace)) { + case SDL_COLOR_RANGE_LIMITED: + case SDL_COLOR_RANGE_UNKNOWN: + return mat_BT709_Limited_8bit; + break; + case SDL_COLOR_RANGE_FULL: + return mat_BT709_Full_8bit; + break; + default: + break; + } + return NULL; +} - static const float mat_BT709_Limited_8bit[] = { - -0.0627451017f, -0.501960814f, -0.501960814f, 0.0f, /* offset */ - 1.1644f, 0.0000f, 1.7927f, 0.0f, /* Rcoeff */ - 1.1644f, -0.2132f, -0.5329f, 0.0f, /* Gcoeff */ - 1.1644f, 2.1124f, 0.0000f, 0.0f, /* Bcoeff */ - }; +static const float *SDL_GetBT2020ConversionMatrix(SDL_Colorspace colorspace) +{ + switch (SDL_COLORSPACERANGE(colorspace)) { + case SDL_COLOR_RANGE_LIMITED: + case SDL_COLOR_RANGE_UNKNOWN: + return mat_BT2020_Limited_10bit; + break; + case SDL_COLOR_RANGE_FULL: + return mat_BT2020_Full_10bit; + break; + default: + break; + } + return NULL; +} - static const float mat_BT709_Full_8bit[] = { - 0.0f, -0.501960814f, -0.501960814f, 0.0f, /* offset */ - 1.0000f, 0.0000f, 1.5748f, 0.0f, /* Rcoeff */ - 1.0000f, -0.1873f, -0.4681f, 0.0f, /* Gcoeff */ - 1.0000f, 1.8556f, 0.0000f, 0.0f, /* Bcoeff */ - }; - - static const float mat_BT2020_Limited_10bit[] = { - -0.062561095f, -0.500488759f, -0.500488759f, 0.0f, /* offset */ - 1.1678f, 0.0000f, 1.6836f, 0.0f, /* Rcoeff */ - 1.1678f, -0.1879f, -0.6523f, 0.0f, /* Gcoeff */ - 1.1678f, 2.1481f, 0.0000f, 0.0f, /* Bcoeff */ - }; - - static const float mat_BT2020_Full_10bit[] = { - 0.0f, -0.500488759f, -0.500488759f, 0.0f, /* offset */ - 1.0000f, 0.0000f, 1.4760f, 0.0f, /* Rcoeff */ - 1.0000f, -0.1647f, -0.5719f, 0.0f, /* Gcoeff */ - 1.0000f, 1.8832f, 0.0000f, 0.0f, /* Bcoeff */ - }; +const float *SDL_GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace, int w, int h, int bits_per_pixel) +{ + const int YUV_SD_THRESHOLD = 576; switch (SDL_COLORSPACEMATRIX(colorspace)) { case SDL_MATRIX_COEFFICIENTS_BT601: - switch (SDL_COLORSPACERANGE(colorspace)) { - case SDL_COLOR_RANGE_LIMITED: - return mat_BT601_Limited_8bit; - break; - case SDL_COLOR_RANGE_FULL: - return mat_BT601_Full_8bit; - break; - default: - break; - } - break; + return SDL_GetBT601ConversionMatrix(colorspace); + case SDL_MATRIX_COEFFICIENTS_BT709: - switch (SDL_COLORSPACERANGE(colorspace)) { - case SDL_COLOR_RANGE_LIMITED: - return mat_BT709_Limited_8bit; - break; - case SDL_COLOR_RANGE_FULL: - return mat_BT709_Full_8bit; - break; - default: - break; - } - break; + return SDL_GetBT709ConversionMatrix(colorspace); + /* FIXME: Are these the same? */ case SDL_MATRIX_COEFFICIENTS_BT2020_NCL: case SDL_MATRIX_COEFFICIENTS_BT2020_CL: - switch (SDL_COLORSPACERANGE(colorspace)) { - case SDL_COLOR_RANGE_LIMITED: - return mat_BT2020_Limited_10bit; - break; - case SDL_COLOR_RANGE_FULL: - return mat_BT2020_Full_10bit; - break; + return SDL_GetBT2020ConversionMatrix(colorspace); + + case SDL_MATRIX_COEFFICIENTS_UNSPECIFIED: + switch (bits_per_pixel) { + case 8: + if (h <= YUV_SD_THRESHOLD) { + return SDL_GetBT601ConversionMatrix(colorspace); + } else { + return SDL_GetBT709ConversionMatrix(colorspace); + } + case 10: + case 16: + return SDL_GetBT2020ConversionMatrix(colorspace); default: break; } diff --git a/src/video/SDL_pixels_c.h b/src/video/SDL_pixels_c.h index 8406168aa4..3f98bbaffa 100644 --- a/src/video/SDL_pixels_c.h +++ b/src/video/SDL_pixels_c.h @@ -40,7 +40,7 @@ extern float SDL_sRGBtoNits(float v); extern float SDL_sRGBfromNits(float v); extern float SDL_PQtoNits(float v); extern float SDL_PQfromNits(float v); -extern const float *SDL_GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace); +extern const float *SDL_GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace, int w, int h, int bits_per_pixel); extern const float *SDL_GetColorPrimariesConversionMatrix(SDL_ColorPrimaries src, SDL_ColorPrimaries dst); extern void SDL_ConvertColorPrimaries(float *fR, float *fG, float *fB, const float *matrix);