mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-05-18 10:48:28 +00:00
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:
parent
3b7533f4a2
commit
4ba6aeee9d
24 changed files with 430 additions and 546 deletions
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue