diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index e6b59bd9d8..7c636b4b9c 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -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" diff --git a/src/video/SDL_blit.c b/src/video/SDL_blit.c index 350d95843b..c0c9bc7791 100644 --- a/src/video/SDL_blit.c +++ b/src/video/SDL_blit.c @@ -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 { diff --git a/src/video/SDL_blit.h b/src/video/SDL_blit.h index 9679a8a867..b78ecdd8a2 100644 --- a/src/video/SDL_blit.h +++ b/src/video/SDL_blit.h @@ -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; diff --git a/src/video/SDL_blit_slow.c b/src/video/SDL_blit_slow.c index 5fdfe6f370..016bd936cb 100644 --- a/src/video/SDL_blit_slow.c +++ b/src/video/SDL_blit_slow.c @@ -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) { diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index 5779212e72..ef38104f64 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -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; +} + diff --git a/src/video/SDL_pixels_c.h b/src/video/SDL_pixels_c.h index e0def8aac6..37a7356968 100644 --- a/src/video/SDL_pixels_c.h +++ b/src/video/SDL_pixels_c.h @@ -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_ */