SDL: Define SCRGB_NITS for use in HDR color calculations

From 148241117bd67c11dba2c00096dd56a8e984e07d Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 23 Jul 2026 08:09:02 -0700
Subject: [PATCH] Define SCRGB_NITS for use in HDR color calculations

---
 src/render/SDL_render.c                  | 4 ++--
 src/render/direct3d11/SDL_render_d3d11.c | 6 +++---
 src/render/direct3d12/SDL_render_d3d12.c | 6 +++---
 src/render/vulkan/SDL_render_vulkan.c    | 6 +++---
 src/video/SDL_pixels_c.h                 | 2 ++
 src/video/windows/SDL_windowsmodes.c     | 3 ++-
 test/testcolorspace.c                    | 7 +++++--
 7 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 8d441e317cfd1..9dbb2739dac19 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -5448,7 +5448,7 @@ SDL_Surface *SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
             SDL_PixelFormat expected_format = (parent ? parent->format : target->format);
 
             if (SDL_COLORSPACETRANSFER(target->colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) {
-                SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, target->SDR_white_point * 80.0f);
+                SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, target->SDR_white_point * SCRGB_NITS);
             } else {
                 SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, target->SDR_white_point);
             }
@@ -5464,7 +5464,7 @@ SDL_Surface *SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
             }
         } else {
             if (SDL_COLORSPACETRANSFER(renderer->output_colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) {
-                SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point * 80.0f);
+                SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point * SCRGB_NITS);
             } else {
                 SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point);
             }
diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c
index 9e775ecd8929b..abbcd50ff6779 100644
--- a/src/render/direct3d11/SDL_render_d3d11.c
+++ b/src/render/direct3d11/SDL_render_d3d11.c
@@ -2681,9 +2681,9 @@ static bool D3D11_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
             color.b *= cmd->data.color.color_scale;
 
             if (renderer->current_colorspace == SDL_COLORSPACE_HDR10) {
-                color.r = SDL_PQfromNits(color.r * 80.0f);
-                color.g = SDL_PQfromNits(color.g * 80.0f);
-                color.b = SDL_PQfromNits(color.b * 80.0f);
+                color.r = SDL_PQfromNits(color.r * SCRGB_NITS);
+                color.g = SDL_PQfromNits(color.g * SCRGB_NITS);
+                color.b = SDL_PQfromNits(color.b * SCRGB_NITS);
             }
 
             ID3D11DeviceContext_ClearRenderTargetView(rendererData->d3dContext, D3D11_GetCurrentRenderTargetView(renderer), &color.r);
diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c
index 418b429fd6260..bb2f5c59be67f 100644
--- a/src/render/direct3d12/SDL_render_d3d12.c
+++ b/src/render/direct3d12/SDL_render_d3d12.c
@@ -3174,9 +3174,9 @@ static bool D3D12_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd
             color.b *= cmd->data.color.color_scale;
 
             if (renderer->current_colorspace == SDL_COLORSPACE_HDR10) {
-                color.r = SDL_PQfromNits(color.r * 80.0f);
-                color.g = SDL_PQfromNits(color.g * 80.0f);
-                color.b = SDL_PQfromNits(color.b * 80.0f);
+                color.r = SDL_PQfromNits(color.r * SCRGB_NITS);
+                color.g = SDL_PQfromNits(color.g * SCRGB_NITS);
+                color.b = SDL_PQfromNits(color.b * SCRGB_NITS);
             }
 
             ID3D12GraphicsCommandList2_ClearRenderTargetView(rendererData->commandList, rtvDescriptor, &color.r, 0, NULL);
diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c
index a0c26f5b3f81a..01e0d33727100 100644
--- a/src/render/vulkan/SDL_render_vulkan.c
+++ b/src/render/vulkan/SDL_render_vulkan.c
@@ -4166,9 +4166,9 @@ static bool VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cm
             color.b *= cmd->data.color.color_scale;
 
             if (renderer->current_colorspace == SDL_COLORSPACE_HDR10) {
-                color.r = SDL_PQfromNits(color.r * 80.0f);
-                color.g = SDL_PQfromNits(color.g * 80.0f);
-                color.b = SDL_PQfromNits(color.b * 80.0f);
+                color.r = SDL_PQfromNits(color.r * SCRGB_NITS);
+                color.g = SDL_PQfromNits(color.g * SCRGB_NITS);
+                color.b = SDL_PQfromNits(color.b * SCRGB_NITS);
             }
 
             VkClearColorValue clearColor;
diff --git a/src/video/SDL_pixels_c.h b/src/video/SDL_pixels_c.h
index ef88d42905ce5..299718741682e 100644
--- a/src/video/SDL_pixels_c.h
+++ b/src/video/SDL_pixels_c.h
@@ -27,6 +27,8 @@
 
 #include "SDL_blit.h"
 
+// The value, in nits, of scRGB 1.0
+#define SCRGB_NITS  80.0f
 
 // Pixel format functions
 extern void SDL_Get8888AlphaMaskAndShift(const SDL_PixelFormatDetails *fmt, Uint32 *mask, Uint32 *shift);
diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c
index ca076356cfffd..afb85e16902e7 100644
--- a/src/video/windows/SDL_windowsmodes.c
+++ b/src/video/windows/SDL_windowsmodes.c
@@ -24,6 +24,7 @@
 
 #include "SDL_windowsvideo.h"
 #include "../../events/SDL_displayevents_c.h"
+#include "../SDL_pixels_c.h"
 
 #ifdef HAVE_DXGI1_6_H
 #define COBJMACROS
@@ -546,7 +547,7 @@ 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->SDR_white_level = WIN_GetSDRWhitePoint(_this, hMonitor);
-            HDR->HDR_headroom = (desc.MaxLuminance / 80.0f) / HDR->SDR_white_level;
+            HDR->HDR_headroom = (desc.MaxLuminance / SCRGB_NITS) / HDR->SDR_white_level;
         }
     }
 }
diff --git a/test/testcolorspace.c b/test/testcolorspace.c
index 5b3fb54fcbcba..7f02263725ab6 100644
--- a/test/testcolorspace.c
+++ b/test/testcolorspace.c
@@ -15,6 +15,9 @@
 #include <SDL3/SDL_test.h>
 #include <SDL3/SDL_main.h>
 
+// The value, in nits, of scRGB 1.0
+#define SCRGB_NITS 80.0f
+
 #define WINDOW_WIDTH  640
 #define WINDOW_HEIGHT 480
 
@@ -85,7 +88,7 @@ static void UpdateHDRState(void)
         }
         SDR_white_level = SDL_GetFloatProperty(props, SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT, 1.0f);
         HDR_headroom = SDL_GetFloatProperty(props, SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT, 1.0f);
-		if (((SDR_white_level * 80.0f) * HDR_headroom) == 10000.0f) {
+        if (((SDR_white_level * SCRGB_NITS) * HDR_headroom) == 10000.0f) {
 			// The system is advertising the PQ luminance range (10000 nits)
 			// and tone mapping into the actual display capabilities.
 			// Let's use a more reasonable range for the gradient test.
@@ -693,7 +696,7 @@ static SDL_Texture *CreateHDR10Texture(int width, float max_nits)
     // The white point for HDR10 textures is the SDR white level in nits
     if (SDR_white_level > 1.0f) {
         // We'll match the current display SDR white level so we don't get any scaling here
-        SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, SDR_white_level * 80.0f);
+        SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, SDR_white_level * SCRGB_NITS);
     } else {
         // ITU-R BT.2408-6 recommends using an SDR white point of 203 nits.
         SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, 203.0f);