From dea182f1d842e102eda6b2d4ce149882a45458d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 23 Oct 2026 12:32:25 +0200 Subject: [PATCH] Use one UI drop shadow for text and atlas images, fix Windows build Text and the SVG button images are mixed on screen, so they now share UIDropShadowParams (3 UI units of blur, 0.66 intensity, no offset, the atlas's existing look) instead of the text having its own. draw_text.cpp included directly, whose min/max macros broke the std::max calls added here. Include CommonWindows.h instead. Co-Authored-By: Claude Opus 5.5 (1M context) --- Common/Render/AtlasGen.cpp | 6 +----- Common/Render/AtlasGen.h | 3 ++- Common/Render/DropShadow.cpp | 11 +++++++++++ Common/Render/DropShadow.h | 3 +++ Common/Render/Text/draw_text.cpp | 17 +++-------------- UI/UIAtlas.cpp | 2 +- unittest/UnitTest.cpp | 6 +++--- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Common/Render/AtlasGen.cpp b/Common/Render/AtlasGen.cpp index 0c805716b5..8707581e49 100644 --- a/Common/Render/AtlasGen.cpp +++ b/Common/Render/AtlasGen.cpp @@ -23,7 +23,6 @@ #include "Common/File/FileUtil.h" #include "Common/File/VFS/VFS.h" #include "Common/Render/AtlasGen.h" -#include "Common/Render/DropShadow.h" typedef unsigned short u16; @@ -241,10 +240,7 @@ void Add1PxTransparentBorder(Image &img) { img.h += 2; } -void AddDropShadow(Image &img, int shadowSize, float intensity) { - DropShadowParams params; - params.radius = std::max(1, (int)(shadowSize * img.scale)); - params.intensity = intensity; +void AddDropShadow(Image &img, const DropShadowParams ¶ms) { const int pad = params.Padding(); std::vector newData((img.w + pad * 2) * (img.h + pad * 2)); CompositeDropShadow(img.dat.data(), img.w, img.h, newData.data(), img.w + pad * 2, params); diff --git a/Common/Render/AtlasGen.h b/Common/Render/AtlasGen.h index 460339355d..dc10d41afa 100644 --- a/Common/Render/AtlasGen.h +++ b/Common/Render/AtlasGen.h @@ -5,6 +5,7 @@ #include "Common/CommonTypes.h" +#include "Common/Render/DropShadow.h" #include "Common/Render/TextureAtlas.h" struct ImageU8 { @@ -92,7 +93,7 @@ struct Image { }; // Slow. -void AddDropShadow(Image &img, int shadowSize, float intensity); +void AddDropShadow(Image &img, const DropShadowParams ¶ms); // To improve filtering. void Add1PxTransparentBorder(Image &img); diff --git a/Common/Render/DropShadow.cpp b/Common/Render/DropShadow.cpp index 51858d335e..29bb492599 100644 --- a/Common/Render/DropShadow.cpp +++ b/Common/Render/DropShadow.cpp @@ -20,6 +20,17 @@ #include "Common/Render/DropShadow.h" +// In UI units. +static constexpr float UI_SHADOW_RADIUS = 3.0f; +static constexpr float UI_SHADOW_INTENSITY = 0.66f; + +DropShadowParams UIDropShadowParams(float pixelsPerUnit) { + DropShadowParams params; + params.radius = std::max(1, (int)(UI_SHADOW_RADIUS * pixelsPerUnit)); + params.intensity = UI_SHADOW_INTENSITY; + return params; +} + void CompositeDropShadow(const u32 *src, int w, int h, u32 *dst, int dstStride, const DropShadowParams ¶ms) { const int radius = std::max(1, params.radius); const int pad = params.Padding(); diff --git a/Common/Render/DropShadow.h b/Common/Render/DropShadow.h index a163a1c655..95f8593ea2 100644 --- a/Common/Render/DropShadow.h +++ b/Common/Render/DropShadow.h @@ -38,6 +38,9 @@ struct DropShadowParams { } }; +// The UI's shadow, used for both the atlas images and text, so the two match on screen. +DropShadowParams UIDropShadowParams(float pixelsPerUnit); + // src is premultiplied RGBA with alpha in the top byte. Writes it composited over its shadow to dst, // (w + 2 * padding) x (h + 2 * padding) pixels with the image centered. Since the shadow is black, // only alpha changes under the image. diff --git a/Common/Render/Text/draw_text.cpp b/Common/Render/Text/draw_text.cpp index 3d6f9c0ad3..70242c7b62 100644 --- a/Common/Render/Text/draw_text.cpp +++ b/Common/Render/Text/draw_text.cpp @@ -3,11 +3,10 @@ #if defined(__linux__) || defined(__FreeBSD__) && !defined(__ANDROID__) # include #elif defined(_WIN32) -# include +# include "Common/CommonWindows.h" #endif #include -#include #include #include "ext/xxhash.h" @@ -46,13 +45,6 @@ static bool IsRenderDocLoaded() { #endif } -// Drop shadow blur radius and offset as fractions of the font size, which is in UI units. The bitmap -// has 1/dpiScale_ pixels per UI unit, so at higher DPI the blur spans more pixels. -static constexpr float SHADOW_BLUR = 1.0f / 7.0f; -static constexpr float SHADOW_OFFSET = 1.0f / 16.0f; -// Blurring thin strokes spreads them thin, so boost the shadow's alpha a bit (clamped). -static constexpr float SHADOW_STRENGTH = 1.5f; - size_t TextCacheKeyHash::operator()(const TextCacheKey &key) const { uint32_t maxWidthBits, scaleBits; memcpy(&maxWidthBits, &key.maxWidth, sizeof(maxWidthBits)); @@ -274,11 +266,8 @@ bool TextDrawer::DrawStringBitmapRect(std::vector &bitmapData, TextStri } void TextDrawer::AddDropShadow(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat srcFormat) const { - const float pixelsPerUnit = 1.0f / dpiScale_; - DropShadowParams params; - params.radius = std::max(1, (int)roundf(fontStyle_.sizePts * SHADOW_BLUR * pixelsPerUnit)); - params.offsetX = params.offsetY = std::max(1, (int)roundf(fontStyle_.sizePts * SHADOW_OFFSET * pixelsPerUnit)); - params.intensity = SHADOW_STRENGTH; + // The bitmap has 1/dpiScale_ pixels per UI unit, so at higher DPI the blur spans more pixels. + const DropShadowParams params = UIDropShadowParams(1.0f / dpiScale_); const int pad = params.Padding(); const int srcW = entry.bmWidth; diff --git a/UI/UIAtlas.cpp b/UI/UIAtlas.cpp index 3430114e95..c776eec9de 100644 --- a/UI/UIAtlas.cpp +++ b/UI/UIAtlas.cpp @@ -401,7 +401,7 @@ static bool GenerateUIAtlasImage(Atlas *atlas, float dpiScale, Image *dest, int if (!images[i].IsEmpty()) { if (imageIDs[i].addShadow) { // DEBUG_LOG(Log::G3D, "Adding drop shadow to %.*s", STR_VIEW(imageIDs[i].id)); - AddDropShadow(images[i], 3, 0.66f); + AddDropShadow(images[i], UIDropShadowParams(images[i].scale)); } else { // Make sure there are transparent pixels to filter from. Add1PxTransparentBorder(images[i]); diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 678d2d9163..6122f58ba3 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -3697,7 +3697,7 @@ bool TestTextDrawerCache() { EXPECT_EQ_INT((int)bitmap.size(), entry1.bmWidth * entry1.bmHeight * 4); const uint32_t *pixels = (const uint32_t *)bitmap.data(); auto pixel = [&](int x, int y) { return pixels[(y + pad) * entry1.bmWidth + x + pad]; }; - // The text stays opaque white, the shadow is black and partly transparent below-right of it, and the far corner is empty. + // The text stays opaque white, the shadow around it is black and partly transparent, and the far corner is empty. EXPECT_EQ_INT(pixel(8, 8), 0xFFFFFFFF); EXPECT_EQ_INT(pixel(12, 12) & 0x00FFFFFF, 0); EXPECT_TRUE((pixel(12, 12) >> 24) > 0); @@ -3708,7 +3708,7 @@ bool TestTextDrawerCache() { return true; } -// The atlas shadow has no offset, so it pads the image evenly and stays symmetric around it. +// The UI shadow has no offset, so it pads the image evenly and stays symmetric around it. bool TestAtlasDropShadow() { Image img; img.scale = 2.0f; @@ -3718,7 +3718,7 @@ bool TestAtlasDropShadow() { img.set1(x, y, 0xFFFFFFFF); } } - AddDropShadow(img, 3, 0.66f); + AddDropShadow(img, UIDropShadowParams(img.scale)); EXPECT_EQ_INT(img.w, 8 + 12); EXPECT_EQ_INT(img.h, 8 + 12); EXPECT_EQ_HEX(img.get1(6 + 3, 6 + 3), 0xFFFFFFFF);