From 8b96d109ff5697bf4dcf5662997173c4b0c61cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 30 Sep 2026 11:29:17 -0600 Subject: [PATCH] IconCache: Know an icon's size before its texture exists GetDimensions only answered once a texture had been created, so the first MeasureNotice for a new icon (and after the texture was released for being unused) measured without it, and the icon was then drawn over the text. Read the size from the PNG header when the icon is inserted or loaded. Also remove a width adjustment in MeasureNotice that was overwritten before use. Co-Authored-By: Claude Opus 5.5 (1M context) --- Common/UI/IconCache.cpp | 30 +++++++++++++++++++++++------- Common/UI/IconCache.h | 2 ++ Common/UI/Notice.cpp | 1 - 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Common/UI/IconCache.cpp b/Common/UI/IconCache.cpp index 1abcea8030..467e3504cf 100644 --- a/Common/UI/IconCache.cpp +++ b/Common/UI/IconCache.cpp @@ -1,4 +1,5 @@ #include +#include #include "Common/UI/IconCache.h" #include "Common/UI/Context.h" @@ -38,6 +39,20 @@ struct DiskCacheEntry { }; static_assert(sizeof(DiskCacheEntry) == 24, "DiskCacheEntry is written to disk as is"); +// Reads the size from the header, so layout can use it before there's a texture. 0x0 if unknown. +static void PeekIconSize(const std::string &data, IconFormat format, int *width, int *height) { + *width = 0; + *height = 0; + if (format == IconFormat::PNG && data.size() >= sizeof(PNGHeaderPeek)) { + PNGHeaderPeek peek; + memcpy(&peek, data.data(), sizeof(peek)); + if (peek.IsValidPNGHeader()) { + *width = peek.Width(); + *height = peek.Height(); + } + } +} + void IconCache::SaveToFile(FILE *file) { std::unique_lock lock(lock_); @@ -115,6 +130,7 @@ bool IconCache::LoadFromFile(FILE *file) { entry.format = entryHeader.format; entry.insertedTimeStamp = entryHeader.insertedTimestamp; entry.usedTimeStamp = now; + PeekIconSize(entry.data, entry.format, &entry.width, &entry.height); cache_.emplace(key, entry); } @@ -224,14 +240,12 @@ bool IconCache::GetDimensions(std::string_view key, int *width, int *height) { } const auto &entry = iter->second; - if (entry.texture) { - // TODO: Store the width/height in the cache. - *width = entry.texture->Width(); - *height = entry.texture->Height(); - return true; - } else { + if (entry.width <= 0 || entry.height <= 0) { return false; } + *width = entry.width; + *height = entry.height; + return true; } bool IconCache::Contains(std::string_view key) { @@ -305,7 +319,9 @@ bool IconCache::InsertIcon(std::string_view key, IconFormat format, std::string } double now = time_now_d(); - cache_.emplace(key, Entry{ std::move(data), format, nullptr, now, now }); + Entry entry{ std::move(data), format, nullptr, now, now }; + PeekIconSize(entry.data, entry.format, &entry.width, &entry.height); + cache_.emplace(key, std::move(entry)); return true; } diff --git a/Common/UI/IconCache.h b/Common/UI/IconCache.h index 3c62379764..45eee95f54 100644 --- a/Common/UI/IconCache.h +++ b/Common/UI/IconCache.h @@ -63,6 +63,8 @@ private: double insertedTimeStamp; double usedTimeStamp; double uploadFailedTime; // When CreateTexture last failed, or 0. + int width; // From the image header, 0 if unknown. + int height; }; std::map> cache_; diff --git a/Common/UI/Notice.cpp b/Common/UI/Notice.cpp index 60d304b619..a5ee6c88eb 100644 --- a/Common/UI/Notice.cpp +++ b/Common/UI/Notice.cpp @@ -54,7 +54,6 @@ void MeasureNotice(const UIContext &dc, NoticeLevel level, std::string_view text // Normal entry but with a cached icon. int iconWidth, iconHeight; if (g_iconCache.GetDimensions(iconName, &iconWidth, &iconHeight)) { - *width += 5.0f + iconWidth; iconW = iconWidth; iconH = iconHeight; }