From c3d8595b722afcf6c4a134c26d7da70652fd4baf Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Fri, 6 Jun 2025 18:07:23 +0100 Subject: [PATCH] palette_update_quest_palette: tone map 1 color (#8027) Co-authored-by: Stephen C. Wills --- Source/DiabloUI/diabloui.cpp | 4 +- Source/engine/palette.cpp | 82 ++++++++++++++++++++++-------------- Source/engine/palette.h | 3 +- Source/interfac.cpp | 2 +- 4 files changed, 55 insertions(+), 36 deletions(-) diff --git a/Source/DiabloUI/diabloui.cpp b/Source/DiabloUI/diabloui.cpp index 8f4480bd1..48cea26a9 100644 --- a/Source/DiabloUI/diabloui.cpp +++ b/Source/DiabloUI/diabloui.cpp @@ -653,7 +653,7 @@ Sint16 GetCenterOffset(Sint16 w, Sint16 bw) void UiLoadDefaultPalette() { LoadPalette("ui_art\\diablo.pal", /*blend=*/false); - ApplyToneMapping(logical_palette, orig_palette, 256); + ApplyToneMapping(logical_palette, orig_palette); } bool UiLoadBlackBackground() @@ -673,7 +673,7 @@ void LoadBackgroundArt(const char *pszFile, int frames) return; LoadPalInMem(pPal); - ApplyToneMapping(logical_palette, orig_palette, 256); + ApplyToneMapping(logical_palette, orig_palette); UiOnBackgroundChange(); } diff --git a/Source/engine/palette.cpp b/Source/engine/palette.cpp index 04935feb8..e95e00ed9 100644 --- a/Source/engine/palette.cpp +++ b/Source/engine/palette.cpp @@ -5,7 +5,10 @@ */ #include "engine/palette.h" +#include +#include #include +#include #include @@ -79,48 +82,50 @@ void CycleColorsReverse(int from, int to) #endif } -} // namespace +// When brightness==0, then a==0 (identity mapping) +// When brightness==100, then a==-MaxAdjustment (maximum brightening) +constexpr float CalculateToneMappingParameter(int brightness) +{ + // Maximum adjustment factor (tweak this constant to change the effect strength) + constexpr float MaxAdjustment = 2.0F; + return -(brightness / 100.0f) * MaxAdjustment; +} -void palette_update(int first, int ncolor) +constexpr uint8_t MapTone(float a, uint8_t color) { - if (HeadlessMode) - return; + const auto x = static_cast(color / 255.0f); + // Our quadratic tone mapping: f(x) = a*x^2 + (1-a)*x. + const float y = std::clamp(a * x * x + (1.0f - a) * x, 0.0f, 1.0f); + return static_cast(y * 255.0f + 0.5f); +} - assert(Palette); - if (SDLC_SetSurfaceAndPaletteColors(PalSurface, Palette.get(), system_palette.data(), first, ncolor) < 0) { - ErrSdl(); - } - pal_surface_palette_version++; +void ApplyToneMappingSingleColor(SDL_Color &dst, const SDL_Color &src) +{ + const float a = CalculateToneMappingParameter(*GetOptions().Graphics.brightness); + dst.r = MapTone(a, src.r); + dst.g = MapTone(a, src.g); + dst.b = MapTone(a, src.b); } +} // namespace + // Applies a tone mapping curve based on the brightness slider value. // The brightness value is in the range [0, 100] where 0 is neutral (no change) // and 100 produces maximum brightening. -void ApplyToneMapping(std::array &dst, - const std::array &src, - int n) +void ApplyToneMapping(std::array &dst, std::span src) { // Get the brightness slider value (0 = neutral, 100 = max brightening) - int brightnessSlider = *GetOptions().Graphics.brightness; // New brightness setting. - - // Maximum adjustment factor (tweak this constant to change the effect strength) - const float maxAdjustment = 2.0f; - // Compute the quadratic parameter: - // When brightnessSlider==0, then a==0 (identity mapping) - // When brightnessSlider==100, then a== -maxAdjustment (maximum brightening) - float a = -(brightnessSlider / 100.0f) * maxAdjustment; + const int brightnessSlider = *GetOptions().Graphics.brightness; // Precompute a lookup table for speed. + const float a = CalculateToneMappingParameter(brightnessSlider); uint8_t toneMap[256]; for (int i = 0; i < 256; i++) { - float x = i / 255.0f; - // Our quadratic tone mapping: f(x) = a*x^2 + (1-a)*x. - const float y = std::clamp(a * x * x + (1.0f - a) * x, 0.0f, 1.0f); - toneMap[i] = static_cast(y * 255.0f + 0.5f); + toneMap[i] = MapTone(a, i); } // Apply the lookup table to each color channel in the palette. - for (int i = 0; i < n; i++) { + for (int i = 0; i < 256; i++) { dst[i].r = toneMap[src[i].r]; dst[i].g = toneMap[src[i].g]; dst[i].b = toneMap[src[i].b]; @@ -128,6 +133,18 @@ void ApplyToneMapping(std::array &dst, RedrawEverything(); } +void palette_update(int first, int ncolor) +{ + if (HeadlessMode) + return; + + assert(Palette); + if (SDLC_SetSurfaceAndPaletteColors(PalSurface, Palette.get(), system_palette.data(), first, ncolor) < 0) { + ErrSdl(); + } + pal_surface_palette_version++; +} + void palette_init() { LoadBrightness(); @@ -206,7 +223,7 @@ void IncreaseBrightness() if (brightnessValue < 100) { int newBrightness = std::min(brightnessValue + 5, 100); GetOptions().Graphics.brightness.SetValue(newBrightness); - ApplyToneMapping(system_palette, logical_palette, 256); + ApplyToneMapping(system_palette, logical_palette); palette_update(); } } @@ -217,7 +234,7 @@ void DecreaseBrightness() if (brightnessValue > 0) { int newBrightness = std::max(brightnessValue - 5, 0); GetOptions().Graphics.brightness.SetValue(newBrightness); - ApplyToneMapping(system_palette, logical_palette, 256); + ApplyToneMapping(system_palette, logical_palette); palette_update(); } } @@ -226,7 +243,7 @@ int UpdateBrightness(int brightness) { if (brightness >= 0) { GetOptions().Graphics.brightness.SetValue(brightness); - ApplyToneMapping(system_palette, logical_palette, 256); + ApplyToneMapping(system_palette, logical_palette); palette_update(); } @@ -267,7 +284,7 @@ void PaletteFadeIn(int fr, const std::array &srcPalette) if (demo::IsRunning()) fr = 0; - ApplyToneMapping(logical_palette, srcPalette, 256); + ApplyToneMapping(logical_palette, srcPalette); if (fr > 0) { const uint32_t tc = SDL_GetTicks(); @@ -368,10 +385,11 @@ void palette_update_hive() void palette_update_quest_palette(int n) { - int i = 32 - n; + // n is in [1, 32], so `i` is in [0, 31]. + const int i = 32 - n; logical_palette[i] = orig_palette[i]; - ApplyToneMapping(system_palette, logical_palette, 32); - palette_update(0, 31); + ApplyToneMappingSingleColor(system_palette[i], logical_palette[i]); + palette_update(i, 1); UpdateBlendedLookupTableSingleColor(i, logical_palette.data(), /*skipFrom=*/1, /*skipTo=*/31); } diff --git a/Source/engine/palette.h b/Source/engine/palette.h index e6993cd85..25b13d81b 100644 --- a/Source/engine/palette.h +++ b/Source/engine/palette.h @@ -7,6 +7,7 @@ #include #include +#include #include @@ -43,7 +44,7 @@ void palette_init(); void LoadPalette(const char *pszFileName, bool blend = true); void LoadRndLvlPal(dungeon_type l); void IncreaseBrightness(); -void ApplyToneMapping(std::array &dst, const std::array &src, int n); +void ApplyToneMapping(std::array &dst, std::span src); void DecreaseBrightness(); int UpdateBrightness(int sliderValue); void BlackPalette(); diff --git a/Source/interfac.cpp b/Source/interfac.cpp index 314ea51ac..9715e6f69 100644 --- a/Source/interfac.cpp +++ b/Source/interfac.cpp @@ -479,7 +479,7 @@ void ProgressEventHandler(const SDL_Event &event, uint16_t modState) if (RenderDirectlyToOutputSurface && PalSurface != nullptr) { // The loading thread sets `orig_palette`, so we make sure to use // our own palette for drawing the foreground. - ApplyToneMapping(logical_palette, ProgressEventHandlerState.palette, 256); + ApplyToneMapping(logical_palette, ProgressEventHandlerState.palette); // Ensure that all back buffers have the full progress bar. const void *initialPixels = PalSurface->pixels;