A second take on HDR support with an SDR white point and HDR headroom

This better reflects how HDR content is actually used, e.g. most content is in the SDR range, with specular highlights and bright details beyond the SDR range, in the HDR headroom.

This more closely matches how HDR is handled on Apple platforms, as EDR.

This also greatly simplifies application code which no longer has to think about color scaling. SDR content is rendered at the appropriate brightness automatically, and HDR content is scaled to the correct range for the display HDR headroom.
This commit is contained in:
Sam Lantinga
2024-02-19 08:45:02 -08:00
parent 3b7533f4a2
commit 4ba6aeee9d
24 changed files with 430 additions and 546 deletions

View File

@@ -996,10 +996,10 @@ SDL_DYNAPI_PROC(int,SDL_SetTextureAlphaModFloat,(SDL_Texture *a, float b),(a,b),
SDL_DYNAPI_PROC(int,SDL_GetTextureAlphaModFloat,(SDL_Texture *a, float *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_SetRenderDrawColorFloat,(SDL_Renderer *a, float b, float c, float d, float e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_GetRenderDrawColorFloat,(SDL_Renderer *a, float *b, float *c, float *d, float *e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(int,SDL_ConvertPixelsAndColorspace,(int a, int b, Uint32 c, SDL_Colorspace d, const void *e, int f, Uint32 g, SDL_Colorspace h, void *i, int j),(a,b,c,d,e,f,g,h,i,j),return)
SDL_DYNAPI_PROC(int,SDL_ConvertPixelsAndColorspace,(int a, int b, Uint32 c, SDL_Colorspace d, SDL_PropertiesID e, const void *f, int g, Uint32 h, SDL_Colorspace i, SDL_PropertiesID j, void *k, int l),(a,b,c,d,e,f,g,h,i,j,k,l),return)
SDL_DYNAPI_PROC(int,SDL_SetSurfaceColorspace,(SDL_Surface *a, SDL_Colorspace b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetSurfaceColorspace,(SDL_Surface *a, SDL_Colorspace *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurfaceFormatAndColorspace,(SDL_Surface *a, Uint32 b, SDL_Colorspace c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurfaceFormatAndColorspace,(SDL_Surface *a, Uint32 b, SDL_Colorspace c, SDL_PropertiesID d),(a,b,c,d),return)
SDL_DYNAPI_PROC(int,SDL_CopyProperties,(SDL_PropertiesID a, SDL_PropertiesID b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_SetRenderColorScale,(SDL_Renderer *a, float b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetRenderColorScale,(SDL_Renderer *a, float *b),(a,b),return)

View File

@@ -150,7 +150,7 @@ SDL_bool SDL_RenderingLinearSpace(SDL_Renderer *renderer)
} else {
colorspace = renderer->output_colorspace;
}
if (colorspace == SDL_COLORSPACE_SCRGB) {
if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
return SDL_TRUE;
}
return SDL_FALSE;
@@ -737,6 +737,47 @@ static void UpdateMainViewDimensions(SDL_Renderer *renderer)
}
}
static void UpdateHDRProperties(SDL_Renderer *renderer)
{
SDL_DisplayID displayID = SDL_GetDisplayForWindow(renderer->window);
SDL_PropertiesID display_props;
SDL_PropertiesID renderer_props;
if (!displayID) {
return;
}
display_props = SDL_GetDisplayProperties(displayID);
if (!display_props) {
return;
}
renderer_props = SDL_GetRendererProperties(renderer);
if (!renderer_props) {
return;
}
renderer->color_scale /= renderer->SDR_white_point;
if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
renderer->SDR_white_point = SDL_GetFloatProperty(display_props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, 1.0f);
renderer->HDR_headroom = SDL_GetFloatProperty(display_props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, 1.0f);
} else {
renderer->SDR_white_point = 1.0f;
renderer->HDR_headroom = 1.0f;
}
if (renderer->HDR_headroom > 1.0f) {
SDL_SetBooleanProperty(renderer_props, SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN, SDL_TRUE);
} else {
SDL_SetBooleanProperty(renderer_props, SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN, SDL_FALSE);
}
SDL_SetFloatProperty(renderer_props, SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point);
SDL_SetFloatProperty(renderer_props, SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT, renderer->HDR_headroom);
renderer->color_scale *= renderer->SDR_white_point;
}
static int UpdateLogicalPresentation(SDL_Renderer *renderer);
@@ -792,8 +833,12 @@ static int SDLCALL SDL_RendererEventWatch(void *userdata, SDL_Event *event)
if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN)) {
renderer->hidden = SDL_FALSE;
}
} else if (event->type == SDL_EVENT_WINDOW_DISPLAY_CHANGED) {
UpdateHDRProperties(renderer);
}
}
} else if (event->type == SDL_EVENT_DISPLAY_HDR_STATE_CHANGED) {
UpdateHDRProperties(renderer);
}
return 0;
@@ -987,6 +1032,8 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
renderer->line_method = SDL_GetRenderLineMethod();
renderer->SDR_white_point = 1.0f;
renderer->HDR_headroom = 1.0f;
renderer->color_scale = 1.0f;
if (window) {
@@ -1008,6 +1055,7 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
SDL_SetProperty(new_props, SDL_PROP_RENDERER_SURFACE_POINTER, surface);
}
SDL_SetNumberProperty(new_props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, renderer->output_colorspace);
UpdateHDRProperties(renderer);
SDL_SetProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_RENDERER_POINTER, renderer);
@@ -1186,9 +1234,15 @@ static Uint32 GetClosestSupportedFormat(SDL_Renderer *renderer, Uint32 format)
}
}
} else if (SDL_ISPIXELFORMAT_10BIT(format) || SDL_ISPIXELFORMAT_FLOAT(format)) {
if (SDL_ISPIXELFORMAT_10BIT(format)) {
for (i = 0; i < renderer->info.num_texture_formats; ++i) {
if (SDL_ISPIXELFORMAT_10BIT(renderer->info.texture_formats[i])) {
return renderer->info.texture_formats[i];
}
}
}
for (i = 0; i < renderer->info.num_texture_formats; ++i) {
if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) &&
SDL_ISPIXELFORMAT_FLOAT(renderer->info.texture_formats[i])) {
if (SDL_ISPIXELFORMAT_FLOAT(renderer->info.texture_formats[i])) {
return renderer->info.texture_formats[i];
}
}
@@ -1214,6 +1268,8 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert
int w = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, 0);
int h = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, 0);
SDL_Colorspace default_colorspace;
float SDR_white_point_default = 1.0f;
float HDR_headroom_default = 1.0f;
SDL_bool texture_is_fourcc_and_target;
CHECK_RENDERER_MAGIC(renderer, NULL);
@@ -1271,6 +1327,15 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert
}
renderer->textures = texture;
if (SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) {
SDR_white_point_default = 100.0f;
HDR_headroom_default = 4.0f;
} else if (SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_LINEAR) {
HDR_headroom_default = 0.0f;
}
texture->SDR_white_point = SDL_GetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, SDR_white_point_default);
texture->HDR_headroom = SDL_GetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, HDR_headroom_default);
/* FOURCC format cannot be used directly by renderer back-ends for target texture */
texture_is_fourcc_and_target = (access == SDL_TEXTUREACCESS_TARGET && SDL_ISPIXELFORMAT_FOURCC(format));
@@ -1335,6 +1400,14 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert
}
}
}
/* Now set the properties for the new texture */
props = SDL_GetTextureProperties(texture);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_COLORSPACE_NUMBER, texture->colorspace);
SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_SDR_WHITE_POINT_FLOAT, texture->SDR_white_point);
if (texture->HDR_headroom > 0.0f) {
SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_HDR_HEADROOM_FLOAT, texture->HDR_headroom);
}
return texture;
}
@@ -1359,8 +1432,9 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s
int i;
Uint32 format = SDL_PIXELFORMAT_UNKNOWN;
SDL_Texture *texture;
SDL_PropertiesID props;
SDL_Colorspace colorspace = SDL_COLORSPACE_UNKNOWN;
SDL_PropertiesID surface_props, props;
SDL_Colorspace surface_colorspace = SDL_COLORSPACE_UNKNOWN;
SDL_Colorspace texture_colorspace = SDL_COLORSPACE_UNKNOWN;
CHECK_RENDERER_MAGIC(renderer, NULL);
@@ -1386,9 +1460,10 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s
}
}
if (SDL_GetSurfaceColorspace(surface, &colorspace) < 0) {
if (SDL_GetSurfaceColorspace(surface, &surface_colorspace) < 0) {
return NULL;
}
texture_colorspace = surface_colorspace;
/* Try to have the best pixel format for the texture */
/* No alpha, but a colorkey => promote to alpha */
@@ -1418,6 +1493,16 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s
}
}
/* Look for 10-bit pixel formats if needed */
if (format == SDL_PIXELFORMAT_UNKNOWN && SDL_ISPIXELFORMAT_10BIT(fmt->format)) {
for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) {
if (SDL_ISPIXELFORMAT_10BIT(renderer->info.texture_formats[i])) {
format = renderer->info.texture_formats[i];
break;
}
}
}
/* Look for floating point pixel formats if needed */
if (format == SDL_PIXELFORMAT_UNKNOWN &&
(SDL_ISPIXELFORMAT_10BIT(fmt->format) || SDL_ISPIXELFORMAT_FLOAT(fmt->format))) {
@@ -1455,17 +1540,29 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s
direct_update = SDL_FALSE;
}
if ((SDL_COLORSPACETRANSFER(colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ && !SDL_ISPIXELFORMAT_10BIT(format)) ||
colorspace == SDL_COLORSPACE_SCRGB) {
if ((SDL_COLORSPACETRANSFER(surface_colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ && !SDL_ISPIXELFORMAT_10BIT(format)) ||
surface_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
if (SDL_ISPIXELFORMAT_FLOAT(format)) {
colorspace = SDL_COLORSPACE_SCRGB;
texture_colorspace = SDL_COLORSPACE_SRGB_LINEAR;
} else {
colorspace = SDL_COLORSPACE_SRGB;
texture_colorspace = SDL_COLORSPACE_SRGB;
}
}
if (surface->flags & SDL_SURFACE_USES_PROPERTIES) {
surface_props = SDL_GetSurfaceProperties(surface);
} else {
surface_props = 0;
}
props = SDL_CreateProperties();
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, colorspace);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, texture_colorspace);
if (surface_colorspace == texture_colorspace) {
SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT,
SDL_GetSurfaceSDRWhitePoint(surface, surface_colorspace));
}
SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT,
SDL_GetSurfaceHDRHeadroom(surface, surface_colorspace));
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, surface->w);
@@ -1487,7 +1584,7 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s
SDL_Surface *temp = NULL;
/* Set up a destination surface for the texture update */
temp = SDL_ConvertSurfaceFormatAndColorspace(surface, format, colorspace);
temp = SDL_ConvertSurfaceFormatAndColorspace(surface, format, texture_colorspace, surface_props);
if (temp) {
SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
SDL_DestroySurface(temp);
@@ -2888,7 +2985,7 @@ int SDL_SetRenderColorScale(SDL_Renderer *renderer, float scale)
{
CHECK_RENDERER_MAGIC(renderer, -1);
renderer->color_scale = scale;
renderer->color_scale = scale * renderer->SDR_white_point;
return 0;
}
@@ -2897,7 +2994,7 @@ int SDL_GetRenderColorScale(SDL_Renderer *renderer, float *scale)
CHECK_RENDERER_MAGIC(renderer, -1);
if (scale) {
*scale = renderer->color_scale;
*scale = renderer->color_scale / renderer->SDR_white_point;
}
return 0;
}
@@ -4229,6 +4326,7 @@ int SDL_RenderGeometryRaw(SDL_Renderer *renderer, SDL_Texture *texture, const fl
SDL_Surface *SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
{
SDL_Rect real_rect;
SDL_Surface *surface;
CHECK_RENDERER_MAGIC(renderer, NULL);
@@ -4247,7 +4345,19 @@ SDL_Surface *SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
}
}
return renderer->RenderReadPixels(renderer, &real_rect);
surface = renderer->RenderReadPixels(renderer, &real_rect);
if (surface) {
SDL_PropertiesID props = SDL_GetSurfaceProperties(surface);
if (renderer->target) {
SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->target->SDR_white_point);
SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, renderer->target->HDR_headroom);
} else {
SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point);
SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, renderer->HDR_headroom);
}
}
return surface;
}
static void SDL_RenderApplyWindowShape(SDL_Renderer *renderer)

View File

@@ -64,6 +64,8 @@ struct SDL_Texture
{
const void *magic;
SDL_Colorspace colorspace; /**< The colorspace of the texture */
float SDR_white_point; /**< The SDR white point for this content */
float HDR_headroom; /**< The HDR headroom needed by this content */
Uint32 format; /**< The pixel format of the texture */
int access; /**< SDL_TextureAccess */
int w; /**< The width of the texture */
@@ -254,6 +256,8 @@ struct SDL_Renderer
SDL_Mutex *target_mutex;
SDL_Colorspace output_colorspace;
float SDR_white_point;
float HDR_headroom;
float color_scale;
SDL_FColor color; /**< Color for drawing operations values */

View File

@@ -246,12 +246,12 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 color
case SDL_PIXELFORMAT_RGBA64_FLOAT:
return DXGI_FORMAT_R16G16B16A16_FLOAT;
case SDL_PIXELFORMAT_ARGB8888:
if (colorspace == SDL_COLORSPACE_SCRGB) {
if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
}
return DXGI_FORMAT_B8G8R8A8_UNORM;
case SDL_PIXELFORMAT_XRGB8888:
if (colorspace == SDL_COLORSPACE_SCRGB) {
if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
}
return DXGI_FORMAT_B8G8R8X8_UNORM;
@@ -276,12 +276,12 @@ static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uin
case SDL_PIXELFORMAT_RGBA64_FLOAT:
return DXGI_FORMAT_R16G16B16A16_FLOAT;
case SDL_PIXELFORMAT_ARGB8888:
if (colorspace == SDL_COLORSPACE_SCRGB) {
if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
}
return DXGI_FORMAT_B8G8R8A8_UNORM;
case SDL_PIXELFORMAT_XRGB8888:
if (colorspace == SDL_COLORSPACE_SCRGB) {
if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
}
return DXGI_FORMAT_B8G8R8X8_UNORM;
@@ -880,7 +880,7 @@ static HRESULT D3D11_CreateSwapChain(SDL_Renderer *renderer, int w, int h)
swapChainDesc.Width = w;
swapChainDesc.Height = h;
switch (renderer->output_colorspace) {
case SDL_COLORSPACE_SCRGB:
case SDL_COLORSPACE_SRGB_LINEAR:
swapChainDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
break;
case SDL_COLORSPACE_HDR10:
@@ -979,7 +979,7 @@ static HRESULT D3D11_CreateSwapChain(SDL_Renderer *renderer, int w, int h)
UINT colorspace_support = 0;
DXGI_COLOR_SPACE_TYPE colorspace;
switch (renderer->output_colorspace) {
case SDL_COLORSPACE_SCRGB:
case SDL_COLORSPACE_SRGB_LINEAR:
colorspace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
break;
case SDL_COLORSPACE_HDR10:
@@ -2699,7 +2699,7 @@ SDL_Renderer *D3D11_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_p
SDL_SetupRendererColorspace(renderer, create_props);
if (renderer->output_colorspace != SDL_COLORSPACE_SRGB &&
renderer->output_colorspace != SDL_COLORSPACE_SCRGB
renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR
/*&& renderer->output_colorspace != SDL_COLORSPACE_HDR10*/) {
SDL_SetError("Unsupported output colorspace");
SDL_free(renderer);

View File

@@ -310,12 +310,12 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 color
case SDL_PIXELFORMAT_RGBA64_FLOAT:
return DXGI_FORMAT_R16G16B16A16_FLOAT;
case SDL_PIXELFORMAT_ARGB8888:
if (colorspace == SDL_COLORSPACE_SCRGB) {
if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
}
return DXGI_FORMAT_B8G8R8A8_UNORM;
case SDL_PIXELFORMAT_XRGB8888:
if (colorspace == SDL_COLORSPACE_SCRGB) {
if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
}
return DXGI_FORMAT_B8G8R8X8_UNORM;
@@ -340,12 +340,12 @@ static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uin
case SDL_PIXELFORMAT_RGBA64_FLOAT:
return DXGI_FORMAT_R16G16B16A16_FLOAT;
case SDL_PIXELFORMAT_ARGB8888:
if (colorspace == SDL_COLORSPACE_SCRGB) {
if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
}
return DXGI_FORMAT_B8G8R8A8_UNORM;
case SDL_PIXELFORMAT_XRGB8888:
if (colorspace == SDL_COLORSPACE_SCRGB) {
if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;
}
return DXGI_FORMAT_B8G8R8X8_UNORM;
@@ -1218,7 +1218,7 @@ static HRESULT D3D12_CreateSwapChain(SDL_Renderer *renderer, int w, int h)
swapChainDesc.Width = w;
swapChainDesc.Height = h;
switch (renderer->output_colorspace) {
case SDL_COLORSPACE_SCRGB:
case SDL_COLORSPACE_SRGB_LINEAR:
swapChainDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
data->renderTargetFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
break;
@@ -1282,7 +1282,7 @@ static HRESULT D3D12_CreateSwapChain(SDL_Renderer *renderer, int w, int h)
UINT colorspace_support = 0;
DXGI_COLOR_SPACE_TYPE colorspace;
switch (renderer->output_colorspace) {
case SDL_COLORSPACE_SCRGB:
case SDL_COLORSPACE_SRGB_LINEAR:
colorspace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
break;
case SDL_COLORSPACE_HDR10:
@@ -2985,11 +2985,7 @@ static SDL_Surface *D3D12_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rec
pitchedDesc.Width = (UINT)textureDesc.Width;
pitchedDesc.Height = textureDesc.Height;
pitchedDesc.Depth = 1;
if (pitchedDesc.Format == DXGI_FORMAT_R8_UNORM) {
bpp = 1;
} else {
bpp = 4;
}
bpp = SDL_BYTESPERPIXEL(D3D12_DXGIFormatToSDLPixelFormat(pitchedDesc.Format));
pitchedDesc.RowPitch = D3D12_Align(pitchedDesc.Width * bpp, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
SDL_zero(placedTextureDesc);
@@ -3155,7 +3151,7 @@ SDL_Renderer *D3D12_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_p
SDL_SetupRendererColorspace(renderer, create_props);
if (renderer->output_colorspace != SDL_COLORSPACE_SRGB &&
renderer->output_colorspace != SDL_COLORSPACE_SCRGB
renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR
/*&& renderer->output_colorspace != SDL_COLORSPACE_HDR10*/) {
SDL_SetError("Unsupported output colorspace");
SDL_free(renderer);

View File

@@ -652,14 +652,14 @@ static int METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL
switch (texture->format) {
case SDL_PIXELFORMAT_ABGR8888:
if (renderer->output_colorspace == SDL_COLORSPACE_SCRGB) {
if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
pixfmt = MTLPixelFormatRGBA8Unorm_sRGB;
} else {
pixfmt = MTLPixelFormatRGBA8Unorm;
}
break;
case SDL_PIXELFORMAT_ARGB8888:
if (renderer->output_colorspace == SDL_COLORSPACE_SCRGB) {
if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
pixfmt = MTLPixelFormatBGRA8Unorm_sRGB;
} else {
pixfmt = MTLPixelFormatBGRA8Unorm;
@@ -1895,7 +1895,7 @@ static SDL_Renderer *METAL_CreateRenderer(SDL_Window *window, SDL_PropertiesID c
}
#endif
if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) {
if (renderer->output_colorspace == SDL_COLORSPACE_SCRGB && scRGB_supported) {
if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR && scRGB_supported) {
/* This colorspace is supported */
} else {
SDL_SetError("Unsupported output colorspace");
@@ -1963,7 +1963,7 @@ static SDL_Renderer *METAL_CreateRenderer(SDL_Window *window, SDL_PropertiesID c
#endif
#ifndef SDL_PLATFORM_TVOS
if (renderer->output_colorspace == SDL_COLORSPACE_SCRGB) {
if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
if (@available(macos 10.11, iOS 16.0, *)) {
layer.wantsExtendedDynamicRangeContent = YES;
} else {

View File

@@ -370,7 +370,7 @@ static Uint16 float_to_half(float a)
return ir;
}
static void ReadFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelFormat *fmt, SDL_Colorspace colorspace,
static void ReadFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelFormat *fmt, SDL_Colorspace colorspace, float SDR_white_point,
float *outR, float *outG, float *outB, float *outA)
{
Uint32 pixel;
@@ -500,20 +500,19 @@ static void ReadFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelF
/* Convert to nits so src and dst are guaranteed to be linear and in the same units */
switch (SDL_COLORSPACETRANSFER(colorspace)) {
case SDL_TRANSFER_CHARACTERISTICS_SRGB:
fR = SDL_sRGBtoNits(fR);
fG = SDL_sRGBtoNits(fG);
fB = SDL_sRGBtoNits(fB);
fR = SDL_sRGBtoLinear(fR);
fG = SDL_sRGBtoLinear(fG);
fB = SDL_sRGBtoLinear(fB);
break;
case SDL_TRANSFER_CHARACTERISTICS_PQ:
fR = SDL_PQtoNits(fR);
fG = SDL_PQtoNits(fG);
fB = SDL_PQtoNits(fB);
fR = SDL_PQtoNits(fR) / SDR_white_point;
fG = SDL_PQtoNits(fG) / SDR_white_point;
fB = SDL_PQtoNits(fB) / SDR_white_point;
break;
case SDL_TRANSFER_CHARACTERISTICS_LINEAR:
/* Assuming scRGB for now */
fR = SDL_scRGBtoNits(fR);
fG = SDL_scRGBtoNits(fG);
fB = SDL_scRGBtoNits(fB);
fR /= SDR_white_point;
fG /= SDR_white_point;
fB /= SDR_white_point;
break;
default:
/* Unknown, leave it alone */
@@ -526,7 +525,7 @@ static void ReadFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelF
*outA = fA;
}
static void WriteFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelFormat *fmt, SDL_Colorspace colorspace,
static void WriteFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelFormat *fmt, SDL_Colorspace colorspace, float SDR_white_point,
float fR, float fG, float fB, float fA)
{
Uint32 R, G, B, A;
@@ -535,20 +534,19 @@ static void WriteFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_Pixel
/* We converted to nits so src and dst are guaranteed to be linear and in the same units */
switch (SDL_COLORSPACETRANSFER(colorspace)) {
case SDL_TRANSFER_CHARACTERISTICS_SRGB:
fR = SDL_sRGBfromNits(fR);
fG = SDL_sRGBfromNits(fG);
fB = SDL_sRGBfromNits(fB);
fR = SDL_sRGBfromLinear(fR);
fG = SDL_sRGBfromLinear(fG);
fB = SDL_sRGBfromLinear(fB);
break;
case SDL_TRANSFER_CHARACTERISTICS_PQ:
fR = SDL_PQfromNits(fR);
fG = SDL_PQfromNits(fG);
fB = SDL_PQfromNits(fB);
fR = SDL_PQfromNits(fR * SDR_white_point);
fG = SDL_PQfromNits(fG * SDR_white_point);
fB = SDL_PQfromNits(fB * SDR_white_point);
break;
case SDL_TRANSFER_CHARACTERISTICS_LINEAR:
/* Assuming scRGB for now */
fR = SDL_scRGBfromNits(fR);
fG = SDL_scRGBfromNits(fG);
fB = SDL_scRGBfromNits(fB);
fR *= SDR_white_point;
fG *= SDR_white_point;
fB *= SDR_white_point;
break;
default:
/* Unknown, leave it alone */
@@ -672,6 +670,7 @@ typedef enum
{
SDL_TONEMAP_NONE,
SDL_TONEMAP_LINEAR,
SDL_TONEMAP_PIECEWISE_HDR,
} SDL_TonemapOperator;
typedef struct
@@ -682,6 +681,11 @@ typedef struct
struct {
float scale;
} linear;
struct
{
float scale;
} piecewise_HDR;
} data;
} SDL_TonemapContext;
@@ -694,6 +698,11 @@ static void ApplyTonemap(SDL_TonemapContext *ctx, float *r, float *g, float *b)
*g *= ctx->data.linear.scale;
*b *= ctx->data.linear.scale;
break;
case SDL_TONEMAP_PIECEWISE_HDR:
*r = (*r <= 1.0f) ? *r : (1.0f + (*r - 1.0f) * ctx->data.piecewise_HDR.scale);
*g = (*g <= 1.0f) ? *g : (1.0f + (*g - 1.0f) * ctx->data.piecewise_HDR.scale);
*b = (*r <= 1.0f) ? *b : (1.0f + (*b - 1.0f) * ctx->data.piecewise_HDR.scale);
break;
default:
break;
}
@@ -701,7 +710,7 @@ static void ApplyTonemap(SDL_TonemapContext *ctx, float *r, float *g, float *b)
static SDL_bool IsHDRColorspace(SDL_Colorspace colorspace)
{
if (colorspace == SDL_COLORSPACE_SCRGB ||
if (colorspace == SDL_COLORSPACE_SRGB_LINEAR ||
SDL_COLORSPACETRANSFER(colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) {
return SDL_TRUE;
}
@@ -732,6 +741,8 @@ void SDL_Blit_Slow_Float(SDL_BlitInfo *info)
SDL_Colorspace src_colorspace;
SDL_Colorspace dst_colorspace;
const float *color_primaries_matrix = NULL;
float SDR_white_point_src = 1.0f;
float SDR_white_point_dst = 1.0f;
SDL_TonemapContext tonemap;
if (SDL_GetSurfaceColorspace(info->src_surface, &src_colorspace) < 0 ||
@@ -756,6 +767,9 @@ void SDL_Blit_Slow_Float(SDL_BlitInfo *info)
}
}
SDR_white_point_src = SDL_GetSurfaceSDRWhitePoint(info->src_surface, src_colorspace);
SDR_white_point_dst = SDL_GetSurfaceSDRWhitePoint(info->dst_surface, dst_colorspace);
src_access = GetPixelAccessMethod(src_fmt);
dst_access = GetPixelAccessMethod(dst_fmt);
@@ -773,7 +787,7 @@ void SDL_Blit_Slow_Float(SDL_BlitInfo *info)
srcx = posx >> 16;
src = (info->src + (srcy * info->src_pitch) + (srcx * srcbpp));
ReadFloatPixel(src, src_access, src_fmt, src_colorspace, &srcR, &srcG, &srcB, &srcA);
ReadFloatPixel(src, src_access, src_fmt, src_colorspace, SDR_white_point_src, &srcR, &srcG, &srcB, &srcA);
if (color_primaries_matrix) {
SDL_ConvertColorPrimaries(&srcR, &srcG, &srcB, color_primaries_matrix);
@@ -787,7 +801,7 @@ void SDL_Blit_Slow_Float(SDL_BlitInfo *info)
/* colorkey isn't supported */
}
if ((flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL))) {
ReadFloatPixel(dst, dst_access, dst_fmt, dst_colorspace, &dstR, &dstG, &dstB, &dstA);
ReadFloatPixel(dst, dst_access, dst_fmt, dst_colorspace, SDR_white_point_dst, &dstR, &dstG, &dstB, &dstA);
} else {
/* don't care */
dstR = dstG = dstB = dstA = 0.0f;
@@ -839,7 +853,7 @@ void SDL_Blit_Slow_Float(SDL_BlitInfo *info)
break;
}
WriteFloatPixel(dst, dst_access, dst_fmt, dst_colorspace, dstR, dstG, dstB, dstA);
WriteFloatPixel(dst, dst_access, dst_fmt, dst_colorspace, SDR_white_point_dst, dstR, dstG, dstB, dstA);
posx += incx;
dst += dstbpp;

View File

@@ -706,7 +706,7 @@ SDL_Colorspace SDL_GetDefaultColorspaceForFormat(Uint32 format)
if (SDL_ISPIXELFORMAT_FOURCC(format)) {
return SDL_COLORSPACE_YUV_DEFAULT;
} else if (SDL_ISPIXELFORMAT_FLOAT(format)) {
return SDL_COLORSPACE_SCRGB;
return SDL_COLORSPACE_SRGB_LINEAR;
} else if (SDL_ISPIXELFORMAT_10BIT(format)) {
return SDL_COLORSPACE_HDR10;
} else {
@@ -714,30 +714,18 @@ SDL_Colorspace SDL_GetDefaultColorspaceForFormat(Uint32 format)
}
}
float SDL_scRGBtoNits(float v)
{
return v * 80.0f;
}
float SDL_scRGBfromNits(float v)
{
return v / 80.0f;
}
float SDL_sRGBtoNits(float v)
float SDL_sRGBtoLinear(float v)
{
if (v <= 0.04045f) {
v = (v / 12.92f);
} else {
v = SDL_powf((v + 0.055f) / 1.055f, 2.4f);
}
return SDL_scRGBtoNits(v);
return v;
}
float SDL_sRGBfromNits(float v)
float SDL_sRGBfromLinear(float v)
{
v = SDL_scRGBfromNits(v);
if (v <= 0.0031308f) {
v = (v * 12.92f);
} else {

View File

@@ -34,10 +34,8 @@ extern int SDL_CalculateSize(Uint32 format, int width, int height, size_t *size,
extern SDL_Colorspace SDL_GetDefaultColorspaceForFormat(Uint32 pixel_format);
/* Colorspace conversion functions */
extern float SDL_scRGBtoNits(float v);
extern float SDL_scRGBfromNits(float v);
extern float SDL_sRGBtoNits(float v);
extern float SDL_sRGBfromNits(float v);
extern float SDL_sRGBtoLinear(float v);
extern float SDL_sRGBfromLinear(float v);
extern float SDL_PQtoNits(float v);
extern float SDL_PQfromNits(float v);
extern const float *SDL_GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace, int w, int h, int bits_per_pixel);
@@ -52,6 +50,10 @@ extern void SDL_FreeBlitMap(SDL_BlitMap *map);
extern void SDL_InvalidateAllBlitMap(SDL_Surface *surface);
/* Surface functions */
extern float SDL_GetSurfaceSDRWhitePoint(SDL_Surface *surface, SDL_Colorspace colorspace);
extern float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colorspace);
/* Miscellaneous functions */
extern void SDL_DitherColors(SDL_Color *colors, int bpp);
extern Uint8 SDL_FindColor(SDL_Palette *pal, Uint8 r, Uint8 g, Uint8 b, Uint8 a);

View File

@@ -303,6 +303,52 @@ int SDL_GetSurfaceColorspace(SDL_Surface *surface, SDL_Colorspace *colorspace)
return 0;
}
float SDL_GetSurfaceSDRWhitePoint(SDL_Surface *surface, SDL_Colorspace colorspace)
{
SDL_TransferCharacteristics transfer = SDL_COLORSPACETRANSFER(colorspace);
if (transfer == SDL_TRANSFER_CHARACTERISTICS_LINEAR ||
transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) {
SDL_PropertiesID props;
float default_value = 1.0f;
if (surface->flags & SDL_SURFACE_USES_PROPERTIES) {
props = SDL_GetSurfaceProperties(surface);
} else {
props = 0;
}
if (transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) {
const float DEFAULT_PQ_SDR_WHITE_POINT = 100.0f;
default_value = DEFAULT_PQ_SDR_WHITE_POINT;
}
return SDL_GetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, default_value);
}
return 1.0f;
}
float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colorspace)
{
SDL_TransferCharacteristics transfer = SDL_COLORSPACETRANSFER(colorspace);
if (transfer == SDL_TRANSFER_CHARACTERISTICS_LINEAR ||
transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) {
SDL_PropertiesID props;
float default_value = 0.0f;
if (surface->flags & SDL_SURFACE_USES_PROPERTIES) {
props = SDL_GetSurfaceProperties(surface);
} else {
props = 0;
}
if (transfer == SDL_TRANSFER_CHARACTERISTICS_PQ &&
SDL_HasProperty(props, SDL_PROP_SURFACE_MAXCLL_NUMBER)) {
default_value = (float)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_MAXCLL_NUMBER, 0) / SDL_GetSurfaceSDRWhitePoint(surface, colorspace);
}
return SDL_GetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, default_value);
}
return 1.0f;
}
int SDL_SetSurfacePalette(SDL_Surface *surface, SDL_Palette *palette)
{
if (!surface) {
@@ -1193,10 +1239,11 @@ int SDL_FlipSurface(SDL_Surface *surface, SDL_FlipMode flip)
}
}
static SDL_Surface *SDL_ConvertSurfaceWithPixelFormatAndColorspace(SDL_Surface *surface, const SDL_PixelFormat *format, Uint32 colorspace)
static SDL_Surface *SDL_ConvertSurfaceWithPixelFormatAndColorspace(SDL_Surface *surface, const SDL_PixelFormat *format, Uint32 colorspace, SDL_PropertiesID props)
{
SDL_Surface *convert;
SDL_Colorspace src_colorspace;
SDL_PropertiesID src_properties;
Uint32 copy_flags;
SDL_Color copy_color;
SDL_Rect bounds;
@@ -1234,6 +1281,12 @@ static SDL_Surface *SDL_ConvertSurfaceWithPixelFormatAndColorspace(SDL_Surface *
return NULL;
}
if (surface->flags & SDL_SURFACE_USES_PROPERTIES) {
src_properties = SDL_GetSurfaceProperties(surface);
} else {
src_properties = 0;
}
/* Create a new surface with the desired format */
convert = SDL_CreateSurface(surface->w, surface->h, format->format);
if (!convert) {
@@ -1246,7 +1299,7 @@ static SDL_Surface *SDL_ConvertSurfaceWithPixelFormatAndColorspace(SDL_Surface *
SDL_SetSurfaceColorspace(convert, colorspace);
if (SDL_ISPIXELFORMAT_FOURCC(format->format) || SDL_ISPIXELFORMAT_FOURCC(surface->format->format)) {
if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, surface->format->format, src_colorspace, surface->pixels, surface->pitch, convert->format->format, colorspace, convert->pixels, convert->pitch) < 0) {
if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, surface->format->format, src_colorspace, src_properties, surface->pixels, surface->pitch, convert->format->format, colorspace, props, convert->pixels, convert->pitch) < 0) {
SDL_DestroySurface(convert);
return NULL;
}
@@ -1411,7 +1464,7 @@ static SDL_Surface *SDL_ConvertSurfaceWithPixelFormatAndColorspace(SDL_Surface *
tmp->map->info.flags &= ~SDL_COPY_COLORKEY;
/* Conversion of the colorkey */
tmp2 = SDL_ConvertSurfaceWithPixelFormatAndColorspace(tmp, format, colorspace);
tmp2 = SDL_ConvertSurfaceWithPixelFormatAndColorspace(tmp, format, colorspace, props);
if (!tmp2) {
SDL_DestroySurface(tmp);
SDL_DestroySurface(convert);
@@ -1460,7 +1513,7 @@ SDL_Surface *SDL_DuplicateSurface(SDL_Surface *surface)
return NULL;
}
return SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, surface->format, SDL_COLORSPACE_UNKNOWN);
return SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, surface->format, SDL_COLORSPACE_UNKNOWN, 0);
}
SDL_Surface *SDL_ConvertSurface(SDL_Surface *surface, const SDL_PixelFormat *format)
@@ -1479,12 +1532,13 @@ SDL_Surface *SDL_ConvertSurface(SDL_Surface *surface, const SDL_PixelFormat *for
colorspace = SDL_GetDefaultColorspaceForFormat(format->format);
return SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, format, colorspace);
return SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, format, colorspace, 0);
}
SDL_Surface *SDL_ConvertSurfaceFormat(SDL_Surface *surface, Uint32 pixel_format)
{
SDL_Colorspace colorspace;
SDL_PropertiesID props;
if (!surface) {
SDL_InvalidParamError("surface");
@@ -1493,17 +1547,23 @@ SDL_Surface *SDL_ConvertSurfaceFormat(SDL_Surface *surface, Uint32 pixel_format)
colorspace = SDL_GetDefaultColorspaceForFormat(pixel_format);
return SDL_ConvertSurfaceFormatAndColorspace(surface, pixel_format, colorspace);
if (surface->flags & SDL_SURFACE_USES_PROPERTIES) {
props = SDL_GetSurfaceProperties(surface);
} else {
props = 0;
}
return SDL_ConvertSurfaceFormatAndColorspace(surface, pixel_format, colorspace, props);
}
SDL_Surface *SDL_ConvertSurfaceFormatAndColorspace(SDL_Surface *surface, Uint32 pixel_format, SDL_Colorspace colorspace)
SDL_Surface *SDL_ConvertSurfaceFormatAndColorspace(SDL_Surface *surface, Uint32 pixel_format, SDL_Colorspace colorspace, SDL_PropertiesID props)
{
SDL_PixelFormat *format;
SDL_Surface *convert = NULL;
format = SDL_CreatePixelFormat(pixel_format);
if (format) {
convert = SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, format, colorspace);
convert = SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, format, colorspace, props);
SDL_DestroyPixelFormat(format);
}
return convert;
@@ -1512,7 +1572,7 @@ SDL_Surface *SDL_ConvertSurfaceFormatAndColorspace(SDL_Surface *surface, Uint32
/*
* Create a surface on the stack for quick blit operations
*/
static SDL_bool SDL_CreateSurfaceOnStack(int width, int height, Uint32 pixel_format, SDL_Colorspace colorspace, void *pixels, int pitch, SDL_Surface *surface, SDL_PixelFormat *format, SDL_BlitMap *blitmap)
static SDL_bool SDL_CreateSurfaceOnStack(int width, int height, Uint32 pixel_format, SDL_Colorspace colorspace, SDL_PropertiesID props, void *pixels, int pitch, SDL_Surface *surface, SDL_PixelFormat *format, SDL_BlitMap *blitmap)
{
if (SDL_ISPIXELFORMAT_INDEXED(pixel_format)) {
SDL_SetError("Indexed pixel formats not supported");
@@ -1542,6 +1602,13 @@ static SDL_bool SDL_CreateSurfaceOnStack(int width, int height, Uint32 pixel_for
SDL_SetSurfaceColorspace(surface, colorspace);
if (props) {
SDL_PropertiesID surface_props = SDL_GetSurfaceProperties(surface);
if (SDL_CopyProperties(props, surface_props) < 0) {
return SDL_FALSE;
}
}
/* The surface is ready to go */
surface->refcount = 1;
return SDL_TRUE;
@@ -1577,8 +1644,8 @@ SDL_Surface *SDL_DuplicatePixels(int width, int height, Uint32 format, SDL_Color
}
int SDL_ConvertPixelsAndColorspace(int width, int height,
Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch,
Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch)
Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch,
Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch)
{
SDL_Surface src_surface, dst_surface;
SDL_PixelFormat src_fmt, dst_fmt;
@@ -1609,11 +1676,11 @@ int SDL_ConvertPixelsAndColorspace(int width, int height,
#if SDL_HAVE_YUV
if (SDL_ISPIXELFORMAT_FOURCC(src_format) && SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src_colorspace, src, src_pitch, dst_format, dst_colorspace, dst, dst_pitch);
return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
} else if (SDL_ISPIXELFORMAT_FOURCC(src_format)) {
return SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src, src_pitch, dst_format, dst_colorspace, dst, dst_pitch);
return SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
} else if (SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
return SDL_ConvertPixels_RGB_to_YUV(width, height, src_format, src_colorspace, src, src_pitch, dst_format, dst_colorspace, dst, dst_pitch);
return SDL_ConvertPixels_RGB_to_YUV(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
}
#else
if (SDL_ISPIXELFORMAT_FOURCC(src_format) || SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
@@ -1634,11 +1701,11 @@ int SDL_ConvertPixelsAndColorspace(int width, int height,
return 0;
}
if (!SDL_CreateSurfaceOnStack(width, height, src_format, src_colorspace, nonconst_src, src_pitch, &src_surface, &src_fmt, &src_blitmap)) {
if (!SDL_CreateSurfaceOnStack(width, height, src_format, src_colorspace, src_properties, nonconst_src, src_pitch, &src_surface, &src_fmt, &src_blitmap)) {
return -1;
}
if (!SDL_CreateSurfaceOnStack(width, height, dst_format, dst_colorspace, dst, dst_pitch, &dst_surface, &dst_fmt, &dst_blitmap)) {
if (!SDL_CreateSurfaceOnStack(width, height, dst_format, dst_colorspace, dst_properties, dst, dst_pitch, &dst_surface, &dst_fmt, &dst_blitmap)) {
return -1;
}
@@ -1660,8 +1727,8 @@ int SDL_ConvertPixels(int width, int height,
Uint32 dst_format, void *dst, int dst_pitch)
{
return SDL_ConvertPixelsAndColorspace(width, height,
src_format, SDL_COLORSPACE_UNKNOWN, src, src_pitch,
dst_format, SDL_COLORSPACE_UNKNOWN, dst, dst_pitch);
src_format, SDL_COLORSPACE_UNKNOWN, 0, src, src_pitch,
dst_format, SDL_COLORSPACE_UNKNOWN, 0, dst, dst_pitch);
}
/*
@@ -1777,7 +1844,7 @@ int SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g,
SDL_Colorspace colorspace;
if (SDL_GetSurfaceColorspace(surface, &colorspace) == 0 &&
SDL_ConvertPixelsAndColorspace(1, 1, surface->format->format, colorspace, p, surface->pitch, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, rgba, sizeof(rgba)) == 0) {
SDL_ConvertPixelsAndColorspace(1, 1, surface->format->format, colorspace, SDL_GetSurfaceProperties(surface), p, surface->pitch, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, rgba, sizeof(rgba)) == 0) {
*r = rgba[0];
*g = rgba[1];
*b = rgba[2];

View File

@@ -120,9 +120,8 @@ struct SDL_Window
typedef struct
{
SDL_bool enabled;
float SDR_whitelevel;
float HDR_whitelevel;
float SDR_white_point;
float HDR_headroom;
} SDL_HDRDisplayProperties;
/*

View File

@@ -701,14 +701,20 @@ SDL_DisplayID SDL_AddVideoDisplay(const SDL_VideoDisplay *display, SDL_bool send
props = SDL_GetDisplayProperties(id);
if (display->HDR.enabled) {
if (display->HDR.HDR_headroom > 1.0f) {
SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_TRUE);
} else {
SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE);
}
if (display->HDR.SDR_whitelevel != 0.0f) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, display->HDR.SDR_whitelevel);
if (display->HDR.SDR_white_point <= 1.0f) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, 1.0f);
} else {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, display->HDR.SDR_white_point);
}
if (display->HDR.HDR_whitelevel != 0.0f) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, display->HDR.HDR_whitelevel);
if (display->HDR.HDR_headroom <= 1.0f) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, 1.0f);
} else {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, display->HDR.HDR_headroom);
}
return id;
@@ -979,22 +985,32 @@ void SDL_SetDisplayHDRProperties(SDL_VideoDisplay *display, const SDL_HDRDisplay
SDL_PropertiesID props = SDL_GetDisplayProperties(display->id);
SDL_bool changed = SDL_FALSE;
if (HDR->enabled != display->HDR.enabled) {
SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, HDR->enabled);
if (HDR->SDR_white_point != display->HDR.SDR_white_point) {
if (HDR->SDR_white_point <= 1.0f) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, 1.0f);
} else {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, HDR->SDR_white_point);
}
changed = SDL_TRUE;
}
if (HDR->SDR_whitelevel != display->HDR.SDR_whitelevel) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, HDR->SDR_whitelevel);
changed = SDL_TRUE;
}
if (HDR->HDR_whitelevel != display->HDR.HDR_whitelevel) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, HDR->HDR_whitelevel);
if (HDR->HDR_headroom != display->HDR.HDR_headroom) {
if (HDR->HDR_headroom > 1.0f) {
SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_TRUE);
} else {
SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE);
}
if (HDR->HDR_headroom <= 1.0f) {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, 1.0f);
} else {
SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, HDR->HDR_headroom);
}
changed = SDL_TRUE;
}
SDL_copyp(&display->HDR, HDR);
if (changed) {
SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_HDR_STATE_CHANGED, HDR->enabled);
SDL_bool enabled = SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE);
SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_HDR_STATE_CHANGED, enabled);
}
}

View File

@@ -555,8 +555,8 @@ static SDL_bool yuv_rgb_std(
}
int SDL_ConvertPixels_YUV_to_RGB(int width, int height,
Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch,
Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch)
Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch,
Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch)
{
const Uint8 *y = NULL;
const Uint8 *u = NULL;
@@ -597,14 +597,14 @@ int SDL_ConvertPixels_YUV_to_RGB(int width, int height,
}
/* convert src/src_format to tmp/ARGB8888 */
ret = SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, SDL_COLORSPACE_SRGB, tmp, tmp_pitch);
ret = SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, SDL_COLORSPACE_SRGB, 0, tmp, tmp_pitch);
if (ret < 0) {
SDL_free(tmp);
return ret;
}
/* convert tmp/ARGB8888 to dst/RGB */
ret = SDL_ConvertPixelsAndColorspace(width, height, SDL_PIXELFORMAT_ARGB8888, SDL_COLORSPACE_SRGB, tmp, tmp_pitch, dst_format, dst_colorspace, dst, dst_pitch);
ret = SDL_ConvertPixelsAndColorspace(width, height, SDL_PIXELFORMAT_ARGB8888, SDL_COLORSPACE_SRGB, 0, tmp, tmp_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
SDL_free(tmp);
return ret;
}
@@ -935,8 +935,8 @@ static int SDL_ConvertPixels_ARGB8888_to_YUV(int width, int height, const void *
}
int SDL_ConvertPixels_RGB_to_YUV(int width, int height,
Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch,
Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch)
Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch,
Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch)
{
YCbCrType yuv_type = YCBCR_601;
@@ -2323,8 +2323,8 @@ static int SDL_ConvertPixels_Packed4_to_Planar2x2(int width, int height,
#endif /* SDL_HAVE_YUV */
int SDL_ConvertPixels_YUV_to_YUV(int width, int height,
Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch,
Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch)
Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch,
Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch)
{
#if SDL_HAVE_YUV
if (src_colorspace != dst_colorspace) {

View File

@@ -26,9 +26,9 @@
/* YUV conversion functions */
extern int SDL_ConvertPixels_YUV_to_RGB(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch);
extern int SDL_ConvertPixels_RGB_to_YUV(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch);
extern int SDL_ConvertPixels_YUV_to_YUV(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch);
extern int SDL_ConvertPixels_YUV_to_RGB(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch);
extern int SDL_ConvertPixels_RGB_to_YUV(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch);
extern int SDL_ConvertPixels_YUV_to_YUV(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch);
extern int SDL_CalculateYUVSize(Uint32 format, int w, int h, size_t *size, size_t *pitch);

View File

@@ -292,17 +292,14 @@ static char *Cocoa_GetDisplayName(CGDirectDisplayID displayID)
static void Cocoa_GetHDRProperties(CGDirectDisplayID displayID, SDL_HDRDisplayProperties *HDR)
{
HDR->enabled = SDL_FALSE;
HDR->SDR_whitelevel = 0.0f;
HDR->SDR_white_point = 1.0f;
HDR->HDR_headroom = 1.0f;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 /* Added in the 10.15 SDK */
if (@available(macOS 10.15, *)) {
NSScreen *screen = GetNSScreenForDisplayID(displayID);
if (screen && screen.maximumPotentialExtendedDynamicRangeColorComponentValue > 1.0f) {
HDR->enabled = SDL_TRUE;
HDR->SDR_whitelevel = 80.0f; /* SDR content is always at scRGB 1.0 */
HDR->HDR_whitelevel = HDR->SDR_whitelevel * screen.maximumExtendedDynamicRangeColorComponentValue;
if (screen) {
HDR->HDR_headroom = screen.maximumExtendedDynamicRangeColorComponentValue;
}
}
#endif

View File

@@ -242,13 +242,12 @@ int UIKit_AddDisplay(UIScreen *uiscreen, SDL_bool send_event)
}
display.desktop_mode = mode;
display.HDR.SDR_white_point = 1.0f;
display.HDR.HDR_headroom = 1.0f;
#ifndef SDL_PLATFORM_TVOS
if (@available(iOS 16.0, *)) {
if (uiscreen.potentialEDRHeadroom > 1.0f) {
display.HDR.enabled = SDL_TRUE;
display.HDR.SDR_whitelevel = 80.0f; /* SDR content is always at scRGB 1.0 */
display.HDR.HDR_whitelevel = display.HDR.SDR_whitelevel * uiscreen.currentEDRHeadroom;
}
display.HDR.HDR_headroom = uiscreen.currentEDRHeadroom;
}
#endif /* !SDL_PLATFORM_TVOS */

View File

@@ -452,10 +452,10 @@ done:
return found;
}
static float WIN_GetSDRWhiteLevel(HMONITOR hMonitor)
static float WIN_GetSDRWhitePoint(HMONITOR hMonitor)
{
DISPLAYCONFIG_PATH_INFO path_info;
float SDR_whitelevel = 200.0f;
float SDR_white_point = 1.0f;
if (WIN_GetMonitorPathInfo(hMonitor, &path_info)) {
DISPLAYCONFIG_SDR_WHITE_LEVEL white_level;
@@ -465,11 +465,12 @@ static float WIN_GetSDRWhiteLevel(HMONITOR hMonitor)
white_level.header.size = sizeof(white_level);
white_level.header.adapterId = path_info.targetInfo.adapterId;
white_level.header.id = path_info.targetInfo.id;
if (DisplayConfigGetDeviceInfo(&white_level.header) == ERROR_SUCCESS) {
SDR_whitelevel = (white_level.SDRWhiteLevel / 1000.0f) * 80.0f;
if (DisplayConfigGetDeviceInfo(&white_level.header) == ERROR_SUCCESS &&
white_level.SDRWhiteLevel > 0) {
SDR_white_point = (white_level.SDRWhiteLevel / 1000.0f);
}
}
return SDR_whitelevel;
return SDR_white_point;
}
static void WIN_GetHDRProperties(SDL_VideoDevice *_this, HMONITOR hMonitor, SDL_HDRDisplayProperties *HDR)
@@ -480,9 +481,8 @@ static void WIN_GetHDRProperties(SDL_VideoDevice *_this, HMONITOR hMonitor, SDL_
if (WIN_GetMonitorDESC1(hMonitor, &desc)) {
if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
HDR->enabled = SDL_TRUE;
HDR->SDR_whitelevel = WIN_GetSDRWhiteLevel(hMonitor);
HDR->HDR_whitelevel = desc.MaxLuminance;
HDR->SDR_white_point = WIN_GetSDRWhitePoint(hMonitor);
HDR->HDR_headroom = (desc.MaxLuminance / 80.0f) / HDR->SDR_white_point;
}
}
}