Added support for other HDR color primaries

Specifically, SDL_COLOR_PRIMARIES_XYZ, SDL_COLOR_PRIMARIES_SMPTE431, and SDL_COLOR_PRIMARIES_SMPTE432
main
Sam Lantinga 2024-01-23 20:15:10 -08:00
parent c5792cc0de
commit 8fe257b541
6 changed files with 77 additions and 25 deletions

View File

@ -262,8 +262,6 @@ extern DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface);
*
* The following properties are understood by SDL:
*
* - `SDL_PROPERTY_SURFACE_HDR_BOOLEAN`: true if this surface has HDR
* properties
* - `SDL_PROPERTY_SURFACE_COLOR_PRIMARIES_NUMBER`: an SDL_ColorPrimaries
* value describing the surface colorspace
* - `SDL_PROPERTY_SURFACE_TRANSFER_CHARACTERISTICS_NUMBER`: an
@ -291,7 +289,6 @@ extern DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface);
*/
extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetSurfaceProperties(SDL_Surface *surface);
#define SDL_PROPERTY_SURFACE_HDR_BOOLEAN "SDL.surface.HDR"
#define SDL_PROPERTY_SURFACE_COLOR_PRIMARIES_NUMBER "SDL.surface.color_primaries"
#define SDL_PROPERTY_SURFACE_TRANSFER_CHARACTERISTICS_NUMBER "SDL.surface.transfer_characteristics"
#define SDL_PROPERTY_SURFACE_MAXCLL_NUMBER "SDL.surface.maxCLL"

View File

@ -187,7 +187,9 @@ static SDL_bool IsSurfaceHDR(SDL_Surface *surface)
{
if (surface->flags & SDL_SURFACE_USES_PROPERTIES) {
SDL_PropertiesID props = SDL_GetSurfaceProperties(surface);
return SDL_GetBooleanProperty(props, SDL_PROPERTY_SURFACE_HDR_BOOLEAN, SDL_FALSE);
if (SDL_GetNumberProperty(props, SDL_PROPERTY_SURFACE_TRANSFER_CHARACTERISTICS_NUMBER, SDL_TRANSFER_CHARACTERISTICS_UNKNOWN) == SDL_TRANSFER_CHARACTERISTICS_SMPTE2084) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
@ -215,8 +217,10 @@ int SDL_CalculateBlit(SDL_Surface *surface)
#endif
map->blit = SDL_SoftBlit;
map->info.src_surface = surface;
map->info.src_fmt = surface->format;
map->info.src_pitch = surface->pitch;
map->info.dst_surface = dst;
map->info.dst_fmt = dst->format;
map->info.dst_pitch = dst->pitch;
@ -255,8 +259,8 @@ int SDL_CalculateBlit(SDL_Surface *surface)
} else {
/* Tone mapping from an HDR surface to SDR surface */
SDL_PropertiesID props = SDL_GetSurfaceProperties(surface);
if (SDL_GetNumberProperty(props, SDL_PROPERTY_SURFACE_COLOR_PRIMARIES_NUMBER, SDL_COLOR_PRIMARIES_BT2020) == SDL_COLOR_PRIMARIES_BT2020 &&
SDL_GetNumberProperty(props, SDL_PROPERTY_SURFACE_TRANSFER_CHARACTERISTICS_NUMBER, SDL_TRANSFER_CHARACTERISTICS_SMPTE2084) == SDL_TRANSFER_CHARACTERISTICS_SMPTE2084) {
SDL_ColorPrimaries primaries = (SDL_ColorPrimaries)SDL_GetNumberProperty(props, SDL_PROPERTY_SURFACE_COLOR_PRIMARIES_NUMBER, SDL_COLOR_PRIMARIES_BT2020);
if (SDL_GetColorPrimariesConversionMatrix(primaries, SDL_COLOR_PRIMARIES_BT709) != NULL) {
if (SDL_ISPIXELFORMAT_10BIT(surface->format->format)) {
blit = SDL_Blit_Slow_PQtoSDR;
} else {

View File

@ -56,10 +56,12 @@ extern Uint8 *SDL_expand_byte[9];
typedef struct
{
SDL_Surface *src_surface;
Uint8 *src;
int src_w, src_h;
int src_pitch;
int src_skip;
SDL_Surface *dst_surface;
Uint8 *dst;
int dst_w, dst_h;
int dst_pitch;

View File

@ -22,6 +22,7 @@
#include "SDL_blit.h"
#include "SDL_blit_slow.h"
#include "SDL_pixels_c.h"
#define FORMAT_ALPHA 0
#define FORMAT_NO_ALPHA (-1)
@ -236,6 +237,18 @@ void SDL_Blit_Slow(SDL_BlitInfo *info)
}
}
static void MatrixMultiply(float v[3], const float *matrix)
{
float out[3];
out[0] = matrix[0 * 3 + 0] * v[0] + matrix[0 * 3 + 1] * v[1] + matrix[0 * 3 + 2] * v[2];
out[1] = matrix[1 * 3 + 0] * v[0] + matrix[1 * 3 + 1] * v[1] + matrix[1 * 3 + 2] * v[2];
out[2] = matrix[2 * 3 + 0] * v[0] + matrix[2 * 3 + 1] * v[1] + matrix[2 * 3 + 2] * v[2];
v[0] = out[0];
v[1] = out[1];
v[2] = out[2];
}
static float PQtoNits(float pq)
{
const float c1 = 0.8359375f;
@ -251,25 +264,9 @@ static float PQtoNits(float pq)
return 10000.0f * SDL_powf(num / den, oo_m1);
}
static void Convert2020to709(float v[3])
{
static const float matrix[3][3] = {
{ 1.660496f, -0.587656f, -0.072840f },
{ -0.124547f, 1.132895f, -0.008348f },
{ -0.018154f, -0.100597f, 1.118751f }
};
float out[3];
out[0] = matrix[0][0] * v[0] + matrix[0][1] * v[1] + matrix[0][2] * v[2];
out[1] = matrix[1][0] * v[0] + matrix[1][1] * v[1] + matrix[1][2] * v[2];
out[2] = matrix[2][0] * v[0] + matrix[2][1] * v[1] + matrix[2][2] * v[2];
v[0] = out[0];
v[1] = out[1];
v[2] = out[2];
}
/* This isn't really a tone mapping algorithm but it generally works well for HDR -> SDR display */
static void PQtoSDR(float floatR, float floatG, float floatB, Uint32 *outR, Uint32 *outG, Uint32 *outB)
static void PQtoSDR(const float *color_primaries_matrix, float floatR, float floatG, float floatB, Uint32 *outR, Uint32 *outG, Uint32 *outB)
{
float v[3];
int i;
@ -278,7 +275,7 @@ static void PQtoSDR(float floatR, float floatG, float floatB, Uint32 *outR, Uint
v[1] = PQtoNits(floatG);
v[2] = PQtoNits(floatB);
Convert2020to709(v);
MatrixMultiply(v, color_primaries_matrix);
for (i = 0; i < SDL_arraysize(v); ++i) {
v[i] /= 400.0f;
@ -313,6 +310,9 @@ void SDL_Blit_Slow_PQtoSDR(SDL_BlitInfo *info)
int dstfmt_val;
Uint32 rgbmask = ~src_fmt->Amask;
Uint32 ckey = info->colorkey & rgbmask;
SDL_PropertiesID props = SDL_GetSurfaceProperties(info->src_surface);
SDL_ColorPrimaries src_primaries = (SDL_ColorPrimaries)SDL_GetNumberProperty(props, SDL_PROPERTY_SURFACE_COLOR_PRIMARIES_NUMBER, SDL_COLOR_PRIMARIES_BT2020);
const float *color_primaries_matrix = SDL_GetColorPrimariesConversionMatrix(src_primaries, SDL_COLOR_PRIMARIES_BT709);
dstfmt_val = detect_format(dst_fmt);
@ -353,7 +353,7 @@ void SDL_Blit_Slow_PQtoSDR(SDL_BlitInfo *info)
break;
}
PQtoSDR(srcFloatR, srcFloatG, srcFloatB, &srcR, &srcG, &srcB);
PQtoSDR(color_primaries_matrix, srcFloatR, srcFloatG, srcFloatB, &srcR, &srcG, &srcB);
srcA = (Uint32)(srcFloatA * 255);
if (flags & SDL_COPY_COLORKEY) {

View File

@ -1173,3 +1173,51 @@ void SDL_FreeBlitMap(SDL_BlitMap *map)
SDL_free(map);
}
}
const float *SDL_GetColorPrimariesConversionMatrix(SDL_ColorPrimaries src, SDL_ColorPrimaries dst)
{
/* Conversion matrices generated using gamescope color helpers and the primaries definitions at:
* https://www.itu.int/rec/T-REC-H.273-201612-S/en
*/
static const float mat2020to709[] = {
1.660496f, -0.587656f, -0.072840f,
-0.124547f, 1.132895f, -0.008348f,
-0.018154f, -0.100597f, 1.118751f
};
static const float matXYZto709[] = {
3.240969f, -1.537383f, -0.498611f,
-0.969243f, 1.875967f, 0.041555f,
0.055630f, -0.203977f, 1.056971f,
};
static const float matSMPTE431to709[] = {
1.120713f, -0.234649f, 0.000000f,
-0.038478f, 1.087034f, 0.000000f,
-0.017967f, -0.082030f, 0.954576f,
};
static const float matSMPTE432to709[] = {
1.224940f, -0.224940f, -0.000000f,
-0.042057f, 1.042057f, 0.000000f,
-0.019638f, -0.078636f, 1.098273f,
};
switch (dst) {
case SDL_COLOR_PRIMARIES_BT709:
switch (src) {
case SDL_COLOR_PRIMARIES_BT2020:
return mat2020to709;
case SDL_COLOR_PRIMARIES_XYZ:
return matXYZto709;
case SDL_COLOR_PRIMARIES_SMPTE431:
return matSMPTE431to709;
case SDL_COLOR_PRIMARIES_SMPTE432:
return matSMPTE432to709;
default:
break;
}
break;
default:
break;
}
return NULL;
}

View File

@ -44,5 +44,6 @@ extern void SDL_InvalidateAllBlitMap(SDL_Surface *surface);
extern void SDL_DitherColors(SDL_Color *colors, int bpp);
extern Uint8 SDL_FindColor(SDL_Palette *pal, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
extern void SDL_DetectPalette(SDL_Palette *pal, SDL_bool *is_opaque, SDL_bool *has_alpha_channel);
extern const float *SDL_GetColorPrimariesConversionMatrix(SDL_ColorPrimaries src, SDL_ColorPrimaries dst);
#endif /* SDL_pixels_c_h_ */