Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 142 additions & 19 deletions QuickView/UIRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#pragma comment(lib, "psapi.lib")

#include "ImageEngine.h" // [v3.1] Access for HasEmbeddedThumb
#include "DisplayColorInfo.h"

// External globals (retained - these are global state needed by overlays)
extern Toolbar g_toolbar;
Expand Down Expand Up @@ -225,6 +226,14 @@ HitTestResult UIRenderer::HitTest(float x, float y) {
const D2D1_RECT_F& rowRect = m_infoGrid[i].hitRect;
if (x >= rowRect.left && x <= rowRect.right &&
y >= rowRect.top && y <= rowRect.bottom) {

if (m_infoGrid[i].label == L"HDR_TOGGLE") {
result.type = UIHitResult::HdrInfoToggle;
result.rowIndex = (int)i;
m_hoverRowIndex = (int)i;
return result;
}

result.type = UIHitResult::InfoRow;
result.rowIndex = (int)i;

Expand Down Expand Up @@ -1592,7 +1601,19 @@ UIRenderer::TooltipInfo UIRenderer::GetTooltipInfo(const std::wstring& label) co
return { L"", L"", L"", L"" };
}

std::vector<InfoRow> UIRenderer::BuildGridRows(const CImageLoader::ImageMetadata& metadata, const std::wstring& imagePath, bool showAdvanced) const {
UIRenderer::RenderPath UIRenderer::DetermineRenderPath(const CImageLoader::ImageMetadata& metadata, const QuickView::DisplayColorState& colorState) const {
bool isHdr = metadata.hdrMetadata.isValid || metadata.hdrMetadata.hasGainMap;

if (colorState.advancedColorActive && isHdr) {
return RenderPath::Direct;
} else if (!colorState.advancedColorActive && isHdr && !metadata.hdrMetadata.hasGainMap) {
return RenderPath::ToneMapped;
} else {
return RenderPath::NativeCMS;
}
}

std::vector<InfoRow> UIRenderer::BuildGridRows(const CImageLoader::ImageMetadata& metadata, const std::wstring& imagePath, bool showAdvanced, const QuickView::DisplayColorState* colorState) const {
std::vector<InfoRow> rows;
if (imagePath.empty()) return rows;

Expand Down Expand Up @@ -1657,25 +1678,119 @@ std::vector<InfoRow> UIRenderer::BuildGridRows(const CImageLoader::ImageMetadata
rows.push_back({ L"\U0001F3AF", L"Focal", metadata.Focal, focalSub, L"", TruncateMode::None, false });
}

if (!metadata.ColorSpace.empty()) {
bool isHdrImage = metadata.hdrMetadata.isValid || metadata.hdrMetadata.hasGainMap;

if (!metadata.ColorSpace.empty() && !isHdrImage) {
std::wstring colorText = metadata.ColorSpace;
if (metadata.HasEmbeddedColorProfile) colorText += L" [ICC]";
rows.push_back({ L"\U0001F3A8", L"Color", colorText, L"", L"", TruncateMode::None, false });
}

if (metadata.hdrMetadata.isValid || metadata.hdrMetadata.hasGainMap) {
const std::wstring hdrSummary = BuildHdrSummary(metadata.hdrMetadata);
const std::wstring hdrDetail = BuildHdrDetail(metadata.hdrMetadata);
if (!hdrSummary.empty() || !hdrDetail.empty()) {
rows.push_back({
L"\U0001F31F",
L"HDR",
hdrSummary.empty() ? L"Metadata" : hdrSummary,
hdrDetail,
hdrSummary + (hdrDetail.empty() ? L"" : L"\n" + hdrDetail),
TruncateMode::EndEllipsis,
false
});
if (isHdrImage) {
std::wstring toggleLabel = m_hdrInfoExpanded ? L"[-] 收起 HDR 专业信息" : L"[+] 展开 HDR 专业信息";
rows.push_back({
L"\U0001F31F",
L"HDR_TOGGLE",
toggleLabel,
L"",
L"Click to toggle HDR professional metadata",
TruncateMode::None,
true
});

if (m_hdrInfoExpanded) {
// Tier 1: Basic Identity
std::wstring drStr;
if (metadata.hdrMetadata.hasGainMap) {
drStr = L"Ultra HDR (Gain Map)";
} else if (metadata.hdrMetadata.transfer == QuickView::TransferFunction::PQ) {
drStr = L"HDR10 (PQ)";
} else if (metadata.hdrMetadata.transfer == QuickView::TransferFunction::HLG) {
drStr = L"HLG";
} else {
drStr = L"SDR / Unknown HDR";
}
rows.push_back({ L" \U0001F4F8", L"D.Range", drStr, L"", L"", TruncateMode::None, false });

// Color Space and Bit Depth
std::wstring colorSpaceText = metadata.ColorSpace;
if (metadata.HasEmbeddedColorProfile) colorSpaceText += L" [ICC]";
rows.push_back({ L" \U0001F3A8", L"C.Space", colorSpaceText.empty() ? L"Unknown" : colorSpaceText, L"", L"", TruncateMode::None, false });

std::wstring bitDepth = ExtractBitDepth(metadata.FormatDetails);
rows.push_back({ L" \U000025D1", L"B.Depth", bitDepth == L"-" ? L"Unknown" : bitDepth, L"", L"", TruncateMode::None, false });

// Tier 2: HDR Metadata
if (metadata.hdrMetadata.isValid) {
std::wstring eotf = QuickView::ToString(metadata.hdrMetadata.transfer);
if (metadata.hdrMetadata.transfer == QuickView::TransferFunction::PQ) eotf = L"SMPTE ST 2084 (PQ)";
else if (metadata.hdrMetadata.transfer == QuickView::TransferFunction::HLG) eotf = L"ARIB STD-B67 (HLG)";
else if (metadata.hdrMetadata.transfer == QuickView::TransferFunction::SRGB) eotf = L"sRGB Gamma";
rows.push_back({ L" \U0001F4DD", L"EOTF", eotf, L"", L"", TruncateMode::None, false });

if (metadata.hdrMetadata.maxCLLNits > 0.0f) {
wchar_t cll[32]; swprintf_s(cll, L"%.0f nits", metadata.hdrMetadata.maxCLLNits);
rows.push_back({ L" \U00002600", L"MaxCLL", cll, L"", L"", TruncateMode::None, false });
}
if (metadata.hdrMetadata.maxFALLNits > 0.0f) {
wchar_t fall[32]; swprintf_s(fall, L"%.0f nits", metadata.hdrMetadata.maxFALLNits);
rows.push_back({ L" \U0001F506", L"MaxFALL", fall, L"", L"", TruncateMode::None, false });
}
if (metadata.hdrMetadata.masteringMaxNits > 0.0f) {
wchar_t mastering[64];
swprintf_s(mastering, L"Min: %.3f, Max: %.0f nits", metadata.hdrMetadata.masteringMinNits, metadata.hdrMetadata.masteringMaxNits);
rows.push_back({ L" \U0001F5A5", L"Master", mastering, L"", L"", TruncateMode::None, false });
}
}

// Tier 3: Active Render Pipeline
if (colorState) {
RenderPath path = DetermineRenderPath(metadata, *colorState);
std::wstring renderPathStr;
if (path == RenderPath::Direct) renderPathStr = L"DirectComposition scRGB (FP16)";
else if (path == RenderPath::ToneMapped) renderPathStr = L"GPU Tone Mapped to SDR";
else if (path == RenderPath::NativeCMS) renderPathStr = L"D2D Color Management (SDR)";
rows.push_back({ L" \U0001F6E0", L"R.Path", renderPathStr, L"", L"", TruncateMode::None, false });

float headroom = colorState->GetHdrHeadroomStops();
float ratio = powf(2.0f, headroom);
wchar_t headroomStr[64];
if (ratio > 1.05f) {
swprintf_s(headroomStr, L"%.1fx (%.0f / %.0f nits)", ratio, colorState->sdrWhiteLevelNits, colorState->maxLuminanceNits);
} else {
swprintf_s(headroomStr, L"1.0x (SDR Display)");
}
rows.push_back({ L" \U0001F4CA", L"Headrm", headroomStr, L"", L"", TruncateMode::None, false });
}

// Tier 4: Gain Map Specifics
if (metadata.hdrMetadata.hasGainMap) {
rows.push_back({ L" \U0001F5BC", L"Base", L"SDR", L"", L"", TruncateMode::None, false });
rows.push_back({ L" \U0001F4C8", L"G.Map", L"Present (ISO 21496-1)", L"", L"", TruncateMode::None, false });

if (metadata.hdrMetadata.gainMapBaseHeadroom >= 0.0f || metadata.hdrMetadata.gainMapAlternateHeadroom > 0.0f) {
float minGain = powf(2.0f, metadata.hdrMetadata.gainMapBaseHeadroom);
float maxGain = powf(2.0f, metadata.hdrMetadata.gainMapAlternateHeadroom);
wchar_t ratioStr[64];
swprintf_s(ratioStr, L"Min: %.1fx, Max: %.1fx", minGain, maxGain);
rows.push_back({ L" \U00002696", L"G.Ratio", ratioStr, L"", L"", TruncateMode::None, false });
}

if (colorState) {
float baseHR = metadata.hdrMetadata.gainMapBaseHeadroom;
float altHR = metadata.hdrMetadata.gainMapAlternateHeadroom;
float appliedHR = metadata.hdrMetadata.gainMapAppliedHeadroom;

float range = altHR - baseHR;
float weight = 0.0f;
if (range > 0.001f) {
weight = (std::max)(0.0f, (std::min)(1.0f, (appliedHR - baseHR) / range));
}
wchar_t weightStr[32];
swprintf_s(weightStr, L"%.2f", weight);
rows.push_back({ L" \U0001F300", L"B.Weight", weightStr, L"", L"", TruncateMode::None, false });
}
}
}
}

Expand Down Expand Up @@ -1706,7 +1821,9 @@ std::vector<InfoRow> UIRenderer::BuildGridRows(const CImageLoader::ImageMetadata
std::wstring quality = ExtractQualityEstimate(metadata.FormatDetails);
std::wstring formatFlags = BuildFormatFlagsSummary(metadata.FormatDetails);

AppendFormatToken(formatTokens, bitDepth == L"-" ? L"" : bitDepth);
if (!isHdrImage) {
AppendFormatToken(formatTokens, bitDepth == L"-" ? L"" : bitDepth);
}
AppendFormatToken(formatTokens, chroma == L"-" ? L"" : chroma);
AppendFormatToken(formatTokens, quality == L"-" ? L"" : quality);
AppendFormatToken(formatTokens, formatFlags);
Expand Down Expand Up @@ -1839,7 +1956,12 @@ namespace {
}

void UIRenderer::BuildInfoGrid() {
m_infoGrid = BuildGridRows(g_currentMetadata, g_imagePath, false);
if (m_compEngine) {
const QuickView::DisplayColorState& colorState = m_compEngine->GetDisplayColorState();
m_infoGrid = BuildGridRows(g_currentMetadata, g_imagePath, false, &colorState);
} else {
m_infoGrid = BuildGridRows(g_currentMetadata, g_imagePath, false, nullptr);
}
}

void UIRenderer::DrawInfoGrid(ID2D1DeviceContext* dc, float startX, float startY, float width) {
Expand Down Expand Up @@ -2600,8 +2722,9 @@ void UIRenderer::DrawCompareInfoHUD(ID2D1DeviceContext* dc) {
// Use centralized row building
// Note: We need some context for these (like path) if we want tooltips to work fully
// But for comparison, labeling is key.
auto leftRows = BuildGridRows(leftMeta, L"Left", true);
auto rightRows = BuildGridRows(rightMeta, L"Right", true);
const QuickView::DisplayColorState* colorState = m_compEngine ? &m_compEngine->GetDisplayColorState() : nullptr;
auto leftRows = BuildGridRows(leftMeta, L"Left", true, colorState);
auto rightRows = BuildGridRows(rightMeta, L"Right", true, colorState);

// --- Smart Logic (Quality Assessment) ---
auto GetQualityTag = [](const CImageLoader::ImageMetadata& meta, int& outColor) -> std::wstring {
Expand Down
15 changes: 13 additions & 2 deletions QuickView/UIRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "pch.h"
#include "CompositionEngine.h"
#include "ImageLoader.h" // For ImageMetadata
#include "DisplayColorInfo.h"
#include <dwrite.h>
#include <array>
#include "EditState.h"
Expand Down Expand Up @@ -58,7 +59,8 @@ enum class UIHitResult {
GPSLink, // Click to open in Maps
InfoRow, // Click to copy row content
HudToggleLite, // Click to toggle HUD Lite mode
HudToggleExpand // Click to toggle HUD Expand mode
HudToggleExpand,// Click to toggle HUD Expand mode
HdrInfoToggle // Toggle Expand/Collapse HDR Info
};

// Window Controls Hit Test Result
Expand Down Expand Up @@ -107,6 +109,7 @@ class UIRenderer {
D2D1_RECT_F GetPanelCloseRect() const { return m_panelCloseRect; }

// ===== UI 状态更新 =====
void ToggleHdrInfoExpanded() { m_hdrInfoExpanded = !m_hdrInfoExpanded; MarkStaticDirty(); }
void SetOSD(const std::wstring& text, float opacity, D2D1_COLOR_F color = D2D1::ColorF(D2D1::ColorF::White), OSDPosition pos = OSDPosition::Bottom);
void SetCompareOSD(const std::wstring& left, const std::wstring& right, float opacity, D2D1_COLOR_F color = D2D1::ColorF(D2D1::ColorF::White));
void SetDebugHUDVisible(bool visible) { m_showDebugHUD = visible; MarkDynamicDirty(); }
Expand Down Expand Up @@ -157,7 +160,14 @@ class UIRenderer {
std::wstring reference; // Typical range
};

std::vector<InfoRow> BuildGridRows(const CImageLoader::ImageMetadata& metadata, const std::wstring& imagePath, bool showAdvanced = false) const;
enum class RenderPath {
Direct, // HDR 直通
ToneMapped, // SDR 降级
NativeCMS // SDR 原生
};
RenderPath DetermineRenderPath(const CImageLoader::ImageMetadata& metadata, const QuickView::DisplayColorState& colorState) const;

std::vector<InfoRow> BuildGridRows(const CImageLoader::ImageMetadata& metadata, const std::wstring& imagePath, bool showAdvanced = false, const QuickView::DisplayColorState* colorState = nullptr) const;
TooltipInfo GetTooltipInfo(const std::wstring& label) const;

private:
Expand Down Expand Up @@ -197,6 +207,7 @@ class UIRenderer {
// Info Panel Hit Rects
D2D1_RECT_F m_panelToggleRect = {};
D2D1_RECT_F m_panelCloseRect = {};
bool m_hdrInfoExpanded = false; // HDR Info Panel Toggle
D2D1_RECT_F m_gpsCoordRect = {};
D2D1_RECT_F m_gpsLinkRect = {};
D2D1_RECT_F m_lastHUDRect = {}; // Track HUD area for hit testing
Expand Down
7 changes: 7 additions & 0 deletions QuickView/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7032,6 +7032,13 @@ SKIP_EDGE_NAV:;
RequestRepaint(PaintLayer::All);
return 0;

case UIHitResult::HdrInfoToggle:
if (g_uiRenderer) {
g_uiRenderer->ToggleHdrInfoExpanded();
RequestRepaint(PaintLayer::All);
}
return 0;

case UIHitResult::InfoRow: if (hit.rowIndex != -2) { // Normal Info Panel row
if (CopyToClipboard(hwnd, hit.payload)) {
g_osd.Show(hwnd, AppStrings::OSD_Copied, false);
Expand Down