Skip to content
Merged
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
12 changes: 6 additions & 6 deletions QuickView/HeavyLanePool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ using namespace QuickView;

namespace {
bool IsCopyOnlyLoaderName(const std::wstring& loaderName) {
return loaderName.find(L"LODCache Slice") != std::wstring::npos ||
loaderName.find(L"Zero-Copy") != std::wstring::npos ||
loaderName.find(L"RAM Copy") != std::wstring::npos ||
loaderName.find(L"MMF Copy") != std::wstring::npos;
return loaderName.contains(L"LODCache Slice") ||
loaderName.contains(L"Zero-Copy") ||
loaderName.contains(L"RAM Copy") ||
loaderName.contains(L"MMF Copy");
}
}

Expand Down Expand Up @@ -1276,7 +1276,7 @@ void HeavyLanePool::PerformDecode(int workerId, const JobInfo& job, std::stop_to
// [v8.4 Fix] If the Base Layer is a Fake 1x1, its real MP/s is 0.
// This would cause the auto-regulator to maliciously throttle the pool to < 3 threads,
// crippling our N+1 Native Region Decoding. We simulate 100 MP/s to unlock full core usage!
if (loaderName.find(L"Fake Base") != std::wstring::npos) {
if (loaderName.contains(L"Fake Base")) {
OutputDebugStringW(L"[HeavyPool] Base Layer is Fake. Simulating 100.0 MP/s baseline to unlock Titan tiles.\n");
RecordBaselineSample(10000000.0, 100.0, srcWidth, srcHeight, isProgressiveJPEG);
} else {
Expand All @@ -1287,7 +1287,7 @@ void HeavyLanePool::PerformDecode(int workerId, const JobInfo& job, std::stop_to
// [JXL] Check if base layer is a true progressive DC preview.
// Do not treat "Fake Base Prog" as native-region capable.
if (m_titanFormat.load() == QuickView::TitanFormat::JXL) {
if (loaderName.find(L"Prog DC") != std::wstring::npos) {
if (loaderName.contains(L"Prog DC")) {
m_isProgressiveJXL = true;
OutputDebugStringW(L"[HeavyPool] Detected Progressive JXL. Enabling native Region Decoding!\n");
} else {
Expand Down
13 changes: 7 additions & 6 deletions QuickView/ImageEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ void ImageEngine::DispatchImageLoad(const std::wstring& path, ImageID imageId, u

// [v9.0] Smart RAW Quality Check
// RAW files require strict quality matching (Preview vs Full) for A/B comparison
if (info.format.find(L"RAW") != std::wstring::npos) {
if (info.format.contains(L"RAW")) {
bool wantFull = m_config.ForceRawDecode;
bool hasFull = (cachedFrame->quality == QuickView::DecodeQuality::Full);

Expand Down Expand Up @@ -336,7 +336,7 @@ void ImageEngine::DispatchImageLoad(const std::wstring& path, ImageID imageId, u
// 2. Recursive RAW Check
// If it's a RAW file with an embedded thumb, check the preview resolution.
// "RAW" detection: check if string contains RAW or format check from loader
if (info.format.find(L"RAW") != std::wstring::npos) {
if (info.format.contains(L"RAW")) {
// [v9.9] If ForceRawDecode is enabled, RAW Full Decode is computationally intensive.
// Always route to HeavyLane to avoid blocking FastLane (UI thread responsiveness).
// [Fix] Use member config!
Expand Down Expand Up @@ -1080,6 +1080,7 @@ void ImageEngine::FastLane::QueueWorker() {
auto safeFrame = std::make_shared<QuickView::RawImageFrame>();
if (rawFrame.IsSvg()) {
safeFrame->format = rawFrame.format;
safeFrame->formatDetails = rawFrame.formatDetails;
safeFrame->width = rawFrame.width;
safeFrame->height = rawFrame.height;
safeFrame->svg = std::make_unique<QuickView::RawImageFrame::SvgData>();
Expand All @@ -1104,14 +1105,13 @@ void ImageEngine::FastLane::QueueWorker() {
e.rawFrame = safeFrame;


// [Fix] Unified Populate Metadata
// PeekHeader dimensions strictly untrusted for FastLane
// RAW/TIFF formats with IFD thumbs may return incorrect info.width/height
e.metadata.Width = rawFrame.width;
e.metadata.Height = rawFrame.height;
e.metadata.Format = info.format; // [Scout] Direct from PeekHeader

// [v5.3] Metadata is now populated by LoadToFrame (Unified path)
// No need to call ReadMetadata separately or access global variables.
// We don't call LoadToFrame with pMetadata in FastLane currently,
// but info.format from PeekHeader is sufficient for Scout mismatch check.

// [Fix] Propagate EXIF Orientation from Decoder to Metadata (Critical for AutoRotate)
e.metadata.ExifOrientation = rawFrame.exifOrientation;
Expand Down Expand Up @@ -1441,6 +1441,7 @@ void ImageEngine::AddToCache(int index, const std::wstring& path, std::shared_pt
if (frame->IsSvg()) {
// SVG: Copy the SVG data struct
cachedFrame->format = frame->format;
cachedFrame->formatDetails = frame->formatDetails;
cachedFrame->width = frame->width;
cachedFrame->height = frame->height;
cachedFrame->svg = std::make_unique<QuickView::RawImageFrame::SvgData>();
Expand Down
10 changes: 7 additions & 3 deletions QuickView/ImageLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3568,7 +3568,7 @@ HRESULT CImageLoader::LoadToMemory(LPCWSTR filePath, IWICBitmap** ppBitmap, std:
ppBitmap
);

if (SUCCEEDED(hrBitmap) && pLoaderName && pLoaderName->find(L"WIC") != std::wstring::npos && !usedNativeScaling) {
if (SUCCEEDED(hrBitmap) && pLoaderName && pLoaderName->contains(L"WIC") && !usedNativeScaling) {
UINT w = 0, h = 0;
(*ppBitmap)->GetSize(&w, &h);
wchar_t buf[32]; swprintf_s(buf, L" [%ux%u]", w, h);
Expand Down Expand Up @@ -5066,8 +5066,8 @@ HRESULT CImageLoader::LoadImageUnified(LPCWSTR filePath, const DecodeContext& ct
// [v9.2] Use path-based detection directly (handles RAW extensions properly)
std::wstring fmt = DetectFormatFromContent(filePath);

// Debug: Log detected format

// [Fix] Ensure Format is stored in metadata for extension mismatch detection (main.cpp)
result.metadata.Format = fmt;

// 3. Dispatch

Expand Down Expand Up @@ -8790,6 +8790,10 @@ HRESULT CImageLoader::LoadToFrame(LPCWSTR filePath, QuickView::RawImageFrame* ou
if (pMetadata->FileSize == 0) {
PopulateFileStats(filePath, pMetadata);
}
// [Fix] Final Guarantee: Format must not be empty for extension mismatch check
if (pMetadata->Format.empty()) {
pMetadata->Format = DetectFormatFromContent(filePath);
}
}

// [Fix] Software Downscale if Unified Codec ignored target dimensions
Expand Down
2 changes: 1 addition & 1 deletion QuickView/Toolbar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Toolbar::Toolbar() {
{},
false,
false,
true}, // Hidden if no mismatch
false}, // Hidden if no mismatch

{ToolbarButtonID::CompareToggle, ICON_COMPARE[0], {}, true, false},

Expand Down
10 changes: 6 additions & 4 deletions QuickView/UIRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include "HelpOverlay.h"
#include "EditState.h"
#include <psapi.h>
#include <algorithm>
#include <ranges>

#pragma comment(lib, "psapi.lib")

Expand Down Expand Up @@ -1599,7 +1601,7 @@ std::vector<InfoRow> UIRenderer::BuildGridRows(const CImageLoader::ImageMetadata
}

// Extraction: Subsampling / Chroma
if (metadata.FormatDetails.find(L"4:") != std::wstring::npos) {
if (metadata.FormatDetails.contains(L"4:")) {
size_t pos = metadata.FormatDetails.find(L"4:");
std::wstring chroma = metadata.FormatDetails.substr(pos, 5);
rows.push_back({ L"\U0001F3A8", L"Chroma", chroma, L"", L"", TruncateMode::None, false });
Expand Down Expand Up @@ -2179,7 +2181,7 @@ namespace {
const std::wstring tokens[] = { L"4:4:4", L"4:2:2", L"4:2:0", L"4:0:0" };
const int ranks[] = { 3, 2, 1, 0 };
for (size_t i = 0; i < 4; ++i) {
if (details.find(tokens[i]) != std::wstring::npos) {
if (details.contains(tokens[i])) {
rank = ranks[i];
return tokens[i];
}
Expand Down Expand Up @@ -2421,7 +2423,7 @@ void UIRenderer::DrawCompareInfoHUD(ID2D1DeviceContext* dc) {
for (const auto& group : hudGroups) {
bool hasData = false;
for (const auto& l : group.labels) {
if (std::find(labels.begin(), labels.end(), l) != labels.end()) {
if (std::ranges::contains(labels, l)) {

// In Lite(0) and Normal(1) mode, hide identical metrics (except File)
if (hudMode < 2 && l != L"File") {
Expand Down Expand Up @@ -2492,7 +2494,7 @@ void UIRenderer::DrawCompareInfoHUD(ID2D1DeviceContext* dc) {
// Group visibility check taking identical hiding into account
bool groupHasData = false;
for (const auto& l : group.labels) {
if (std::find(labels.begin(), labels.end(), l) != labels.end()) {
if (std::ranges::contains(labels, l)) {
if (hudMode < 2 && l != L"File") {
const InfoRow* lRow = nullptr;
const InfoRow* rRow = nullptr;
Expand Down
8 changes: 4 additions & 4 deletions QuickView/UpdateManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ void UpdateManager::CheckThread(int delaySeconds) {
bool downloadSuccess = false;

// Determine if ZIP or EXE
bool isZip = (m_remoteInfo.downloadUrl.find(".zip") != std::string::npos);
bool isZip = (m_remoteInfo.downloadUrl.contains(".zip"));

// Adjust destination based on actual type (if url says .zip but we defaulted to .exe in dest)
if (isZip) {
Expand Down Expand Up @@ -294,9 +294,9 @@ VersionInfo UpdateManager::ParseJson(const std::string& json) {

if (v.is<picojson::object>()) {
picojson::object& jsonObj = v.get<picojson::object>();
if (jsonObj.find("version") != jsonObj.end()) info.version = jsonObj.at("version").to_str();
if (jsonObj.find("url") != jsonObj.end()) info.downloadUrl = jsonObj.at("url").to_str();
if (jsonObj.find("changelog") != jsonObj.end()) info.changelog = jsonObj.at("changelog").to_str();
if (jsonObj.contains("version")) info.version = jsonObj.at("version").to_str();
if (jsonObj.contains("url")) info.downloadUrl = jsonObj.at("url").to_str();
if (jsonObj.contains("changelog")) info.changelog = jsonObj.at("changelog").to_str();
}
return info;
}
Expand Down
38 changes: 30 additions & 8 deletions QuickView/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3831,30 +3831,46 @@ struct FormatExtRule {
std::wstring_view alt1 = {};
std::wstring_view alt2 = {};
std::wstring_view alt3 = {};
std::wstring_view alt4 = {};
std::wstring_view alt5 = {};
};

static constexpr FormatExtRule g_formatRules[] = {
// Specific compound/derived names first to avoid prefix/substring matches
{ L"wbmp", L".wbmp" },
{ L"jpeg xl", L".jxl" },
{ L"jxl", L".jxl" },
{ L"libavif", L".avif" },
{ L"tinyexr", L".exr" },
{ L"jpeg (mmf)", L".jpg", L".jpeg", L".jpe", L".jfif" },

// Core formats
{ L"jpeg", L".jpg", L".jpeg", L".jpe", L".jfif" },
{ L"png", L".png" },
{ L"webp", L".webp" },
{ L"avif", L".avif", L"libavif" },
{ L"avif", L".avif" },
{ L"gif", L".gif" },
{ L"bmp", L".bmp", L".dib" },
{ L"tiff", L".tiff", L".tif" },
{ L"tif", L".tiff", L".tif" },
{ L"heif", L".heic", L".heif" },
{ L"heic", L".heic", L".heif" },
{ L"jxl", L".jxl", L"jpeg xl" },
{ L"hdr", L".hdr", L".pic" },
{ L"psd", L".psd", L".psb" },
{ L"exr", L".exr", L"tinyexr" },
{ L"exr", L".exr" },
{ L"qoi", L".qoi" },
{ L"tga", L".tga" },
{ L"tga", L".tga", L".icb", L".vda", L".vst" },
{ L"pcx", L".pcx" },
{ L"svg", L".svg" },
{ L"ico", L".ico" },
{ L"wbmp", L".wbmp" },
{ L"pnm", L".pnm", L".pgm", L".ppm" }
{ L"pnm", L".pnm", L".pgm", L".ppm", L".pbm" },
{ L"pgm", L".pgm", L".pnm", L".ppm", L".pbm" },
{ L"ppm", L".ppm", L".pnm", L".pgm", L".pbm" },
{ L"pbm", L".pbm", L".pnm", L".pgm", L".ppm" },

// [v10.1] New: Support all RAW formats in extension check
{ L"raw", L".dng", L".arw", L".nef", L".cr2", L".cr3", L".raf" },
{ L"dds", L".dds" },
};

static std::wstring_view GetPrimaryExtensionForFormat(std::wstring_view format) {
Expand Down Expand Up @@ -3893,6 +3909,8 @@ bool CheckExtensionMismatch(std::wstring_view path, std::wstring_view format) {
if (!rule.alt1.empty() && ext == rule.alt1) return false;
if (!rule.alt2.empty() && ext == rule.alt2) return false;
if (!rule.alt3.empty() && ext == rule.alt3) return false;
if (!rule.alt4.empty() && ext == rule.alt4) return false;
if (!rule.alt5.empty() && ext == rule.alt5) return false;
return true;
}
}
Expand Down Expand Up @@ -8666,6 +8684,10 @@ void ProcessEngineEvents(HWND hwnd) {
g_toolbar.SetExtensionWarning(false);
}

// Refresh Layout to recalculate Warning Icon bounds
RECT rc; GetClientRect(hwnd, &rc);
g_toolbar.UpdateLayout((float)rc.right, (float)rc.bottom);

// [v5.3] Set EXIF Orientation based on AutoRotate config
if (g_config.AutoRotate) {
// Trust the metadata.
Expand Down Expand Up @@ -9817,15 +9839,15 @@ void OnPaint(HWND hwnd) {
// [No-DC JXL Guard] For fake/tiny placeholder bases, force tile scheduling immediately.
std::wstring fmtUpper = titanMeta.Format;
std::transform(fmtUpper.begin(), fmtUpper.end(), fmtUpper.begin(), ::towupper);
bool isJxlLike = (fmtUpper.find(L"JXL") != std::wstring::npos || fmtUpper.find(L"JPEG XL") != std::wstring::npos);
bool isJxlLike = (fmtUpper.contains(L"JXL") || fmtUpper.contains(L"JPEG XL"));
if (!isJxlLike && !g_imagePath.empty()) {
std::wstring pathLower = g_imagePath;
std::transform(pathLower.begin(), pathLower.end(), pathLower.begin(), ::towlower);
if (pathLower.ends_with(L".jxl")) isJxlLike = true;
}

constexpr float kVirtualNoDcRatio = 0.125f; // 1:8
bool fakeBase = (titanMeta.LoaderName.find(L"Fake Base") != std::wstring::npos);
bool fakeBase = (titanMeta.LoaderName.contains(L"Fake Base"));
bool tinyPreview = (previewW <= 2.0f || previewH <= 2.0f);
bool weakPreview = (previewW <= 16.0f || previewH <= 16.0f); // Expanded threshold for 4x4 or 8x8

Expand Down