From 9591b2cfb4454f7aab7a3b93337a816abb2c84ee Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 2 Feb 2024 12:09:37 -0800 Subject: [PATCH] Finalized the SDL render API colorspaces The renderer will always use the sRGB colorspace for drawing, and will default to the sRGB output colorspace. If you want blending in linear space and HDR support, you can select the scRGB output colorspace, which is supported by the direct3d11 and direct3d12 --- include/SDL3/SDL_pixels.h | 10 +++ include/SDL3/SDL_render.h | 48 +---------- src/dynapi/SDL_dynapi.sym | 2 - src/dynapi/SDL_dynapi_overrides.h | 2 - src/dynapi/SDL_dynapi_procs.h | 2 - src/render/SDL_render.c | 101 +++++------------------ src/render/SDL_sysrender.h | 7 +- src/render/direct3d11/SDL_render_d3d11.c | 78 ++++++++--------- src/render/direct3d12/SDL_render_d3d12.c | 63 ++++++++------ src/render/opengl/SDL_render_gl.c | 24 ++---- src/render/software/SDL_render_sw.c | 17 +++- test/testcolorspace.c | 89 +++++++++++++------- 12 files changed, 195 insertions(+), 248 deletions(-) diff --git a/include/SDL3/SDL_pixels.h b/include/SDL3/SDL_pixels.h index ecbc129f6..f53cd7f64 100644 --- a/include/SDL3/SDL_pixels.h +++ b/include/SDL3/SDL_pixels.h @@ -558,6 +558,8 @@ typedef enum typedef enum { SDL_COLORSPACE_UNKNOWN, + + /* sRGB is a gamma corrected colorspace, and the default colorspace for SDL rendering and 8-bit RGB surfaces */ SDL_COLORSPACE_SRGB = /**< Equivalent to DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 */ SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_RGB, SDL_COLOR_RANGE_FULL, @@ -565,6 +567,8 @@ typedef enum SDL_TRANSFER_CHARACTERISTICS_SRGB, SDL_MATRIX_COEFFICIENTS_UNSPECIFIED, SDL_CHROMA_LOCATION_NONE), + + /* scRGB is a linear colorspace and the default colorspace for floating point surfaces */ SDL_COLORSPACE_SCRGB = /**< Equivalent to DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 */ SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_RGB, SDL_COLOR_RANGE_FULL, @@ -572,6 +576,8 @@ typedef enum SDL_TRANSFER_CHARACTERISTICS_LINEAR, SDL_MATRIX_COEFFICIENTS_UNSPECIFIED, SDL_CHROMA_LOCATION_NONE), + + /* HDR10 is a non-linear HDR colorspace and the default colorspace for 10-bit surfaces */ SDL_COLORSPACE_HDR10 = /**< Equivalent to DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 */ SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_RGB, SDL_COLOR_RANGE_FULL, @@ -579,6 +585,7 @@ typedef enum SDL_TRANSFER_CHARACTERISTICS_PQ, SDL_MATRIX_COEFFICIENTS_UNSPECIFIED, SDL_CHROMA_LOCATION_NONE), + SDL_COLORSPACE_BT601_LIMITED = /**< Equivalent to DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601 */ SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, SDL_COLOR_RANGE_LIMITED, @@ -586,6 +593,7 @@ typedef enum SDL_TRANSFER_CHARACTERISTICS_BT601, SDL_MATRIX_COEFFICIENTS_BT601, SDL_CHROMA_LOCATION_LEFT), + SDL_COLORSPACE_BT601_FULL = /**< Equivalent to DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601 */ SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, SDL_COLOR_RANGE_FULL, @@ -593,6 +601,7 @@ typedef enum SDL_TRANSFER_CHARACTERISTICS_BT601, SDL_MATRIX_COEFFICIENTS_BT601, SDL_CHROMA_LOCATION_LEFT), + SDL_COLORSPACE_BT709_LIMITED = /**< Equivalent to DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 */ SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, SDL_COLOR_RANGE_LIMITED, @@ -600,6 +609,7 @@ typedef enum SDL_TRANSFER_CHARACTERISTICS_BT709, SDL_MATRIX_COEFFICIENTS_BT709, SDL_CHROMA_LOCATION_LEFT), + SDL_COLORSPACE_BT709_FULL = /**< Equivalent to DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 */ SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, SDL_COLOR_RANGE_FULL, diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 5ad57210c..4b0498280 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -240,15 +240,9 @@ extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window, co * is displayed, if you want a software renderer without a window * - `SDL_PROP_RENDERER_CREATE_NAME_STRING`: the name of the rendering driver * to use, if a specific one is desired - * - `SDL_PROP_RENDERER_CREATE_INPUT_COLORSPACE_NUMBER`: an SDL_ColorSpace - * value describing the colorspace for input colors, defaults to - * SDL_COLORSPACE_SRGB * - `SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER`: an SDL_ColorSpace * value describing the colorspace for output to the display, defaults to - * SDL_COLORSPACE_SRGB - * - `SDL_PROP_RENDERER_CREATE_COLORSPACE_CONVERSION_BOOLEAN`: true if you - * want conversion between the input colorspace and the output colorspace, - * defaults to SDL_TRUE + * SDL_COLORSPACE_SRGB. The direct3d11 and direct3d12 renderers support SDL_COLORSPACE_SCRGB, which is a linear color space and supports HDR output. * - `SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_BOOLEAN`: true if you want * present synchronized with the refresh rate * @@ -273,9 +267,7 @@ extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRendererWithProperties(SDL_Prop #define SDL_PROP_RENDERER_CREATE_WINDOW_POINTER "window" #define SDL_PROP_RENDERER_CREATE_SURFACE_POINTER "surface" #define SDL_PROP_RENDERER_CREATE_NAME_STRING "name" -#define SDL_PROP_RENDERER_CREATE_INPUT_COLORSPACE_NUMBER "input_colorspace" #define SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER "output_colorspace" -#define SDL_PROP_RENDERER_CREATE_COLORSPACE_CONVERSION_BOOLEAN "colorspace_conversion" #define SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_BOOLEAN "present_vsync" /** @@ -1360,44 +1352,6 @@ extern DECLSPEC int SDLCALL SDL_SetRenderScale(SDL_Renderer *renderer, float sca */ extern DECLSPEC int SDLCALL SDL_GetRenderScale(SDL_Renderer *renderer, float *scaleX, float *scaleY); -/** - * Set the colorspace used for drawing operations - * - * The default colorspace for drawing operations is SDL_COLORSPACE_SRGB, but - * you can change it to other colorspaces such as SDL_COLORSPACE_SCRGB for HDR - * rendering. - * - * This does not affect the colorspace of textures, which is specified via - * properties when the texture is created and does not change. - * - * \param renderer the rendering context - * \param colorspace an SDL_ColorSpace value describing the colorspace for - * drawing operations - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. - * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_GetRenderDrawColorspace - */ -extern DECLSPEC int SDLCALL SDL_SetRenderDrawColorspace(SDL_Renderer *renderer, SDL_Colorspace colorspace); - -/** - * Get the colorspace used for drawing operations - * - * \param renderer the rendering context - * \param colorspace a pointer filled in with an SDL_ColorSpace value - * describing the colorspace for drawing operations - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. - * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_SetRenderDrawColorspace - */ -extern DECLSPEC int SDLCALL SDL_GetRenderDrawColorspace(SDL_Renderer *renderer, SDL_Colorspace *colorspace); - - /** * Set the color used for drawing operations. * diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index c9d6cceeb..1dc5a5405 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -969,8 +969,6 @@ SDL3_0.0.0 { SDL_SetSurfaceColorspace; SDL_GetSurfaceColorspace; SDL_ConvertSurfaceFormatAndColorspace; - SDL_SetRenderDrawColorspace; - SDL_GetRenderDrawColorspace; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 51530f670..42a2e4846 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -994,5 +994,3 @@ #define SDL_SetSurfaceColorspace SDL_SetSurfaceColorspace_REAL #define SDL_GetSurfaceColorspace SDL_GetSurfaceColorspace_REAL #define SDL_ConvertSurfaceFormatAndColorspace SDL_ConvertSurfaceFormatAndColorspace_REAL -#define SDL_SetRenderDrawColorspace SDL_SetRenderDrawColorspace_REAL -#define SDL_GetRenderDrawColorspace SDL_GetRenderDrawColorspace_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 8eb2f14d9..b6221bb51 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1019,5 +1019,3 @@ SDL_DYNAPI_PROC(int,SDL_ConvertPixelsAndColorspace,(int a, int b, Uint32 c, SDL_ 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(int,SDL_SetRenderDrawColorspace,(SDL_Renderer *a, SDL_Colorspace b),(a,b),return) -SDL_DYNAPI_PROC(int,SDL_GetRenderDrawColorspace,(SDL_Renderer *a, SDL_Colorspace *b),(a,b),return) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 2fc35c15a..491e102bd 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -128,9 +128,7 @@ char SDL_texture_magic; void SDL_SetupRendererColorspace(SDL_Renderer *renderer, SDL_PropertiesID props) { - renderer->input_colorspace = (SDL_Colorspace)SDL_GetNumberProperty(props, SDL_PROP_RENDERER_CREATE_INPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB); renderer->output_colorspace = (SDL_Colorspace)SDL_GetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB); - renderer->colorspace_conversion = SDL_GetBooleanProperty(props, SDL_PROP_RENDERER_CREATE_COLORSPACE_CONVERSION_BOOLEAN, SDL_TRUE); } static float sRGBtoLinear(float v) @@ -143,46 +141,33 @@ static float sRGBfromLinear(float v) return v <= 0.0031308f ? (v * 12.92f) : (SDL_powf(v, 1.0f / 2.4f) * 1.055f - 0.055f); } -void SDL_ConvertToLinear(SDL_Renderer *renderer, SDL_FColor *color) +SDL_bool SDL_RenderingLinearSpace(SDL_Renderer *renderer) { - if (!renderer->colorspace_conversion) { - return; - } + SDL_Colorspace colorspace; - switch (SDL_COLORSPACETRANSFER(renderer->input_colorspace)) { - case SDL_TRANSFER_CHARACTERISTICS_SRGB: - color->r = sRGBtoLinear(color->r); - color->g = sRGBtoLinear(color->g); - color->b = sRGBtoLinear(color->b); - break; - case SDL_TRANSFER_CHARACTERISTICS_LINEAR: - /* No conversion needed */ - break; - default: - /* Unsupported */ - break; + if (renderer->target) { + colorspace = renderer->target->colorspace; + } else { + colorspace = renderer->output_colorspace; } + if (colorspace == SDL_COLORSPACE_SCRGB) { + return SDL_TRUE; + } + return SDL_FALSE; } -void SDL_ConvertFromLinear(SDL_Renderer *renderer, SDL_FColor *color) +void SDL_ConvertToLinear(SDL_FColor *color) { - if (!renderer->colorspace_conversion) { - return; - } + color->r = sRGBtoLinear(color->r); + color->g = sRGBtoLinear(color->g); + color->b = sRGBtoLinear(color->b); +} - switch (SDL_COLORSPACETRANSFER(renderer->input_colorspace)) { - case SDL_TRANSFER_CHARACTERISTICS_SRGB: - color->r = sRGBfromLinear(color->r); - color->g = sRGBfromLinear(color->g); - color->b = sRGBfromLinear(color->b); - break; - case SDL_TRANSFER_CHARACTERISTICS_LINEAR: - /* No conversion needed */ - break; - default: - /* Unsupported */ - break; - } +void SDL_ConvertFromLinear(SDL_FColor *color) +{ + color->r = sRGBfromLinear(color->r); + color->g = sRGBfromLinear(color->g); + color->b = sRGBfromLinear(color->b); } static SDL_INLINE void DebugLogRenderCommands(const SDL_RenderCommand *cmd) @@ -1549,7 +1534,6 @@ int SDL_SetTextureColorModFloat(SDL_Texture *texture, float r, float g, float b) texture->color.r = r; texture->color.g = g; texture->color.b = b; - SDL_ConvertToLinear(texture->renderer, &texture->color); if (texture->native) { return SDL_SetTextureColorModFloat(texture->native, r, g, b); } @@ -1583,7 +1567,6 @@ int SDL_GetTextureColorModFloat(SDL_Texture *texture, float *r, float *g, float CHECK_TEXTURE_MAGIC(texture, -1); color = texture->color; - SDL_ConvertFromLinear(texture->renderer, &color); if (r) { *r = color.r; @@ -2785,30 +2768,6 @@ int SDL_GetRenderScale(SDL_Renderer *renderer, float *scaleX, float *scaleY) return 0; } -int SDL_SetRenderDrawColorspace(SDL_Renderer *renderer, SDL_Colorspace colorspace) -{ - CHECK_RENDERER_MAGIC(renderer, -1); - - if (colorspace != SDL_COLORSPACE_SRGB && - colorspace != SDL_COLORSPACE_SCRGB) { - return SDL_SetError("Unsupported colorspace"); - } - - renderer->input_colorspace = colorspace; - return 0; -} - -int SDL_GetRenderDrawColorspace(SDL_Renderer *renderer, SDL_Colorspace *colorspace) -{ - CHECK_RENDERER_MAGIC(renderer, -1); - - if (colorspace) { - *colorspace = renderer->input_colorspace; - } - return 0; -} - - int SDL_SetRenderDrawColor(SDL_Renderer *renderer, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { const float fR = (float)r / 255.0f; @@ -2827,7 +2786,6 @@ int SDL_SetRenderDrawColorFloat(SDL_Renderer *renderer, float r, float g, float renderer->color.g = g; renderer->color.b = b; renderer->color.a = a; - SDL_ConvertToLinear(renderer, &renderer->color); return 0; } @@ -2861,7 +2819,6 @@ int SDL_GetRenderDrawColorFloat(SDL_Renderer *renderer, float *r, float *g, floa CHECK_RENDERER_MAGIC(renderer, -1); color = renderer->color; - SDL_ConvertFromLinear(renderer, &color); if (r) { *r = color.r; @@ -4163,24 +4120,6 @@ int SDL_RenderGeometryRaw(SDL_Renderer *renderer, indices, num_indices, size_indices); } - /* Transform the colors if necessary */ - if (renderer->colorspace_conversion && - SDL_COLORSPACETRANSFER(renderer->input_colorspace) == SDL_TRANSFER_CHARACTERISTICS_SRGB) { - int num_colors = (color_stride > 0) ? num_vertices : 1; - updated_colors = SDL_small_alloc(SDL_FColor, num_colors, &isstack); - if (!updated_colors) { - return -1; - } - for (i = 0; i < num_colors; ++i) { - updated_colors[i] = *(const SDL_FColor *)(((const Uint8 *)color) + i * color_stride); - SDL_ConvertToLinear(renderer, &updated_colors[i]); - } - color = updated_colors; - if (color_stride > 0) { - color_stride = sizeof(SDL_FColor); - } - } - retval = QueueCmdGeometry(renderer, texture, xy, xy_stride, color, color_stride, uv, uv_stride, num_vertices, diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 58536876f..0894fd3db 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -250,9 +250,7 @@ struct SDL_Renderer SDL_Texture *target; SDL_Mutex *target_mutex; - SDL_Colorspace input_colorspace; SDL_Colorspace output_colorspace; - SDL_bool colorspace_conversion; SDL_FColor color; /**< Color for drawing operations values */ SDL_BlendMode blendMode; /**< The drawing blend mode */ @@ -303,8 +301,9 @@ extern SDL_RenderDriver VITA_GXM_RenderDriver; extern void SDL_SetupRendererColorspace(SDL_Renderer *renderer, SDL_PropertiesID props); /* Colorspace conversion functions */ -extern void SDL_ConvertToLinear(SDL_Renderer *renderer, SDL_FColor *color); -extern void SDL_ConvertFromLinear(SDL_Renderer *renderer, SDL_FColor *color); +extern SDL_bool SDL_RenderingLinearSpace(SDL_Renderer *renderer); +extern void SDL_ConvertToLinear(SDL_FColor *color); +extern void SDL_ConvertFromLinear(SDL_FColor *color); /* Blend mode functions */ extern SDL_BlendFactor SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode); diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index c5f103835..3c61a192e 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -214,18 +214,18 @@ Uint32 D3D11_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat) } } -static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 colorspace, SDL_bool colorspace_conversion) +static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 colorspace) { switch (format) { case SDL_PIXELFORMAT_RGBA64_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; case SDL_PIXELFORMAT_ARGB8888: - if (colorspace_conversion && colorspace == SDL_COLORSPACE_SRGB) { + if (colorspace == SDL_COLORSPACE_SCRGB) { return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8A8_UNORM; case SDL_PIXELFORMAT_XRGB8888: - if (colorspace_conversion && colorspace == SDL_COLORSPACE_SRGB) { + if (colorspace == SDL_COLORSPACE_SCRGB) { return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8X8_UNORM; @@ -240,18 +240,18 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 color } } -static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uint32 colorspace, SDL_bool colorspace_conversion) +static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uint32 colorspace) { switch (format) { case SDL_PIXELFORMAT_RGBA64_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; case SDL_PIXELFORMAT_ARGB8888: - if (colorspace_conversion && colorspace == SDL_COLORSPACE_SRGB) { + if (colorspace == SDL_COLORSPACE_SCRGB) { return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8A8_UNORM; case SDL_PIXELFORMAT_XRGB8888: - if (colorspace_conversion && colorspace == SDL_COLORSPACE_SRGB) { + if (colorspace == SDL_COLORSPACE_SCRGB) { return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8X8_UNORM; @@ -523,6 +523,7 @@ static HRESULT D3D11_CreateDeviceResources(SDL_Renderer *renderer) if (SDL_GetHintBoolean(SDL_HINT_RENDER_DIRECT3D11_DEBUG, SDL_FALSE)) { creationFlags |= D3D11_CREATE_DEVICE_DEBUG; } + creationFlags |= D3D11_CREATE_DEVICE_DEBUG; /* Create a single-threaded device unless the app requests otherwise. */ if (!SDL_GetHintBoolean(SDL_HINT_RENDER_DIRECT3D_THREADSAFE, SDL_FALSE)) { @@ -893,23 +894,31 @@ static HRESULT D3D11_CreateSwapChain(SDL_Renderer *renderer, int w, int h) IDXGISwapChain3 *swapChain3 = NULL; if (SUCCEEDED(IDXGISwapChain1_QueryInterface(data->swapChain, &SDL_IID_IDXGISwapChain2, (void **)&swapChain3))) { - DXGI_COLOR_SPACE_TYPE ColorSpace; + UINT colorspace_support = 0; + DXGI_COLOR_SPACE_TYPE colorspace; switch (renderer->output_colorspace) { case SDL_COLORSPACE_SCRGB: - ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; + colorspace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; break; case SDL_COLORSPACE_HDR10: - ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + colorspace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; break; default: /* sRGB */ - ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + colorspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; break; } - result = IDXGISwapChain3_SetColorSpace1(swapChain3, ColorSpace); - if (FAILED(result)) { - WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain3::SetColorSpace1"), result); - goto done; + if (SUCCEEDED(IDXGISwapChain3_CheckColorSpaceSupport(swapChain3, colorspace, &colorspace_support)) && + (colorspace_support & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) { + result = IDXGISwapChain3_SetColorSpace1(swapChain3, colorspace); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain3::SetColorSpace1"), result); + goto done; + } + } else if (colorspace != DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709) { + /* Not the default, we're not going to be able to present in this colorspace */ + SDL_SetError("Unsupported output colorspace"); + result = DXGI_ERROR_UNSUPPORTED; } } @@ -1050,27 +1059,9 @@ static HRESULT D3D11_CreateWindowSizeDependentResources(SDL_Renderer *renderer) } /* Create a render target view of the swap chain back buffer. */ - D3D11_RENDER_TARGET_VIEW_DESC desc; - SDL_zero(desc); - switch (renderer->output_colorspace) { - case SDL_COLORSPACE_SCRGB: - desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; - break; - case SDL_COLORSPACE_HDR10: - desc.Format = DXGI_FORMAT_R10G10B10A2_UNORM; - break; - default: - if (renderer->colorspace_conversion && renderer->output_colorspace == SDL_COLORSPACE_SRGB) { - desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; - } else { - desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - } - break; - } - desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; result = ID3D11Device_CreateRenderTargetView(data->d3dDevice, (ID3D11Resource *)backBuffer, - &desc, + NULL, &data->mainRenderTargetView); if (FAILED(result)) { WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device::CreateRenderTargetView"), result); @@ -1163,7 +1154,7 @@ static int D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->driverdata; D3D11_TextureData *textureData; HRESULT result; - DXGI_FORMAT textureFormat = SDLPixelFormatToDXGITextureFormat(texture->format, texture->colorspace, renderer->colorspace_conversion); + DXGI_FORMAT textureFormat = SDLPixelFormatToDXGITextureFormat(texture->format, renderer->output_colorspace); D3D11_TEXTURE2D_DESC textureDesc; D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc; @@ -1262,7 +1253,7 @@ static int D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL } #endif /* SDL_HAVE_YUV */ SDL_zero(resourceViewDesc); - resourceViewDesc.Format = SDLPixelFormatToDXGIMainResourceViewFormat(texture->format, texture->colorspace, renderer->colorspace_conversion); + resourceViewDesc.Format = SDLPixelFormatToDXGIMainResourceViewFormat(texture->format, renderer->output_colorspace); resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; resourceViewDesc.Texture2D.MostDetailedMip = 0; resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels; @@ -1774,6 +1765,7 @@ static int D3D11_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, { VertexPositionColor *verts = (VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(VertexPositionColor), 0, &cmd->data.draw.first); int i; + SDL_bool convert_color = SDL_RenderingLinearSpace(renderer); if (!verts) { return -1; @@ -1787,6 +1779,9 @@ static int D3D11_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, verts->tex.x = 0.0f; verts->tex.y = 0.0f; verts->color = cmd->data.draw.color; + if (convert_color) { + SDL_ConvertToLinear(&verts->color); + } verts++; } @@ -1801,6 +1796,7 @@ static int D3D11_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, S int i; int count = indices ? num_indices : num_vertices; VertexPositionColor *verts = (VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(VertexPositionColor), 0, &cmd->data.draw.first); + SDL_bool convert_color = SDL_RenderingLinearSpace(renderer); if (!verts) { return -1; @@ -1827,6 +1823,9 @@ static int D3D11_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, S verts->pos.x = xy_[0] * scale_x; verts->pos.y = xy_[1] * scale_y; verts->color = *(SDL_FColor *)((char *)color + j * color_stride); + if (convert_color) { + SDL_ConvertToLinear(&verts->color); + } if (texture) { float *uv_ = (float *)((char *)uv + j * uv_stride); @@ -2270,7 +2269,12 @@ static int D3D11_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, case SDL_RENDERCMD_CLEAR: { - ID3D11DeviceContext_ClearRenderTargetView(rendererData->d3dContext, D3D11_GetCurrentRenderTargetView(renderer), &cmd->data.color.color.r); + SDL_bool convert_color = SDL_RenderingLinearSpace(renderer); + SDL_FColor color = cmd->data.color.color; + if (convert_color) { + SDL_ConvertToLinear(&color); + } + ID3D11DeviceContext_ClearRenderTargetView(rendererData->d3dContext, D3D11_GetCurrentRenderTargetView(renderer), &color.r); break; } @@ -2419,7 +2423,7 @@ static int D3D11_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect, textureMemory.pData, textureMemory.RowPitch, format, - renderer->input_colorspace, + SDL_COLORSPACE_SRGB, pixels, pitch); diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index bfd5da1ab..0d6c40590 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -288,18 +288,18 @@ Uint32 D3D12_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat) } } -static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 colorspace, SDL_bool colorspace_conversion) +static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 colorspace) { switch (format) { case SDL_PIXELFORMAT_RGBA64_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; case SDL_PIXELFORMAT_ARGB8888: - if (colorspace_conversion && colorspace == SDL_COLORSPACE_SRGB) { + if (colorspace == SDL_COLORSPACE_SCRGB) { return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8A8_UNORM; case SDL_PIXELFORMAT_XRGB8888: - if (colorspace_conversion && colorspace == SDL_COLORSPACE_SRGB) { + if (colorspace == SDL_COLORSPACE_SCRGB) { return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8X8_UNORM; @@ -314,18 +314,18 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 color } } -static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uint32 colorspace, SDL_bool colorspace_conversion) +static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uint32 colorspace) { switch (format) { case SDL_PIXELFORMAT_RGBA64_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; case SDL_PIXELFORMAT_ARGB8888: - if (colorspace_conversion && colorspace == SDL_COLORSPACE_SRGB) { + if (colorspace == SDL_COLORSPACE_SCRGB) { return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8A8_UNORM; case SDL_PIXELFORMAT_XRGB8888: - if (colorspace_conversion && colorspace == SDL_COLORSPACE_SRGB) { + if (colorspace == SDL_COLORSPACE_SCRGB) { return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8X8_UNORM; @@ -1200,11 +1200,7 @@ static HRESULT D3D12_CreateSwapChain(SDL_Renderer *renderer, int w, int h) break; default: swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; /* This is the most common swap chain format. */ - if (renderer->colorspace_conversion && renderer->output_colorspace == SDL_COLORSPACE_SRGB) { - data->renderTargetFormat = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; - } else { - data->renderTargetFormat = DXGI_FORMAT_B8G8R8A8_UNORM; - } + data->renderTargetFormat = DXGI_FORMAT_B8G8R8A8_UNORM; break; } swapChainDesc.Stereo = FALSE; @@ -1255,23 +1251,31 @@ static HRESULT D3D12_CreateSwapChain(SDL_Renderer *renderer, int w, int h) data->swapEffect = swapChainDesc.SwapEffect; data->swapFlags = swapChainDesc.Flags; - DXGI_COLOR_SPACE_TYPE ColorSpace; + UINT colorspace_support = 0; + DXGI_COLOR_SPACE_TYPE colorspace; switch (renderer->output_colorspace) { case SDL_COLORSPACE_SCRGB: - ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; + colorspace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; break; case SDL_COLORSPACE_HDR10: - ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + colorspace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; break; default: /* sRGB */ - ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + colorspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; break; } - result = D3D_CALL(data->swapChain, SetColorSpace1, ColorSpace); - if (FAILED(result)) { - WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain3::SetColorSpace1"), result); - goto done; + if (SUCCEEDED(D3D_CALL(data->swapChain, CheckColorSpaceSupport, colorspace, &colorspace_support)) && + (colorspace_support & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) { + result = D3D_CALL(data->swapChain, SetColorSpace1, colorspace); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain3::SetColorSpace1"), result); + goto done; + } + } else { + /* Not the default, we're not going to be able to present in this colorspace */ + SDL_SetError("Unsupported output colorspace"); + result = DXGI_ERROR_UNSUPPORTED; } done: @@ -1511,7 +1515,7 @@ static int D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->driverdata; D3D12_TextureData *textureData; HRESULT result; - DXGI_FORMAT textureFormat = SDLPixelFormatToDXGITextureFormat(texture->format, texture->colorspace, renderer->colorspace_conversion); + DXGI_FORMAT textureFormat = SDLPixelFormatToDXGITextureFormat(texture->format, renderer->output_colorspace); D3D12_RESOURCE_DESC textureDesc; D3D12_HEAP_PROPERTIES heapProps; D3D12_SHADER_RESOURCE_VIEW_DESC resourceViewDesc; @@ -1624,7 +1628,7 @@ static int D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL #endif /* SDL_HAVE_YUV */ SDL_zero(resourceViewDesc); resourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - resourceViewDesc.Format = SDLPixelFormatToDXGIMainResourceViewFormat(texture->format, texture->colorspace, renderer->colorspace_conversion); + resourceViewDesc.Format = SDLPixelFormatToDXGIMainResourceViewFormat(texture->format, renderer->output_colorspace); resourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels; @@ -2200,6 +2204,7 @@ static int D3D12_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, { VertexPositionColor *verts = (VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(VertexPositionColor), 0, &cmd->data.draw.first); int i; + SDL_bool convert_color = SDL_RenderingLinearSpace(renderer); if (!verts) { return -1; @@ -2213,6 +2218,9 @@ static int D3D12_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, verts->tex.x = 0.0f; verts->tex.y = 0.0f; verts->color = cmd->data.draw.color; + if (convert_color) { + SDL_ConvertToLinear(&verts->color); + } verts++; } @@ -2227,6 +2235,7 @@ static int D3D12_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, S int i; int count = indices ? num_indices : num_vertices; VertexPositionColor *verts = (VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(VertexPositionColor), 0, &cmd->data.draw.first); + SDL_bool convert_color = SDL_RenderingLinearSpace(renderer); if (!verts) { return -1; @@ -2253,6 +2262,9 @@ static int D3D12_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, S verts->pos.x = xy_[0] * scale_x; verts->pos.y = xy_[1] * scale_y; verts->color = *(SDL_FColor *)((char *)color + j * color_stride); + if (convert_color) { + SDL_ConvertToLinear(&verts->color); + } if (texture) { float *uv_ = (float *)((char *)uv + j * uv_stride); @@ -2716,7 +2728,12 @@ static int D3D12_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, case SDL_RENDERCMD_CLEAR: { D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptor = D3D12_GetCurrentRenderTargetView(renderer); - D3D_CALL(rendererData->commandList, ClearRenderTargetView, rtvDescriptor, &cmd->data.color.color.r, 0, NULL); + SDL_bool convert_color = SDL_RenderingLinearSpace(renderer); + SDL_FColor color = cmd->data.color.color; + if (convert_color) { + SDL_ConvertToLinear(&color); + } + D3D_CALL(rendererData->commandList, ClearRenderTargetView, rtvDescriptor, &color.r, 0, NULL); break; } @@ -2927,7 +2944,7 @@ static int D3D12_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect, textureMemory, pitchedDesc.RowPitch, format, - renderer->input_colorspace, + SDL_COLORSPACE_SRGB, pixels, pitch); diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index 22553053c..e758e0de0 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -401,27 +401,18 @@ static SDL_bool GL_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blend return SDL_TRUE; } -static SDL_bool convert_format(Uint32 pixel_format, Uint32 colorspace, SDL_bool colorspace_conversion, - GLint *internalFormat, GLenum *format, GLenum *type) +static SDL_bool convert_format(Uint32 pixel_format, GLint *internalFormat, GLenum *format, GLenum *type) { switch (pixel_format) { case SDL_PIXELFORMAT_ARGB8888: case SDL_PIXELFORMAT_XRGB8888: - if (colorspace_conversion && colorspace == SDL_COLORSPACE_SRGB) { - *internalFormat = GL_SRGB8_ALPHA8; - } else { - *internalFormat = GL_RGBA8; - } + *internalFormat = GL_RGBA8; *format = GL_BGRA; *type = GL_UNSIGNED_INT_8_8_8_8_REV; break; case SDL_PIXELFORMAT_ABGR8888: case SDL_PIXELFORMAT_XBGR8888: - if (colorspace_conversion && colorspace == SDL_COLORSPACE_SRGB) { - *internalFormat = GL_SRGB8_ALPHA8; - } else { - *internalFormat = GL_RGBA8; - } + *internalFormat = GL_RGBA8; *format = GL_RGBA; *type = GL_UNSIGNED_INT_8_8_8_8_REV; break; @@ -466,8 +457,7 @@ static int GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Pr return SDL_SetError("Render targets not supported by OpenGL"); } - if (!convert_format(texture->format, texture->colorspace, renderer->colorspace_conversion, - &internalFormat, &format, &type)) { + if (!convert_format(texture->format, &internalFormat, &format, &type)) { return SDL_SetError("Texture format %s not supported by OpenGL", SDL_GetPixelFormatName(texture->format)); } @@ -1481,8 +1471,7 @@ static int GL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect, GL_ActivateRenderer(renderer); - if (!convert_format(temp_format, renderer->input_colorspace, renderer->colorspace_conversion, - &internalFormat, &format, &type)) { + if (!convert_format(temp_format, &internalFormat, &format, &type)) { return SDL_SetError("Texture format %s not supported by OpenGL", SDL_GetPixelFormatName(temp_format)); } @@ -1925,9 +1914,6 @@ static SDL_Renderer *GL_CreateRenderer(SDL_Window *window, SDL_PropertiesID crea data->glDisable(data->textype); data->glClearColor(1.0f, 1.0f, 1.0f, 1.0f); data->glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - if (renderer->colorspace_conversion && renderer->output_colorspace == SDL_COLORSPACE_SRGB) { - data->glEnable(GL_FRAMEBUFFER_SRGB); - } /* This ended up causing video discrepancies between OpenGL and Direct3D */ /* data->glEnable(GL_LINE_SMOOTH); */ diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c index d814b5443..a4832af70 100644 --- a/src/render/software/SDL_render_sw.c +++ b/src/render/software/SDL_render_sw.c @@ -1176,6 +1176,7 @@ SDL_Renderer *SW_CreateRendererForSurface(SDL_Surface *surface) static SDL_Renderer *SW_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_props) { + SDL_Renderer *renderer; const char *hint; SDL_Surface *surface; SDL_bool no_hint_set; @@ -1206,7 +1207,21 @@ static SDL_Renderer *SW_CreateRenderer(SDL_Window *window, SDL_PropertiesID crea if (!surface) { return NULL; } - return SW_CreateRendererForSurface(surface); + + renderer = SW_CreateRendererForSurface(surface); + if (!renderer) { + return NULL; + } + + SDL_SetupRendererColorspace(renderer, create_props); + + if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) { + SDL_SetError("Unsupported output colorspace"); + SW_DestroyRenderer(renderer); + return NULL; + } + + return renderer; } SDL_RenderDriver SW_RenderDriver = { diff --git a/test/testcolorspace.c b/test/testcolorspace.c index 211636408..9763bf72c 100644 --- a/test/testcolorspace.c +++ b/test/testcolorspace.c @@ -51,9 +51,7 @@ static void CreateRenderer(void) props = SDL_CreateProperties(); SDL_SetProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window); SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, SDL_GetRenderDriver(renderer_index)); - SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_INPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB); SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, colorspace); - SDL_SetBooleanProperty(props, SDL_PROP_RENDERER_CREATE_COLORSPACE_CONVERSION_BOOLEAN, SDL_TRUE); renderer = SDL_CreateRendererWithProperties(props); SDL_DestroyProperties(props); if (!renderer) { @@ -254,13 +252,13 @@ static void RenderBlendDrawing(void) float y = TEXT_START_Y; DrawText(x, y, "%s %s", renderer_name, colorspace_name); y += TEXT_LINE_ADVANCE; - DrawText(x, y, "Test: Draw Linear Blending"); + DrawText(x, y, "Test: Draw Blending"); y += TEXT_LINE_ADVANCE; if (cr.r == 199 && cr.g == 193 && cr.b == 121) { - DrawText(x, y, "Correct blend color in linear space"); + DrawText(x, y, "Correct blend color, blending in linear space"); } else if ((cr.r == 192 && cr.g == 163 && cr.b == 83) || (cr.r == 191 && cr.g == 162 && cr.b == 82)) { - DrawText(x, y, "Incorrect blend color, blending in sRGB space"); + DrawText(x, y, "Correct blend color, blending in sRGB space"); } else if (cr.r == 214 && cr.g == 156 && cr.b == 113) { DrawText(x, y, "Incorrect blend color, blending in PQ space"); } else { @@ -314,13 +312,13 @@ static void RenderBlendTexture(void) float y = TEXT_START_Y; DrawText(x, y, "%s %s", renderer_name, colorspace_name); y += TEXT_LINE_ADVANCE; - DrawText(x, y, "Test: Texture Linear Blending"); + DrawText(x, y, "Test: Texture Blending"); y += TEXT_LINE_ADVANCE; if (cr.r == 199 && cr.g == 193 && cr.b == 121) { - DrawText(x, y, "Correct blend color in linear space"); + DrawText(x, y, "Correct blend color, blending in linear space"); } else if ((cr.r == 192 && cr.g == 163 && cr.b == 83) || (cr.r == 191 && cr.g == 162 && cr.b == 82)) { - DrawText(x, y, "Incorrect blend color, blending in sRGB space"); + DrawText(x, y, "Correct blend color, blending in sRGB space"); } else { DrawText(x, y, "Incorrect blend color, unknown reason"); } @@ -351,13 +349,10 @@ static void DrawGradient(float x, float y, float width, float height, float star xy[0] = minx; xy[1] = miny; - xy[2] = maxx; xy[3] = miny; - xy[4] = maxx; xy[5] = maxy; - xy[6] = minx; xy[7] = maxy; @@ -369,6 +364,36 @@ static void DrawGradient(float x, float y, float width, float height, float star SDL_RenderGeometryRaw(renderer, NULL, xy, xy_stride, color, color_stride, NULL, 0, num_vertices, indices, num_indices, size_indices); } +static float scRGBfromNits(float v) +{ + return v / 80.0f; +} + +static float sRGBtoLinear(float v) +{ + if (v <= 0.04045f) { + v = (v / 12.92f); + } else { + v = SDL_powf(((v + 0.055f) / 1.055f), 2.4f); + } + return v; +} + +static float sRGBFromLinear(float v) +{ + if (v <= 0.0031308f) { + v = (v * 12.92f); + } else { + v = (SDL_powf(v, 1.0f / 2.4f) * 1.055f - 0.055f); + } + return v; +} + +static float sRGBfromNits(float v) +{ + return sRGBFromLinear(scRGBfromNits(v)); +} + static void RenderGradientDrawing(void) { SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); @@ -383,29 +408,25 @@ static void RenderGradientDrawing(void) y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "SDR mathematically linear gradient (%d nits)", 80); + DrawTextWhite(x, y, "SDR gradient (%d nits)", 80); y += TEXT_LINE_ADVANCE; - DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, 1.0f); + DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, sRGBfromNits(80.0f)); y += 64.0f; y += TEXT_LINE_ADVANCE; y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "HDR mathematically linear gradient (%d nits)", 400); + DrawTextWhite(x, y, "HDR gradient (%d nits)", 400); y += TEXT_LINE_ADVANCE; - SDL_SetRenderDrawColorspace(renderer, SDL_COLORSPACE_SCRGB); - DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, 5.0f); - SDL_SetRenderDrawColorspace(renderer, SDL_COLORSPACE_SRGB); + DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, sRGBfromNits(400.0f)); y += 64.0f; y += TEXT_LINE_ADVANCE; y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "HDR mathematically linear gradient (%d nits)", 1000); + DrawTextWhite(x, y, "HDR gradient (%d nits)", 1000); y += TEXT_LINE_ADVANCE; - SDL_SetRenderDrawColorspace(renderer, SDL_COLORSPACE_SCRGB); - DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, 12.5f); - SDL_SetRenderDrawColorspace(renderer, SDL_COLORSPACE_SRGB); + DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, sRGBfromNits(1000)); y += 64.0f; } @@ -414,19 +435,27 @@ static SDL_Texture *CreateGradientTexture(int width, float start, float end) SDL_Texture *texture; float *pixels; + /* Floating point textures are in the scRGB colorspace by default */ texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA128_FLOAT, SDL_TEXTUREACCESS_STATIC, width, 1); if (!texture) { return NULL; } + if (colorspace == SDL_COLORSPACE_SCRGB) { + /* The input values are in the sRGB colorspace, but we're blending in linear space */ + start = sRGBtoLinear(start); + end = sRGBtoLinear(end); + } else if (colorspace == SDL_COLORSPACE_SRGB) { + /* The input values are in the sRGB colorspace, and we're blending in sRGB space */ + } + pixels = (float *)SDL_malloc(width * sizeof(float) * 4); if (pixels) { int i; float length = (end - start); for (i = 0; i < width; ++i) { - /* Use a 2.4 gamma function to create a perceptually linear gradient */ - float v = SDL_powf(start + (length * i) / width, 2.4f); + float v = (start + (length * i) / width); pixels[i * 4 + 0] = v; pixels[i * 4 + 1] = v; pixels[i * 4 + 2] = v; @@ -455,30 +484,30 @@ static void RenderGradientTexture(void) float y = TEXT_START_Y; DrawTextWhite(x, y, "%s %s", renderer_name, colorspace_name); y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "Test: Draw SDR and HDR gradients"); + DrawTextWhite(x, y, "Test: Texture SDR and HDR gradients"); y += TEXT_LINE_ADVANCE; y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "SDR perceptually linear gradient (%d nits)", 80); + DrawTextWhite(x, y, "SDR gradient (%d nits)", 80); y += TEXT_LINE_ADVANCE; - DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, 1.0f); + DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, sRGBfromNits(80)); y += 64.0f; y += TEXT_LINE_ADVANCE; y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "HDR perceptually linear gradient (%d nits)", 400); + DrawTextWhite(x, y, "HDR gradient (%d nits)", 400); y += TEXT_LINE_ADVANCE; - DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, 5.0f); + DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, sRGBfromNits(400)); y += 64.0f; y += TEXT_LINE_ADVANCE; y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "HDR perceptually linear gradient (%d nits)", 1000); + DrawTextWhite(x, y, "HDR gradient (%d nits)", 1000); y += TEXT_LINE_ADVANCE; - DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, 12.5f); + DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, sRGBfromNits(1000)); y += 64.0f; }