diff --git a/QuickView/HeavyLanePool.cpp b/QuickView/HeavyLanePool.cpp index 4befef3..bb37e79 100644 --- a/QuickView/HeavyLanePool.cpp +++ b/QuickView/HeavyLanePool.cpp @@ -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"); } } @@ -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 { @@ -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 { diff --git a/QuickView/ImageEngine.cpp b/QuickView/ImageEngine.cpp index 0f9b48b..27d77b8 100644 --- a/QuickView/ImageEngine.cpp +++ b/QuickView/ImageEngine.cpp @@ -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); @@ -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! @@ -1080,6 +1080,7 @@ void ImageEngine::FastLane::QueueWorker() { auto safeFrame = std::make_shared(); if (rawFrame.IsSvg()) { safeFrame->format = rawFrame.format; + safeFrame->formatDetails = rawFrame.formatDetails; safeFrame->width = rawFrame.width; safeFrame->height = rawFrame.height; safeFrame->svg = std::make_unique(); @@ -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; @@ -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(); diff --git a/QuickView/ImageLoader.cpp b/QuickView/ImageLoader.cpp index 4daa3e9..3aa8433 100644 --- a/QuickView/ImageLoader.cpp +++ b/QuickView/ImageLoader.cpp @@ -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); @@ -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 @@ -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 diff --git a/QuickView/Toolbar.cpp b/QuickView/Toolbar.cpp index b9bab0e..b04bb08 100644 --- a/QuickView/Toolbar.cpp +++ b/QuickView/Toolbar.cpp @@ -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}, diff --git a/QuickView/UIRenderer.cpp b/QuickView/UIRenderer.cpp index 17d8778..f5b946f 100644 --- a/QuickView/UIRenderer.cpp +++ b/QuickView/UIRenderer.cpp @@ -8,6 +8,8 @@ #include "HelpOverlay.h" #include "EditState.h" #include +#include +#include #pragma comment(lib, "psapi.lib") @@ -1599,7 +1601,7 @@ std::vector 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 }); @@ -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]; } @@ -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") { @@ -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; diff --git a/QuickView/UpdateManager.cpp b/QuickView/UpdateManager.cpp index 39084f7..cc95a35 100644 --- a/QuickView/UpdateManager.cpp +++ b/QuickView/UpdateManager.cpp @@ -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) { @@ -294,9 +294,9 @@ VersionInfo UpdateManager::ParseJson(const std::string& json) { if (v.is()) { picojson::object& jsonObj = v.get(); - 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; } diff --git a/QuickView/main.cpp b/QuickView/main.cpp index c90ada3..c0440b6 100644 --- a/QuickView/main.cpp +++ b/QuickView/main.cpp @@ -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) { @@ -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; } } @@ -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. @@ -9817,7 +9839,7 @@ 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); @@ -9825,7 +9847,7 @@ void OnPaint(HWND hwnd) { } 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