From 7dd694bf43bdd226eaa7c634f87a85722efa902f Mon Sep 17 00:00:00 2001 From: Florian M Date: Fri, 28 Mar 2025 20:20:32 +0100 Subject: [PATCH 01/20] first set of tweaks --- src/JPEGView/EXIFDisplay.cpp | 17 +++-- src/JPEGView/EXIFDisplayCtl.cpp | 121 ++++++++++++++++++++++++++------ src/JPEGView/EXIFReader.cpp | 17 ++--- src/JPEGView/GUIControls.cpp | 2 +- src/JPEGView/JPEGView.vcxproj | 8 +-- src/JPEGView/MainDlg.cpp | 9 ++- src/JPEGView/stdafx.h | 4 +- src/WICLoader/WICLoader.vcxproj | 8 +-- 8 files changed, 137 insertions(+), 49 deletions(-) diff --git a/src/JPEGView/EXIFDisplay.cpp b/src/JPEGView/EXIFDisplay.cpp index ae510804..8a711cf9 100644 --- a/src/JPEGView/EXIFDisplay.cpp +++ b/src/JPEGView/EXIFDisplay.cpp @@ -28,7 +28,7 @@ static CRect InflateRect(const CRect& rect, float fAmount) { return r; } -CEXIFDisplay::CEXIFDisplay(HWND hWnd, INotifiyMouseCapture* pNotifyMouseCapture) : CPanel(hWnd, pNotifyMouseCapture, true, true) { +CEXIFDisplay::CEXIFDisplay(HWND hWnd, INotifiyMouseCapture* pNotifyMouseCapture) : CPanel(hWnd, pNotifyMouseCapture, false, true) { m_bShowHistogram = false; m_nGap = (int)(m_fDPIScale * 10); m_nTab1 = 0; @@ -44,8 +44,10 @@ CEXIFDisplay::CEXIFDisplay(HWND hWnd, INotifiyMouseCapture* pNotifyMouseCapture) m_nNoHistogramSize = CSize(0, 0); m_pHistogram = NULL; - AddUserPaintButton(ID_btnShowHideHistogram, &ShowHistogramTooltip, &PaintShowHistogramBtn, NULL, this, this); - AddUserPaintButton(ID_btnClose, CNLS::GetString(_T("Close")), &PaintCloseBtn, NULL, this, this); + LPCTSTR nullTooltip = NULL; // Needed because passing NULL directly to overloaded function makes it ambiguous + + AddUserPaintButton(ID_btnShowHideHistogram, nullTooltip, &PaintShowHistogramBtn, NULL, this, this); + AddUserPaintButton(ID_btnClose, nullTooltip, &PaintCloseBtn, NULL, this, this); CURLCtrl* pLinkLocation = AddURL(ID_urlLocation, _T(""), _T(""), false); pLinkLocation->SetShow(false, false); } @@ -202,7 +204,7 @@ CRect CEXIFDisplay::PanelRect() { int nLen1 = 0, nLen2 = 0; std::list::iterator iter; for (iter = m_lines.begin( ); iter != m_lines.end( ); iter++ ) { - if (iter->Desc != NULL) { + if (iter->Desc != NULL && m_bShowHistogram) { ::GetTextExtentPoint32(dc, iter->Desc, (int)_tcslen(iter->Desc), &size); m_nLineHeight = max(m_nLineHeight, size.cy); nMaxLength1 = max(nMaxLength1, size.cx); @@ -218,7 +220,7 @@ CRect CEXIFDisplay::PanelRect() { } int nButtonWidth = (int)(m_fDPIScale * BUTTON_SIZE); - bool bNeedsExpansionForButton = (nMaxLength2 - max(nLen1, nLen2)) < nButtonWidth + m_nGap; + bool bNeedsExpansionForButton = (m_bShowHistogram) && (nMaxLength2 - max(nLen1, nLen2)) < nButtonWidth + m_nGap; int nNeededWidthNoBorders = max(nTitleLength, nMaxLength1 + nMaxLength2 + m_nGap) + (bNeedsExpansionForButton ? m_nGap + nButtonWidth : 0); int nExpansionX = 0, nExpansionY = 0; if (m_bShowHistogram) { @@ -238,6 +240,9 @@ CRect CEXIFDisplay::PanelRect() { m_nNoHistogramSize = CSize(m_size.cx - nExpansionX, m_size.cy - nExpansionY); m_nTab1 = nMaxLength1 + m_nGap; + if (!m_bShowHistogram) { + m_nTab1 = 0; + } } return CRect(m_pos, m_size); } @@ -289,7 +294,7 @@ void CEXIFDisplay::OnPaint(CDC & dc, const CPoint& offset) { std::list::iterator iter; for (iter = m_lines.begin( ); iter != m_lines.end( ); iter++ ) { - if (iter->Desc != NULL) { + if (iter->Desc != NULL && m_bShowHistogram) { ::TextOut(dc, nX + m_nGap, nRunningY, iter->Desc, (int)_tcslen(iter->Desc)); } if (iter->Value != NULL) { diff --git a/src/JPEGView/EXIFDisplayCtl.cpp b/src/JPEGView/EXIFDisplayCtl.cpp index 65cb4ae4..ffd55e3b 100644 --- a/src/JPEGView/EXIFDisplayCtl.cpp +++ b/src/JPEGView/EXIFDisplayCtl.cpp @@ -114,8 +114,31 @@ void CEXIFDisplayCtl::FillEXIFDataDisplay() { LPCTSTR sComment = NULL; m_pEXIFDisplay->AddPrefix(sPrefix); m_pEXIFDisplay->AddTitle(sFileTitle); - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Image width:")), CurrentImage()->OrigWidth()); - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Image height:")), CurrentImage()->OrigHeight()); + + CString sFileSize = _T(""); + if (!CurrentImage()->IsClipboardImage()) { + HANDLE hFile = ::CreateFile(pFileList->Current(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + __int64 fileSize = 0; + ::GetFileSizeEx(hFile, (PLARGE_INTEGER)&fileSize); + ::CloseHandle(hFile); + if (fileSize > 0) { + const TCHAR* units[] = { _T("Bytes"), _T("KiB"), _T("MiB"), _T("GiB") }; + double value = fileSize; + int exponent = 0; + while (value >= 1024 && exponent < sizeof(units) / sizeof(units[0]) - 1) { + value /= 1024.0; + exponent++; + } + sFileSize.Format(_T(" %.1f %s"), value, units[exponent]); + } + } + } + + CString sFormattedSize; + sFormattedSize.Format(_T("%d × %d%s"), CurrentImage()->OrigWidth(), CurrentImage()->OrigHeight(), sFileSize); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Size:")), sFormattedSize); + bool bShowMoreDetails = m_pEXIFDisplay->GetShowHistogram(); if (!CurrentImage()->IsClipboardImage()) { CEXIFReader* pEXIFReader = CurrentImage()->GetEXIFReader(); CRawMetadata* pRawMetaData = CurrentImage()->GetRawMetadata(); @@ -125,46 +148,100 @@ void CEXIFDisplayCtl::FillEXIFDataDisplay() { sComment = pEXIFReader->GetImageDescription(); } if (pEXIFReader->GetAcquisitionTimePresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Acquisition date:")), pEXIFReader->GetAcquisitionTime()); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Acquired:")), pEXIFReader->GetAcquisitionTime()); } else if (pEXIFReader->GetDateTimePresent()) { m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exif Date Time:")), pEXIFReader->GetDateTime()); } else { const FILETIME* pFileTime = pFileList->CurrentModificationTime(); if (pFileTime != NULL) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Modification date:")), *pFileTime); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Modified:")), *pFileTime); } } - if (pEXIFReader->IsGPSInformationPresent()) { - CString sGPSLocation = CreateGPSString(pEXIFReader->GetGPSLatitude(), pEXIFReader->GetGPSLongitude()); - m_pEXIFDisplay->SetGPSLocation(sGPSLocation, CreateGPSURL(pEXIFReader->GetGPSLatitude(), pEXIFReader->GetGPSLongitude())); - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Location:")), sGPSLocation, true); - if (pEXIFReader->IsGPSAltitudePresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Altitude (m):")), pEXIFReader->GetGPSAltitude(), 0); + if (pEXIFReader->GetExposureTimePresent() && pEXIFReader->GetISOSpeedPresent()) { + CString sFormattedExposureTime; + Rational exposure = pEXIFReader->GetExposureTime(); + if (exposure.Denominator == 1) { + sFormattedExposureTime.Format(_T("%d"), exposure.Numerator); + } + else if (exposure.Numerator > 9) { + if (exposure.Numerator * 3 < exposure.Denominator) { + sFormattedExposureTime.Format(_T("%d"), exposure.Denominator / exposure.Numerator); + } + else { + sFormattedExposureTime.Format(_T("%g"), double(exposure.Numerator) / exposure.Denominator); + } + } + else { + sFormattedExposureTime.Format(_T("%d/%d"), exposure.Numerator, exposure.Denominator); + } + + CString padding; + if ((int)pEXIFReader->GetISOSpeed() < 1000) { + padding = CString(" "); } + if ((int)pEXIFReader->GetISOSpeed() < 100) { + padding = CString(" "); + } + + CString sFormattedExposure; + sFormattedExposure.Format(_T("ISO %d %s %s sec"), + (int)pEXIFReader->GetISOSpeed(), + padding, + sFormattedExposureTime + ); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exposure:")), sFormattedExposure); } - if (pEXIFReader->GetCameraModelPresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Camera model:")), pEXIFReader->GetCameraModel()); + if (pEXIFReader->GetFocalLengthPresent() && pEXIFReader->GetFNumberPresent()) { + CString sFormattedLens; + CString padding; + if (pEXIFReader->GetFocalLength() < 100) { + padding = CString(" "); + } + if (pEXIFReader->GetFocalLength() < 10) { + padding = CString(" "); + } + sFormattedLens.Format(_T("%g mm %s 𝑓/%g"), + pEXIFReader->GetFocalLength(), + padding, + pEXIFReader->GetFNumber() + ); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Lens:")), sFormattedLens); } + /* if (pEXIFReader->GetExposureTimePresent()) { m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exposure time (s):")), pEXIFReader->GetExposureTime()); } - if (pEXIFReader->GetExposureBiasPresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exposure bias (EV):")), pEXIFReader->GetExposureBias(), 2); - } - if (pEXIFReader->GetFlashFiredPresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Flash fired:")), pEXIFReader->GetFlashFired() ? CNLS::GetString(_T("yes")) : CNLS::GetString(_T("no"))); - } if (pEXIFReader->GetFocalLengthPresent()) { m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Focal length (mm):")), pEXIFReader->GetFocalLength(), 1); } if (pEXIFReader->GetFNumberPresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("F-Number:")), pEXIFReader->GetFNumber(), 1); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("𝑓-Number:")), pEXIFReader->GetFNumber(), 1); } if (pEXIFReader->GetISOSpeedPresent()) { m_pEXIFDisplay->AddLine(CNLS::GetString(_T("ISO Speed:")), (int)pEXIFReader->GetISOSpeed()); - } - if (pEXIFReader->GetSoftwarePresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Software:")), pEXIFReader->GetSoftware()); + }*/ + + if (bShowMoreDetails) { + if (pEXIFReader->IsGPSInformationPresent()) { + CString sGPSLocation = CreateGPSString(pEXIFReader->GetGPSLatitude(), pEXIFReader->GetGPSLongitude()); + m_pEXIFDisplay->SetGPSLocation(sGPSLocation, CreateGPSURL(pEXIFReader->GetGPSLatitude(), pEXIFReader->GetGPSLongitude())); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Location:")), sGPSLocation, true); + if (pEXIFReader->IsGPSAltitudePresent()) { + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Altitude (m):")), pEXIFReader->GetGPSAltitude(), 0); + } + } + if (pEXIFReader->GetExposureBiasPresent() && (pEXIFReader->GetExposureBias() < -0.00001 || pEXIFReader->GetExposureBias() > 0.00001)) { + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exposure bias (EV):")), pEXIFReader->GetExposureBias(), 2); + } + if (pEXIFReader->GetFlashFiredPresent()) { + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Flash fired:")), pEXIFReader->GetFlashFired() ? CNLS::GetString(_T("yes")) : CNLS::GetString(_T("no"))); + } + if (pEXIFReader->GetCameraModelPresent()) { + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Camera:")), pEXIFReader->GetCameraModel()); + } + if (pEXIFReader->GetSoftwarePresent()) { + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Software:")), pEXIFReader->GetSoftware()); + } } } else if (pRawMetaData != NULL) { diff --git a/src/JPEGView/EXIFReader.cpp b/src/JPEGView/EXIFReader.cpp index cc32f50d..c12bf2a2 100644 --- a/src/JPEGView/EXIFReader.cpp +++ b/src/JPEGView/EXIFReader.cpp @@ -2,6 +2,7 @@ #include "EXIFReader.h" #include "ImageProcessingTypes.h" #include "Helpers.h" +#include double CEXIFReader::UNKNOWN_DOUBLE_VALUE = 283740261.192864; @@ -284,14 +285,14 @@ CEXIFReader::CEXIFReader(void* pApp1Block, EImageFormat eImageFormat) m_bLittleEndian = bLittleEndian; uint8* pIFD0 = pTIFFHeader + ReadUInt(pTIFFHeader + 4, bLittleEndian); - if (pIFD0 - m_pApp1 >= nApp1Size) { + if (pIFD0 - m_pApp1 > nApp1Size) { return; } // Read IFD0 uint16 nNumTags = ReadUShort(pIFD0, bLittleEndian); pIFD0 += 2; uint8* pLastIFD0 = pIFD0 + nNumTags*12; - if (pLastIFD0 - m_pApp1 + 4 >= nApp1Size) { + if (pLastIFD0 - m_pApp1 + 4 > nApp1Size) { return; } uint32 nOffsetIFD1 = ReadUInt(pLastIFD0, bLittleEndian); @@ -352,13 +353,13 @@ CEXIFReader::CEXIFReader(void* pApp1Block, EImageFormat eImageFormat) return; } uint8* pEXIFIFD = pTIFFHeader + nOffsetEXIF; - if (pEXIFIFD - m_pApp1 >= nApp1Size) { + if (pEXIFIFD - m_pApp1 > nApp1Size) { return; } nNumTags = ReadUShort(pEXIFIFD, bLittleEndian); pEXIFIFD += 2; uint8* pLastEXIF = pEXIFIFD + nNumTags*12; - if (pLastEXIF - m_pApp1 >= nApp1Size) { + if (pLastEXIF - m_pApp1 > nApp1Size) { return; } uint8* pTagAcquisitionDate = FindTag(pEXIFIFD, pLastEXIF, 0x9003, bLittleEndian); @@ -403,13 +404,13 @@ CEXIFReader::CEXIFReader(void* pApp1Block, EImageFormat eImageFormat) if (nOffsetIFD1 != 0) { m_pIFD1 = pTIFFHeader + nOffsetIFD1; - if (m_pIFD1 - m_pApp1 >= nApp1Size || m_pIFD1 - m_pApp1 < 0) { + if (m_pIFD1 - m_pApp1 > nApp1Size || m_pIFD1 - m_pApp1 < 0) { return; } nNumTags = ReadUShort(m_pIFD1, bLittleEndian); m_pIFD1 += 2; uint8* pLastIFD1 = m_pIFD1 + nNumTags*12; - if (pLastIFD1 - m_pApp1 >= nApp1Size) { + if (pLastIFD1 - m_pApp1 > nApp1Size) { return; } m_pLastIFD1 = pLastIFD1; @@ -485,13 +486,13 @@ void CEXIFReader::ReadGPSData(uint8* pTIFFHeader, uint8* pTagGPSIFD, int nApp1Si return; } uint8* pGPSIFD = pTIFFHeader + nOffsetGPS; - if (pGPSIFD - m_pApp1 >= nApp1Size) { + if (pGPSIFD - m_pApp1 > nApp1Size) { return; } int nNumTags = ReadUShort(pGPSIFD, bLittleEndian); pGPSIFD += 2; uint8* pLastGPS = pGPSIFD + nNumTags * 12; - if (pLastGPS - m_pApp1 >= nApp1Size) { + if (pLastGPS - m_pApp1 > nApp1Size) { return; } diff --git a/src/JPEGView/GUIControls.cpp b/src/JPEGView/GUIControls.cpp index 1f8b154b..eda4c6be 100644 --- a/src/JPEGView/GUIControls.cpp +++ b/src/JPEGView/GUIControls.cpp @@ -466,7 +466,7 @@ void CButtonCtrl::Draw(CDC & dc, CRect position, bool bBlack) { hPen = ::CreatePen(PS_SOLID, 1, (m_bDragging && m_bHighlight) ? CSettingsProvider::This().ColorHighlight() : m_bActive ? CSettingsProvider::This().ColorSelected() : CSettingsProvider::This().ColorGUI()); hOldPen = dc.SelectPen(hPen); } - HelpersGUI::DrawRectangle(dc, position); + // HelpersGUI::DrawRectangle(dc, position); if (m_sText.GetLength() > 0) { HelpersGUI::SelectDefaultGUIFont(dc); dc.SetBkMode(TRANSPARENT); diff --git a/src/JPEGView/JPEGView.vcxproj b/src/JPEGView/JPEGView.vcxproj index 6801186b..9160c7a1 100644 --- a/src/JPEGView/JPEGView.vcxproj +++ b/src/JPEGView/JPEGView.vcxproj @@ -26,22 +26,22 @@ Application Unicode - v142 + v143 Application Unicode - v142 + v143 Application Unicode - v142 + v143 Application Unicode - v142 + v143 diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 2721016a..6b35c1bb 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -396,7 +396,7 @@ LRESULT CMainDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam if (!m_bFullScreenMode) { // Window mode, set correct window size SetCurrentWindowStyle(); - if (!IsAdjustWindowToImage()) { + if (!IsAdjustWindowToImage() && !sp.DefaultMaximized()) { CRect windowRect = CMultiMonitorSupport::GetDefaultWindowRect(); this->SetWindowPos(HWND_TOP, windowRect.left, windowRect.top, windowRect.Width(), windowRect.Height(), SWP_NOZORDER | SWP_NOCOPYBITS); } else { @@ -1766,7 +1766,12 @@ void CMainDlg::ExecuteCommand(int nCommand) { CSize(MIN_WND_WIDTH, MIN_WND_HEIGHT), defaultWindowRect.Size(), dZoom, m_pCurrentImage, false, true, m_bWindowBorderless); - this->SetWindowPos(HWND_TOP, windowRect.left, windowRect.top, windowRect.Width(), windowRect.Height(), SWP_NOZORDER | SWP_NOCOPYBITS); + if (sp.DefaultMaximized()) { + this->ShowWindow(SW_MAXIMIZE); + } + else { + this->SetWindowPos(HWND_TOP, windowRect.left, windowRect.top, windowRect.Width(), windowRect.Height(), SWP_NOZORDER | SWP_NOCOPYBITS); + } this->MouseOn(); m_bSpanVirtualDesktop = false; } else { diff --git a/src/JPEGView/stdafx.h b/src/JPEGView/stdafx.h index 763bbf71..f89353c8 100644 --- a/src/JPEGView/stdafx.h +++ b/src/JPEGView/stdafx.h @@ -9,8 +9,8 @@ #pragma warning(disable:4800) // Change these values to use different versions -#define WINVER 0x0501 -#define _WIN32_WINNT 0x0501 +#define WINVER 0x0A00 +#define _WIN32_WINNT 0x0A00 #define _WIN32_IE 0x0600 #define _RICHEDIT_VER 0x0300 diff --git a/src/WICLoader/WICLoader.vcxproj b/src/WICLoader/WICLoader.vcxproj index 0a2ad09b..ac488318 100644 --- a/src/WICLoader/WICLoader.vcxproj +++ b/src/WICLoader/WICLoader.vcxproj @@ -29,27 +29,27 @@ DynamicLibrary true Unicode - v142 + v143 DynamicLibrary true Unicode - v142 + v143 DynamicLibrary false true Unicode - v142 + v143 DynamicLibrary false true Unicode - v142 + v143 From 9f6fb07b5371d60ad1dab267517b5190f141cdbd Mon Sep 17 00:00:00 2001 From: Florian M Date: Fri, 28 Mar 2025 21:37:15 +0100 Subject: [PATCH 02/20] new layouting for two-column exif --- src/JPEGView/EXIFDisplay.cpp | 15 ++++- src/JPEGView/EXIFDisplay.h | 6 +- src/JPEGView/EXIFDisplayCtl.cpp | 101 ++++++++++++++++---------------- 3 files changed, 65 insertions(+), 57 deletions(-) diff --git a/src/JPEGView/EXIFDisplay.cpp b/src/JPEGView/EXIFDisplay.cpp index 8a711cf9..9c51fec2 100644 --- a/src/JPEGView/EXIFDisplay.cpp +++ b/src/JPEGView/EXIFDisplay.cpp @@ -107,8 +107,8 @@ void CEXIFDisplay::SetGPSLocation(LPCTSTR sLocation, LPCTSTR sURL) { pLinkLocation->SetShow(true, false); } -void CEXIFDisplay::AddLine(LPCTSTR sDescription, LPCTSTR sValue, bool valueIsURL) { - m_lines.push_back(TextLine(CopyStrAlloc(sDescription), CopyStrAlloc(sValue), valueIsURL)); +void CEXIFDisplay::AddLine(LPCTSTR sDescription, LPCTSTR sValue, bool valueIsURL, bool sameLine) { + m_lines.push_back(TextLine(CopyStrAlloc(sDescription), CopyStrAlloc(sValue), valueIsURL, sameLine && !m_bShowHistogram)); } void CEXIFDisplay::AddLine(LPCTSTR sDescription, double dValue, int nDigits) { @@ -203,7 +203,11 @@ CRect CEXIFDisplay::PanelRect() { int nLen1 = 0, nLen2 = 0; std::list::iterator iter; + int nLineCountExcludingSameLineLines = 0; for (iter = m_lines.begin( ); iter != m_lines.end( ); iter++ ) { + if (!(iter->SameLine)) { + nLineCountExcludingSameLineLines += 1; + } if (iter->Desc != NULL && m_bShowHistogram) { ::GetTextExtentPoint32(dc, iter->Desc, (int)_tcslen(iter->Desc), &size); m_nLineHeight = max(m_nLineHeight, size.cy); @@ -228,8 +232,10 @@ CRect CEXIFDisplay::PanelRect() { nExpansionY = HelpersGUI::ScaleToScreen(HISTOGRAM_HEIGHT) + m_nGap; } + + m_size = CSize(nNeededWidthNoBorders + m_nGap*2 + nExpansionX, - m_nTitleHeight + (int)m_lines.size()*m_nLineHeight + m_nGap * 2 + nExpansionY); + m_nTitleHeight + nLineCountExcludingSameLineLines*m_nLineHeight + m_nGap * 2 + nExpansionY); if (m_sComment != NULL) { CRect rectComment(0, 0, m_size.cx - m_nGap*2, HelpersGUI::ScaleToScreen(200)); @@ -294,6 +300,9 @@ void CEXIFDisplay::OnPaint(CDC & dc, const CPoint& offset) { std::list::iterator iter; for (iter = m_lines.begin( ); iter != m_lines.end( ); iter++ ) { + if (iter->SameLine) { + nRunningY -= m_nLineHeight; + } if (iter->Desc != NULL && m_bShowHistogram) { ::TextOut(dc, nX + m_nGap, nRunningY, iter->Desc, (int)_tcslen(iter->Desc)); } diff --git a/src/JPEGView/EXIFDisplay.h b/src/JPEGView/EXIFDisplay.h index a8f7352f..6224fb22 100644 --- a/src/JPEGView/EXIFDisplay.h +++ b/src/JPEGView/EXIFDisplay.h @@ -25,7 +25,7 @@ class CEXIFDisplay : public CPanel void AddTitle(LPCTSTR sTitle); void SetComment(LPCTSTR sComment); void SetGPSLocation(LPCTSTR sLocation, LPCTSTR sURL); - void AddLine(LPCTSTR sDescription, LPCTSTR sValue, bool valueIsURL = false); + void AddLine(LPCTSTR sDescription, LPCTSTR sValue, bool valueIsURL = false, bool sameLine = false); void AddLine(LPCTSTR sDescription, double dValue, int nDigits); void AddLine(LPCTSTR sDescription, int nValue); void AddLine(LPCTSTR sDescription, const SYSTEMTIME &time); // time is in local time @@ -49,15 +49,17 @@ class CEXIFDisplay : public CPanel private: struct TextLine { - TextLine(LPCTSTR desc, LPCTSTR value, bool valueIsURL = false) { + TextLine(LPCTSTR desc, LPCTSTR value, bool valueIsURL = false, bool sameLine = false) { Desc = desc; Value = value; ValueIsURL = valueIsURL; + SameLine = sameLine; } LPCTSTR Desc; LPCTSTR Value; bool ValueIsURL; + bool SameLine; }; bool m_bShowHistogram; diff --git a/src/JPEGView/EXIFDisplayCtl.cpp b/src/JPEGView/EXIFDisplayCtl.cpp index ffd55e3b..52469aae 100644 --- a/src/JPEGView/EXIFDisplayCtl.cpp +++ b/src/JPEGView/EXIFDisplayCtl.cpp @@ -19,9 +19,9 @@ static int GetFileNameHeight(HDC dc) { static CString CreateGPSString(GPSCoordinate* latitude, GPSCoordinate* longitude) { const int BUFF_SIZE = 96; TCHAR buff[BUFF_SIZE]; - _stprintf_s(buff, BUFF_SIZE, _T("%.0f° %.0f' %.0f'' %s / %.0f° %.0f' %.0f'' %s"), - latitude->Degrees, latitude->Minutes, latitude->Seconds, latitude->GetReference(), - longitude->Degrees, longitude->Minutes, longitude->Seconds, longitude->GetReference()); + _stprintf_s(buff, BUFF_SIZE, _T("%s%.0f°%.0f′%.0f″ %s%.0f°%.0f′%.0f″"), + latitude->GetReference(), latitude->Degrees, latitude->Minutes, latitude->Seconds, + longitude->GetReference(), longitude->Degrees, longitude->Minutes, longitude->Seconds); return CString(buff); } @@ -115,6 +115,17 @@ void CEXIFDisplayCtl::FillEXIFDataDisplay() { m_pEXIFDisplay->AddPrefix(sPrefix); m_pEXIFDisplay->AddTitle(sFileTitle); + CString sFormattedSize; + sFormattedSize.Format(_T("%d × %d"), CurrentImage()->OrigWidth(), CurrentImage()->OrigHeight()); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Size:")), sFormattedSize); + + CString secondColPadding = _T(""); + if (!m_pEXIFDisplay->GetShowHistogram()) { + for (int i = 0; i < (max(2 * sFormattedSize.GetLength(), 18) + 3); i++) { + secondColPadding += _T(' '); + }; + } + CString sFileSize = _T(""); if (!CurrentImage()->IsClipboardImage()) { HANDLE hFile = ::CreateFile(pFileList->Current(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); @@ -130,14 +141,12 @@ void CEXIFDisplayCtl::FillEXIFDataDisplay() { value /= 1024.0; exponent++; } - sFileSize.Format(_T(" %.1f %s"), value, units[exponent]); + sFileSize.Format(_T("%s%.1f %s"), secondColPadding, value, units[exponent]); } } + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("File size:")), sFileSize, false, true); } - CString sFormattedSize; - sFormattedSize.Format(_T("%d × %d%s"), CurrentImage()->OrigWidth(), CurrentImage()->OrigHeight(), sFileSize); - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Size:")), sFormattedSize); bool bShowMoreDetails = m_pEXIFDisplay->GetShowHistogram(); if (!CurrentImage()->IsClipboardImage()) { CEXIFReader* pEXIFReader = CurrentImage()->GetEXIFReader(); @@ -150,62 +159,55 @@ void CEXIFDisplayCtl::FillEXIFDataDisplay() { if (pEXIFReader->GetAcquisitionTimePresent()) { m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Acquired:")), pEXIFReader->GetAcquisitionTime()); } else if (pEXIFReader->GetDateTimePresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exif Date Time:")), pEXIFReader->GetDateTime()); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exif date:")), pEXIFReader->GetDateTime()); } else { const FILETIME* pFileTime = pFileList->CurrentModificationTime(); if (pFileTime != NULL) { m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Modified:")), *pFileTime); } } - if (pEXIFReader->GetExposureTimePresent() && pEXIFReader->GetISOSpeedPresent()) { - CString sFormattedExposureTime; + + if (pEXIFReader->GetISOSpeedPresent()) { + CString sFormattedIso; + sFormattedIso.Format(_T("ISO %d"), (int)pEXIFReader->GetISOSpeed()); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("ISO speed:")), sFormattedIso); + } + + if (pEXIFReader->GetExposureTimePresent()) { + CString sFormattedExposureTimeRational; Rational exposure = pEXIFReader->GetExposureTime(); if (exposure.Denominator == 1) { - sFormattedExposureTime.Format(_T("%d"), exposure.Numerator); + sFormattedExposureTimeRational.Format(_T("%d"), exposure.Numerator); } else if (exposure.Numerator > 9) { if (exposure.Numerator * 3 < exposure.Denominator) { - sFormattedExposureTime.Format(_T("%d"), exposure.Denominator / exposure.Numerator); + sFormattedExposureTimeRational.Format(_T("%d"), exposure.Denominator / exposure.Numerator); } else { - sFormattedExposureTime.Format(_T("%g"), double(exposure.Numerator) / exposure.Denominator); + sFormattedExposureTimeRational.Format(_T("%g"), double(exposure.Numerator) / exposure.Denominator); } } else { - sFormattedExposureTime.Format(_T("%d/%d"), exposure.Numerator, exposure.Denominator); + sFormattedExposureTimeRational.Format(_T("%d/%d"), exposure.Numerator, exposure.Denominator); } - - CString padding; - if ((int)pEXIFReader->GetISOSpeed() < 1000) { - padding = CString(" "); - } - if ((int)pEXIFReader->GetISOSpeed() < 100) { - padding = CString(" "); - } - - CString sFormattedExposure; - sFormattedExposure.Format(_T("ISO %d %s %s sec"), - (int)pEXIFReader->GetISOSpeed(), - padding, - sFormattedExposureTime - ); - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exposure:")), sFormattedExposure); + CString sFormattedExposureTime; + sFormattedExposureTime.Format(_T("%s%s sec"), secondColPadding, sFormattedExposureTimeRational); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exposure time:")), sFormattedExposureTime, false, true); } - if (pEXIFReader->GetFocalLengthPresent() && pEXIFReader->GetFNumberPresent()) { - CString sFormattedLens; - CString padding; - if (pEXIFReader->GetFocalLength() < 100) { - padding = CString(" "); - } - if (pEXIFReader->GetFocalLength() < 10) { - padding = CString(" "); - } - sFormattedLens.Format(_T("%g mm %s 𝑓/%g"), - pEXIFReader->GetFocalLength(), - padding, - pEXIFReader->GetFNumber() - ); - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Lens:")), sFormattedLens); + if (pEXIFReader->GetFocalLengthPresent()) { + CString sFormattedFocalLength; + sFormattedFocalLength.Format(_T("%g mm"), pEXIFReader->GetFocalLength()); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Focal length:")), sFormattedFocalLength); + } + if (pEXIFReader->GetFNumberPresent()) { + CString sFormattedFNumber; + sFormattedFNumber.Format(_T("%s𝑓/%g"), secondColPadding, pEXIFReader->GetFNumber()); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Aperture:")), sFormattedFNumber, false, true); + } + if (pEXIFReader->IsGPSInformationPresent()) { + CString sGPSLocation = CreateGPSString(pEXIFReader->GetGPSLatitude(), pEXIFReader->GetGPSLongitude()); + m_pEXIFDisplay->SetGPSLocation(sGPSLocation, CreateGPSURL(pEXIFReader->GetGPSLatitude(), pEXIFReader->GetGPSLongitude())); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Location:")), sGPSLocation, true); } /* if (pEXIFReader->GetExposureTimePresent()) { @@ -222,13 +224,8 @@ void CEXIFDisplayCtl::FillEXIFDataDisplay() { }*/ if (bShowMoreDetails) { - if (pEXIFReader->IsGPSInformationPresent()) { - CString sGPSLocation = CreateGPSString(pEXIFReader->GetGPSLatitude(), pEXIFReader->GetGPSLongitude()); - m_pEXIFDisplay->SetGPSLocation(sGPSLocation, CreateGPSURL(pEXIFReader->GetGPSLatitude(), pEXIFReader->GetGPSLongitude())); - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Location:")), sGPSLocation, true); - if (pEXIFReader->IsGPSAltitudePresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Altitude (m):")), pEXIFReader->GetGPSAltitude(), 0); - } + if (pEXIFReader->IsGPSAltitudePresent()) { + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Altitude (m):")), pEXIFReader->GetGPSAltitude(), 0); } if (pEXIFReader->GetExposureBiasPresent() && (pEXIFReader->GetExposureBias() < -0.00001 || pEXIFReader->GetExposureBias() > 0.00001)) { m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exposure bias (EV):")), pEXIFReader->GetExposureBias(), 2); From 79183536eee2dcce3cbbef0ef0c4d64b9a66ceb1 Mon Sep 17 00:00:00 2001 From: Florian M Date: Sun, 30 Mar 2025 09:56:40 +0200 Subject: [PATCH 03/20] exif view fixes, switch toggle zoom logic, fix mbutton dblclck --- src/JPEGView/EXIFDisplayCtl.cpp | 112 ++++++++++++++++---------------- src/JPEGView/EXIFDisplayCtl.h | 3 + src/JPEGView/JPEGImage.cpp | 5 +- src/JPEGView/MainDlg.cpp | 51 +++++++++++---- src/JPEGView/MainDlg.h | 2 + 5 files changed, 103 insertions(+), 70 deletions(-) diff --git a/src/JPEGView/EXIFDisplayCtl.cpp b/src/JPEGView/EXIFDisplayCtl.cpp index 52469aae..da8d584d 100644 --- a/src/JPEGView/EXIFDisplayCtl.cpp +++ b/src/JPEGView/EXIFDisplayCtl.cpp @@ -96,6 +96,25 @@ void CEXIFDisplayCtl::OnPrePaintMainDlg(HDC hPaintDC) { } } +CString CEXIFDisplayCtl::FormatRational(Rational rational) { + CString sFormatted; + if (rational.Denominator == 1) { + sFormatted.Format(_T("%d"), rational.Numerator); + } + else if (rational.Numerator > 9) { + if (rational.Numerator * 3 < rational.Denominator) { + sFormatted.Format(_T("1/%d"), rational.Denominator/ rational.Numerator); + } + else { + sFormatted.Format(_T("%g"), double(rational.Numerator) / rational.Denominator); + } + } + else { + sFormatted.Format(_T("%d/%d"), rational.Numerator, rational.Denominator); + } + return sFormatted; +} + void CEXIFDisplayCtl::FillEXIFDataDisplay() { m_pEXIFDisplay->ClearTexts(); @@ -127,7 +146,7 @@ void CEXIFDisplayCtl::FillEXIFDataDisplay() { } CString sFileSize = _T(""); - if (!CurrentImage()->IsClipboardImage()) { + if (!CurrentImage()->IsClipboardImage() && pFileList->Current() != NULL) { HANDLE hFile = ::CreateFile(pFileList->Current(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile != INVALID_HANDLE_VALUE) { __int64 fileSize = 0; @@ -172,27 +191,12 @@ void CEXIFDisplayCtl::FillEXIFDataDisplay() { sFormattedIso.Format(_T("ISO %d"), (int)pEXIFReader->GetISOSpeed()); m_pEXIFDisplay->AddLine(CNLS::GetString(_T("ISO speed:")), sFormattedIso); } - if (pEXIFReader->GetExposureTimePresent()) { - CString sFormattedExposureTimeRational; Rational exposure = pEXIFReader->GetExposureTime(); - if (exposure.Denominator == 1) { - sFormattedExposureTimeRational.Format(_T("%d"), exposure.Numerator); - } - else if (exposure.Numerator > 9) { - if (exposure.Numerator * 3 < exposure.Denominator) { - sFormattedExposureTimeRational.Format(_T("%d"), exposure.Denominator / exposure.Numerator); - } - else { - sFormattedExposureTimeRational.Format(_T("%g"), double(exposure.Numerator) / exposure.Denominator); - } - } - else { - sFormattedExposureTimeRational.Format(_T("%d/%d"), exposure.Numerator, exposure.Denominator); - } CString sFormattedExposureTime; - sFormattedExposureTime.Format(_T("%s%s sec"), secondColPadding, sFormattedExposureTimeRational); - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exposure time:")), sFormattedExposureTime, false, true); + sFormattedExposureTime.Format(_T("%s%s sec"), secondColPadding, FormatRational(exposure)); + bool bExposureTimeSameLine = pEXIFReader->GetISOSpeedPresent(); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exposure time:")), sFormattedExposureTime, false, bExposureTimeSameLine); } if (pEXIFReader->GetFocalLengthPresent()) { CString sFormattedFocalLength; @@ -202,27 +206,14 @@ void CEXIFDisplayCtl::FillEXIFDataDisplay() { if (pEXIFReader->GetFNumberPresent()) { CString sFormattedFNumber; sFormattedFNumber.Format(_T("%s𝑓/%g"), secondColPadding, pEXIFReader->GetFNumber()); - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Aperture:")), sFormattedFNumber, false, true); + bool bFNumberSameLine = pEXIFReader->GetFocalLengthPresent(); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Aperture:")), sFormattedFNumber, false, bFNumberSameLine); } if (pEXIFReader->IsGPSInformationPresent()) { CString sGPSLocation = CreateGPSString(pEXIFReader->GetGPSLatitude(), pEXIFReader->GetGPSLongitude()); m_pEXIFDisplay->SetGPSLocation(sGPSLocation, CreateGPSURL(pEXIFReader->GetGPSLatitude(), pEXIFReader->GetGPSLongitude())); m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Location:")), sGPSLocation, true); } - /* - if (pEXIFReader->GetExposureTimePresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exposure time (s):")), pEXIFReader->GetExposureTime()); - } - if (pEXIFReader->GetFocalLengthPresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Focal length (mm):")), pEXIFReader->GetFocalLength(), 1); - } - if (pEXIFReader->GetFNumberPresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("𝑓-Number:")), pEXIFReader->GetFNumber(), 1); - } - if (pEXIFReader->GetISOSpeedPresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("ISO Speed:")), (int)pEXIFReader->GetISOSpeed()); - }*/ - if (bShowMoreDetails) { if (pEXIFReader->IsGPSAltitudePresent()) { m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Altitude (m):")), pEXIFReader->GetGPSAltitude(), 0); @@ -243,41 +234,52 @@ void CEXIFDisplayCtl::FillEXIFDataDisplay() { } else if (pRawMetaData != NULL) { if (pRawMetaData->GetAcquisitionTime().wYear > 1985) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Acquisition date:")), pRawMetaData->GetAcquisitionTime()); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Acquired:")), pRawMetaData->GetAcquisitionTime()); } else { const FILETIME* pFileTime = pFileList->CurrentModificationTime(); if (pFileTime != NULL) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Modification date:")), *pFileTime); - } - } - if (pRawMetaData->IsGPSInformationPresent()) { - CString sGPSLocation = CreateGPSString(pRawMetaData->GetGPSLatitude(), pRawMetaData->GetGPSLongitude()); - m_pEXIFDisplay->SetGPSLocation(sGPSLocation, CreateGPSURL(pRawMetaData->GetGPSLatitude(), pRawMetaData->GetGPSLongitude())); - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Location:")), sGPSLocation, true); - if (pRawMetaData->IsGPSAltitudePresent()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Altitude (m):")), pRawMetaData->GetGPSAltitude(), 0); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Modified:")), *pFileTime); } } - if (pRawMetaData->GetManufacturer()[0] != 0) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Camera model:")), CString(pRawMetaData->GetManufacturer()) + _T(" ") + pRawMetaData->GetModel()); + if (pRawMetaData->GetIsoSpeed() > 0.0) { + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("ISO Speed:")), (int)pRawMetaData->GetIsoSpeed()); } if (pRawMetaData->GetExposureTime() > 0.0) { double exposureTime = pRawMetaData->GetExposureTime(); - Rational rational = (exposureTime < 1.0) ? Rational(1, Helpers::RoundToInt(1.0 / exposureTime)) : Rational(Helpers::RoundToInt(exposureTime), 1); - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exposure time (s):")), rational); - } - if (pRawMetaData->IsFlashFired()) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Flash fired:")), CNLS::GetString(_T("yes"))); + Rational exposureRational = (exposureTime < 1.0) ? Rational(1, Helpers::RoundToInt(1.0 / exposureTime)) : Rational(Helpers::RoundToInt(exposureTime), 1); + CString sFormattedExposureTime; + sFormattedExposureTime.Format(_T("%s%s sec"), secondColPadding, FormatRational(exposureRational)); + bool bExposureTimeSameLine = pRawMetaData->GetIsoSpeed() > 0.0; + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Exposure time:")), sFormattedExposureTime, false, bExposureTimeSameLine); } if (pRawMetaData->GetFocalLength() > 0.0) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Focal length (mm):")), pRawMetaData->GetFocalLength(), 1); + CString sFormattedFocalLength; + sFormattedFocalLength.Format(_T("%g mm"), pRawMetaData->GetFocalLength()); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Focal length:")), sFormattedFocalLength); } if (pRawMetaData->GetAperture() > 0.0) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("F-Number:")), pRawMetaData->GetAperture(), 1); + CString sFormattedFNumber; + sFormattedFNumber.Format(_T("%s𝑓/%g"), secondColPadding, pRawMetaData->GetAperture()); + bool bApertureSameLine = pRawMetaData->GetFocalLength() > 0.0; + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Aperture:")), sFormattedFNumber, false, bApertureSameLine); } - if (pRawMetaData->GetIsoSpeed() > 0.0) { - m_pEXIFDisplay->AddLine(CNLS::GetString(_T("ISO Speed:")), (int)pRawMetaData->GetIsoSpeed()); + if (pRawMetaData->IsGPSInformationPresent()) { + CString sGPSLocation = CreateGPSString(pRawMetaData->GetGPSLatitude(), pRawMetaData->GetGPSLongitude()); + m_pEXIFDisplay->SetGPSLocation(sGPSLocation, CreateGPSURL(pRawMetaData->GetGPSLatitude(), pRawMetaData->GetGPSLongitude())); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Location:")), sGPSLocation, true); + } + + if (bShowMoreDetails) { + if (pRawMetaData->IsGPSAltitudePresent()) { + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Altitude (m):")), pRawMetaData->GetGPSAltitude(), 0); + } + if (pRawMetaData->GetManufacturer()[0] != 0) { + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Camera model:")), CString(pRawMetaData->GetManufacturer()) + _T(" ") + pRawMetaData->GetModel()); + } + if (pRawMetaData->IsFlashFired()) { + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Flash fired:")), CNLS::GetString(_T("yes"))); + } } } else { diff --git a/src/JPEGView/EXIFDisplayCtl.h b/src/JPEGView/EXIFDisplayCtl.h index 2d89d695..98499554 100644 --- a/src/JPEGView/EXIFDisplayCtl.h +++ b/src/JPEGView/EXIFDisplayCtl.h @@ -1,6 +1,7 @@ #pragma once #include "PanelController.h" +#include "EXIFReader.h" class CEXIFDisplay; @@ -24,6 +25,8 @@ class CEXIFDisplayCtl : public CPanelController virtual bool OnMouseMove(int nX, int nY); virtual void OnPrePaintMainDlg(HDC hPaintDC); + virtual CString CEXIFDisplayCtl::FormatRational(Rational rational); + private: bool m_bVisible; CEXIFDisplay* m_pEXIFDisplay; diff --git a/src/JPEGView/JPEGImage.cpp b/src/JPEGView/JPEGImage.cpp index 1ec23f41..1a000244 100644 --- a/src/JPEGView/JPEGImage.cpp +++ b/src/JPEGView/JPEGImage.cpp @@ -600,7 +600,10 @@ void* CJPEGImage::Resample(CSize fullTargetSize, CSize clippingSize, CPoint targ if (fullTargetSize.cx > 65535 || fullTargetSize.cy > 65535) return NULL; - if (GetProcessingFlag(eProcFlags, PFLAG_HighQualityResampling) && + // MOD: no HQ resampling on upsample. Should really be a config option. + bool bUseHQResampling = GetProcessingFlag(eProcFlags, PFLAG_HighQualityResampling) && eResizeType == DownSample; + + if (bUseHQResampling && !(eResizeType == NoResize && (filter == Filter_Downsampling_Best_Quality || filter == Filter_Downsampling_No_Aliasing))) { if (SupportsSIMD(cpu)) { if (eResizeType == UpSample) { diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 6b35c1bb..801d6c9b 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -840,18 +840,21 @@ LRESULT CMainDlg::OnRButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam* return 0; } -LRESULT CMainDlg::OnMButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) { - this->SetCapture(); - if (HandleMouseButtonByKeymap(VK_MBUTTON)) { - return 0; - } - StartDragging(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), false); +LRESULT CMainDlg::OnMButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { + // MOD: I dont want dragging with middle click, instead normal handling so even fast-pased clicks are handled + bHandled = HandleMouseButtonByKeymap(VK_MBUTTON); return 0; } -LRESULT CMainDlg::OnMButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { - EndDragging(); - ::ReleaseCapture(); +LRESULT CMainDlg::OnMButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { + bHandled = HandleMouseButtonByKeymap(VK_MBUTTON, false); + return 0; +} + + +LRESULT CMainDlg::OnMButtonDblClk(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { + // MOD: Also have to handle MButtonDblClk so even fast-pased clicks are handled (I have it set to a toggle, and I want to toggle fast) + bHandled = HandleMouseButtonByKeymap(VK_MBUTTON, true); return 0; } @@ -1213,7 +1216,7 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, ::EnableMenuItem(hMenuTrackPopup, IDM_SAVE, MF_BYCOMMAND | MF_GRAYED); ::EnableMenuItem(hMenuTrackPopup, IDM_RELOAD, MF_BYCOMMAND | MF_GRAYED); //::EnableMenuItem(hMenuTrackPopup, IDM_EXPLORE, MF_BYCOMMAND | MF_GRAYED); // can still show path to an image which could not be loaded. If file doesn't exist, nothing happens anyways - ::EnableMenuItem(hMenuTrackPopup, IDM_PRINT, MF_BYCOMMAND | MF_GRAYED); + ::EnableMenuItem(hMenuTrackPopup, IDM_PRINT, MF_BYCOMMAND | MF_DISABLED); ::EnableMenuItem(hMenuTrackPopup, IDM_COPY, MF_BYCOMMAND | MF_GRAYED); ::EnableMenuItem(hMenuTrackPopup, IDM_COPY_FULL, MF_BYCOMMAND | MF_GRAYED); ::EnableMenuItem(hMenuTrackPopup, IDM_COPY_PATH, MF_BYCOMMAND | MF_GRAYED); @@ -1262,6 +1265,22 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, ::DeleteMenu(hMenuTrackPopup, 0, MF_BYPOSITION); } + // Hide some menus I never use + ::DeleteMenu(hMenuTrackPopup, IDM_PRINT, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_SET_WALLPAPER_ORIG, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_SET_WALLPAPER_DISPLAY, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_BATCH_COPY, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_COPY, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_SHOW_FILENAME, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_PASTE, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_AUTO_CORRECTION, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_LDC, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_KEEP_PARAMETERS, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_SAVE_PARAMETERS, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_SAVE_PARAM_DB, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_CLEAR_PARAM_DB, MF_BYCOMMAND); + + int nMenuCmd = TrackPopupMenu(CPoint(nX, nY), hMenuTrackPopup); ExecuteCommand(nMenuCmd); @@ -1724,10 +1743,14 @@ void CMainDlg::ExecuteCommand(int nCommand) { break; case IDM_TOGGLE_FIT_TO_SCREEN_100_PERCENTS: case IDM_TOGGLE_FILL_WITH_CROP_100_PERCENTS: - if (fabs(m_dZoom - 1) < 0.01) { - ResetZoomToFitScreen(nCommand == IDM_TOGGLE_FILL_WITH_CROP_100_PERCENTS, true, true); - } else { - ResetZoomTo100Percents(m_bMouseOn); + if (m_pCurrentImage != NULL) { + double dZoomForFitToScreen = GetZoomFactorForFitToScreen(nCommand == IDM_TOGGLE_FILL_WITH_CROP_100_PERCENTS, true); + if (abs(dZoomForFitToScreen - m_dZoom) < 0.001) { + ResetZoomTo100Percents(m_bMouseOn); + } + else { + ResetZoomToFitScreen(nCommand == IDM_TOGGLE_FILL_WITH_CROP_100_PERCENTS, true, true); + } } break; case IDM_SPAN_SCREENS: diff --git a/src/JPEGView/MainDlg.h b/src/JPEGView/MainDlg.h index f7c629e8..082eacfe 100644 --- a/src/JPEGView/MainDlg.h +++ b/src/JPEGView/MainDlg.h @@ -72,6 +72,7 @@ class CMainDlg : public CDialogImpl MESSAGE_HANDLER(WM_RBUTTONUP, OnRButtonUp) MESSAGE_HANDLER(WM_MBUTTONDOWN, OnMButtonDown) MESSAGE_HANDLER(WM_MBUTTONUP, OnMButtonUp) + MESSAGE_HANDLER(WM_MBUTTONDBLCLK, OnMButtonDblClk) MESSAGE_HANDLER(WM_XBUTTONDOWN, OnXButtonDown) MESSAGE_HANDLER(WM_XBUTTONDBLCLK, OnXButtonDown) MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) @@ -115,6 +116,7 @@ class CMainDlg : public CDialogImpl LRESULT OnLButtonDblClk(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnMButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnMButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); + LRESULT OnMButtonDblClk(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnXButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnMouseWheel(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/); From a30611a5ac232aa449e1cc2de10c751449b3fb62 Mon Sep 17 00:00:00 2001 From: Florian M Date: Sun, 30 Mar 2025 10:54:25 +0200 Subject: [PATCH 04/20] WIP clean up context menu --- src/JPEGView/JPEGView.rc | 295 ++++++++++++++++++++------------------- src/JPEGView/MainDlg.cpp | 22 +-- src/JPEGView/resource.h | 4 +- 3 files changed, 162 insertions(+), 159 deletions(-) diff --git a/src/JPEGView/JPEGView.rc b/src/JPEGView/JPEGView.rc index d215cb7d..3e55023f 100644 --- a/src/JPEGView/JPEGView.rc +++ b/src/JPEGView/JPEGView.rc @@ -275,182 +275,193 @@ POPUPMENU MENU BEGIN POPUP "PopupMenu" BEGIN - MENUITEM "Stop slide show/movie\t0xff", IDM_STOP_MOVIE + MENUITEM "Stop slideshow\t0xff", IDM_STOP_MOVIE MENUITEM SEPARATOR - MENUITEM "Open image...", IDM_OPEN - POPUP "Open image with" + POPUP "Open with" BEGIN - MENUITEM "_empty_", IDM_ABOUT - END - MENUITEM "Save processed image...\t0x5de", IDM_SAVE - MENUITEM "Reload image", IDM_RELOAD - MENUITEM "Open containing folder in Explorer", IDM_EXPLORE - MENUITEM "Print image...", IDM_PRINT - MENUITEM "Batch rename/copy...", IDM_BATCH_COPY - POPUP "Set modification date" - BEGIN - MENUITEM "to current date", IDM_TOUCH_IMAGE - MENUITEM "to EXIF date", IDM_TOUCH_IMAGE_EXIF - MENUITEM "to EXIF date (all files in folder)", IDM_TOUCH_IMAGE_EXIF_FOLDER - END - POPUP "Set as desktop wallpaper" - BEGIN - MENUITEM "Use original image", IDM_SET_WALLPAPER_ORIG - MENUITEM "Use processed image as displayed", IDM_SET_WALLPAPER_DISPLAY + MENUITEM "_empty_", IDM_ABOUT END + MENUITEM "Image info", IDM_SHOW_FILEINFO + MENUITEM "Show in Explorer", IDM_EXPLORE MENUITEM SEPARATOR - MENUITEM "Copy to clipboard", IDM_COPY - MENUITEM "Copy original size image", IDM_COPY_FULL - MENUITEM "Copy file path", IDM_COPY_PATH - MENUITEM "Paste from clipboard", IDM_PASTE - MENUITEM SEPARATOR - MENUITEM "Show picture info (EXIF)", IDM_SHOW_FILEINFO - MENUITEM "Show filename", IDM_SHOW_FILENAME - MENUITEM "Show navigation panel", IDM_SHOW_NAVPANEL - MENUITEM SEPARATOR - MENUITEM "Next image", IDM_NEXT - MENUITEM "Previous image", IDM_PREV - POPUP "Navigation" + MENUITEM "Print image...", IDM_PRINT //HIDDEN + // POPUP "Set as desktop wallpaper" + // BEGIN + // MENUITEM "Use original image", IDM_SET_WALLPAPER_ORIG + // MENUITEM "Use processed image as displayed", IDM_SET_WALLPAPER_DISPLAY + // END + POPUP "Copy / Paste" BEGIN - MENUITEM "Loop folder", IDM_LOOP_FOLDER - MENUITEM "Loop recursively", IDM_LOOP_RECURSIVELY - MENUITEM "Loop siblings", IDM_LOOP_SIBLINGS - END - POPUP "Display order" - BEGIN - MENUITEM "Modification date", IDM_SORT_MOD_DATE - MENUITEM "Creation date", IDM_SORT_CREATION_DATE - MENUITEM "File name", IDM_SORT_NAME - MENUITEM "File size", IDM_SORT_SIZE - MENUITEM "Random", IDM_SORT_RANDOM + MENUITEM "Copy full size image", IDM_COPY_FULL + MENUITEM "Copy current size image", IDM_COPY + MENUITEM "Copy file path", IDM_COPY_PATH MENUITEM SEPARATOR - MENUITEM "Ascending", IDM_SORT_ASCENDING - MENUITEM "Descending", IDM_SORT_DESCENDING + MENUITEM "Paste from clipboard", IDM_PASTE END - POPUP "Play folder as slideshow/movie" + MENUITEM "Show filename", IDM_SHOW_FILENAME // HIDDEN + MENUITEM "Show navigation panel", IDM_SHOW_NAVPANEL // HIDDEN + POPUP "Navigation" BEGIN - MENUITEM "Slideshow", IDM_SLIDESHOW_START - MENUITEM "Waiting time\t1 sec", IDM_SLIDESHOW_1 - MENUITEM "Waiting time\t2 sec", IDM_SLIDESHOW_2 - MENUITEM "Waiting time\t3 sec", IDM_SLIDESHOW_3 - MENUITEM "Waiting time\t4 sec", IDM_SLIDESHOW_4 - MENUITEM "Waiting time\t5 sec", IDM_SLIDESHOW_5 - MENUITEM "Waiting time\t7 sec", IDM_SLIDESHOW_7 - MENUITEM "Waiting time\t10 sec", IDM_SLIDESHOW_10 - MENUITEM "Waiting time\t20 sec", IDM_SLIDESHOW_20 - POPUP "Transition Effect" + MENUITEM "Next image", IDM_NEXT + MENUITEM "Previous image", IDM_PREV + MENUITEM SEPARATOR + MENUITEM "Open image...", IDM_OPEN + MENUITEM "Reload image", IDM_RELOAD + MENUITEM SEPARATOR + POPUP "Loop" BEGIN - MENUITEM "None", IDM_EFFECT_NONE - MENUITEM SEPARATOR - MENUITEM "Blend", IDM_EFFECT_BLEND - MENUITEM SEPARATOR - MENUITEM "Slide from right", IDM_EFFECT_SLIDE_RL - MENUITEM "Slide from left", IDM_EFFECT_SLIDE_LR - MENUITEM "Slide from top", IDM_EFFECT_SLIDE_TB - MENUITEM "Slide from bottom", IDM_EFFECT_SLIDE_BT - MENUITEM SEPARATOR - MENUITEM "Roll from right", IDM_EFFECT_ROLL_RL - MENUITEM "Roll from left", IDM_EFFECT_ROLL_LR - MENUITEM "Roll from top", IDM_EFFECT_ROLL_TB - MENUITEM "Roll from bottom", IDM_EFFECT_ROLL_BT - MENUITEM SEPARATOR - MENUITEM "Scroll from right", IDM_EFFECT_SCROLL_RL - MENUITEM "Scroll from left", IDM_EFFECT_SCROLL_LR - MENUITEM "Scroll from top", IDM_EFFECT_SCROLL_TB - MENUITEM "Scroll from bottom", IDM_EFFECT_SCROLL_BT + MENUITEM "Loop folder", IDM_LOOP_FOLDER + MENUITEM "Loop recursively", IDM_LOOP_RECURSIVELY + MENUITEM "Loop siblings", IDM_LOOP_SIBLINGS END - POPUP "Transition Speed" + POPUP "Display order" BEGIN - MENUITEM "Very fast", IDM_EFFECTTIME_VERY_FAST - MENUITEM "Fast", IDM_EFFECTTIME_FAST - MENUITEM "Normal", IDM_EFFECTTIME_NORMAL - MENUITEM "Slow", IDM_EFFECTTIME_SLOW - MENUITEM "Very slow", IDM_EFFECTTIME_VERY_SLOW + MENUITEM "Modification date", IDM_SORT_MOD_DATE + MENUITEM "Creation date", IDM_SORT_CREATION_DATE + MENUITEM "File name", IDM_SORT_NAME + MENUITEM "File size", IDM_SORT_SIZE + MENUITEM "Random", IDM_SORT_RANDOM + MENUITEM SEPARATOR + MENUITEM "Ascending", IDM_SORT_ASCENDING + MENUITEM "Descending", IDM_SORT_DESCENDING + END + POPUP "Slideshow" + BEGIN + MENUITEM "Slideshow", IDM_SLIDESHOW_START + MENUITEM "Waiting time\t1 sec", IDM_SLIDESHOW_1 + MENUITEM "Waiting time\t2 sec", IDM_SLIDESHOW_2 + MENUITEM "Waiting time\t3 sec", IDM_SLIDESHOW_3 + MENUITEM "Waiting time\t4 sec", IDM_SLIDESHOW_4 + MENUITEM "Waiting time\t5 sec", IDM_SLIDESHOW_5 + MENUITEM "Waiting time\t7 sec", IDM_SLIDESHOW_7 + MENUITEM "Waiting time\t10 sec", IDM_SLIDESHOW_10 + MENUITEM "Waiting time\t20 sec", IDM_SLIDESHOW_20 + POPUP "Transition Effect" + BEGIN + MENUITEM "None", IDM_EFFECT_NONE + MENUITEM SEPARATOR + MENUITEM "Blend", IDM_EFFECT_BLEND + MENUITEM SEPARATOR + MENUITEM "Slide from right", IDM_EFFECT_SLIDE_RL + MENUITEM "Slide from left", IDM_EFFECT_SLIDE_LR + MENUITEM "Slide from top", IDM_EFFECT_SLIDE_TB + MENUITEM "Slide from bottom", IDM_EFFECT_SLIDE_BT + MENUITEM SEPARATOR + MENUITEM "Roll from right", IDM_EFFECT_ROLL_RL + MENUITEM "Roll from left", IDM_EFFECT_ROLL_LR + MENUITEM "Roll from top", IDM_EFFECT_ROLL_TB + MENUITEM "Roll from bottom", IDM_EFFECT_ROLL_BT + MENUITEM SEPARATOR + MENUITEM "Scroll from right", IDM_EFFECT_SCROLL_RL + MENUITEM "Scroll from left", IDM_EFFECT_SCROLL_LR + MENUITEM "Scroll from top", IDM_EFFECT_SCROLL_TB + MENUITEM "Scroll from bottom", IDM_EFFECT_SCROLL_BT + END + POPUP "Transition Speed" + BEGIN + MENUITEM "Very fast", IDM_EFFECTTIME_VERY_FAST + MENUITEM "Fast", IDM_EFFECTTIME_FAST + MENUITEM "Normal", IDM_EFFECTTIME_NORMAL + MENUITEM "Slow", IDM_EFFECTTIME_SLOW + MENUITEM "Very slow", IDM_EFFECTTIME_VERY_SLOW + END + MENUITEM SEPARATOR + MENUITEM "Movie", IDM_MOVIE_START_FPS + MENUITEM "Playback speed\t5 fps", IDM_MOVIE_5_FPS + MENUITEM "Playback speed\t10 fps", IDM_MOVIE_10_FPS + MENUITEM "Playback speed\t25 fps", IDM_MOVIE_25_FPS + MENUITEM "Playback speed\t30 fps", IDM_MOVIE_30_FPS + MENUITEM "Playback speed\t50 fps", IDM_MOVIE_50_FPS + MENUITEM "Playback speed\t100 fps", IDM_MOVIE_100_FPS END - MENUITEM SEPARATOR - MENUITEM "Movie", IDM_MOVIE_START_FPS - MENUITEM "Playback speed\t5 fps", IDM_MOVIE_5_FPS - MENUITEM "Playback speed\t10 fps", IDM_MOVIE_10_FPS - MENUITEM "Playback speed\t25 fps", IDM_MOVIE_25_FPS - MENUITEM "Playback speed\t30 fps", IDM_MOVIE_30_FPS - MENUITEM "Playback speed\t50 fps", IDM_MOVIE_50_FPS - MENUITEM "Playback speed\t100 fps", IDM_MOVIE_100_FPS END - MENUITEM SEPARATOR - POPUP "Transform image" + POPUP "Zoom" BEGIN - MENUITEM "Rotate +90", IDM_ROTATE_90 - MENUITEM "Rotate -90", IDM_ROTATE_270 - MENUITEM "Rotate...", IDM_ROTATE - MENUITEM "Change size...", IDM_CHANGESIZE - MENUITEM "Perspective correction...", IDM_PERSPECTIVE + MENUITEM "Fit to screen", IDM_FIT_TO_SCREEN + MENUITEM "Fill with crop", IDM_FILL_WITH_CROP + MENUITEM "Span all screens", IDM_SPAN_SCREENS + MENUITEM "Full screen mode", IDM_FULL_SCREEN_MODE + MENUITEM "Fit window to image", IDM_FIT_WINDOW_TO_IMAGE MENUITEM SEPARATOR - MENUITEM "Mirror horizontally", IDM_MIRROR_H - MENUITEM "Mirror vertically", IDM_MIRROR_V - END - POPUP "Lossless JPEG transformations" - BEGIN - MENUITEM "Rotate +90\t0x24B9", IDM_ROTATE_90_LOSSLESS - MENUITEM "Rotate -90\t0x24C3", IDM_ROTATE_270_LOSSLESS - MENUITEM "Rotate 180", IDM_ROTATE_180_LOSSLESS + MENUITEM "Hide window title bar", IDM_HIDE_TITLE_BAR + MENUITEM "Set window always on top", IDM_ALWAYS_ON_TOP + MENUITEM SEPARATOR + MENUITEM "400 %", IDM_ZOOM_400 + MENUITEM "200 %", IDM_ZOOM_200 + MENUITEM "100 %\t0x3a98", IDM_ZOOM_100 + MENUITEM "50 %", IDM_ZOOM_50 + MENUITEM "25 %", IDM_ZOOM_25 MENUITEM SEPARATOR - MENUITEM "Mirror horizontally", IDM_MIRROR_H_LOSSLESS - MENUITEM "Mirror vertically", IDM_MIRROR_V_LOSSLESS + POPUP "Auto zoom mode" + BEGIN + MENUITEM "Fit to screen (no zoom)", IDM_AUTO_ZOOM_FIT_NO_ZOOM + MENUITEM "Fill with crop (no zoom)", IDM_AUTO_ZOOM_FILL_NO_ZOOM + MENUITEM "Fit to screen", IDM_AUTO_ZOOM_FIT + MENUITEM "Fill with crop", IDM_AUTO_ZOOM_FILL + END END - MENUITEM "Auto correction", IDM_AUTO_CORRECTION - MENUITEM "Local density correction", IDM_LDC - MENUITEM "Keep parameters", IDM_KEEP_PARAMETERS - MENUITEM SEPARATOR - MENUITEM "Save parameters to DB", IDM_SAVE_PARAM_DB - MENUITEM "Clear parameters from DB", IDM_CLEAR_PARAM_DB - MENUITEM SEPARATOR - POPUP "Zoom" + POPUP "Modify image" BEGIN - MENUITEM "Fit to screen", IDM_FIT_TO_SCREEN - MENUITEM "Fill with crop", IDM_FILL_WITH_CROP - MENUITEM "Span all screens", IDM_SPAN_SCREENS - MENUITEM "Full screen mode", IDM_FULL_SCREEN_MODE - MENUITEM "Fit window to image", IDM_FIT_WINDOW_TO_IMAGE + MENUITEM "Save processed image...\t0x5de", IDM_SAVE MENUITEM SEPARATOR - MENUITEM "Hide window title bar", IDM_HIDE_TITLE_BAR - MENUITEM "Set window always on top", IDM_ALWAYS_ON_TOP + POPUP "Transform" + BEGIN + MENUITEM "Rotate +90", IDM_ROTATE_90 + MENUITEM "Rotate -90", IDM_ROTATE_270 + MENUITEM "Rotate...", IDM_ROTATE + MENUITEM "Change size...", IDM_CHANGESIZE + MENUITEM "Perspective correction...", IDM_PERSPECTIVE + MENUITEM SEPARATOR + MENUITEM "Mirror horizontally", IDM_MIRROR_H + MENUITEM "Mirror vertically", IDM_MIRROR_V + END + POPUP "Transform (JPEG lossless)" + BEGIN + MENUITEM "Rotate +90\t0x24B9", IDM_ROTATE_90_LOSSLESS + MENUITEM "Rotate -90\t0x24C3", IDM_ROTATE_270_LOSSLESS + MENUITEM "Rotate 180", IDM_ROTATE_180_LOSSLESS + MENUITEM SEPARATOR + MENUITEM "Mirror horizontally", IDM_MIRROR_H_LOSSLESS + MENUITEM "Mirror vertically", IDM_MIRROR_V_LOSSLESS + END + MENUITEM SEPARATOR + MENUITEM "Batch rename/copy...", IDM_BATCH_COPY + POPUP "Set modification date" + BEGIN + MENUITEM "to current date", IDM_TOUCH_IMAGE + MENUITEM "to EXIF date", IDM_TOUCH_IMAGE_EXIF + MENUITEM "to EXIF date (all files in folder)", IDM_TOUCH_IMAGE_EXIF_FOLDER + END MENUITEM SEPARATOR - MENUITEM "400 %", IDM_ZOOM_400 - MENUITEM "200 %", IDM_ZOOM_200 - MENUITEM "100 %\t0x3a98", IDM_ZOOM_100 - MENUITEM "50 %", IDM_ZOOM_50 - MENUITEM "25 %", IDM_ZOOM_25 + MENUITEM "Auto correction", IDM_AUTO_CORRECTION + MENUITEM "Local density correction", IDM_LDC + MENUITEM "Keep parameters", IDM_KEEP_PARAMETERS + MENUITEM SEPARATOR + MENUITEM "Save parameters to DB", IDM_SAVE_PARAM_DB + MENUITEM "Clear parameters from DB", IDM_CLEAR_PARAM_DB END - POPUP "Auto zoom mode" + POPUP "User commands" BEGIN - MENUITEM "Fit to screen (no zoom)", IDM_AUTO_ZOOM_FIT_NO_ZOOM - MENUITEM "Fill with crop (no zoom)", IDM_AUTO_ZOOM_FILL_NO_ZOOM - MENUITEM "Fit to screen", IDM_AUTO_ZOOM_FIT - MENUITEM "Fill with crop", IDM_AUTO_ZOOM_FILL + MENUITEM "_empty_", IDM_ABOUT END MENUITEM SEPARATOR POPUP "Settings/Admin" BEGIN - MENUITEM "Edit global settings...", IDM_EDIT_GLOBAL_CONFIG - MENUITEM "Edit user settings...", IDM_EDIT_USER_CONFIG + MENUITEM "Edit user settings...", IDM_EDIT_USER_CONFIG + MENUITEM "Edit user keymap...", IDM_EDIT_USER_CONFIG // TODO + MENUITEM "Edit global settings...", IDM_EDIT_GLOBAL_CONFIG + MENUITEM "Edit global keymap...", IDM_EDIT_GLOBAL_CONFIG // TODO MENUITEM "Update user settings...", IDM_UPDATE_USER_CONFIG MENUITEM "Manage 'Open image with' menu...", IDM_MANAGE_OPEN_WITH_MENU MENUITEM SEPARATOR - MENUITEM "Set current parameters as default values...", IDM_SAVE_PARAMETERS - MENUITEM SEPARATOR MENUITEM "Set as default viewer...", IDM_SET_AS_DEFAULT_VIEWER MENUITEM SEPARATOR + MENUITEM "Set current parameters as default values...", IDM_SAVE_PARAMETERS MENUITEM "Backup parameter DB...", IDM_BACKUP_PARAMDB MENUITEM "Restore parameter DB...", IDM_RESTORE_PARAMDB + MENUITEM SEPARATOR + MENUITEM "About JPEGView...", IDM_ABOUT END - MENUITEM SEPARATOR - POPUP "User commands" - BEGIN - MENUITEM "_empty_", IDM_ABOUT - END - MENUITEM SEPARATOR - MENUITEM "About JPEGView...", IDM_ABOUT - MENUITEM SEPARATOR MENUITEM "Exit\t0xffff", IDM_EXIT END END diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 801d6c9b..98eff6e0 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -647,6 +647,7 @@ void CMainDlg::BlendBlackRect(CDC & targetDC, CPanel& panel, float fBlendFactor) void CMainDlg::DisplayErrors(CJPEGImage* pCurrentImage, const CRect& clientRect, CDC& dc) { dc.SetTextColor(CSettingsProvider::This().ColorGUI()); + HelpersGUI::SelectDefaultGUIFont(dc); if (m_sStartupFile.IsEmpty() && m_pCurrentImage == NULL) { CRect rectText(0, clientRect.Height()/2 - HelpersGUI::ScaleToScreen(40), clientRect.Width(), clientRect.Height()); if (m_isBeforeFileSelected) { @@ -1175,7 +1176,7 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, HMENU hMenuModDate = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_MODDATE); HMENU hMenuUserCommands = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_USER_COMMANDS); HMENU hMenuOpenWithCommands = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_OPENWITH); - HMENU hMenuWallpaper = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_WALLPAPER); + // HMENU hMenuWallpaper = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_WALLPAPER); if (!HelpersGUI::CreateUserCommandsMenu(hMenuUserCommands)) { ::DeleteMenu(hMenuTrackPopup, SUBMENU_POS_USER_COMMANDS + 1, MF_BYPOSITION); @@ -1225,7 +1226,7 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, ::EnableMenuItem(hMenuTrackPopup, SUBMENU_POS_ZOOM, MF_BYPOSITION | MF_GRAYED); ::EnableMenuItem(hMenuTrackPopup, SUBMENU_POS_MODDATE, MF_BYPOSITION | MF_GRAYED); ::EnableMenuItem(hMenuTrackPopup, SUBMENU_POS_TRANSFORM, MF_BYPOSITION | MF_GRAYED); - ::EnableMenuItem(hMenuTrackPopup, SUBMENU_POS_WALLPAPER, MF_BYPOSITION | MF_GRAYED); + // ::EnableMenuItem(hMenuTrackPopup, SUBMENU_POS_WALLPAPER, MF_BYPOSITION | MF_GRAYED); } else { if (m_bKeepParams || m_pCurrentImage->IsClipboardImage() || CParameterDB::This().FindEntry(m_pCurrentImage->GetPixelHash()) == NULL) @@ -1242,11 +1243,11 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, ::EnableMenuItem(hMenuModDate, IDM_TOUCH_IMAGE_EXIF, MF_BYCOMMAND | MF_GRAYED); } int windowsVersion = Helpers::GetWindowsVersion(); - if (m_pCurrentImage->IsClipboardImage() || (windowsVersion < 600 && m_pCurrentImage->GetImageFormat() != IF_WindowsBMP) || + /*if (m_pCurrentImage->IsClipboardImage() || (windowsVersion < 600 && m_pCurrentImage->GetImageFormat() != IF_WindowsBMP) || (windowsVersion < 602 && !(m_pCurrentImage->GetImageFormat() == IF_WindowsBMP || m_pCurrentImage->GetImageFormat() == IF_JPEG)) || !m_pCurrentImage->IsGDIPlusFormat()) { ::EnableMenuItem(hMenuWallpaper, IDM_SET_WALLPAPER_ORIG, MF_BYCOMMAND | MF_GRAYED); - } + }*/ } if (!HelpersGUI::CreateOpenWithCommandsMenu(hMenuOpenWithCommands) || m_pCurrentImage == NULL) { ::DeleteMenu(hMenuTrackPopup, SUBMENU_POS_OPENWITH, MF_BYPOSITION); @@ -1264,22 +1265,13 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, ::DeleteMenu(hMenuTrackPopup, 0, MF_BYPOSITION); ::DeleteMenu(hMenuTrackPopup, 0, MF_BYPOSITION); } - // Hide some menus I never use ::DeleteMenu(hMenuTrackPopup, IDM_PRINT, MF_BYCOMMAND); ::DeleteMenu(hMenuTrackPopup, IDM_SET_WALLPAPER_ORIG, MF_BYCOMMAND); ::DeleteMenu(hMenuTrackPopup, IDM_SET_WALLPAPER_DISPLAY, MF_BYCOMMAND); - ::DeleteMenu(hMenuTrackPopup, IDM_BATCH_COPY, MF_BYCOMMAND); - ::DeleteMenu(hMenuTrackPopup, IDM_COPY, MF_BYCOMMAND); + ::DeleteMenu(hMenuTrackPopup, IDM_UPDATE_USER_CONFIG, MF_BYCOMMAND); ::DeleteMenu(hMenuTrackPopup, IDM_SHOW_FILENAME, MF_BYCOMMAND); - ::DeleteMenu(hMenuTrackPopup, IDM_PASTE, MF_BYCOMMAND); - ::DeleteMenu(hMenuTrackPopup, IDM_AUTO_CORRECTION, MF_BYCOMMAND); - ::DeleteMenu(hMenuTrackPopup, IDM_LDC, MF_BYCOMMAND); - ::DeleteMenu(hMenuTrackPopup, IDM_KEEP_PARAMETERS, MF_BYCOMMAND); - ::DeleteMenu(hMenuTrackPopup, IDM_SAVE_PARAMETERS, MF_BYCOMMAND); - ::DeleteMenu(hMenuTrackPopup, IDM_SAVE_PARAM_DB, MF_BYCOMMAND); - ::DeleteMenu(hMenuTrackPopup, IDM_CLEAR_PARAM_DB, MF_BYCOMMAND); - + ::DeleteMenu(hMenuTrackPopup, IDM_SHOW_NAVPANEL, MF_BYCOMMAND); int nMenuCmd = TrackPopupMenu(CPoint(nX, nY), hMenuTrackPopup); ExecuteCommand(nMenuCmd); diff --git a/src/JPEGView/resource.h b/src/JPEGView/resource.h index 9fc158ef..bb7f8140 100644 --- a/src/JPEGView/resource.h +++ b/src/JPEGView/resource.h @@ -321,9 +321,9 @@ // in the main menu // these position must be changed if menu items are inserted -#define SUBMENU_POS_OPENWITH 3 +#define SUBMENU_POS_OPENWITH 2 #define SUBMENU_POS_MODDATE 9 -#define SUBMENU_POS_WALLPAPER 10 +// #define SUBMENU_POS_WALLPAPER 10 #define SUBMENU_POS_NAVIGATION 23 #define SUBMENU_POS_DISPLAY_ORDER 24 #define SUBMENU_POS_MOVIE 25 From 304c444c3a6e704011cd4620be30319c0e510318 Mon Sep 17 00:00:00 2001 From: Florian M Date: Sun, 30 Mar 2025 12:05:17 +0200 Subject: [PATCH 05/20] fix context menu handling --- src/JPEGView/JPEGView.rc | 14 +++++------ src/JPEGView/MainDlg.cpp | 54 ++++++++++++++++++++++++---------------- src/JPEGView/resource.h | 32 ++++++++++++++++-------- 3 files changed, 61 insertions(+), 39 deletions(-) diff --git a/src/JPEGView/JPEGView.rc b/src/JPEGView/JPEGView.rc index 3e55023f..148727be 100644 --- a/src/JPEGView/JPEGView.rc +++ b/src/JPEGView/JPEGView.rc @@ -285,11 +285,11 @@ BEGIN MENUITEM "Show in Explorer", IDM_EXPLORE MENUITEM SEPARATOR MENUITEM "Print image...", IDM_PRINT //HIDDEN - // POPUP "Set as desktop wallpaper" - // BEGIN - // MENUITEM "Use original image", IDM_SET_WALLPAPER_ORIG - // MENUITEM "Use processed image as displayed", IDM_SET_WALLPAPER_DISPLAY - // END + POPUP "Set as desktop wallpaper" + BEGIN + MENUITEM "Use original image", IDM_SET_WALLPAPER_ORIG + MENUITEM "Use processed image as displayed", IDM_SET_WALLPAPER_DISPLAY + END POPUP "Copy / Paste" BEGIN MENUITEM "Copy full size image", IDM_COPY_FULL @@ -448,9 +448,9 @@ BEGIN POPUP "Settings/Admin" BEGIN MENUITEM "Edit user settings...", IDM_EDIT_USER_CONFIG - MENUITEM "Edit user keymap...", IDM_EDIT_USER_CONFIG // TODO + MENUITEM "Edit user keymap...", IDM_EDIT_USER_KEYMAP MENUITEM "Edit global settings...", IDM_EDIT_GLOBAL_CONFIG - MENUITEM "Edit global keymap...", IDM_EDIT_GLOBAL_CONFIG // TODO + MENUITEM "Edit global keymap...", IDM_EDIT_GLOBAL_KEYMAP MENUITEM "Update user settings...", IDM_UPDATE_USER_CONFIG MENUITEM "Manage 'Open image with' menu...", IDM_MANAGE_OPEN_WITH_MENU MENUITEM SEPARATOR diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 98eff6e0..59127089 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -1141,16 +1141,20 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, HMENU hMenuTrackPopup = ::GetSubMenu(hMenu, 0); HelpersGUI::TranslateMenuStrings(hMenuTrackPopup, m_pKeyMap); - + + int deletedMenusToplevel = 0; + if (m_pEXIFDisplayCtl->IsActive()) ::CheckMenuItem(hMenuTrackPopup, IDM_SHOW_FILEINFO, MF_CHECKED); if (m_bShowFileName) ::CheckMenuItem(hMenuTrackPopup, IDM_SHOW_FILENAME, MF_CHECKED); if (m_pNavPanelCtl->IsActive()) ::CheckMenuItem(hMenuTrackPopup, IDM_SHOW_NAVPANEL, MF_CHECKED); if (m_bAutoContrast) ::CheckMenuItem(hMenuTrackPopup, IDM_AUTO_CORRECTION, MF_CHECKED); if (m_bLDC) ::CheckMenuItem(hMenuTrackPopup, IDM_LDC, MF_CHECKED); if (m_bKeepParams) ::CheckMenuItem(hMenuTrackPopup, IDM_KEEP_PARAMETERS, MF_CHECKED); + HMENU hMenuNavigation = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_NAVIGATION); - ::CheckMenuItem(hMenuNavigation, m_pFileList->GetNavigationMode()*10 + IDM_LOOP_FOLDER, MF_CHECKED); - HMENU hMenuOrdering = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_DISPLAY_ORDER); + HMENU hMenuNavigationLoop = ::GetSubMenu(hMenuNavigation, SUBMENU_POS_NAVIGATION_LOOP); + ::CheckMenuItem(hMenuNavigationLoop, m_pFileList->GetNavigationMode()*10 + IDM_LOOP_FOLDER, MF_CHECKED); + HMENU hMenuOrdering = ::GetSubMenu(hMenuNavigation, SUBMENU_POS_DISPLAY_ORDER); ::CheckMenuItem(hMenuOrdering, (m_pFileList->GetSorting() == Helpers::FS_LastModTime) ? IDM_SORT_MOD_DATE : (m_pFileList->GetSorting() == Helpers::FS_CreationTime) ? IDM_SORT_CREATION_DATE : @@ -1162,7 +1166,7 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, ::EnableMenuItem(hMenuOrdering, IDM_SORT_ASCENDING, MF_BYCOMMAND | MF_GRAYED); ::EnableMenuItem(hMenuOrdering, IDM_SORT_DESCENDING, MF_BYCOMMAND | MF_GRAYED); } - HMENU hMenuMovie = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_MOVIE); + HMENU hMenuMovie = ::GetSubMenu(hMenuNavigation, SUBMENU_POS_SLIDESHOW); if (!m_bMovieMode) ::EnableMenuItem(hMenuMovie, IDM_STOP_MOVIE, MF_BYCOMMAND | MF_GRAYED); HMENU hMenuZoom = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_ZOOM); if (m_bSpanVirtualDesktop) ::CheckMenuItem(hMenuZoom, IDM_SPAN_SCREENS, MF_CHECKED); @@ -1170,19 +1174,14 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, if (m_bWindowBorderless) ::CheckMenuItem(hMenuZoom, IDM_HIDE_TITLE_BAR, MF_CHECKED); if (m_bAlwaysOnTop) ::CheckMenuItem(hMenuZoom, IDM_ALWAYS_ON_TOP, MF_CHECKED); if (IsAdjustWindowToImage() && IsImageExactlyFittingWindow()) ::CheckMenuItem(hMenuZoom, IDM_FIT_WINDOW_TO_IMAGE, MF_CHECKED); - HMENU hMenuAutoZoomMode = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_AUTOZOOMMODE); + HMENU hMenuAutoZoomMode = ::GetSubMenu(hMenuZoom, SUBMENU_POS_AUTOZOOMMODE); ::CheckMenuItem(hMenuAutoZoomMode, GetAutoZoomMode() * 10 + IDM_AUTO_ZOOM_FIT_NO_ZOOM, MF_CHECKED); HMENU hMenuSettings = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_SETTINGS); - HMENU hMenuModDate = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_MODDATE); + HMENU hMenuModify = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_MODIFY); + HMENU hMenuModDate = ::GetSubMenu(hMenuModify, SUBMENU_POS_MODIFY_MODDATE); HMENU hMenuUserCommands = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_USER_COMMANDS); HMENU hMenuOpenWithCommands = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_OPENWITH); - // HMENU hMenuWallpaper = ::GetSubMenu(hMenuTrackPopup, SUBMENU_POS_WALLPAPER); - if (!HelpersGUI::CreateUserCommandsMenu(hMenuUserCommands)) { - ::DeleteMenu(hMenuTrackPopup, SUBMENU_POS_USER_COMMANDS + 1, MF_BYPOSITION); - ::DeleteMenu(hMenuTrackPopup, SUBMENU_POS_USER_COMMANDS, MF_BYPOSITION); - ::DeleteMenu(hMenuTrackPopup, SUBMENU_POS_USER_COMMANDS - 1, MF_BYPOSITION); - } if (!m_bFullScreenMode) { // Transition effect and speed only available in full screen mode ::DeleteMenu(hMenuMovie, 9, MF_BYPOSITION); @@ -1203,7 +1202,8 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, ::EnableMenuItem(hMenuMovie, IDM_MOVIE_START_FPS, MF_BYCOMMAND | MF_GRAYED); if (!CSettingsProvider::This().AllowEditGlobalSettings()) { - ::DeleteMenu(hMenuSettings, 0, MF_BYPOSITION); + ::DeleteMenu(hMenuSettings, IDM_EDIT_GLOBAL_CONFIG, MF_BYCOMMAND); + ::DeleteMenu(hMenuSettings, IDM_EDIT_GLOBAL_KEYMAP, MF_BYCOMMAND); } bool bCanPaste = ::IsClipboardFormatAvailable(CF_DIB); @@ -1211,7 +1211,7 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, bool bCanDoLosslessJPEGTransform = (m_pCurrentImage != NULL) && m_pCurrentImage->GetImageFormat() == IF_JPEG && !m_pCurrentImage->IsDestructivelyProcessed(); - if (!bCanDoLosslessJPEGTransform) ::EnableMenuItem(hMenuTrackPopup, SUBMENU_POS_TRANSFORM_LOSSLESS, MF_BYPOSITION | MF_GRAYED); + if (!bCanDoLosslessJPEGTransform) ::EnableMenuItem(hMenuModify, SUBMENU_POS_TRANSFORM_LOSSLESS, MF_BYPOSITION | MF_GRAYED); if (m_pCurrentImage == NULL) { ::EnableMenuItem(hMenuTrackPopup, IDM_SAVE, MF_BYCOMMAND | MF_GRAYED); @@ -1224,8 +1224,8 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, ::EnableMenuItem(hMenuTrackPopup, IDM_SAVE_PARAM_DB, MF_BYCOMMAND | MF_GRAYED); ::EnableMenuItem(hMenuTrackPopup, IDM_CLEAR_PARAM_DB, MF_BYCOMMAND | MF_GRAYED); ::EnableMenuItem(hMenuTrackPopup, SUBMENU_POS_ZOOM, MF_BYPOSITION | MF_GRAYED); - ::EnableMenuItem(hMenuTrackPopup, SUBMENU_POS_MODDATE, MF_BYPOSITION | MF_GRAYED); - ::EnableMenuItem(hMenuTrackPopup, SUBMENU_POS_TRANSFORM, MF_BYPOSITION | MF_GRAYED); + ::EnableMenuItem(hMenuModify, SUBMENU_POS_MODIFY_MODDATE, MF_BYPOSITION | MF_GRAYED); + ::EnableMenuItem(hMenuModify, SUBMENU_POS_TRANSFORM, MF_BYPOSITION | MF_GRAYED); // ::EnableMenuItem(hMenuTrackPopup, SUBMENU_POS_WALLPAPER, MF_BYPOSITION | MF_GRAYED); } else { if (m_bKeepParams || m_pCurrentImage->IsClipboardImage() || @@ -1250,7 +1250,8 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, }*/ } if (!HelpersGUI::CreateOpenWithCommandsMenu(hMenuOpenWithCommands) || m_pCurrentImage == NULL) { - ::DeleteMenu(hMenuTrackPopup, SUBMENU_POS_OPENWITH, MF_BYPOSITION); + ::DeleteMenu(hMenuTrackPopup, SUBMENU_POS_OPENWITH - deletedMenusToplevel, MF_BYPOSITION); + deletedMenusToplevel += 1; } if (m_bMovieMode) { ::EnableMenuItem(hMenuTrackPopup, IDM_SAVE, MF_BYCOMMAND | MF_GRAYED); @@ -1263,15 +1264,26 @@ LRESULT CMainDlg::OnContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, } else { // Delete the 'Stop movie' menu entry if no movie is playing ::DeleteMenu(hMenuTrackPopup, 0, MF_BYPOSITION); - ::DeleteMenu(hMenuTrackPopup, 0, MF_BYPOSITION); + ::DeleteMenu(hMenuTrackPopup, 0, MF_BYPOSITION); // the separator + deletedMenusToplevel += 2; } // Hide some menus I never use ::DeleteMenu(hMenuTrackPopup, IDM_PRINT, MF_BYCOMMAND); - ::DeleteMenu(hMenuTrackPopup, IDM_SET_WALLPAPER_ORIG, MF_BYCOMMAND); - ::DeleteMenu(hMenuTrackPopup, IDM_SET_WALLPAPER_DISPLAY, MF_BYCOMMAND); - ::DeleteMenu(hMenuTrackPopup, IDM_UPDATE_USER_CONFIG, MF_BYCOMMAND); + deletedMenusToplevel += 1; + ::DeleteMenu(hMenuTrackPopup, IDM_UPDATE_USER_CONFIG, MF_BYCOMMAND); // not in toplevel + + ::DeleteMenu(hMenuTrackPopup, SUBMENU_POS_WALLPAPER - deletedMenusToplevel, MF_BYPOSITION); + deletedMenusToplevel += 1; + ::DeleteMenu(hMenuTrackPopup, IDM_SHOW_FILENAME, MF_BYCOMMAND); ::DeleteMenu(hMenuTrackPopup, IDM_SHOW_NAVPANEL, MF_BYCOMMAND); + deletedMenusToplevel += 2; + + if (!HelpersGUI::CreateUserCommandsMenu(hMenuUserCommands)) { + // User commands are empty, delete the menu + ::DeleteMenu(hMenuTrackPopup, SUBMENU_POS_USER_COMMANDS - deletedMenusToplevel, MF_BYPOSITION); + deletedMenusToplevel += 1; + } int nMenuCmd = TrackPopupMenu(CPoint(nX, nY), hMenuTrackPopup); ExecuteCommand(nMenuCmd); diff --git a/src/JPEGView/resource.h b/src/JPEGView/resource.h index bb7f8140..94c62f8d 100644 --- a/src/JPEGView/resource.h +++ b/src/JPEGView/resource.h @@ -258,6 +258,8 @@ #define IDM_AUTO_ZOOM_FILL_NO_ZOOM 12510 // :KeyMap: set auto zoom mode fill screen, never zoom #define IDM_AUTO_ZOOM_FIT 12520 // :KeyMap: set auto zoom mode fit to screen #define IDM_AUTO_ZOOM_FILL 12530 // :KeyMap: set auto zoom mode fill screen +#define IDM_EDIT_GLOBAL_KEYMAP 12590 // :KeyMap: edit global keymap +#define IDM_EDIT_USER_KEYMAP 12695 // :KeyMap: edit user keymap #define IDM_EDIT_GLOBAL_CONFIG 12600 // :KeyMap: edit global configuration #define IDM_EDIT_USER_CONFIG 12610 // :KeyMap: edit user configuration #define IDM_MANAGE_OPEN_WITH_MENU 12612 @@ -322,17 +324,25 @@ // in the main menu // these position must be changed if menu items are inserted #define SUBMENU_POS_OPENWITH 2 -#define SUBMENU_POS_MODDATE 9 -// #define SUBMENU_POS_WALLPAPER 10 -#define SUBMENU_POS_NAVIGATION 23 -#define SUBMENU_POS_DISPLAY_ORDER 24 -#define SUBMENU_POS_MOVIE 25 -#define SUBMENU_POS_TRANSFORM 27 -#define SUBMENU_POS_TRANSFORM_LOSSLESS 28 -#define SUBMENU_POS_ZOOM 36 -#define SUBMENU_POS_AUTOZOOMMODE 37 -#define SUBMENU_POS_SETTINGS 39 -#define SUBMENU_POS_USER_COMMANDS 41 +#define SUBMENU_POS_WALLPAPER 7 +#define SUBMENU_POS_NAVIGATION 11 +#define SUBMENU_POS_ZOOM 12 +#define SUBMENU_POS_MODIFY 13 +#define SUBMENU_POS_USER_COMMANDS 14 +#define SUBMENU_POS_SETTINGS 16 + +// in modify menu +#define SUBMENU_POS_MODIFY_MODDATE 6 +#define SUBMENU_POS_TRANSFORM 2 +#define SUBMENU_POS_TRANSFORM_LOSSLESS 3 + +// in navigation menu +#define SUBMENU_POS_NAVIGATION_LOOP 6 +#define SUBMENU_POS_DISPLAY_ORDER 7 +#define SUBMENU_POS_SLIDESHOW 8 + +// in zoom menu +#define SUBMENU_POS_AUTOZOOMMODE 15 // in the crop menu #define SUBMENU_POS_CROPMODE 3 From 5702e6095a1370c1ec0cc18be5e03d4e6a16e978 Mon Sep 17 00:00:00 2001 From: Florian M Date: Sun, 30 Mar 2025 14:05:42 +0200 Subject: [PATCH 06/20] allow to edit keymap from context menu; esc closes full screen by default --- src/JPEGView/Config/KeyMap-README.html | 22 +++- src/JPEGView/Config/symbols.km | 2 + src/JPEGView/KeyMap.cpp | 18 ++++ src/JPEGView/KeyMap.h | 5 + src/JPEGView/MainDlg.cpp | 142 +++++++++++++++---------- src/JPEGView/MainDlg.h | 6 +- 6 files changed, 138 insertions(+), 57 deletions(-) diff --git a/src/JPEGView/Config/KeyMap-README.html b/src/JPEGView/Config/KeyMap-README.html index 31f754dc..457919d5 100644 --- a/src/JPEGView/Config/KeyMap-README.html +++ b/src/JPEGView/Config/KeyMap-README.html @@ -336,6 +336,14 @@ content: "edit user configuration" } +.IDM_EDIT_GLOBAL_KEYMAP:before { + content: "edit global keymap" +} + +.IDM_EDIT_USER_KEYMAP:before { + content: "edit user keymap" +} + .IDM_BACKUP_PARAMDB:before { content: "backup parameter DB" } @@ -1082,6 +1090,18 @@

+ IDM_EDIT_GLOBAL_KEYMAP + + + + + + IDM_EDIT_USER_KEYMAP + + + + + IDM_BACKUP_PARAMDB @@ -1255,4 +1275,4 @@

- \ No newline at end of file + diff --git a/src/JPEGView/Config/symbols.km b/src/JPEGView/Config/symbols.km index ed123970..0bd542d5 100644 --- a/src/JPEGView/Config/symbols.km +++ b/src/JPEGView/Config/symbols.km @@ -89,6 +89,8 @@ #define IDM_AUTO_ZOOM_FILL_NO_ZOOM 12510 #define IDM_AUTO_ZOOM_FIT 12520 #define IDM_AUTO_ZOOM_FILL 12530 +#define IDM_EDIT_GLOBAL_KEYMAP 12590 +#define IDM_EDIT_USER_KEYMAP 12695 #define IDM_EDIT_GLOBAL_CONFIG 12600 #define IDM_EDIT_USER_CONFIG 12610 #define IDM_BACKUP_PARAMDB 12620 diff --git a/src/JPEGView/KeyMap.cpp b/src/JPEGView/KeyMap.cpp index 098d43db..aee914a6 100644 --- a/src/JPEGView/KeyMap.cpp +++ b/src/JPEGView/KeyMap.cpp @@ -153,6 +153,24 @@ static CString _GetKeyShortcutName(int nShortcut) { return sKeyDesc; } +CString CKeyMap::UserKeymapFileName() { + return CString(Helpers::JPEGViewAppDataPath()) + KEYMAP_USER_FILE_NAME; +} + +CString CKeyMap::GlobalKeymapFileName() { + // Not the template, but the keymap file in exe dir. + return CString(CSettingsProvider::This().GetEXEPath()) + KEYMAP_USER_FILE_NAME; +} + +bool CKeyMap::ExistsUserKeyMap() { + return ::GetFileAttributes(UserKeymapFileName()) != INVALID_FILE_ATTRIBUTES; +} + +void CKeyMap::CopyUserKeymapFromTemplate() { + CString sTemplateFileName = CString(CSettingsProvider::This().GetEXEPath()) + KEYMAP_DEFAULT_FILE_NAME; + ::CopyFile(sTemplateFileName, UserKeymapFileName(), TRUE); +} + // Similar to SettingsProvider, attemps to load the keymap from various locations // before settling for the default // diff --git a/src/JPEGView/KeyMap.h b/src/JPEGView/KeyMap.h index 2ab1b072..d4d1973e 100644 --- a/src/JPEGView/KeyMap.h +++ b/src/JPEGView/KeyMap.h @@ -26,6 +26,11 @@ class CKeyMap // gets the shortcut key name, e.g. 'Ctrl+P' static CString GetShortcutKey(int combinedKeyCode); + + static CString UserKeymapFileName(); + static CString GlobalKeymapFileName(); + static bool ExistsUserKeyMap(); + static void CopyUserKeymapFromTemplate(); private: // key is the key code, value the command ID diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 59127089..b1016cbd 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -1773,54 +1773,7 @@ void CMainDlg::ExecuteCommand(int nCommand) { } break; case IDM_FULL_SCREEN_MODE: - m_bFullScreenMode = !m_bFullScreenMode; - m_dZoomAtResizeStart = 1.0; - if (!m_bFullScreenMode) { - CRect windowRect; - - // restore hidden title bar if enabled - SetCurrentWindowStyle(); - - HICON hIconSmall = (HICON)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME), - IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); - SetIcon(hIconSmall, FALSE); - CRect defaultWindowRect = CMultiMonitorSupport::GetDefaultWindowRect(); - double dZoom = -1; - windowRect = sp.ExplicitWindowRect() ? - defaultWindowRect : - Helpers::GetWindowRectMatchingImageSize( - m_hWnd, - CSize(MIN_WND_WIDTH, MIN_WND_HEIGHT), - defaultWindowRect.Size(), - dZoom, m_pCurrentImage, false, true, m_bWindowBorderless); - if (sp.DefaultMaximized()) { - this->ShowWindow(SW_MAXIMIZE); - } - else { - this->SetWindowPos(HWND_TOP, windowRect.left, windowRect.top, windowRect.Width(), windowRect.Height(), SWP_NOZORDER | SWP_NOCOPYBITS); - } - this->MouseOn(); - m_bSpanVirtualDesktop = false; - } else { - if (!IsZoomed() && sp.ExplicitWindowRect()) { - // Save the old window rect to be able to restore it - CRect rect; - GetWindowRect(&rect); - CMultiMonitorSupport::SetDefaultWindowRect(rect); - } - HMONITOR hMonitor = ::MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST); - MONITORINFO monitorInfo; - monitorInfo.cbSize = sizeof(MONITORINFO); - if (::GetMonitorInfo(hMonitor, &monitorInfo)) { - CRect monitorRect(&(monitorInfo.rcMonitor)); - this->SetWindowLongW(GWL_STYLE, WS_VISIBLE); - this->SetWindowPos(HWND_TOP, monitorRect.left, monitorRect.top, monitorRect.Width(), monitorRect.Height(), SWP_NOZORDER | SWP_NOCOPYBITS); - } - this->MouseOn(); - } - m_dZoom = -1; - StartLowQTimer(ZOOM_TIMEOUT); - this->SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_FRAMECHANGED); + ToggleFullScreen(sp); break; case IDM_HIDE_TITLE_BAR: if (!m_bFullScreenMode) { @@ -1923,7 +1876,10 @@ void CMainDlg::ExecuteCommand(int nCommand) { } case IDM_EDIT_GLOBAL_CONFIG: case IDM_EDIT_USER_CONFIG: - EditINIFile(nCommand == IDM_EDIT_GLOBAL_CONFIG); + EditSettingsFile(nCommand == IDM_EDIT_GLOBAL_CONFIG); + break; + case IDM_EDIT_USER_KEYMAP: + EditKeymapFile(nCommand == IDM_EDIT_GLOBAL_CONFIG); break; case IDM_UPDATE_USER_CONFIG: if (::MessageBox(m_hWnd, CString(CNLS::GetString(_T("Update user settings with new settings from settings template file?"))) + _T('\n') + @@ -1969,7 +1925,11 @@ void CMainDlg::ExecuteCommand(int nCommand) { CleanupAndTerminate(); else StopAnimation(); // stop any running animation - } else { + } + else if (m_bFullScreenMode) { + ToggleFullScreen(sp); + } + else { CleanupAndTerminate(); } break; @@ -2143,6 +2103,58 @@ void CMainDlg::ExecuteCommand(int nCommand) { } } +void CMainDlg::ToggleFullScreen(CSettingsProvider& sp) { + m_bFullScreenMode = !m_bFullScreenMode; + m_dZoomAtResizeStart = 1.0; + if (!m_bFullScreenMode) { + CRect windowRect; + + // restore hidden title bar if enabled + SetCurrentWindowStyle(); + + HICON hIconSmall = (HICON)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME), + IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); + SetIcon(hIconSmall, FALSE); + CRect defaultWindowRect = CMultiMonitorSupport::GetDefaultWindowRect(); + double dZoom = -1; + windowRect = sp.ExplicitWindowRect() ? + defaultWindowRect : + Helpers::GetWindowRectMatchingImageSize( + m_hWnd, + CSize(MIN_WND_WIDTH, MIN_WND_HEIGHT), + defaultWindowRect.Size(), + dZoom, m_pCurrentImage, false, true, m_bWindowBorderless); + if (sp.DefaultMaximized()) { + this->ShowWindow(SW_MAXIMIZE); + } + else { + this->SetWindowPos(HWND_TOP, windowRect.left, windowRect.top, windowRect.Width(), windowRect.Height(), SWP_NOZORDER | SWP_NOCOPYBITS); + } + this->MouseOn(); + m_bSpanVirtualDesktop = false; + } + else { + if (!IsZoomed() && sp.ExplicitWindowRect()) { + // Save the old window rect to be able to restore it + CRect rect; + GetWindowRect(&rect); + CMultiMonitorSupport::SetDefaultWindowRect(rect); + } + HMONITOR hMonitor = ::MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST); + MONITORINFO monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFO); + if (::GetMonitorInfo(hMonitor, &monitorInfo)) { + CRect monitorRect(&(monitorInfo.rcMonitor)); + this->SetWindowLongW(GWL_STYLE, WS_VISIBLE); + this->SetWindowPos(HWND_TOP, monitorRect.left, monitorRect.top, monitorRect.Width(), monitorRect.Height(), SWP_NOZORDER | SWP_NOCOPYBITS); + } + this->MouseOn(); + } + m_dZoom = -1; + StartLowQTimer(ZOOM_TIMEOUT); + this->SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_FRAMECHANGED); +} + // Setting window styles have gotten out of hand with the addition of no title bar // instead of each call trying to figure out the logic, consolidate it to one function LONG CMainDlg::SetCurrentWindowStyle() { @@ -3233,36 +3245,56 @@ CRect CMainDlg::GetZoomTextRect(CRect imageProcessingArea) { nEndX, imageProcessingArea.bottom - HelpersGUI::ScaleToScreen(nZoomTextRectBottomOffset)); } -void CMainDlg::EditINIFile(bool bGlobalINI) { +void CMainDlg::EditSettingsFile(bool bGlobalINI) { LPCTSTR sINIFileName = bGlobalINI ? CSettingsProvider::This().GetGlobalINIFileName() : CSettingsProvider::This().GetUserINIFileName(); if (!bGlobalINI) { if (!CSettingsProvider::This().ExistsUserINI()) { // No user INI file, ask if global INI shall be copied if (IDYES == ::MessageBox(m_hWnd, CNLS::GetString(_T("No user INI file exists yet. Create user INI file from INI file template?")), _T(JPEGVIEW_TITLE), MB_YESNO | MB_ICONQUESTION)) { CSettingsProvider::This().CopyUserINIFromTemplate(); - } else { + } + else { + return; + } + } + } + EditTextFile(sINIFileName); +} + +void CMainDlg::EditKeymapFile(bool bGlobalKeymap) { + if (!bGlobalKeymap) { + if (!CKeyMap::ExistsUserKeyMap()) { + // No user keymap file, ask if global keymap shall be copied + if (IDYES == ::MessageBox(m_hWnd, CNLS::GetString(_T("No user keymap file exists yet. Create user keymap file from keymap file template?")), _T(JPEGVIEW_TITLE), MB_YESNO | MB_ICONQUESTION)) { + CKeyMap::CopyUserKeymapFromTemplate(); + } + else { return; } } } + EditTextFile(bGlobalKeymap ? CKeyMap::GlobalKeymapFileName() : CKeyMap::UserKeymapFileName()); +} +void CMainDlg::EditTextFile(LPCTSTR sFileName) { Helpers::EIniEditor iniEditor = CSettingsProvider::This().IniEditor(); CString command; LPCTSTR argument; if (iniEditor == Helpers::INI_Notepad) { command = _T("notepad.exe"); - argument = sINIFileName; + argument = sFileName; } else if (iniEditor == Helpers::INI_System) { - command = sINIFileName; + command = sFileName; argument = NULL; } else { command = CSettingsProvider::This().CustomIniEditor(); command.Replace(_T("%exepath%"), CSettingsProvider::This().GetEXEPath()); - argument = sINIFileName; + argument = sFileName; } ::ShellExecute(m_hWnd, _T("open"), command, argument, NULL, SW_SHOW); } + void CMainDlg::UpdateWindowTitle() { bool bShowFullPathInTitle = CSettingsProvider::This().ShowFullPathInTitle(); LPCTSTR sCurrentFileName = CurrentFileName(!bShowFullPathInTitle); diff --git a/src/JPEGView/MainDlg.h b/src/JPEGView/MainDlg.h index 082eacfe..7a2e0705 100644 --- a/src/JPEGView/MainDlg.h +++ b/src/JPEGView/MainDlg.h @@ -7,6 +7,7 @@ #include "ProcessParams.h" #include "Helpers.h" #include "CropCtl.h" +#include "SettingsProvider.h" class CFileList; class CJPEGProvider; @@ -337,6 +338,7 @@ class CMainDlg : public CDialogImpl bool m_bSelectZoom; // keeps track of select-to-zoom mode when CTRL+SHIFT+LMouse void ExploreFile(); + void ToggleFullScreen(CSettingsProvider& sp); bool OpenFileWithDialog(bool bFullScreen, bool bAfterStartup); void OpenFile(LPCTSTR sFileName, bool bAfterStartup); bool SaveImage(bool bFullSize); @@ -367,7 +369,9 @@ class CMainDlg : public CDialogImpl CRect ScreenToDIB(const CSize& sizeDIB, const CRect& rect); void ToggleMonitor(); CRect GetZoomTextRect(CRect imageProcessingArea); - void EditINIFile(bool bGlobalINI); + void EditSettingsFile(bool bGlobalINI); + void EditKeymapFile(bool bGlobalKeymap); + void EditTextFile(LPCTSTR sFileName); int GetLoadErrorAfterOpenFile(); void CheckIfApplyAutoFitWndToImage(bool bInInitDialog); void PrefetchDIB(const CRect& clientRect); From 90fadf614c939862d7785caf1f62e5234f79f823 Mon Sep 17 00:00:00 2001 From: Florian M Date: Sun, 30 Mar 2025 14:51:07 +0200 Subject: [PATCH 07/20] nicer confirm modals for jpeg rotation --- src/JPEGView/JPEGImage.cpp | 11 +++++++- src/JPEGView/JPEGImage.h | 2 ++ src/JPEGView/MainDlg.cpp | 55 +++++++++++++++++++------------------- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/JPEGView/JPEGImage.cpp b/src/JPEGView/JPEGImage.cpp index 1a000244..c2e8b340 100644 --- a/src/JPEGView/JPEGImage.cpp +++ b/src/JPEGView/JPEGImage.cpp @@ -1,4 +1,4 @@ -#include "StdAfx.h" +#include "StdAfx.h" #include "JPEGImage.h" #include "BasicProcessing.h" #include "XMMImage.h" @@ -199,6 +199,15 @@ bool CJPEGImage::CanUseLosslessJPEGTransformations() { (m_nOrigHeight % tjMCUHeight[m_eJPEGChromoSampling]) == 0; } +CString CJPEGImage::GetBlockSizeFormatted() { + if (m_eImageFormat == IF_JPEG) { + CString formatted; + formatted.Format(_T("%d × %d"), tjMCUWidth[m_eJPEGChromoSampling], tjMCUHeight[m_eJPEGChromoSampling]); + return formatted; + } + return _T(""); +} + void CJPEGImage::TrimRectToMCUBlockSize(CRect& rect) { int nBlockWidth = tjMCUWidth[m_eJPEGChromoSampling]; rect.left = rect.left & ~(nBlockWidth - 1); diff --git a/src/JPEGView/JPEGImage.h b/src/JPEGView/JPEGImage.h index 538b2c11..2b5d5628 100644 --- a/src/JPEGView/JPEGImage.h +++ b/src/JPEGView/JPEGImage.h @@ -179,6 +179,8 @@ class CJPEGImage { // Checks if this is a JPEG image and if the dimension of this image is dividable by the JPEG block size used. bool CanUseLosslessJPEGTransformations(); + CString GetBlockSizeFormatted(); + // Trims the given rectangle to MCU block size of this image (allowing lossless JPEG transformations) void TrimRectToMCUBlockSize(CRect& rect); diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index b1016cbd..19ac9a19 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -1,4 +1,4 @@ -// MainDlg.cpp : implementation of the CMainDlg class +// MainDlg.cpp : implementation of the CMainDlg class // ///////////////////////////////////////////////////////////////////////////// @@ -842,7 +842,7 @@ LRESULT CMainDlg::OnRButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam* } LRESULT CMainDlg::OnMButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { - // MOD: I dont want dragging with middle click, instead normal handling so even fast-pased clicks are handled + // MOD: I don’t want dragging with middle click, instead normal handling so even fast-pased clicks are handled bHandled = HandleMouseButtonByKeymap(VK_MBUTTON); return 0; } @@ -1655,35 +1655,36 @@ void CMainDlg::ExecuteCommand(int nCommand) { case IDM_MIRROR_V_LOSSLESS: if (m_pCurrentImage != NULL && m_pCurrentImage->GetImageFormat() == IF_JPEG) { bool bCanTransformWithoutCrop = m_pCurrentImage->CanUseLosslessJPEGTransformations(); - bool bAskIfToCrop = !(bCanTransformWithoutCrop ||sp.CropWithoutPromptLosslessJPEG()); + bool bAskConfirm = !(bCanTransformWithoutCrop ||sp.CropWithoutPromptLosslessJPEG()) || nCommand == IDM_ROTATE_90_LOSSLESS_CONFIRM || nCommand == IDM_ROTATE_270_LOSSLESS_CONFIRM; bool bPerformTransformation = true; bool bCrop = false; MouseOn(); - if (bAskIfToCrop) { - bCrop = IDYES == ::MessageBox(m_hWnd, CString(CNLS::GetString(_T("Image width and height must be dividable by the JPEG block size (8 or 16) for lossless transformations!"))) + _T("\n") + - CNLS::GetString(_T("The transformation can be applied if the image is cropped to the next matching size but this will remove some border pixels.")) + _T("\n") + - CNLS::GetString(_T("Crop the image and apply transformation? Cropping cannot be undone!")) + _T("\n\n") + - CNLS::GetString(_T("Note: Set the key 'CropWithoutPromptLosslessJPEG=true' in the INI file to always crop without showing this message.")), - CNLS::GetString(_T("Lossless JPEG transformations")), MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2); - } - if (!bAskIfToCrop || bCrop) { - if (!bAskIfToCrop && (nCommand == IDM_ROTATE_90_LOSSLESS_CONFIRM || nCommand == IDM_ROTATE_270_LOSSLESS_CONFIRM)) { - LPCTSTR sConfirmMsg = (nCommand == IDM_ROTATE_90_LOSSLESS_CONFIRM) ? - CNLS::GetString(_T("Rotate current file on disk lossless by 90 deg (W/H must be multiple of 16)")) : - CNLS::GetString(_T("Rotate current file on disk lossless by 270 deg (W/H must be multiple of 16)")); - bPerformTransformation = IDYES == ::MessageBox(m_hWnd, - sConfirmMsg, CNLS::GetString(_T("Confirm")), MB_YESNOCANCEL | MB_ICONWARNING); + if (bAskConfirm) { + CString safeMsg; + if (bCanTransformWithoutCrop) { + safeMsg = _T("✓ Transformation will be fully lossless.\n"); + } + else { + safeMsg.Format(_T("Image will be cropped to match JPEG block size %s\n"), m_pCurrentImage->GetBlockSizeFormatted()); } - if (bPerformTransformation) { - CJPEGLosslessTransform::EResult eResult = - CJPEGLosslessTransform::PerformTransformation(m_pFileList->Current(), m_pFileList->Current(), HelpersGUI::CommandIdToLosslessTransformation(nCommand), bCrop || sp.CropWithoutPromptLosslessJPEG()); - if (eResult != CJPEGLosslessTransform::Success) { - ::MessageBox(m_hWnd, CString(CNLS::GetString(_T("Performing the lossless transformation failed!"))) + - _T("\n") + CNLS::GetString(_T("Reason:")) + _T(" ") + HelpersGUI::LosslessTransformationResultToString(eResult), - CNLS::GetString(_T("Lossless JPEG transformations")), MB_OK | MB_ICONWARNING); - } else { - ReloadImage(false); // reload current image - } + CString confirm90; + confirm90.Format(_T("↻ Rotate %s on disk by 90° clockwise?\n%s"), CurrentFileName(true), safeMsg); + CString confirm270; + confirm270.Format(_T("↺ Rotate %s on disk by 90° counterclockwise?\n%s"), CurrentFileName(true), safeMsg); + LPCTSTR sConfirmMsg = (nCommand == IDM_ROTATE_90_LOSSLESS_CONFIRM) ? confirm90 : confirm270; + long icon = bCanTransformWithoutCrop? MB_ICONQUESTION : MB_ICONWARNING; + bPerformTransformation = IDOK == ::MessageBox(m_hWnd, + sConfirmMsg, CNLS::GetString(_T("Confirm")), MB_OKCANCEL | icon); + } + if (bPerformTransformation) { + CJPEGLosslessTransform::EResult eResult = + CJPEGLosslessTransform::PerformTransformation(m_pFileList->Current(), m_pFileList->Current(), HelpersGUI::CommandIdToLosslessTransformation(nCommand), bCrop || sp.CropWithoutPromptLosslessJPEG()); + if (eResult != CJPEGLosslessTransform::Success) { + ::MessageBox(m_hWnd, CString(CNLS::GetString(_T("Performing the lossless transformation failed!"))) + + _T("\n") + CNLS::GetString(_T("Reason:")) + _T(" ") + HelpersGUI::LosslessTransformationResultToString(eResult), + CNLS::GetString(_T("Lossless JPEG transformations")), MB_OK | MB_ICONERROR); + } else { + ReloadImage(false); // reload current image } } } From f40bf8240eb173cd193c62d80960cbad7c3cc1e6 Mon Sep 17 00:00:00 2001 From: Florian M Date: Mon, 31 Mar 2025 21:20:25 +0200 Subject: [PATCH 08/20] more space for expand button in exif view, use new windows file open dialog, improve messages on lossless transforms --- src/JPEGView/EXIFDisplay.cpp | 14 +++-- src/JPEGView/FileOpenDialog.cpp | 86 +++++++-------------------- src/JPEGView/FileOpenDialog.h | 24 ++------ src/JPEGView/JPEGProvider.cpp | 4 ++ src/JPEGView/JPEGView.cpp | 8 +++ src/JPEGView/JPEGView.vcxproj | 4 +- src/JPEGView/JPEGView.vcxproj.filters | 12 ++-- src/JPEGView/MainDlg.cpp | 38 +++++++++--- 8 files changed, 86 insertions(+), 104 deletions(-) diff --git a/src/JPEGView/EXIFDisplay.cpp b/src/JPEGView/EXIFDisplay.cpp index 9c51fec2..9561af71 100644 --- a/src/JPEGView/EXIFDisplay.cpp +++ b/src/JPEGView/EXIFDisplay.cpp @@ -173,7 +173,8 @@ CRect CEXIFDisplay::PanelRect() { m_nPrefixLength = 0; m_nTitleWidth = 0; int nTitleLength = 0; - int nMaxLength1 = 0, nMaxLength2 = 0; + int nMaxLengthColumn1 = 0, nMaxLengthColumn2 = 0; + int nLastLineLengthColumn2 = 0; CSize size; if (m_sPrefix != NULL) { ::GetTextExtentPoint32(dc, m_sPrefix, (int)_tcslen(m_sPrefix), &size); @@ -211,21 +212,22 @@ CRect CEXIFDisplay::PanelRect() { if (iter->Desc != NULL && m_bShowHistogram) { ::GetTextExtentPoint32(dc, iter->Desc, (int)_tcslen(iter->Desc), &size); m_nLineHeight = max(m_nLineHeight, size.cy); - nMaxLength1 = max(nMaxLength1, size.cx); + nMaxLengthColumn1 = max(nMaxLengthColumn1, size.cx); } nLen2 = nLen1; nLen1 = 0; if (iter->Value != NULL) { ::GetTextExtentPoint32(dc, iter->Value, (int)_tcslen(iter->Value), &size); m_nLineHeight = max(m_nLineHeight, size.cy); - nMaxLength2 = max(nMaxLength2, size.cx); + nMaxLengthColumn2 = max(nMaxLengthColumn2, size.cx); + nLastLineLengthColumn2 = size.cx; nLen1 = size.cx; } } int nButtonWidth = (int)(m_fDPIScale * BUTTON_SIZE); - bool bNeedsExpansionForButton = (m_bShowHistogram) && (nMaxLength2 - max(nLen1, nLen2)) < nButtonWidth + m_nGap; - int nNeededWidthNoBorders = max(nTitleLength, nMaxLength1 + nMaxLength2 + m_nGap) + (bNeedsExpansionForButton ? m_nGap + nButtonWidth : 0); + int columnGap = m_bShowHistogram ? m_nGap : 0; + int nNeededWidthNoBorders = max(max(nTitleLength, nMaxLengthColumn1 + nMaxLengthColumn2 + m_nGap), nMaxLengthColumn1 + columnGap + nLastLineLengthColumn2 + m_nGap/2 + nButtonWidth); int nExpansionX = 0, nExpansionY = 0; if (m_bShowHistogram) { nExpansionX = max(0, HelpersGUI::ScaleToScreen(256) - nNeededWidthNoBorders); @@ -245,7 +247,7 @@ CRect CEXIFDisplay::PanelRect() { } m_nNoHistogramSize = CSize(m_size.cx - nExpansionX, m_size.cy - nExpansionY); - m_nTab1 = nMaxLength1 + m_nGap; + m_nTab1 = nMaxLengthColumn1 + m_nGap; if (!m_bShowHistogram) { m_nTab1 = 0; } diff --git a/src/JPEGView/FileOpenDialog.cpp b/src/JPEGView/FileOpenDialog.cpp index f9414481..33270bb1 100644 --- a/src/JPEGView/FileOpenDialog.cpp +++ b/src/JPEGView/FileOpenDialog.cpp @@ -1,68 +1,28 @@ #include "StdAfx.h" #include "FileOpenDialog.h" -#include "NLS.h" -#include "Helpers.h" -#include "SettingsProvider.h" -#include "MultiMonitorSupport.h" -#include "dlgs.h" -bool CFileOpenDialog::m_bSized = false; - -// Command code to set the thumbnail view on the file open dialog -const unsigned int ODM_VIEW_THUMBS= 0x702d; - -CFileOpenDialog::CFileOpenDialog(HWND parentWindow, LPCTSTR sInitialFileName, LPCTSTR sFileEndings, bool bFullScreen) : -CFileDialog(TRUE, _T("jpg"), sInitialFileName, - OFN_EXPLORER | OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, - Helpers::CReplacePipe(CString(CNLS::GetString(_T("Images"))) + _T(" (") + sFileEndings + _T(")|") + sFileEndings + _T("|") + - CNLS::GetString(_T("All Files")) + _T("|*.*|")), parentWindow) { - m_bFullScreen = bFullScreen; -} - -LRESULT CFileOpenDialog::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { - if (!m_bSized) { - HWND wndParent = this->GetParent(); - // moving the window out of the visible range prevents flickering - ::SetWindowPos(wndParent, 0, 20000, 20000, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOREDRAW); - } - - // Will be processed after the dialog is really initialized - ::PostMessage(m_hWnd, MYWM_POSTINIT, 0, 0); - - return 0; -} - -LRESULT CFileOpenDialog::OnPostInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { - HWND wndParent = this->GetParent(); // real dialog is parent of us - - SizeDialog(); - - // Get the shell window hosting the list view of the files and send a WM_COMMAND to it to - // switch to thumbnail view - // http://msdn.microsoft.com/msdnmag/issues/04/03/CQA/ - HWND hLst2 = ::GetDlgItem(wndParent, lst2); - if (hLst2 != NULL) { - ::SendMessage(hLst2, WM_COMMAND, ODM_VIEW_THUMBS, 0); - } - return 0; +FileOpenDialog::FileOpenDialog(LPCTSTR sInitialFileName, LPCTSTR sFileEndings) { + COMDLG_FILTERSPEC fileTypes[] = { + { _T("Images"), sFileEndings }, + { L"All Files", L"*.*" } + }; + GetPtr()->SetFileTypes(_countof(fileTypes), fileTypes); + GetPtr()->SetOptions(FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST); + + if (sInitialFileName != NULL) { + PIDLIST_ABSOLUTE pidl; + SHILCreateFromPath(sInitialFileName, &pidl, NULL); + IShellItem* pItem; + SHCreateShellItem(NULL, NULL, pidl, &pItem); + IShellItem* pParent; + pItem->GetParent(&pParent); + + HRESULT hr = GetPtr()->SetFolder(pParent); + } } -void CFileOpenDialog::SizeDialog() { - if (!m_bSized || m_bFullScreen) { - HWND wndParent = this->GetParent(); // real dialog is parent of us - - CSettingsProvider& sp = CSettingsProvider::This(); - CRect monitorRect = CMultiMonitorSupport::GetMonitorRect(sp.DisplayMonitor()); - int nScreenX = monitorRect.Width(); - int nScreenY = monitorRect.Height(); - int nSizeX = nScreenX*90/100; - int nSizeY = nScreenY*90/100; - ::MoveWindow(wndParent, monitorRect.left + (nScreenX - nSizeX) / 2, monitorRect.top + (nScreenY - nSizeY) / 2, - nSizeX, nSizeY, TRUE); - if (!m_bFullScreen) { - m_bSized = true; - } - } -} - - +CString FileOpenDialog::GetFilePathStr() { + WCHAR szPath[MAX_PATH]; + GetFilePath(szPath, MAX_PATH); + return CString(szPath); +} \ No newline at end of file diff --git a/src/JPEGView/FileOpenDialog.h b/src/JPEGView/FileOpenDialog.h index 32fcf3ec..5ec44624 100644 --- a/src/JPEGView/FileOpenDialog.h +++ b/src/JPEGView/FileOpenDialog.h @@ -1,24 +1,8 @@ #pragma once +#include "StdAfx.h" -#define MYWM_POSTINIT WM_USER+1 - -class CFileOpenDialog : public CFileDialog -{ +class FileOpenDialog : public CShellFileOpenDialogImpl { public: - // The file extensions must be separated by pipe (|) - CFileOpenDialog(HWND parentWindow, LPCTSTR sInitialFileName, LPCTSTR sFileEndings, bool bFullScreen); - - BEGIN_MSG_MAP(CFileOpenDialog) - MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) - MESSAGE_HANDLER(MYWM_POSTINIT, OnPostInitDialog) - END_MSG_MAP() - - LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); - LRESULT OnPostInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); - -private: - void SizeDialog(); - - bool m_bFullScreen; - static bool m_bSized; + FileOpenDialog(LPCTSTR sInitialFileName, LPCTSTR sFileEndings); + CString GetFilePathStr(); }; diff --git a/src/JPEGView/JPEGProvider.cpp b/src/JPEGView/JPEGProvider.cpp index 7ea45801..458b5155 100644 --- a/src/JPEGView/JPEGProvider.cpp +++ b/src/JPEGView/JPEGProvider.cpp @@ -286,6 +286,10 @@ CImageLoadThread* CJPEGProvider::SearchThreadForNewRequest(void) { } void CJPEGProvider::RemoveUnusedImages(bool bRemoveAlsoActiveRequests) { + /*if (m_requestList.size() < 50) { + // We have plenty memory. Lets keep more images in cache! + return; + }*/ bool bRemoved = false; int nTimeStampToRemove = -2; do { diff --git a/src/JPEGView/JPEGView.cpp b/src/JPEGView/JPEGView.cpp index 159868a6..b34a2621 100644 --- a/src/JPEGView/JPEGView.cpp +++ b/src/JPEGView/JPEGView.cpp @@ -7,6 +7,8 @@ #include "MainDlg.h" #include "SettingsProvider.h" +#include + #ifdef DEBUG #include #endif @@ -186,6 +188,7 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lp ::SetUnhandledExceptionFilter(CrashHandler); #endif + auto start = std::chrono::high_resolution_clock::now(); // use the locale from the operating system, however for numeric input/output always use 'C' locale // with the OS locale the INI file can not be read correctly (due to the different decimal point characters) @@ -242,6 +245,11 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lp dlgMain.SetStartupInfo(sStartupFile, nAutostartSlideShow, eSorting, eTransitionEffect, nTransitionTime, bAutoExit, nDisplayMonitor); try { + auto afterInit = std::chrono::high_resolution_clock::now(); + CString durationStr; + long double durationLongDouble = std::chrono::duration_cast(afterInit - start).count(); + durationStr.Format(_T("init took %g ms\n"), durationLongDouble); + ::OutputDebugString(durationStr); nRet = (int)dlgMain.DoModal(); if (CSettingsProvider::This().StickyWindowSize() && !dlgMain.IsFullScreenMode()) { CSettingsProvider::This().SaveStickyWindowRect(dlgMain.WindowRectOnClose()); diff --git a/src/JPEGView/JPEGView.vcxproj b/src/JPEGView/JPEGView.vcxproj index 9160c7a1..d4a193e7 100644 --- a/src/JPEGView/JPEGView.vcxproj +++ b/src/JPEGView/JPEGView.vcxproj @@ -330,6 +330,7 @@ + @@ -386,7 +387,6 @@ - @@ -423,6 +423,7 @@ + @@ -477,7 +478,6 @@ - diff --git a/src/JPEGView/JPEGView.vcxproj.filters b/src/JPEGView/JPEGView.vcxproj.filters index 8a484a5d..ba4167a1 100644 --- a/src/JPEGView/JPEGView.vcxproj.filters +++ b/src/JPEGView/JPEGView.vcxproj.filters @@ -192,9 +192,6 @@ Source Files\Dialogs - - Source Files\Dialogs - Source Files\Dialogs @@ -291,6 +288,9 @@ Source Files\Image Types + + Source Files\Dialogs + @@ -452,9 +452,6 @@ Header Files\Dialogs - - Header Files\Dialogs - Header Files\Dialogs @@ -563,6 +560,9 @@ Header Files\Image Types + + Header Files\Dialogs + diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 19ac9a19..c939e598 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -6,6 +6,7 @@ #include "resource.h" #include #include +#include #include "MainDlg.h" #include "HelpDlg.h" @@ -307,6 +308,7 @@ void CMainDlg::SetStartupInfo(LPCTSTR sStartupFile, int nAutostartSlideShow, Hel } LRESULT CMainDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { + auto before = std::chrono::high_resolution_clock::now(); UpdateWindowTitle(); // set the scaling of the screen (DPI) compared to 96 DPI (design value) @@ -428,11 +430,18 @@ LRESULT CMainDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam this->DragAcceptFiles(); + auto afterInitDialog = std::chrono::high_resolution_clock::now(); + CString durationStr; + long double durationLongDouble = std::chrono::duration_cast(afterInitDialog - before).count(); + durationStr.Format(_T("initDialog took %g ms\n"), durationLongDouble); + ::OutputDebugString(durationStr); + return TRUE; } LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { + auto before = std::chrono::high_resolution_clock::now(); static bool s_bFirst = true; if (m_bLockPaint) { @@ -580,6 +589,12 @@ LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, B SetCursorForMoveSection(); + auto afterPaint = std::chrono::high_resolution_clock::now(); + CString durationStr; + long double durationLongDouble = std::chrono::duration_cast(afterPaint- before).count(); + durationStr.Format(_T("Paint took %g ms\n"), durationLongDouble); + ::OutputDebugString(durationStr); + return 0; } @@ -1660,17 +1675,17 @@ void CMainDlg::ExecuteCommand(int nCommand) { bool bCrop = false; MouseOn(); if (bAskConfirm) { - CString safeMsg; + CString sLosslessOrCropMessage; if (bCanTransformWithoutCrop) { - safeMsg = _T("✓ Transformation will be fully lossless.\n"); + sLosslessOrCropMessage = _T("✓ Transformation will be fully lossless.\n"); } else { - safeMsg.Format(_T("Image will be cropped to match JPEG block size %s\n"), m_pCurrentImage->GetBlockSizeFormatted()); + sLosslessOrCropMessage.Format(_T("Image will be cropped to match JPEG block size %s\n"), m_pCurrentImage->GetBlockSizeFormatted()); } CString confirm90; - confirm90.Format(_T("↻ Rotate %s on disk by 90° clockwise?\n%s"), CurrentFileName(true), safeMsg); + confirm90.Format(_T("↻ Rotate %s on disk by 90° clockwise?\n%s"), CurrentFileName(true), sLosslessOrCropMessage); CString confirm270; - confirm270.Format(_T("↺ Rotate %s on disk by 90° counterclockwise?\n%s"), CurrentFileName(true), safeMsg); + confirm270.Format(_T("↺ Rotate %s on disk by 90° counterclockwise?\n%s"), CurrentFileName(true), sLosslessOrCropMessage); LPCTSTR sConfirmMsg = (nCommand == IDM_ROTATE_90_LOSSLESS_CONFIRM) ? confirm90 : confirm270; long icon = bCanTransformWithoutCrop? MB_ICONQUESTION : MB_ICONWARNING; bPerformTransformation = IDOK == ::MessageBox(m_hWnd, @@ -2181,10 +2196,11 @@ bool CMainDlg::OpenFileWithDialog(bool bFullScreen, bool bAfterStartup) { StopMovieMode(); StopAnimation(); MouseOn(); - CFileOpenDialog dlgOpen(this->m_hWnd, m_pFileList->Current(), CFileList::GetSupportedFileEndings(), bFullScreen); + FileOpenDialog dlgOpen(m_pFileList->Current(), CFileList::GetSupportedFileEndings()); if (IDOK == dlgOpen.DoModal(this->m_hWnd)) { m_isBeforeFileSelected = false; - OpenFile(dlgOpen.m_szFileName, bAfterStartup); + CString filePath = dlgOpen.GetFilePathStr(); + OpenFile(filePath, bAfterStartup); return true; } m_isBeforeFileSelected = false; @@ -2454,6 +2470,8 @@ void CMainDlg::GotoImage(EImagePosition ePos) { } void CMainDlg::GotoImage(EImagePosition ePos, int nFlags) { + auto before = std::chrono::high_resolution_clock::now(); + // Timer handling for slideshows if (ePos == POS_Next || ePos == POS_NextSlideShow) { if (m_nCurrentTimeout > 0) { @@ -2620,6 +2638,12 @@ void CMainDlg::GotoImage(EImagePosition ePos, int nFlags) { MSG msg; while (::PeekMessage(&msg, this->m_hWnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); } + + auto afterGotoImage= std::chrono::high_resolution_clock::now(); + CString durationStr; + long double durationLongDouble = std::chrono::duration_cast(afterGotoImage - before).count(); + durationStr.Format(_T("GotoImage took %g ms\n"), durationLongDouble); + ::OutputDebugString(durationStr); } void CMainDlg::ReloadImage(bool keepParameters, bool updateWindow) { From 6c30821ae5b8d84b696e2aeef8f96dff3578ea2a Mon Sep 17 00:00:00 2001 From: Florian M Date: Tue, 1 Apr 2025 19:59:00 +0200 Subject: [PATCH 09/20] shortuct for show histogram --- src/JPEGView/Config/KeyMap-README.html | 10 ++++++++++ src/JPEGView/Config/symbols.km | 1 + src/JPEGView/EXIFDisplayCtl.cpp | 15 ++++++++++++++- src/JPEGView/EXIFDisplayCtl.h | 3 +++ src/JPEGView/MainDlg.cpp | 11 +++++++++++ src/JPEGView/resource.h | 3 ++- 6 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/JPEGView/Config/KeyMap-README.html b/src/JPEGView/Config/KeyMap-README.html index 457919d5..a0817c7c 100644 --- a/src/JPEGView/Config/KeyMap-README.html +++ b/src/JPEGView/Config/KeyMap-README.html @@ -100,6 +100,10 @@ content: "toggle show file and EXIF info box in top, left corner" } +.IDM_SHOW_FILEINFO_HISTOGRAM:before { + content: "toggle show histogram and details in EXIF info box in top, left corner" +} + .IDM_SHOW_FILENAME:before { content: "toggle show file name on top of screen" } @@ -736,6 +740,12 @@

+ IDM_SHOW_FILEINFO_HISTOGRAM + + + + + IDM_SHOW_FILENAME diff --git a/src/JPEGView/Config/symbols.km b/src/JPEGView/Config/symbols.km index 0bd542d5..55f03efd 100644 --- a/src/JPEGView/Config/symbols.km +++ b/src/JPEGView/Config/symbols.km @@ -32,6 +32,7 @@ #define IDM_SET_WALLPAPER_ORIG 2770 #define IDM_SET_WALLPAPER_DISPLAY 2774 #define IDM_SHOW_FILEINFO 2800 +#define IDM_SHOW_FILEINFO_HISTOGRAM 2830 #define IDM_SHOW_FILENAME 3000 #define IDM_SHOW_NAVPANEL 3100 #define IDM_NEXT 4000 diff --git a/src/JPEGView/EXIFDisplayCtl.cpp b/src/JPEGView/EXIFDisplayCtl.cpp index da8d584d..80636de8 100644 --- a/src/JPEGView/EXIFDisplayCtl.cpp +++ b/src/JPEGView/EXIFDisplayCtl.cpp @@ -57,7 +57,8 @@ CEXIFDisplayCtl::CEXIFDisplayCtl(CMainDlg* pMainDlg, CPanel* pImageProcPanel) : CButtonCtrl* pCloseBtn = m_pEXIFDisplay->GetControl(CEXIFDisplay::ID_btnClose); pCloseBtn->SetButtonPressedHandler(&OnClose, this); pCloseBtn->SetShow(false); - m_pEXIFDisplay->SetShowHistogram(CSettingsProvider::This().ShowHistogram()); + m_bShowHistogram = CSettingsProvider::This().ShowHistogram(); + m_pEXIFDisplay->SetShowHistogram(m_bShowHistogram); } CEXIFDisplayCtl::~CEXIFDisplayCtl() { @@ -80,6 +81,18 @@ void CEXIFDisplayCtl::SetActive(bool bActive) { SetVisible(bActive); } +void CEXIFDisplayCtl::SetShowHistogram(bool bShowHistogram) { + m_bShowHistogram = bShowHistogram; + if (m_bShowHistogram) { + ::OutputDebugString(_T("show!")); + } + else { + ::OutputDebugString(_T("hide!")); + } + m_pEXIFDisplay->SetShowHistogram(m_bShowHistogram); + InvalidateMainDlg(); +} + void CEXIFDisplayCtl::AfterNewImageLoaded() { m_pEXIFDisplay->ClearTexts(); m_pEXIFDisplay->SetHistogram(NULL); diff --git a/src/JPEGView/EXIFDisplayCtl.h b/src/JPEGView/EXIFDisplayCtl.h index 98499554..7d2bf071 100644 --- a/src/JPEGView/EXIFDisplayCtl.h +++ b/src/JPEGView/EXIFDisplayCtl.h @@ -16,9 +16,11 @@ class CEXIFDisplayCtl : public CPanelController virtual bool IsVisible(); virtual bool IsActive() { return m_bVisible; } + virtual bool GetShowHistogram() { return m_bShowHistogram; } virtual void SetVisible(bool bVisible); virtual void SetActive(bool bActive); + virtual void SetShowHistogram(bool bShowHistogram); virtual void AfterNewImageLoaded(); @@ -29,6 +31,7 @@ class CEXIFDisplayCtl : public CPanelController private: bool m_bVisible; + bool m_bShowHistogram; CEXIFDisplay* m_pEXIFDisplay; CPanel* m_pImageProcPanel; int m_nFileNameHeight; diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index c939e598..33130f2c 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -1482,6 +1482,17 @@ void CMainDlg::ExecuteCommand(int nCommand) { m_pEXIFDisplayCtl->SetActive(!m_pEXIFDisplayCtl->IsActive()); m_pNavPanelCtl->GetNavPanel()->GetBtnShowInfo()->SetActive(m_pEXIFDisplayCtl->IsActive()); break; + case IDM_SHOW_FILEINFO_HISTOGRAM: + if (m_pCurrentImage == NULL) { + return; + } + if (m_pEXIFDisplayCtl->IsVisible()) { + m_pEXIFDisplayCtl->SetShowHistogram(!m_pEXIFDisplayCtl->GetShowHistogram()); + } else { + m_pEXIFDisplayCtl->SetActive(true); + m_pEXIFDisplayCtl->SetShowHistogram(true); + } + break; case IDM_SHOW_FILENAME: m_bShowFileName = !m_bShowFileName; this->Invalidate(FALSE); diff --git a/src/JPEGView/resource.h b/src/JPEGView/resource.h index 94c62f8d..1f746c06 100644 --- a/src/JPEGView/resource.h +++ b/src/JPEGView/resource.h @@ -162,7 +162,8 @@ #define IDM_TOUCH_IMAGE_EXIF_FOLDER 2720 // :KeyMap: set modification time to EXIF time for all images in folder #define IDM_SET_WALLPAPER_ORIG 2770 // :KeyMap: Set original image file as desktop wallpaper #define IDM_SET_WALLPAPER_DISPLAY 2774 // :KeyMap: Set image as displayed as desktop wallpaper -#define IDM_SHOW_FILEINFO 2800 // :KeyMap: toggle show file and EXIF info box in top, left corner +#define IDM_SHOW_FILEINFO 2800 // :KeyMap: toggle show file and EXIF info box in top-left corner +#define IDM_SHOW_FILEINFO_HISTOGRAM 2830 // :KeyMap: toggle show details and histogram in EXIF info box in top-left corner #define IDM_SHOW_FILENAME 3000 // :KeyMap: toggle show file name on top of screen #define IDM_SHOW_NAVPANEL 3100 // :KeyMap: toggle show navigation panel #define IDM_NEXT 4000 // :KeyMap: go to next image From 1d5fb228106bb4261889cecd6f6496fe4679fd03 Mon Sep 17 00:00:00 2001 From: Florian M Date: Thu, 3 Apr 2025 21:49:50 +0200 Subject: [PATCH 10/20] lens model; animation info; exif panel dimFactor; toggleZoom depends on image size; leftClick with no image: open dialog --- src/JPEGView/EXIFDisplayCtl.cpp | 15 +++++++++++++++ src/JPEGView/EXIFDisplayCtl.h | 2 +- src/JPEGView/EXIFReader.cpp | 5 +++++ src/JPEGView/EXIFReader.h | 3 +++ src/JPEGView/MainDlg.cpp | 27 +++++++++++++++++++++------ 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/JPEGView/EXIFDisplayCtl.cpp b/src/JPEGView/EXIFDisplayCtl.cpp index 80636de8..05c87c2f 100644 --- a/src/JPEGView/EXIFDisplayCtl.cpp +++ b/src/JPEGView/EXIFDisplayCtl.cpp @@ -240,6 +240,9 @@ void CEXIFDisplayCtl::FillEXIFDataDisplay() { if (pEXIFReader->GetCameraModelPresent()) { m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Camera:")), pEXIFReader->GetCameraModel()); } + if (pEXIFReader->GetLensModelPresent()) { + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Lens:")), pEXIFReader->GetLensModel()); + } if (pEXIFReader->GetSoftwarePresent()) { m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Software:")), pEXIFReader->GetSoftware()); } @@ -303,6 +306,18 @@ void CEXIFDisplayCtl::FillEXIFDataDisplay() { } } + if (CurrentImage()->IsAnimation()) { + if (CurrentImage()->NumberOfFrames() != NULL) { + CString formattedFrameInfo; + CString padding = ""; + if (CurrentImage()->NumberOfFrames() >= 10 && CurrentImage()->FrameIndex() < 10) { + padding = " "; + } + formattedFrameInfo.Format(_T("Frame %s%d of %d (%d ms)"), padding, CurrentImage()->FrameIndex(), CurrentImage() ->NumberOfFrames(), CurrentImage()->FrameTimeMs()); + m_pEXIFDisplay->AddLine(CNLS::GetString(_T("Animation:")), formattedFrameInfo); + } + } + if (sComment == NULL || sComment[0] == 0 || ((std::wstring)sComment).find_first_not_of(L" \t\n\r\f\v", 0) == std::wstring::npos) { sComment = CurrentImage()->GetJPEGComment(); } diff --git a/src/JPEGView/EXIFDisplayCtl.h b/src/JPEGView/EXIFDisplayCtl.h index 7d2bf071..4d7360d4 100644 --- a/src/JPEGView/EXIFDisplayCtl.h +++ b/src/JPEGView/EXIFDisplayCtl.h @@ -12,7 +12,7 @@ class CEXIFDisplayCtl : public CPanelController CEXIFDisplayCtl(CMainDlg* pMainDlg, CPanel* pImageProcPanel); virtual ~CEXIFDisplayCtl(); - virtual float DimFactor() { return 0.5f; } + virtual float DimFactor() { return 0.65f; } virtual bool IsVisible(); virtual bool IsActive() { return m_bVisible; } diff --git a/src/JPEGView/EXIFReader.cpp b/src/JPEGView/EXIFReader.cpp index c12bf2a2..2761a3ff 100644 --- a/src/JPEGView/EXIFReader.cpp +++ b/src/JPEGView/EXIFReader.cpp @@ -398,6 +398,11 @@ CEXIFReader::CEXIFReader(void* pApp1Block, EImageFormat eImageFormat) m_sUserComment = ""; } + uint8* pTagLensModel = FindTag(pEXIFIFD, pLastEXIF, 0xa434, bLittleEndian); + CString sLensModel; + ReadStringTag(sLensModel, pTagLensModel, pTIFFHeader, bLittleEndian); + m_sLensModel = sLensModel; + // https://exiv2.org/tags.html // uint8* pTagXPComment = FindTag(pIFD0, pLastIFD0, 0x9c9c, bLittleEndian); // this is the XPComment tag to resolve this issue https://github.com/sylikc/jpegview/issues/72 , but I'm not sure how to decode it diff --git a/src/JPEGView/EXIFReader.h b/src/JPEGView/EXIFReader.h index f13264e5..530a8ce5 100644 --- a/src/JPEGView/EXIFReader.h +++ b/src/JPEGView/EXIFReader.h @@ -58,8 +58,10 @@ class CEXIFReader { LPCTSTR GetUserComment() { return m_sUserComment; } LPCTSTR GetImageDescription() { return m_sImageDescription; } LPCTSTR GetSoftware() { return m_sSoftware; } + LPCTSTR GetLensModel() { return m_sLensModel; } bool GetCameraModelPresent() { return !m_sModel.IsEmpty(); } bool GetSoftwarePresent() { return !m_sSoftware.IsEmpty(); } + bool GetLensModelPresent() { return !m_sLensModel.IsEmpty(); } // Date-time the picture was taken const SYSTEMTIME& GetAcquisitionTime() { return m_acqDate; } bool GetAcquisitionTimePresent() { return m_acqDate.wYear > 1600; } @@ -120,6 +122,7 @@ class CEXIFReader { CString m_sUserComment; CString m_sImageDescription; CString m_sSoftware; + CString m_sLensModel; SYSTEMTIME m_acqDate; SYSTEMTIME m_dateTime; Rational m_exposureTime; diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 33130f2c..ed426e8e 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -529,7 +529,7 @@ LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, B visRectZoomNavigator = m_pZoomNavigatorCtl->GetVisibleRect(newSize, clippedSize, offsetsInImage); excludedClippingRects.push_back(m_pZoomNavigatorCtl->PanelRect()); } - + m_pPanelMgr->OnPrePaint(dc); m_pPanelMgr->PrepareMemDCMgr(memDCMgr, excludedClippingRects); memDCMgr.ExcludeFromClippingRegion(dc, excludedClippingRects); @@ -635,7 +635,7 @@ void CMainDlg::PaintToDC(CDC& dc) { if (m_pEXIFDisplayCtl->IsVisible()) { m_pEXIFDisplayCtl->OnPrePaintMainDlg(dc); - BlendBlackRect(dc, *m_pEXIFDisplayCtl->GetPanel(), 0.5f); + BlendBlackRect(dc, *m_pEXIFDisplayCtl->GetPanel(), 0.5f); m_pEXIFDisplayCtl->OnPaintPanel(dc, CPoint(0, 0)); } @@ -769,6 +769,11 @@ LRESULT CMainDlg::OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, bool bEatenByPanel = isCropping ? false : m_pPanelMgr->OnMouseLButton(MouseEvent_BtnDown, pointClicked.x, pointClicked.y); if (!bEatenByPanel) { + if (m_pCurrentImage == NULL && !isCropping) { + OpenFileWithDialog(false, false); + return 0; + } + bool bCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0; bool bShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0; @@ -1776,11 +1781,21 @@ void CMainDlg::ExecuteCommand(int nCommand) { case IDM_TOGGLE_FILL_WITH_CROP_100_PERCENTS: if (m_pCurrentImage != NULL) { double dZoomForFitToScreen = GetZoomFactorForFitToScreen(nCommand == IDM_TOGGLE_FILL_WITH_CROP_100_PERCENTS, true); - if (abs(dZoomForFitToScreen - m_dZoom) < 0.001) { - ResetZoomTo100Percents(m_bMouseOn); + if (dZoomForFitToScreen < 1.0) { // The image is larger than the screen. Always go to fit-to-screen, unless we’re already there. + if (abs(dZoomForFitToScreen - m_dZoom) < 0.001) { + ResetZoomTo100Percents(m_bMouseOn); + } + else { + ResetZoomToFitScreen(nCommand == IDM_TOGGLE_FILL_WITH_CROP_100_PERCENTS, true, true); + } } - else { - ResetZoomToFitScreen(nCommand == IDM_TOGGLE_FILL_WITH_CROP_100_PERCENTS, true, true); + else { // The image is smaller than the screen. Always go to 100%, unless we’re already there. + if (abs(m_dZoom - 1.0) < 0.01) { + ResetZoomToFitScreen(nCommand == IDM_TOGGLE_FILL_WITH_CROP_100_PERCENTS, true, true); + } + else { + ResetZoomTo100Percents(m_bMouseOn); + } } } break; From 110956e53c6ec42968dea4daa83232bb2d28bd0a Mon Sep 17 00:00:00 2001 From: Florian M Date: Fri, 4 Apr 2025 09:15:04 +0200 Subject: [PATCH 11/20] different background color in fullscreen --- src/JPEGView/MainDlg.cpp | 6 +++--- src/JPEGView/MainDlg.h | 1 + src/JPEGView/NavigationPanelCtl.cpp | 4 ++-- src/JPEGView/PaintMemDCMgr.cpp | 10 ++++++---- src/JPEGView/PaintMemDCMgr.h | 7 +++++-- src/JPEGView/SettingsProvider.cpp | 1 + src/JPEGView/SettingsProvider.h | 3 ++- 7 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index ed426e8e..0598cac1 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -468,7 +468,7 @@ LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, B CRect imageProcessingArea = m_pImageProcPanelCtl->PanelRect(); CRectF visRectZoomNavigator(0.0f, 0.0f, 1.0f, 1.0f); CBrush backBrush; - backBrush.CreateSolidBrush(CSettingsProvider::This().ColorBackground()); + backBrush.CreateSolidBrush(CSettingsProvider::This().ColorBackground(m_bFullScreenMode)); #ifdef DEBUG CString a; a.Format(_T("client rect w/h pix: %d %d = %d\n"), m_clientRect.Width(), m_clientRect.Height(), m_clientRect.Width() * m_clientRect.Height()); @@ -478,7 +478,7 @@ LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, B std::list excludedClippingRects; // Panels are handled over memory DCs to eliminate flickering - CPaintMemDCMgr memDCMgr(dc); + CPaintMemDCMgr memDCMgr(this, dc); if (m_pCurrentImage == NULL) { m_pPanelMgr->OnPrePaint(dc); @@ -599,7 +599,7 @@ LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, B } void CMainDlg::PaintToDC(CDC& dc) { - COLORREF backColor = CSettingsProvider::This().ColorBackground(); + COLORREF backColor = CSettingsProvider::This().ColorBackground(m_bFullScreenMode); if (backColor == 0) backColor = RGB(0, 0, 1); // these f**ing nVidia drivers have a bug when blending pure black CBrush backBrush; diff --git a/src/JPEGView/MainDlg.h b/src/JPEGView/MainDlg.h index 7a2e0705..ff9ad2ae 100644 --- a/src/JPEGView/MainDlg.h +++ b/src/JPEGView/MainDlg.h @@ -3,6 +3,7 @@ #pragma once +#include "resource.h" #include "MessageDef.h" #include "ProcessParams.h" #include "Helpers.h" diff --git a/src/JPEGView/NavigationPanelCtl.cpp b/src/JPEGView/NavigationPanelCtl.cpp index a0d4f4d2..b2af48a3 100644 --- a/src/JPEGView/NavigationPanelCtl.cpp +++ b/src/JPEGView/NavigationPanelCtl.cpp @@ -241,11 +241,11 @@ void CNavigationPanelCtl::DoNavPanelAnimation() { if (pDIBData != NULL) { if (m_pMemDCAnimation == NULL) { m_pMemDCAnimation = new CDC(); - m_hOffScreenBitmapAnimation = CPaintMemDCMgr::PrepareRectForMemDCPainting(*m_pMemDCAnimation, screenDC, rectNavPanel); + m_hOffScreenBitmapAnimation = CPaintMemDCMgr::PrepareRectForMemDCPainting(*m_pMemDCAnimation, screenDC, rectNavPanel, m_pMainDlg->IsFullScreenMode()); } CBrush backBrush; - backBrush.CreateSolidBrush(CSettingsProvider::This().ColorBackground()); + backBrush.CreateSolidBrush(CSettingsProvider::This().ColorBackground(m_pMainDlg->IsFullScreenMode())); m_pMemDCAnimation->FillRect(CRect(0, 0, rectNavPanel.Width(), rectNavPanel.Height()), backBrush); BITMAPINFO bmInfo{ 0 }; diff --git a/src/JPEGView/PaintMemDCMgr.cpp b/src/JPEGView/PaintMemDCMgr.cpp index 8dd0cf9b..b7745032 100644 --- a/src/JPEGView/PaintMemDCMgr.cpp +++ b/src/JPEGView/PaintMemDCMgr.cpp @@ -3,8 +3,10 @@ #include "EXIFDisplay.h" #include "SettingsProvider.h" #include "PaintMemDCMgr.h" +#include "MainDlg.h" -CPaintMemDCMgr::CPaintMemDCMgr(CPaintDC& paintDC) : m_paintDC(paintDC) { +CPaintMemDCMgr::CPaintMemDCMgr(CMainDlg* pMainDlg, CPaintDC& paintDC) : m_paintDC(paintDC) { + m_pMainDlg = pMainDlg; m_nNumElems = 0; for (int i = 0; i < MAX_REGIONS_CPaintMemDCMgr; i++) { m_managedRegions[i].MemoryDC = CDC(); @@ -37,9 +39,9 @@ void CPaintMemDCMgr::IncludeIntoClippingRegion(CDC & paintDC, const std::listIsFullScreenMode()); m_nNumElems++; return displayRect; diff --git a/src/JPEGView/PaintMemDCMgr.h b/src/JPEGView/PaintMemDCMgr.h index 797e8b0a..667cea40 100644 --- a/src/JPEGView/PaintMemDCMgr.h +++ b/src/JPEGView/PaintMemDCMgr.h @@ -2,6 +2,7 @@ // forward declarations class CPanel; +class CMainDlg; #define MAX_REGIONS_CPaintMemDCMgr 16 @@ -9,7 +10,7 @@ class CPanel; // This eliminates flickering. class CPaintMemDCMgr { public: - CPaintMemDCMgr(CPaintDC& paintDC); + CPaintMemDCMgr(CMainDlg* pMainDlg, CPaintDC& paintDC); ~CPaintMemDCMgr(); CPaintDC& GetPaintDC() { return m_paintDC; } @@ -19,7 +20,7 @@ class CPaintMemDCMgr { static void IncludeIntoClippingRegion(CDC & paintDC, const std::list& listExcludedRects); // Prepares a memory DC of given size by creating the backing store bitmap and clearing it - static HBITMAP PrepareRectForMemDCPainting(CDC & memDC, CDC & paintDC, const CRect& rect); + static HBITMAP PrepareRectForMemDCPainting(CDC & memDC, CDC & paintDC, const CRect& rect, const bool isFullscreen); // Blits the DIB data section to target DC using dimming (blending with a black bitmap) static void BitBltBlended(CDC & dc, CDC & paintDC, const CSize& dcSize, void* pDIBData, BITMAPINFO* pbmInfo, @@ -50,4 +51,6 @@ class CPaintMemDCMgr { CPaintDC& m_paintDC; int m_nNumElems; CManagedRegion m_managedRegions[MAX_REGIONS_CPaintMemDCMgr]; + + CMainDlg* m_pMainDlg; }; \ No newline at end of file diff --git a/src/JPEGView/SettingsProvider.cpp b/src/JPEGView/SettingsProvider.cpp index 3ba29504..3228e872 100644 --- a/src/JPEGView/SettingsProvider.cpp +++ b/src/JPEGView/SettingsProvider.cpp @@ -222,6 +222,7 @@ CSettingsProvider::CSettingsProvider(void) { } m_colorBackground = GetColor(_T("BackgroundColor"), 0); + m_colorBackgroundFullscreen = GetColor(_T("BackgroundColorFullscreen"), m_colorBackground); m_colorGUI = GetColor(_T("GUIColor"), RGB(243, 242, 231)); m_colorHighlight = GetColor(_T("HighlightColor"), RGB(255, 205, 0)); m_colorSelected = GetColor(_T("SelectionColor"), RGB(255, 205, 0)); diff --git a/src/JPEGView/SettingsProvider.h b/src/JPEGView/SettingsProvider.h index bce046ca..05e6bc54 100644 --- a/src/JPEGView/SettingsProvider.h +++ b/src/JPEGView/SettingsProvider.h @@ -101,7 +101,7 @@ class CSettingsProvider bool DefaultMaximized() { return m_bDefaultMaximized; } bool ExplicitWindowRect() { return m_bExplicitWindowRect; } CSize DefaultFixedCropSize() { return m_DefaultFixedCropSize; } - COLORREF ColorBackground() { return m_colorBackground; } + COLORREF ColorBackground(int isFullscreen = false) { if (isFullscreen) return m_colorBackgroundFullscreen; else return m_colorBackground; } COLORREF ColorGUI() { return m_colorGUI; } COLORREF ColorHighlight() { return m_colorHighlight; } COLORREF ColorSelected() { return m_colorSelected; } @@ -272,6 +272,7 @@ class CSettingsProvider bool m_bExplicitWindowRect; CSize m_DefaultFixedCropSize; COLORREF m_colorBackground; + COLORREF m_colorBackgroundFullscreen; COLORREF m_colorGUI; COLORREF m_colorHighlight; COLORREF m_colorSelected; From f5ffa1884eda1927e35cf5fa06dc15d61507febe Mon Sep 17 00:00:00 2001 From: Florian M Date: Fri, 4 Apr 2025 09:45:09 +0200 Subject: [PATCH 12/20] WIP: different transparency color in fullscreen --- src/JPEGView/ImageLoadThread.cpp | 28 ++++++++++++++-------------- src/JPEGView/ImageLoadThread.h | 6 ++++-- src/JPEGView/JPEGProvider.cpp | 24 ++++++++++++------------ src/JPEGView/JPEGProvider.h | 8 ++++---- src/JPEGView/MainDlg.cpp | 4 +++- src/JPEGView/SettingsProvider.cpp | 1 + src/JPEGView/SettingsProvider.h | 5 +++-- 7 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/JPEGView/ImageLoadThread.cpp b/src/JPEGView/ImageLoadThread.cpp index 54580b53..95fc968c 100644 --- a/src/JPEGView/ImageLoadThread.cpp +++ b/src/JPEGView/ImageLoadThread.cpp @@ -118,7 +118,7 @@ static EImageFormat GetBitmapFormat(Gdiplus::Bitmap * pBitmap) { } static CJPEGImage* ConvertGDIPlusBitmapToJPEGImage(Gdiplus::Bitmap* pBitmap, int nFrameIndex, void* pEXIFData, - __int64 nJPEGHash, bool &isOutOfMemory, bool &isAnimatedGIF) { + __int64 nJPEGHash, COLORREF colorTransparency, bool &isOutOfMemory, bool &isAnimatedGIF) { isOutOfMemory = false; isAnimatedGIF = false; @@ -174,7 +174,7 @@ static CJPEGImage* ConvertGDIPlusBitmapToJPEGImage(Gdiplus::Bitmap* pBitmap, int if (bHasAlphaChannel) { pBmTarget = new Gdiplus::Bitmap(pBitmap->GetWidth(), pBitmap->GetHeight(), PixelFormat32bppRGB); pBmGraphics = new Gdiplus::Graphics(pBmTarget); - COLORREF bkColor = CSettingsProvider::This().ColorTransparency(); + COLORREF bkColor = colorTransparency; Gdiplus::SolidBrush bkBrush(Gdiplus::Color(GetRValue(bkColor), GetGValue(bkColor), GetBValue(bkColor))); pBmGraphics->FillRectangle(&bkBrush, 0, 0, pBmTarget->GetWidth(), pBmTarget->GetHeight()); pBmGraphics->DrawImage(pBitmap, 0, 0, pBmTarget->GetWidth(), pBmTarget->GetHeight()); @@ -229,8 +229,8 @@ CImageLoadThread::~CImageLoadThread(void) { DeleteCachedAvifDecoder(); } -int CImageLoadThread::AsyncLoad(LPCTSTR strFileName, int nFrameIndex, const CProcessParams & processParams, HWND targetWnd, HANDLE eventFinished) { - CRequest* pRequest = new CRequest(strFileName, nFrameIndex, targetWnd, processParams, eventFinished); +int CImageLoadThread::AsyncLoad(LPCTSTR strFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency, HWND targetWnd, HANDLE eventFinished) { + CRequest* pRequest = new CRequest(strFileName, nFrameIndex, targetWnd, processParams, colorTransparency, eventFinished); ProcessAsync(pRequest); @@ -504,7 +504,7 @@ void CImageLoadThread::ProcessReadJPEGRequest(CRequest * request) { Gdiplus::Bitmap* pBitmap = Gdiplus::Bitmap::FromStream(pStream, CSettingsProvider::This().UseEmbeddedColorProfiles()); bool isOutOfMemory, isAnimatedGIF; request->Image = ConvertGDIPlusBitmapToJPEGImage(pBitmap, 0, Helpers::FindEXIFBlock(pBuffer, nFileSize), - Helpers::CalculateJPEGFileHash(pBuffer, nFileSize), isOutOfMemory, isAnimatedGIF); + Helpers::CalculateJPEGFileHash(pBuffer, nFileSize), request->ColorTransparency, isOutOfMemory, isAnimatedGIF); request->OutOfMemory = request->Image == NULL && isOutOfMemory; if (request->Image != NULL) { request->Image->SetJPEGComment(Helpers::GetJPEGComment(pBuffer, nFileSize)); @@ -569,7 +569,7 @@ void CImageLoadThread::ProcessReadBMPRequest(CRequest * request) { void CImageLoadThread::ProcessReadTGARequest(CRequest * request) { bool bOutOfMemory; - request->Image = CReaderTGA::ReadTgaImage(request->FileName, CSettingsProvider::This().ColorTransparency(), bOutOfMemory); + request->Image = CReaderTGA::ReadTgaImage(request->FileName, request->ColorTransparency, bOutOfMemory); if (bOutOfMemory) { request->OutOfMemory = true; } @@ -625,7 +625,7 @@ void CImageLoadThread::ProcessReadWEBPRequest(CRequest * request) { // Multiply alpha value into each AABBGGRR pixel uint32* pImage32 = (uint32*)pPixelData; for (int i = 0; i < nWidth * nHeight; i++) - *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, CSettingsProvider::This().ColorTransparency()); + *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, request->ColorTransparency); if (bHasAnimation) { m_sLastWebpFileName = sFileName; @@ -710,7 +710,7 @@ void CImageLoadThread::ProcessReadPNGRequest(CRequest* request) { // Multiply alpha value into each AABBGGRR pixel uint32* pImage32 = (uint32*)pPixelData; for (int i = 0; i < nWidth * nHeight; i++) - *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, CSettingsProvider::This().ColorTransparency()); + *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, request->ColorTransparency); request->Image = new CJPEGImage(nWidth, nHeight, pPixelData, pEXIFData, 4, 0, IF_PNG, bHasAnimation, request->FrameIndex, nFrameCount, nFrameTimeMs); } else { @@ -721,7 +721,7 @@ void CImageLoadThread::ProcessReadPNGRequest(CRequest* request) { Gdiplus::Bitmap* pBitmap = Gdiplus::Bitmap::FromStream(pStream, CSettingsProvider::This().UseEmbeddedColorProfiles()); bool isOutOfMemory, isAnimatedGIF; pEXIFData = PngReader::GetEXIFBlock(pBuffer, nFileSize); - request->Image = ConvertGDIPlusBitmapToJPEGImage(pBitmap, 0, pEXIFData, 0, isOutOfMemory, isAnimatedGIF); + request->Image = ConvertGDIPlusBitmapToJPEGImage(pBitmap, 0, pEXIFData, 0, request->ColorTransparency, isOutOfMemory, isAnimatedGIF); request->OutOfMemory = request->Image == NULL && isOutOfMemory; pStream->Release(); delete pBitmap; @@ -795,7 +795,7 @@ void CImageLoadThread::ProcessReadJXLRequest(CRequest* request) { // Multiply alpha value into each AABBGGRR pixel uint32* pImage32 = (uint32*)pPixelData; for (int i = 0; i < nWidth * nHeight; i++) - *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, CSettingsProvider::This().ColorTransparency()); + *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, request->ColorTransparency); request->Image = new CJPEGImage(nWidth, nHeight, pPixelData, pEXIFData, 4, 0, IF_JXL, bHasAnimation, request->FrameIndex, nFrameCount, nFrameTimeMs); free(pEXIFData); @@ -869,7 +869,7 @@ void CImageLoadThread::ProcessReadAVIFRequest(CRequest* request) { // Multiply alpha value into each AABBGGRR pixel uint32* pImage32 = (uint32*)pPixelData; for (int i = 0; i < nWidth * nHeight; i++) - *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, CSettingsProvider::This().ColorTransparency()); + *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, request->ColorTransparency); request->Image = new CJPEGImage(nWidth, nHeight, pPixelData, pEXIFData, 4, 0, IF_AVIF, bHasAnimation, request->FrameIndex, nFrameCount, nFrameTimeMs); free(pEXIFData); @@ -929,7 +929,7 @@ void CImageLoadThread::ProcessReadHEIFRequest(CRequest* request) { // Multiply alpha value into each AABBGGRR pixel uint32* pImage32 = (uint32*)pPixelData; for (int i = 0; i < nWidth * nHeight; i++) - *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, CSettingsProvider::This().ColorTransparency()); + *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, request->ColorTransparency); request->Image = new CJPEGImage(nWidth, nHeight, pPixelData, pEXIFData, nBPP, 0, IF_HEIF, false, request->FrameIndex, nFrameCount, nFrameTimeMs); free(pEXIFData); @@ -989,7 +989,7 @@ void CImageLoadThread::ProcessReadQOIRequest(CRequest* request) { // Multiply alpha value into each AABBGGRR pixel uint32* pImage32 = (uint32*)pPixelData; for (int i = 0; i < nWidth * nHeight; i++) - *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, CSettingsProvider::This().ColorTransparency()); + *pImage32++ = Helpers::AlphaBlendBackground(*pImage32, request->ColorTransparency); } request->Image = new CJPEGImage(nWidth, nHeight, pPixelData, NULL, nBPP, 0, IF_QOI, false, 0, 1, 0); } @@ -1055,7 +1055,7 @@ void CImageLoadThread::ProcessReadGDIPlusRequest(CRequest * request) { m_sLastFileName = sFileName; } bool isOutOfMemory, isAnimatedGIF; - request->Image = ConvertGDIPlusBitmapToJPEGImage(pBitmap, request->FrameIndex, NULL, 0, isOutOfMemory, isAnimatedGIF); + request->Image = ConvertGDIPlusBitmapToJPEGImage(pBitmap, request->FrameIndex, NULL, 0, request->ColorTransparency, isOutOfMemory, isAnimatedGIF); request->OutOfMemory = request->Image == NULL && isOutOfMemory; if (!isAnimatedGIF) { DeleteCachedGDIBitmap(); diff --git a/src/JPEGView/ImageLoadThread.h b/src/JPEGView/ImageLoadThread.h index e2f2c13b..7375852e 100644 --- a/src/JPEGView/ImageLoadThread.h +++ b/src/JPEGView/ImageLoadThread.h @@ -38,7 +38,7 @@ class CImageLoadThread : public CWorkThread // received or the event has been signaled. // The file to load is given by its filename (with path) and the frame index (for multiframe images). The // frame index needs to be zero when the image only has one frame. - int AsyncLoad(LPCTSTR strFileName, int nFrameIndex, const CProcessParams & processParams, HWND targetWnd, HANDLE eventFinished); + int AsyncLoad(LPCTSTR strFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency, HWND targetWnd, HANDLE eventFinished); // Get loaded image, CImageData::Image is null if not (yet) available - use handle returned by AsyncLoad(). // Call after having received the WM_IMAGE_LOAD_COMPLETED message to retrieve the loaded image. @@ -56,7 +56,7 @@ class CImageLoadThread : public CWorkThread // Request for loading an image class CRequest : public CRequestBase { public: - CRequest(LPCTSTR strFileName, int nFrameIndex, HWND wndTarget, const CProcessParams& processParams, HANDLE eventFinished) + CRequest(LPCTSTR strFileName, int nFrameIndex, HWND wndTarget, const CProcessParams& processParams, COLORREF colorTransparency, HANDLE eventFinished) : CRequestBase(eventFinished), ProcessParams(processParams) { FileName = strFileName; FrameIndex = nFrameIndex; @@ -65,6 +65,7 @@ class CImageLoadThread : public CWorkThread Image = NULL; OutOfMemory = false; ExceptionError = false; + ColorTransparency = colorTransparency; } CString FileName; @@ -73,6 +74,7 @@ class CImageLoadThread : public CWorkThread int RequestHandle; CJPEGImage* Image; CProcessParams ProcessParams; + COLORREF ColorTransparency; bool OutOfMemory; // load caused an out of memory condition bool ExceptionError; // an unhandled exception caused the load to fail }; diff --git a/src/JPEGView/JPEGProvider.cpp b/src/JPEGView/JPEGProvider.cpp index 458b5155..9c3bc705 100644 --- a/src/JPEGView/JPEGProvider.cpp +++ b/src/JPEGView/JPEGProvider.cpp @@ -32,7 +32,7 @@ CJPEGProvider::~CJPEGProvider(void) { } CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirection eDirection, - LPCTSTR strFileName, int nFrameIndex, const CProcessParams & processParams, + LPCTSTR strFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency, bool& bOutOfMemory, bool& bExceptionError) { if (strFileName == NULL) { bOutOfMemory = false; @@ -49,11 +49,11 @@ CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirectio if (pRequest == NULL) { // no request pending for this file, add to request queue and start async - pRequest = StartNewRequest(strFileName, nFrameIndex, processParams); + pRequest = StartNewRequest(strFileName, nFrameIndex, processParams, colorTransparency); // wait with read ahead when direction changed - maybe user just wants to re-see last image if (!bDirectionChanged && eDirection != NONE) { // start parallel if more than one thread - StartNewRequestBundle(pFileList, eDirection, processParams, m_nNumThread - 1, NULL); + StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, m_nNumThread - 1, NULL); } } @@ -91,7 +91,7 @@ CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirectio bWasOutOfMemory = true; if (FreeAllPossibleMemory()) { DeleteElement(pRequest); - pRequest = StartRequestAndWaitUntilReady(strFileName, nFrameIndex, processParams); + pRequest = StartRequestAndWaitUntilReady(strFileName, nFrameIndex, processParams, colorTransparency); } } @@ -101,7 +101,7 @@ CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirectio // check if we shall start new requests (don't start another request if we are short of memory!) if (m_requestList.size() < (unsigned int)m_nNumBuffers && !bDirectionChanged && !bWasOutOfMemory && eDirection != NONE) { - StartNewRequestBundle(pFileList, eDirection, processParams, m_nNumThread, pRequest); + StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, m_nNumThread, pRequest); } bOutOfMemory = pRequest->OutOfMemory; @@ -205,15 +205,15 @@ CJPEGProvider::CImageRequest* CJPEGProvider::FindRequest(LPCTSTR strFileName, in return NULL; } -CJPEGProvider::CImageRequest* CJPEGProvider::StartRequestAndWaitUntilReady(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams) { - CImageRequest* pRequest = StartNewRequest(sFileName, nFrameIndex, processParams); +CJPEGProvider::CImageRequest* CJPEGProvider::StartRequestAndWaitUntilReady(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency) { + CImageRequest* pRequest = StartNewRequest(sFileName, nFrameIndex, processParams, colorTransparency); ::WaitForSingleObject(pRequest->EventFinished, INFINITE); GetLoadedImageFromWorkThread(pRequest); return pRequest; } void CJPEGProvider::StartNewRequestBundle(CFileList* pFileList, EReadAheadDirection eDirection, - const CProcessParams & processParams, int nNumRequests, CImageRequest* pLastReadyRequest) { + const CProcessParams & processParams, COLORREF colorTransparency, int nNumRequests, CImageRequest* pLastReadyRequest) { if (nNumRequests == 0 || pFileList == NULL) { return; } @@ -226,15 +226,15 @@ void CJPEGProvider::StartNewRequestBundle(CFileList* pFileList, EReadAheadDirect // The read ahead threads need this flag to be deleted - we can speculatively process the image with good hit rate CProcessParams paramsCopied = processParams; paramsCopied.ProcFlags = SetProcessingFlag(paramsCopied.ProcFlags, PFLAG_NoProcessingAfterLoad, false); - StartNewRequest(sFileName, nFrameIndex, paramsCopied); + StartNewRequest(sFileName, nFrameIndex, paramsCopied, colorTransparency); } else { - StartNewRequest(sFileName, nFrameIndex, processParams); + StartNewRequest(sFileName, nFrameIndex, processParams, colorTransparency); } } } } -CJPEGProvider::CImageRequest* CJPEGProvider::StartNewRequest(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams) { +CJPEGProvider::CImageRequest* CJPEGProvider::StartNewRequest(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency) { #ifdef DEBUG ::OutputDebugString(_T("Start new request: ")); ::OutputDebugString(sFileName); ::OutputDebugString(_T("\n")); #endif @@ -242,7 +242,7 @@ CJPEGProvider::CImageRequest* CJPEGProvider::StartNewRequest(LPCTSTR sFileName, m_requestList.push_back(pRequest); pRequest->HandlingThread = SearchThreadForNewRequest(); pRequest->Handle = pRequest->HandlingThread->AsyncLoad(pRequest->FileName, nFrameIndex, - processParams, m_hHandlerWnd, pRequest->EventFinished); + processParams, colorTransparency, m_hHandlerWnd, pRequest->EventFinished); return pRequest; } diff --git a/src/JPEGView/JPEGProvider.h b/src/JPEGView/JPEGProvider.h index 23f4d1cd..be1fe403 100644 --- a/src/JPEGView/JPEGProvider.h +++ b/src/JPEGView/JPEGProvider.h @@ -40,7 +40,7 @@ class CJPEGProvider // blocks until the image is ready. If not specified otherwise, a read-ahead request for the next image is // created automatically so that the next image will be ready immediately when requested in the future. CJPEGImage* RequestImage(CFileList* pFileList, EReadAheadDirection eDirection, LPCTSTR strFileName, int nFrameIndex, - const CProcessParams & processParams, bool& bOutOfMemory, bool& bExceptionError); + const CProcessParams & processParams, COLORREF colorTransparency, bool& bOutOfMemory, bool& bExceptionError); // Notifies that the specified image is no longer used and its memory can be freed. // The CJPEGProvider class may decide to keep the image cached. @@ -118,9 +118,9 @@ class CJPEGProvider void GetLoadedImageFromWorkThread(CImageRequest* pRequest); CImageLoadThread* SearchThreadForNewRequest(void); void RemoveUnusedImages(bool bRemoveAlsoReadAhead); - CImageRequest* StartRequestAndWaitUntilReady(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams); - CImageRequest* StartNewRequest(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams); - void StartNewRequestBundle(CFileList* pFileList, EReadAheadDirection eDirection, const CProcessParams & processParams, int nNumRequests, CImageRequest* pLastReadyRequest); + CImageRequest* StartRequestAndWaitUntilReady(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency); + CImageRequest* StartNewRequest(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency); + void StartNewRequestBundle(CFileList* pFileList, EReadAheadDirection eDirection, const CProcessParams & processParams, COLORREF colorTransparency, int nNumRequests, CImageRequest* pLastReadyRequest); CImageRequest* FindRequest(LPCTSTR strFileName, int nFrameIndex); void ClearOldestInactiveRequest(); void DeleteElementAt(std::list::iterator iteratorAt); // also deletes the request and the image in the request diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 0598cac1..3a4d194c 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -387,7 +387,7 @@ LRESULT CMainDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam // create JPEG provider and request first image - do no processing yet if not in fullscreen mode (as we do not know the size yet) m_pJPEGProvider = new CJPEGProvider(m_hWnd, NUM_THREADS, READ_AHEAD_BUFFERS); m_pCurrentImage = m_pJPEGProvider->RequestImage(m_pFileList, CJPEGProvider::FORWARD, - m_pFileList->Current(), 0, CreateProcessParams(!m_bFullScreenMode), m_bOutOfMemoryLastImage, m_bExceptionErrorLastImage); + m_pFileList->Current(), 0, CreateProcessParams(!m_bFullScreenMode), CSettingsProvider::This().ColorTransparency(m_bFullScreenMode), m_bOutOfMemoryLastImage, m_bExceptionErrorLastImage); if (m_pCurrentImage != NULL && m_pCurrentImage->IsAnimation()) { StartAnimation(); } @@ -2248,6 +2248,7 @@ void CMainDlg::OpenFile(LPCTSTR sFileName, bool bAfterStartup) { m_pJPEGProvider->ClearAllRequests(); m_pCurrentImage = m_pJPEGProvider->RequestImage(m_pFileList, CJPEGProvider::FORWARD, m_pFileList->Current(), 0, CreateProcessParams(!m_bFullScreenMode && (bAfterStartup || IsAdjustWindowToImage())), + CSettingsProvider::This().ColorTransparency(m_bFullScreenMode), m_bOutOfMemoryLastImage, m_bExceptionErrorLastImage); m_nLastLoadError = GetLoadErrorAfterOpenFile(); if (bAfterStartup) CheckIfApplyAutoFitWndToImage(false); @@ -2624,6 +2625,7 @@ void CMainDlg::GotoImage(EImagePosition ePos, int nFlags) { } else { m_pCurrentImage = m_pJPEGProvider->RequestImage(m_pFileList, (ePos == POS_AwayFromCurrent) ? CJPEGProvider::NONE : eDirection, m_pFileList->Current(), nFrameIndex, procParams, + CSettingsProvider::This().ColorTransparency(m_bFullScreenMode), m_bOutOfMemoryLastImage, m_bExceptionErrorLastImage); m_nLastLoadError = (m_pCurrentImage == NULL) ? ((m_pFileList->Current() == NULL) ? HelpersGUI::FileLoad_NoFilesInDirectory : HelpersGUI::FileLoad_LoadError) : HelpersGUI::FileLoad_Ok; } diff --git a/src/JPEGView/SettingsProvider.cpp b/src/JPEGView/SettingsProvider.cpp index 3228e872..cd599a18 100644 --- a/src/JPEGView/SettingsProvider.cpp +++ b/src/JPEGView/SettingsProvider.cpp @@ -229,6 +229,7 @@ CSettingsProvider::CSettingsProvider(void) { m_colorSlider = GetColor(_T("SliderColor"), RGB(255, 0, 80)); m_colorFileName = GetColor(_T("FileNameColor"), m_colorGUI); m_colorTransparency = GetColor(_T("TransparencyColor"), m_colorBackground); + m_colorTransparencyFullscreen = GetColor(_T("TransparencyColorFullscreen"), m_colorBackgroundFullscreen); m_defaultGUIFont = GetString(_T("DefaultGUIFont"), _T("Default")); m_fileNameFont = GetString(_T("FileNameFont"), _T("Default")); diff --git a/src/JPEGView/SettingsProvider.h b/src/JPEGView/SettingsProvider.h index 05e6bc54..29f3f21f 100644 --- a/src/JPEGView/SettingsProvider.h +++ b/src/JPEGView/SettingsProvider.h @@ -101,13 +101,13 @@ class CSettingsProvider bool DefaultMaximized() { return m_bDefaultMaximized; } bool ExplicitWindowRect() { return m_bExplicitWindowRect; } CSize DefaultFixedCropSize() { return m_DefaultFixedCropSize; } - COLORREF ColorBackground(int isFullscreen = false) { if (isFullscreen) return m_colorBackgroundFullscreen; else return m_colorBackground; } + COLORREF ColorBackground(int bIsFullscreen = false) { if (bIsFullscreen) return m_colorBackgroundFullscreen; else return m_colorBackground; } COLORREF ColorGUI() { return m_colorGUI; } COLORREF ColorHighlight() { return m_colorHighlight; } COLORREF ColorSelected() { return m_colorSelected; } COLORREF ColorSlider() { return m_colorSlider; } COLORREF ColorFileName() { return m_colorFileName; } - COLORREF ColorTransparency() { return m_colorTransparency; } + COLORREF ColorTransparency(int bIsFullscreen = false) { if (bIsFullscreen) return m_colorTransparencyFullscreen; else return m_colorTransparency; } LPCTSTR DefaultGUIFont() { return m_defaultGUIFont; } LPCTSTR FileNameFont() { return m_fileNameFont; } const CUnsharpMaskParams& UnsharpMaskParams() { return m_unsharpMaskParms; } @@ -279,6 +279,7 @@ class CSettingsProvider COLORREF m_colorSlider; COLORREF m_colorFileName; COLORREF m_colorTransparency; + COLORREF m_colorTransparencyFullscreen; CString m_defaultGUIFont; CString m_fileNameFont; CUnsharpMaskParams m_unsharpMaskParms; From 8ace4ee0a4acae39c67eb829f742e1ecc55924f5 Mon Sep 17 00:00:00 2001 From: Florian M Date: Sat, 5 Apr 2025 11:49:36 +0200 Subject: [PATCH 13/20] reload on toggleFullscreen if bg color changes; WIP more generous read-ahead --- src/JPEGView/JPEGImage.cpp | 2 ++ src/JPEGView/JPEGImage.h | 4 ++++ src/JPEGView/JPEGProvider.cpp | 36 ++++++++++++++++++++++++++++------- src/JPEGView/JPEGProvider.h | 3 ++- src/JPEGView/MainDlg.cpp | 23 ++++++++++++---------- 5 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/JPEGView/JPEGImage.cpp b/src/JPEGView/JPEGImage.cpp index c2e8b340..da94e86c 100644 --- a/src/JPEGView/JPEGImage.cpp +++ b/src/JPEGView/JPEGImage.cpp @@ -68,6 +68,8 @@ CJPEGImage::CJPEGImage(int nWidth, int nHeight, void* pPixels, void* pEXIFData, : m_rotationParams{ 0 }, m_fColorCorrectionFactorsNull{ 0 } { + m_nSourceOriginalChannels = nChannels; + if (nChannels == 3 || nChannels == 4) { m_pOrigPixels = pPixels; m_nOriginalChannels = nChannels; diff --git a/src/JPEGView/JPEGImage.h b/src/JPEGView/JPEGImage.h index 2b5d5628..a352ea1e 100644 --- a/src/JPEGView/JPEGImage.h +++ b/src/JPEGView/JPEGImage.h @@ -208,6 +208,9 @@ class CJPEGImage { // returns the number of channels in the OriginalPixels (3 or 4, corresponding to 24 bpp and 32 bpp) int OriginalChannels() const { return m_nOriginalChannels; } + // returns the number of channels in the OriginalPixels (3 or 4, corresponding to 24 bpp and 32 bpp) + int SourceOriginalChannels() const { return m_nSourceOriginalChannels; } + // raw access to DIB pixels with no LUT applied - do not delete or store the returned pointer // note that this DIB can be NULL due to optimization if currently only the processed DIB is maintained void* DIBPixels() { return m_pDIBPixels; } @@ -359,6 +362,7 @@ class CJPEGImage { int m_nOrigWidth, m_nOrigHeight; // these may changes by rotation int m_nInitOrigWidth, m_nInitOrigHeight; // original width of image when constructed (before any rotation and crop) int m_nOriginalChannels; + int m_nSourceOriginalChannels; __int64 m_nPixelHash; EImageFormat m_eImageFormat; TJSAMP m_eJPEGChromoSampling; diff --git a/src/JPEGView/JPEGProvider.cpp b/src/JPEGView/JPEGProvider.cpp index 9c3bc705..13ee2725 100644 --- a/src/JPEGView/JPEGProvider.cpp +++ b/src/JPEGView/JPEGProvider.cpp @@ -217,6 +217,11 @@ void CJPEGProvider::StartNewRequestBundle(CFileList* pFileList, EReadAheadDirect if (nNumRequests == 0 || pFileList == NULL) { return; } + CString out; + out.Format(_T("StartNewRequestBundle for %d requests\n"), nNumRequests); + ::OutputDebugString(out); + + // in specified direction for (int i = 0; i < nNumRequests; i++) { bool bSwitchImage = true; int nFrameIndex = (pLastReadyRequest != NULL) ? Helpers::GetFrameIndex(pLastReadyRequest->Image, eDirection == FORWARD, true, bSwitchImage) : 0; @@ -232,11 +237,32 @@ void CJPEGProvider::StartNewRequestBundle(CFileList* pFileList, EReadAheadDirect } } } + + // in opposite direction + int nNumRequestsOppositeDirection = 2; + for (int i = 0; i < nNumRequestsOppositeDirection; i++) { + bool bSwitchImage = true; + int nFrameIndex = 0; + LPCTSTR sFileName = pFileList->PeekNextPrev(i + 1, eDirection == BACKWARD, eDirection == TOGGLE); + if (sFileName != NULL && FindRequest(sFileName, nFrameIndex) == NULL) { + if (GetProcessingFlag(PFLAG_NoProcessingAfterLoad, processParams.ProcFlags)) { + // The read ahead threads need this flag to be deleted - we can speculatively process the image with good hit rate + CProcessParams paramsCopied = processParams; + paramsCopied.ProcFlags = SetProcessingFlag(paramsCopied.ProcFlags, PFLAG_NoProcessingAfterLoad, false); + StartNewRequest(sFileName, nFrameIndex, paramsCopied, colorTransparency); + } + else { + StartNewRequest(sFileName, nFrameIndex, processParams, colorTransparency); + } + } + } } CJPEGProvider::CImageRequest* CJPEGProvider::StartNewRequest(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency) { #ifdef DEBUG - ::OutputDebugString(_T("Start new request: ")); ::OutputDebugString(sFileName); ::OutputDebugString(_T("\n")); + CString out; + out.Format(_T("StartNewRequest for %s Frame %d\n"), sFileName, nFrameIndex); + ::OutputDebugString(out); #endif CImageRequest* pRequest = new CImageRequest(sFileName, nFrameIndex); m_requestList.push_back(pRequest); @@ -285,11 +311,7 @@ CImageLoadThread* CJPEGProvider::SearchThreadForNewRequest(void) { return (pBestOccupiedThread == NULL) ? m_pWorkThreads[0] : pBestOccupiedThread; } -void CJPEGProvider::RemoveUnusedImages(bool bRemoveAlsoActiveRequests) { - /*if (m_requestList.size() < 50) { - // We have plenty memory. Lets keep more images in cache! - return; - }*/ +void CJPEGProvider::RemoveUnusedImages(bool bRemoveAlsoActiveRequests, bool bRemoveAll) { bool bRemoved = false; int nTimeStampToRemove = -2; do { @@ -304,7 +326,7 @@ void CJPEGProvider::RemoveUnusedImages(bool bRemoveAlsoActiveRequests) { } // remove the readahead images - if we get here with read ahead, the strategy was wrong and // the read ahead image is not used. - if ((*iter)->AccessTimeStamp == nTimeStampToRemove || IsDestructivelyProcessed((*iter)->Image)) { + if ((*iter)->AccessTimeStamp == nTimeStampToRemove || IsDestructivelyProcessed((*iter)->Image) || bRemoveAll) { #ifdef DEBUG ::OutputDebugString(_T("Delete request: ")); ::OutputDebugString((*iter)->FileName); ::OutputDebugString(_T("\n")); #endif diff --git a/src/JPEGView/JPEGProvider.h b/src/JPEGView/JPEGProvider.h index be1fe403..920a6dc9 100644 --- a/src/JPEGView/JPEGProvider.h +++ b/src/JPEGView/JPEGProvider.h @@ -67,6 +67,8 @@ class CJPEGProvider // message was received. void OnImageLoadCompleted(int nHandle); + void RemoveUnusedImages(bool bRemoveAlsoReadAhead, bool bRemoveAll = false); + private: // stores a request for loading and processing a JPEG image struct CImageRequest { @@ -117,7 +119,6 @@ class CJPEGProvider bool WaitForAsyncRequest(int nHandle, int nMessage); void GetLoadedImageFromWorkThread(CImageRequest* pRequest); CImageLoadThread* SearchThreadForNewRequest(void); - void RemoveUnusedImages(bool bRemoveAlsoReadAhead); CImageRequest* StartRequestAndWaitUntilReady(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency); CImageRequest* StartNewRequest(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency); void StartNewRequestBundle(CFileList* pFileList, EReadAheadDirection eDirection, const CProcessParams & processParams, COLORREF colorTransparency, int nNumRequests, CImageRequest* pLastReadyRequest); diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 3a4d194c..5f11573d 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -66,8 +66,8 @@ static const double GAMMA_FACTOR = 1.02; // multiplicator for gamma value static const double CONTRAST_INC = 0.03; // increment for contrast value static const double SHARPEN_INC = 0.05; // increment for sharpen value static const double LDC_INC = 0.1; // increment for LDC (lighten shadows and darken highlights) -static const int NUM_THREADS = 1; // number of readahead threads to use -static const int READ_AHEAD_BUFFERS = 2; // number of readahead buffers to use (NUM_THREADS+1 is a good choice) +static const int NUM_THREADS = 4; // number of readahead threads to use +static const int READ_AHEAD_BUFFERS = 5; // number of readahead buffers to use (NUM_THREADS+1 is a good choice) static const int ZOOM_TIMEOUT = 200; // refinement done after this many milliseconds static const int ZOOM_TEXT_TIMEOUT = 1000; // zoom label disappears after this many milliseconds @@ -434,7 +434,7 @@ LRESULT CMainDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam CString durationStr; long double durationLongDouble = std::chrono::duration_cast(afterInitDialog - before).count(); durationStr.Format(_T("initDialog took %g ms\n"), durationLongDouble); - ::OutputDebugString(durationStr); + // ::OutputDebugString(durationStr); return TRUE; } @@ -589,12 +589,6 @@ LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, B SetCursorForMoveSection(); - auto afterPaint = std::chrono::high_resolution_clock::now(); - CString durationStr; - long double durationLongDouble = std::chrono::duration_cast(afterPaint- before).count(); - durationStr.Format(_T("Paint took %g ms\n"), durationLongDouble); - ::OutputDebugString(durationStr); - return 0; } @@ -2148,6 +2142,15 @@ void CMainDlg::ExecuteCommand(int nCommand) { void CMainDlg::ToggleFullScreen(CSettingsProvider& sp) { m_bFullScreenMode = !m_bFullScreenMode; m_dZoomAtResizeStart = 1.0; + if (sp.ColorTransparency(true) != sp.ColorTransparency(false)) { + if (m_pCurrentImage->SourceOriginalChannels() == 4) { + // Image has alpha channel. Reload it to blend it with changed ColorTransparency. + ReloadImage(false); + } + // Clear cache because the other images may have alpha so they need to be re-blended too. + m_pJPEGProvider->RemoveUnusedImages(true, true); + } + if (!m_bFullScreenMode) { CRect windowRect; @@ -2671,7 +2674,7 @@ void CMainDlg::GotoImage(EImagePosition ePos, int nFlags) { CString durationStr; long double durationLongDouble = std::chrono::duration_cast(afterGotoImage - before).count(); durationStr.Format(_T("GotoImage took %g ms\n"), durationLongDouble); - ::OutputDebugString(durationStr); + // ::OutputDebugString(durationStr); } void CMainDlg::ReloadImage(bool keepParameters, bool updateWindow) { From cc7a3853bc552591a598d1633e44de95f2dc545a Mon Sep 17 00:00:00 2001 From: Florian M Date: Sat, 5 Apr 2025 15:29:16 +0200 Subject: [PATCH 14/20] iterate on more preloading --- src/JPEGView/JPEGProvider.cpp | 32 ++++++++++++++++++++++++++------ src/JPEGView/MainDlg.cpp | 25 +++++++++++++++++-------- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/JPEGView/JPEGProvider.cpp b/src/JPEGView/JPEGProvider.cpp index 13ee2725..3a79612c 100644 --- a/src/JPEGView/JPEGProvider.cpp +++ b/src/JPEGView/JPEGProvider.cpp @@ -7,6 +7,8 @@ #include "ProcessParams.h" #include "BasicProcessing.h" +#include + CJPEGProvider::CJPEGProvider(HWND handlerWnd, int nNumThreads, int nNumBuffers) { m_hHandlerWnd = handlerWnd; m_nNumThread = nNumThreads; @@ -34,6 +36,9 @@ CJPEGProvider::~CJPEGProvider(void) { CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirection eDirection, LPCTSTR strFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency, bool& bOutOfMemory, bool& bExceptionError) { + + auto before = std::chrono::high_resolution_clock::now(); + if (strFileName == NULL) { bOutOfMemory = false; bExceptionError = false; @@ -48,12 +53,13 @@ CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirectio m_eOldDirection = eDirection; if (pRequest == NULL) { + ::OutputDebugString(_T("Cache miss for ")); ::OutputDebugString(strFileName); ::OutputDebugString(_T("\n")); // no request pending for this file, add to request queue and start async pRequest = StartNewRequest(strFileName, nFrameIndex, processParams, colorTransparency); // wait with read ahead when direction changed - maybe user just wants to re-see last image if (!bDirectionChanged && eDirection != NONE) { // start parallel if more than one thread - StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, m_nNumThread - 1, NULL); + StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, m_nNumThread - 2, NULL); } } @@ -101,11 +107,19 @@ CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirectio // check if we shall start new requests (don't start another request if we are short of memory!) if (m_requestList.size() < (unsigned int)m_nNumBuffers && !bDirectionChanged && !bWasOutOfMemory && eDirection != NONE) { - StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, m_nNumThread, pRequest); + StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, m_nNumThread - 1, pRequest); } bOutOfMemory = pRequest->OutOfMemory; bExceptionError = pRequest->ExceptionError; + + + auto afterRequestImage = std::chrono::high_resolution_clock::now(); + CString durationStr; + long double durationLongDouble = std::chrono::duration_cast(afterRequestImage - before).count(); + durationStr.Format(_T("RequestImage took %g ms\n"), durationLongDouble); + ::OutputDebugString(durationStr); + return pRequest->Image; } @@ -217,12 +231,19 @@ void CJPEGProvider::StartNewRequestBundle(CFileList* pFileList, EReadAheadDirect if (nNumRequests == 0 || pFileList == NULL) { return; } + int nNumRequestsBackward = 0; + int nNumRequestsForward = nNumRequests; + if (nNumRequestsForward > 2) { + nNumRequestsBackward = 1; + nNumRequestsForward -= nNumRequestsBackward; + } + CString out; - out.Format(_T("StartNewRequestBundle for %d requests\n"), nNumRequests); + out.Format(_T("StartNewRequestBundle for %d requests (%d inDir, %d oppositeDir)\n"), nNumRequests, nNumRequestsForward, nNumRequestsBackward); ::OutputDebugString(out); // in specified direction - for (int i = 0; i < nNumRequests; i++) { + for (int i = 0; i < nNumRequestsForward; i++) { bool bSwitchImage = true; int nFrameIndex = (pLastReadyRequest != NULL) ? Helpers::GetFrameIndex(pLastReadyRequest->Image, eDirection == FORWARD, true, bSwitchImage) : 0; LPCTSTR sFileName = bSwitchImage ? pFileList->PeekNextPrev(i + 1, eDirection == FORWARD, eDirection == TOGGLE) : pFileList->Current(); @@ -239,8 +260,7 @@ void CJPEGProvider::StartNewRequestBundle(CFileList* pFileList, EReadAheadDirect } // in opposite direction - int nNumRequestsOppositeDirection = 2; - for (int i = 0; i < nNumRequestsOppositeDirection; i++) { + for (int i = 0; i < nNumRequestsBackward; i++) { bool bSwitchImage = true; int nFrameIndex = 0; LPCTSTR sFileName = pFileList->PeekNextPrev(i + 1, eDirection == BACKWARD, eDirection == TOGGLE); diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 5f11573d..c353e832 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -66,8 +66,8 @@ static const double GAMMA_FACTOR = 1.02; // multiplicator for gamma value static const double CONTRAST_INC = 0.03; // increment for contrast value static const double SHARPEN_INC = 0.05; // increment for sharpen value static const double LDC_INC = 0.1; // increment for LDC (lighten shadows and darken highlights) -static const int NUM_THREADS = 4; // number of readahead threads to use -static const int READ_AHEAD_BUFFERS = 5; // number of readahead buffers to use (NUM_THREADS+1 is a good choice) +static const int NUM_THREADS = 5; // number of readahead threads to use +static const int READ_AHEAD_BUFFERS = 6; // number of readahead buffers to use (NUM_THREADS+1 is a good choice) static const int ZOOM_TIMEOUT = 200; // refinement done after this many milliseconds static const int ZOOM_TEXT_TIMEOUT = 1000; // zoom label disappears after this many milliseconds @@ -2658,10 +2658,19 @@ void CMainDlg::GotoImage(EImagePosition ePos, int nFlags) { AdjustWindowToImage(false); } - if (((nFlags & NO_UPDATE_WINDOW) == 0) && !(ePos == POS_NextSlideShow && UseSlideShowTransitionEffect())) { + if (((nFlags & NO_UPDATE_WINDOW) == 0) && !(ePos == POS_NextSlideShow && UseSlideShowTransitionEffect())) { this->Invalidate(FALSE); + + auto beforeUpdateWindow = std::chrono::high_resolution_clock::now(); + // this will force to wait until really redrawn, preventing to process images but do not show them this->UpdateWindow(); + + auto afterUpdateWindow = std::chrono::high_resolution_clock::now(); + CString durationStr2; + long double durationLongDouble2 = std::chrono::duration_cast(afterUpdateWindow - before).count(); + durationStr2.Format(_T("UpdateWindow took %g ms\n"), durationLongDouble2); + ::OutputDebugString(durationStr2); } // remove key messages accumulated so far @@ -2670,11 +2679,11 @@ void CMainDlg::GotoImage(EImagePosition ePos, int nFlags) { while (::PeekMessage(&msg, this->m_hWnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); } - auto afterGotoImage= std::chrono::high_resolution_clock::now(); - CString durationStr; - long double durationLongDouble = std::chrono::duration_cast(afterGotoImage - before).count(); - durationStr.Format(_T("GotoImage took %g ms\n"), durationLongDouble); - // ::OutputDebugString(durationStr); + auto afterGotoImage = std::chrono::high_resolution_clock::now(); + CString durationStr2; + long double durationLongDouble2 = std::chrono::duration_cast(afterGotoImage - before).count(); + durationStr2.Format(_T("GotoImage total took %g ms\n"), durationLongDouble2); + ::OutputDebugString(durationStr2); } void CMainDlg::ReloadImage(bool keepParameters, bool updateWindow) { From 74366ba0eb4ae9ca5adaff25b8ec33ed44635fc8 Mon Sep 17 00:00:00 2001 From: Florian M Date: Sat, 5 Apr 2025 16:45:59 +0200 Subject: [PATCH 15/20] maximize before loading images, making preload DIB match target size --- src/JPEGView/ImageLoadThread.cpp | 2 ++ src/JPEGView/JPEGImage.cpp | 4 ++++ src/JPEGView/JPEGProvider.cpp | 2 -- src/JPEGView/MainDlg.cpp | 18 ++++++++++++++++-- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/JPEGView/ImageLoadThread.cpp b/src/JPEGView/ImageLoadThread.cpp index 95fc968c..3d27c6cb 100644 --- a/src/JPEGView/ImageLoadThread.cpp +++ b/src/JPEGView/ImageLoadThread.cpp @@ -1105,6 +1105,8 @@ bool CImageLoadThread::ProcessImageAfterLoad(CRequest * request) { return true; } + ::OutputDebugString(_T("ProcessImageAfterLoad ")); ::OutputDebugString(request->FileName); ::OutputDebugString(_T("\n")); + int nWidth = request->Image->OrigWidth(); int nHeight = request->Image->OrigHeight(); diff --git a/src/JPEGView/JPEGImage.cpp b/src/JPEGView/JPEGImage.cpp index da94e86c..6ed695df 100644 --- a/src/JPEGView/JPEGImage.cpp +++ b/src/JPEGView/JPEGImage.cpp @@ -991,6 +991,7 @@ void* CJPEGImage::GetDIBInternal(CSize fullTargetSize, CSize clippingSize, CPoin void * pDIB = NULL; void * pDIBUnsharpMasked = NULL; if (!bMustResampleQuality && !bMustResampleGeometry && !bMustResampleProcessings) { + ::OutputDebugString(_T("GetDIB: No resizing needed.\n")); // no resizing needed (maybe even nothing must be done) bool bNoChangesLDCandLUTs = ApplyCorrectionLUTandLDC(imageProcParams, eProcFlags, m_pDIBPixelsLUTProcessed, fullTargetSize, targetOffset, m_pDIBPixels, clippingSize, bMustResampleGeometry, true, false) != NULL; @@ -999,6 +1000,9 @@ void* CJPEGImage::GetDIBInternal(CSize fullTargetSize, CSize clippingSize, CPoin fullTargetSize, targetOffset, (pDIBUnsharpMasked != NULL) ? pDIBUnsharpMasked : m_pDIBPixels, clippingSize, bMustResampleGeometry, false, pDIBUnsharpMasked != NULL, bParametersChanged); } + else { + ::OutputDebugString(_T("GetDIB: MUST RESIZE.\n")); + } // ApplyCorrectionLUTandLDC() could have failed, then recreate the DIBs if (pDIB == NULL) { // if the image is reprocessed more than once, it is worth to convert the original to 4 channels diff --git a/src/JPEGView/JPEGProvider.cpp b/src/JPEGView/JPEGProvider.cpp index 3a79612c..4e438be4 100644 --- a/src/JPEGView/JPEGProvider.cpp +++ b/src/JPEGView/JPEGProvider.cpp @@ -347,9 +347,7 @@ void CJPEGProvider::RemoveUnusedImages(bool bRemoveAlsoActiveRequests, bool bRem // remove the readahead images - if we get here with read ahead, the strategy was wrong and // the read ahead image is not used. if ((*iter)->AccessTimeStamp == nTimeStampToRemove || IsDestructivelyProcessed((*iter)->Image) || bRemoveAll) { -#ifdef DEBUG ::OutputDebugString(_T("Delete request: ")); ::OutputDebugString((*iter)->FileName); ::OutputDebugString(_T("\n")); -#endif DeleteElementAt(iter); bRemoved = true; break; diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index c353e832..47787bdf 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -384,6 +384,12 @@ LRESULT CMainDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam // create thread pool for processing requests on multiple CPU cores CProcessingThreadPool::This().CreateThreadPoolThreads(); + + if (sp.DefaultMaximized() && !m_bFullScreenMode) { + // maximize *before* loading images, so that the post-loading getDIB already gets the right dimensions. + this->ShowWindow(SW_MAXIMIZE); + } + // create JPEG provider and request first image - do no processing yet if not in fullscreen mode (as we do not know the size yet) m_pJPEGProvider = new CJPEGProvider(m_hWnd, NUM_THREADS, READ_AHEAD_BUFFERS); m_pCurrentImage = m_pJPEGProvider->RequestImage(m_pFileList, CJPEGProvider::FORWARD, @@ -441,7 +447,6 @@ LRESULT CMainDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { - auto before = std::chrono::high_resolution_clock::now(); static bool s_bFirst = true; if (m_bLockPaint) { @@ -508,6 +513,9 @@ LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, B CSize clippedSize(min(m_clientRect.Width(), newSize.cx), min(m_clientRect.Height(), newSize.cy)); CPoint offsetsInImage = m_pCurrentImage->ConvertOffset(newSize, clippedSize, m_offsets); + + auto beforeDIB = std::chrono::high_resolution_clock::now(); + void* pDIBData; if (m_pUnsharpMaskPanelCtl->IsVisible()) { pDIBData = m_pUnsharpMaskPanelCtl->GetUSMDIBForPreview(clippedSize, offsetsInImage, @@ -524,6 +532,12 @@ LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, B CreateProcessingFlags(m_bHQResampling && !m_bTemporaryLowQ && !m_bZoomMode, m_bAutoContrast, m_bAutoContrastSection, m_bLDC, false, m_bLandscapeMode)); } + auto afterDIB = std::chrono::high_resolution_clock::now(); + CString durationStr; + long double durationLongDouble2 = std::chrono::duration_cast(afterDIB - beforeDIB).count(); + durationStr.Format(_T("OnPaint DIBData took %g ms\n"), durationLongDouble2); + ::OutputDebugString(durationStr); + // Zoom navigator - check if visible and create exclusion rectangle if (m_pZoomNavigatorCtl->IsVisible()) { visRectZoomNavigator = m_pZoomNavigatorCtl->GetVisibleRect(newSize, clippedSize, offsetsInImage); @@ -543,7 +557,7 @@ LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, B } if (m_bZoomMode) m_offsets = unlimitedOffsets; } - + // Restore the old clipping region by adding the excluded rectangles again memDCMgr.IncludeIntoClippingRegion(dc, excludedClippingRects); From 49b7636670d138cdccb5dcc95b6a380d5dcf17b4 Mon Sep 17 00:00:00 2001 From: Florian M Date: Sat, 5 Apr 2025 16:59:45 +0200 Subject: [PATCH 16/20] undo increase read ahead buffers for now, as it broke png preloading --- src/JPEGView/JPEGProvider.cpp | 8 ++++---- src/JPEGView/MainDlg.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/JPEGView/JPEGProvider.cpp b/src/JPEGView/JPEGProvider.cpp index 4e438be4..81304877 100644 --- a/src/JPEGView/JPEGProvider.cpp +++ b/src/JPEGView/JPEGProvider.cpp @@ -59,7 +59,7 @@ CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirectio // wait with read ahead when direction changed - maybe user just wants to re-see last image if (!bDirectionChanged && eDirection != NONE) { // start parallel if more than one thread - StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, m_nNumThread - 2, NULL); + StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, m_nNumThread - 1, NULL); } } @@ -107,7 +107,7 @@ CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirectio // check if we shall start new requests (don't start another request if we are short of memory!) if (m_requestList.size() < (unsigned int)m_nNumBuffers && !bDirectionChanged && !bWasOutOfMemory && eDirection != NONE) { - StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, m_nNumThread - 1, pRequest); + StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, m_nNumThread, pRequest); } bOutOfMemory = pRequest->OutOfMemory; @@ -233,10 +233,10 @@ void CJPEGProvider::StartNewRequestBundle(CFileList* pFileList, EReadAheadDirect } int nNumRequestsBackward = 0; int nNumRequestsForward = nNumRequests; - if (nNumRequestsForward > 2) { + /*if (nNumRequestsForward > 2) { nNumRequestsBackward = 1; nNumRequestsForward -= nNumRequestsBackward; - } + }*/ CString out; out.Format(_T("StartNewRequestBundle for %d requests (%d inDir, %d oppositeDir)\n"), nNumRequests, nNumRequestsForward, nNumRequestsBackward); diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 47787bdf..2aa7f974 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -66,8 +66,8 @@ static const double GAMMA_FACTOR = 1.02; // multiplicator for gamma value static const double CONTRAST_INC = 0.03; // increment for contrast value static const double SHARPEN_INC = 0.05; // increment for sharpen value static const double LDC_INC = 0.1; // increment for LDC (lighten shadows and darken highlights) -static const int NUM_THREADS = 5; // number of readahead threads to use -static const int READ_AHEAD_BUFFERS = 6; // number of readahead buffers to use (NUM_THREADS+1 is a good choice) +static const int NUM_THREADS = 1; // number of readahead threads to use +static const int READ_AHEAD_BUFFERS = 2; // number of readahead buffers to use (NUM_THREADS+1 is a good choice) static const int ZOOM_TIMEOUT = 200; // refinement done after this many milliseconds static const int ZOOM_TEXT_TIMEOUT = 1000; // zoom label disappears after this many milliseconds From 71268358a64cb5eb1a107cfd2cdaafbf153893b0 Mon Sep 17 00:00:00 2001 From: Florian M Date: Sun, 6 Apr 2025 11:44:40 +0200 Subject: [PATCH 17/20] new strategy for readahead + cache cleanup --- src/JPEGView/ImageLoadThread.cpp | 2 - src/JPEGView/JPEGImage.cpp | 4 - src/JPEGView/JPEGProvider.cpp | 177 +++++++++++++++++-------------- src/JPEGView/JPEGProvider.h | 8 +- src/JPEGView/MainDlg.cpp | 23 +--- 5 files changed, 104 insertions(+), 110 deletions(-) diff --git a/src/JPEGView/ImageLoadThread.cpp b/src/JPEGView/ImageLoadThread.cpp index 3d27c6cb..95fc968c 100644 --- a/src/JPEGView/ImageLoadThread.cpp +++ b/src/JPEGView/ImageLoadThread.cpp @@ -1105,8 +1105,6 @@ bool CImageLoadThread::ProcessImageAfterLoad(CRequest * request) { return true; } - ::OutputDebugString(_T("ProcessImageAfterLoad ")); ::OutputDebugString(request->FileName); ::OutputDebugString(_T("\n")); - int nWidth = request->Image->OrigWidth(); int nHeight = request->Image->OrigHeight(); diff --git a/src/JPEGView/JPEGImage.cpp b/src/JPEGView/JPEGImage.cpp index 6ed695df..da94e86c 100644 --- a/src/JPEGView/JPEGImage.cpp +++ b/src/JPEGView/JPEGImage.cpp @@ -991,7 +991,6 @@ void* CJPEGImage::GetDIBInternal(CSize fullTargetSize, CSize clippingSize, CPoin void * pDIB = NULL; void * pDIBUnsharpMasked = NULL; if (!bMustResampleQuality && !bMustResampleGeometry && !bMustResampleProcessings) { - ::OutputDebugString(_T("GetDIB: No resizing needed.\n")); // no resizing needed (maybe even nothing must be done) bool bNoChangesLDCandLUTs = ApplyCorrectionLUTandLDC(imageProcParams, eProcFlags, m_pDIBPixelsLUTProcessed, fullTargetSize, targetOffset, m_pDIBPixels, clippingSize, bMustResampleGeometry, true, false) != NULL; @@ -1000,9 +999,6 @@ void* CJPEGImage::GetDIBInternal(CSize fullTargetSize, CSize clippingSize, CPoin fullTargetSize, targetOffset, (pDIBUnsharpMasked != NULL) ? pDIBUnsharpMasked : m_pDIBPixels, clippingSize, bMustResampleGeometry, false, pDIBUnsharpMasked != NULL, bParametersChanged); } - else { - ::OutputDebugString(_T("GetDIB: MUST RESIZE.\n")); - } // ApplyCorrectionLUTandLDC() could have failed, then recreate the DIBs if (pDIB == NULL) { // if the image is reprocessed more than once, it is worth to convert the original to 4 channels diff --git a/src/JPEGView/JPEGProvider.cpp b/src/JPEGView/JPEGProvider.cpp index 81304877..50476454 100644 --- a/src/JPEGView/JPEGProvider.cpp +++ b/src/JPEGView/JPEGProvider.cpp @@ -9,6 +9,10 @@ #include +int READ_AHEAD_RANGE_IN_DIRECTION = 2; +int READ_AHEAD_RANGE_OPPOSITE_DIRECTION = 1; +int READ_AHEAD_RANGE_KEEP_EXTRA = 4; + CJPEGProvider::CJPEGProvider(HWND handlerWnd, int nNumThreads, int nNumBuffers) { m_hHandlerWnd = handlerWnd; m_nNumThread = nNumThreads; @@ -37,8 +41,6 @@ CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirectio LPCTSTR strFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency, bool& bOutOfMemory, bool& bExceptionError) { - auto before = std::chrono::high_resolution_clock::now(); - if (strFileName == NULL) { bOutOfMemory = false; bExceptionError = false; @@ -53,14 +55,17 @@ CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirectio m_eOldDirection = eDirection; if (pRequest == NULL) { - ::OutputDebugString(_T("Cache miss for ")); ::OutputDebugString(strFileName); ::OutputDebugString(_T("\n")); + ::OutputDebugString(_T("RequestImage cache miss for ")); ::OutputDebugString(strFileName); ::OutputDebugString(_T("\n")); // no request pending for this file, add to request queue and start async pRequest = StartNewRequest(strFileName, nFrameIndex, processParams, colorTransparency); + + /* // wait with read ahead when direction changed - maybe user just wants to re-see last image if (!bDirectionChanged && eDirection != NONE) { // start parallel if more than one thread - StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, m_nNumThread - 1, NULL); + StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, 3, NULL); } + */ } // wait for request if not yet ready @@ -102,24 +107,17 @@ CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirectio } // cleanup stuff no longer used - RemoveUnusedImages(bRemoveAlsoActiveRequests); - ClearOldestInactiveRequest(); + RemoveUnusedImages(pFileList, eDirection, pRequest); + MarkOldestRequestsAsInactive(); // check if we shall start new requests (don't start another request if we are short of memory!) - if (m_requestList.size() < (unsigned int)m_nNumBuffers && !bDirectionChanged && !bWasOutOfMemory && eDirection != NONE) { - StartNewRequestBundle(pFileList, eDirection, processParams, colorTransparency, m_nNumThread, pRequest); + if (!bWasOutOfMemory) { + StartNewPreloadRequestBundle(pFileList, eDirection, processParams, colorTransparency, pRequest); } bOutOfMemory = pRequest->OutOfMemory; bExceptionError = pRequest->ExceptionError; - - auto afterRequestImage = std::chrono::high_resolution_clock::now(); - CString durationStr; - long double durationLongDouble = std::chrono::duration_cast(afterRequestImage - before).count(); - durationStr.Format(_T("RequestImage took %g ms\n"), durationLongDouble); - ::OutputDebugString(durationStr); - return pRequest->Image; } @@ -226,52 +224,45 @@ CJPEGProvider::CImageRequest* CJPEGProvider::StartRequestAndWaitUntilReady(LPCTS return pRequest; } -void CJPEGProvider::StartNewRequestBundle(CFileList* pFileList, EReadAheadDirection eDirection, - const CProcessParams & processParams, COLORREF colorTransparency, int nNumRequests, CImageRequest* pLastReadyRequest) { - if (nNumRequests == 0 || pFileList == NULL) { - return; - } - int nNumRequestsBackward = 0; - int nNumRequestsForward = nNumRequests; - /*if (nNumRequestsForward > 2) { - nNumRequestsBackward = 1; - nNumRequestsForward -= nNumRequestsBackward; - }*/ - - CString out; - out.Format(_T("StartNewRequestBundle for %d requests (%d inDir, %d oppositeDir)\n"), nNumRequests, nNumRequestsForward, nNumRequestsBackward); - ::OutputDebugString(out); - - // in specified direction - for (int i = 0; i < nNumRequestsForward; i++) { +std::list> CJPEGProvider::GetReadAheadFileList(CFileList* pFileList, EReadAheadDirection eDirection, CImageRequest* pLastReadyRequest, int extraPerDirection) { + // TODO priority + std::list> filesWithFrameIndex{}; + for (int i = 0; i < READ_AHEAD_RANGE_IN_DIRECTION + extraPerDirection; i++) { bool bSwitchImage = true; int nFrameIndex = (pLastReadyRequest != NULL) ? Helpers::GetFrameIndex(pLastReadyRequest->Image, eDirection == FORWARD, true, bSwitchImage) : 0; LPCTSTR sFileName = bSwitchImage ? pFileList->PeekNextPrev(i + 1, eDirection == FORWARD, eDirection == TOGGLE) : pFileList->Current(); - if (sFileName != NULL && FindRequest(sFileName, nFrameIndex) == NULL) { - if (GetProcessingFlag(PFLAG_NoProcessingAfterLoad, processParams.ProcFlags)) { - // The read ahead threads need this flag to be deleted - we can speculatively process the image with good hit rate - CProcessParams paramsCopied = processParams; - paramsCopied.ProcFlags = SetProcessingFlag(paramsCopied.ProcFlags, PFLAG_NoProcessingAfterLoad, false); - StartNewRequest(sFileName, nFrameIndex, paramsCopied, colorTransparency); - } else { - StartNewRequest(sFileName, nFrameIndex, processParams, colorTransparency); - } + if (sFileName != NULL) { + filesWithFrameIndex.push_back({ sFileName, nFrameIndex }); } } - - // in opposite direction - for (int i = 0; i < nNumRequestsBackward; i++) { + for (int i = 0; i < READ_AHEAD_RANGE_OPPOSITE_DIRECTION + extraPerDirection; i++) { bool bSwitchImage = true; - int nFrameIndex = 0; - LPCTSTR sFileName = pFileList->PeekNextPrev(i + 1, eDirection == BACKWARD, eDirection == TOGGLE); - if (sFileName != NULL && FindRequest(sFileName, nFrameIndex) == NULL) { + int nFrameIndex = (pLastReadyRequest != NULL) ? Helpers::GetFrameIndex(pLastReadyRequest->Image, eDirection == BACKWARD, true, bSwitchImage) : 0; + LPCTSTR sFileName = bSwitchImage ? pFileList->PeekNextPrev(i + 1, eDirection == BACKWARD, eDirection == TOGGLE) : pFileList->Current(); + if (sFileName != NULL) { + filesWithFrameIndex.push_back({ sFileName, nFrameIndex }); + } + } + return filesWithFrameIndex; +} + +void CJPEGProvider::StartNewPreloadRequestBundle(CFileList* pFileList, EReadAheadDirection eDirection, const CProcessParams & processParams, COLORREF colorTransparency, CImageRequest* pLastReadyRequest) { + if (pFileList == NULL) { + return; + } + + ::OutputDebugString(_T("StartNewRequestBundle\n")); + + std::list> filesWithFrameIndex = GetReadAheadFileList(pFileList, eDirection, pLastReadyRequest); + + for (auto [sFileName, nFrameIndex] : filesWithFrameIndex) { + if (FindRequest(sFileName, nFrameIndex) == NULL) { if (GetProcessingFlag(PFLAG_NoProcessingAfterLoad, processParams.ProcFlags)) { // The read ahead threads need this flag to be deleted - we can speculatively process the image with good hit rate CProcessParams paramsCopied = processParams; paramsCopied.ProcFlags = SetProcessingFlag(paramsCopied.ProcFlags, PFLAG_NoProcessingAfterLoad, false); StartNewRequest(sFileName, nFrameIndex, paramsCopied, colorTransparency); - } - else { + } else { StartNewRequest(sFileName, nFrameIndex, processParams, colorTransparency); } } @@ -279,12 +270,8 @@ void CJPEGProvider::StartNewRequestBundle(CFileList* pFileList, EReadAheadDirect } CJPEGProvider::CImageRequest* CJPEGProvider::StartNewRequest(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency) { -#ifdef DEBUG - CString out; - out.Format(_T("StartNewRequest for %s Frame %d\n"), sFileName, nFrameIndex); - ::OutputDebugString(out); -#endif CImageRequest* pRequest = new CImageRequest(sFileName, nFrameIndex); + ::OutputDebugString(_T("StartNewRequest: ")); ::OutputDebugString(sFileName); ::OutputDebugString(_T("\n")); m_requestList.push_back(pRequest); pRequest->HandlingThread = SearchThreadForNewRequest(); pRequest->Handle = pRequest->HandlingThread->AsyncLoad(pRequest->FileName, nFrameIndex, @@ -331,6 +318,60 @@ CImageLoadThread* CJPEGProvider::SearchThreadForNewRequest(void) { return (pBestOccupiedThread == NULL) ? m_pWorkThreads[0] : pBestOccupiedThread; } + +void CJPEGProvider::RemoveUnusedImages(CFileList* pFileList, EReadAheadDirection eDirection, void* pLastReadyRequestRaw) { + ::OutputDebugString(_T("RemoveUnused\n")); + CImageRequest* pLastReadyRequest = reinterpret_cast(pLastReadyRequestRaw); + int keepExtraPerDirection = 4; + std::list> readAheadRange = GetReadAheadFileList(pFileList, eDirection, reinterpret_cast(pLastReadyRequest), keepExtraPerDirection); + std::list::iterator iter; + auto compare = [](std::tuple a, std::tuple b) { return lstrcmp(std::get<0>(a), std::get<0>(b)) && std::get<1>(a) == std::get<1>(b); }; + bool bRemoved = false; + do { // Wrapped in a loop because we have to break iterating when deleting element, as it breaks the iterator. + bRemoved = false; + for (iter = m_requestList.begin(); iter != m_requestList.end(); iter++) { + bool isInReadAheadRange = false; + for (auto [fileName, frameIndex] : readAheadRange) { + if ((*iter)->FileName == fileName && (*iter)->FrameIndex == frameIndex) { + isInReadAheadRange = true; + break; + } + } + bool isCurrentImage = pLastReadyRequest != NULL && ((*iter)->FileName == pLastReadyRequest->FileName && (*iter)->FrameIndex == pLastReadyRequest->FrameIndex); + if (!isInReadAheadRange && !isCurrentImage && !(*iter)->InUse && !(*iter)->IsActive) { + ::OutputDebugString(_T("Deleting from cache: ")); ::OutputDebugString((*iter)->FileName); ::OutputDebugString(_T("\n")); + DeleteElementAt(iter); + bRemoved = true; + break; + } + } + } while (bRemoved); // repeat until no element was removed anymore +} + +void CJPEGProvider::MarkOldestRequestsAsInactive() { + if (m_requestList.size() >= (unsigned int)m_nNumBuffers) { + int nFirstHandle = INT_MAX; + CImageRequest* pFirstRequest = NULL; + std::list::iterator iter; + for (iter = m_requestList.begin(); iter != m_requestList.end(); iter++) { + if ((*iter)->IsActive) { + // mark very old requests for removal + if (CImageLoadThread::GetCurHandleValue() - (*iter)->Handle > m_nNumBuffers) { + (*iter)->IsActive = false; + } + if ((*iter)->Handle < nFirstHandle) { + nFirstHandle = (*iter)->Handle; + pFirstRequest = *iter; + } + } + } + if (pFirstRequest != NULL) { + pFirstRequest->IsActive = false; + MarkOldestRequestsAsInactive(); + } + } +} + void CJPEGProvider::RemoveUnusedImages(bool bRemoveAlsoActiveRequests, bool bRemoveAll) { bool bRemoved = false; int nTimeStampToRemove = -2; @@ -347,7 +388,7 @@ void CJPEGProvider::RemoveUnusedImages(bool bRemoveAlsoActiveRequests, bool bRem // remove the readahead images - if we get here with read ahead, the strategy was wrong and // the read ahead image is not used. if ((*iter)->AccessTimeStamp == nTimeStampToRemove || IsDestructivelyProcessed((*iter)->Image) || bRemoveAll) { - ::OutputDebugString(_T("Delete request: ")); ::OutputDebugString((*iter)->FileName); ::OutputDebugString(_T("\n")); + ::OutputDebugString(_T("Deleting from cache: ")); ::OutputDebugString((*iter)->FileName); ::OutputDebugString(_T("\n")); DeleteElementAt(iter); bRemoved = true; break; @@ -367,30 +408,6 @@ void CJPEGProvider::RemoveUnusedImages(bool bRemoveAlsoActiveRequests, bool bRem } while (bRemoved); // repeat until no element could be removed anymore } -void CJPEGProvider::ClearOldestInactiveRequest() { - if (m_requestList.size() >= (unsigned int)m_nNumBuffers) { - int nFirstHandle = INT_MAX; - CImageRequest* pFirstRequest = NULL; - std::list::iterator iter; - for (iter = m_requestList.begin( ); iter != m_requestList.end( ); iter++ ) { - if ((*iter)->IsActive) { - // mark very old requests for removal - if (CImageLoadThread::GetCurHandleValue() - (*iter)->Handle > m_nNumBuffers) { - (*iter)->IsActive = false; - } - if ((*iter)->Handle < nFirstHandle) { - nFirstHandle = (*iter)->Handle; - pFirstRequest = *iter; - } - } - } - if (pFirstRequest != NULL) { - pFirstRequest->IsActive = false; - ClearOldestInactiveRequest(); - } - } -} - void CJPEGProvider::DeleteElementAt(std::list::iterator iteratorAt) { delete (*iteratorAt)->Image; delete *iteratorAt; diff --git a/src/JPEGView/JPEGProvider.h b/src/JPEGView/JPEGProvider.h index 920a6dc9..fe9eeb56 100644 --- a/src/JPEGView/JPEGProvider.h +++ b/src/JPEGView/JPEGProvider.h @@ -1,5 +1,7 @@ #pragma once +#include + class CJPEGImage; class CImageLoadThread; class CFileList; @@ -68,6 +70,8 @@ class CJPEGProvider void OnImageLoadCompleted(int nHandle); void RemoveUnusedImages(bool bRemoveAlsoReadAhead, bool bRemoveAll = false); + void RemoveUnusedImages(CFileList* pFileList, EReadAheadDirection eDirection, void* pLastReadyRequest); + void MarkOldestRequestsAsInactive(); private: // stores a request for loading and processing a JPEG image @@ -121,9 +125,9 @@ class CJPEGProvider CImageLoadThread* SearchThreadForNewRequest(void); CImageRequest* StartRequestAndWaitUntilReady(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency); CImageRequest* StartNewRequest(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency); - void StartNewRequestBundle(CFileList* pFileList, EReadAheadDirection eDirection, const CProcessParams & processParams, COLORREF colorTransparency, int nNumRequests, CImageRequest* pLastReadyRequest); + void StartNewPreloadRequestBundle(CFileList* pFileList, EReadAheadDirection eDirection, const CProcessParams & processParams, COLORREF colorTransparency, CImageRequest* pLastReadyRequest); + std::list> CJPEGProvider::GetReadAheadFileList(CFileList* pFileList, EReadAheadDirection eDirection, CImageRequest* pLastReadyRequest, int extraPerDirection = 0); CImageRequest* FindRequest(LPCTSTR strFileName, int nFrameIndex); - void ClearOldestInactiveRequest(); void DeleteElementAt(std::list::iterator iteratorAt); // also deletes the request and the image in the request void DeleteElement(CImageRequest* pRequest); bool IsDestructivelyProcessed(CJPEGImage* pImage); diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 2aa7f974..0be2d973 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -67,7 +67,7 @@ static const double CONTRAST_INC = 0.03; // increment for contrast value static const double SHARPEN_INC = 0.05; // increment for sharpen value static const double LDC_INC = 0.1; // increment for LDC (lighten shadows and darken highlights) static const int NUM_THREADS = 1; // number of readahead threads to use -static const int READ_AHEAD_BUFFERS = 2; // number of readahead buffers to use (NUM_THREADS+1 is a good choice) +static const int READ_AHEAD_BUFFERS = 10; // number of readahead buffers to use static const int ZOOM_TIMEOUT = 200; // refinement done after this many milliseconds static const int ZOOM_TEXT_TIMEOUT = 1000; // zoom label disappears after this many milliseconds @@ -410,9 +410,6 @@ LRESULT CMainDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam } else { AdjustWindowToImage(true); } - if (sp.DefaultMaximized()) { - this->ShowWindow(SW_MAXIMIZE); - } } else { PrefetchDIB(m_monitorRect); SetWindowLong(GWL_STYLE, WS_VISIBLE); @@ -513,9 +510,6 @@ LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, B CSize clippedSize(min(m_clientRect.Width(), newSize.cx), min(m_clientRect.Height(), newSize.cy)); CPoint offsetsInImage = m_pCurrentImage->ConvertOffset(newSize, clippedSize, m_offsets); - - auto beforeDIB = std::chrono::high_resolution_clock::now(); - void* pDIBData; if (m_pUnsharpMaskPanelCtl->IsVisible()) { pDIBData = m_pUnsharpMaskPanelCtl->GetUSMDIBForPreview(clippedSize, offsetsInImage, @@ -532,12 +526,6 @@ LRESULT CMainDlg::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, B CreateProcessingFlags(m_bHQResampling && !m_bTemporaryLowQ && !m_bZoomMode, m_bAutoContrast, m_bAutoContrastSection, m_bLDC, false, m_bLandscapeMode)); } - auto afterDIB = std::chrono::high_resolution_clock::now(); - CString durationStr; - long double durationLongDouble2 = std::chrono::duration_cast(afterDIB - beforeDIB).count(); - durationStr.Format(_T("OnPaint DIBData took %g ms\n"), durationLongDouble2); - ::OutputDebugString(durationStr); - // Zoom navigator - check if visible and create exclusion rectangle if (m_pZoomNavigatorCtl->IsVisible()) { visRectZoomNavigator = m_pZoomNavigatorCtl->GetVisibleRect(newSize, clippedSize, offsetsInImage); @@ -2674,17 +2662,8 @@ void CMainDlg::GotoImage(EImagePosition ePos, int nFlags) { if (((nFlags & NO_UPDATE_WINDOW) == 0) && !(ePos == POS_NextSlideShow && UseSlideShowTransitionEffect())) { this->Invalidate(FALSE); - - auto beforeUpdateWindow = std::chrono::high_resolution_clock::now(); - // this will force to wait until really redrawn, preventing to process images but do not show them this->UpdateWindow(); - - auto afterUpdateWindow = std::chrono::high_resolution_clock::now(); - CString durationStr2; - long double durationLongDouble2 = std::chrono::duration_cast(afterUpdateWindow - before).count(); - durationStr2.Format(_T("UpdateWindow took %g ms\n"), durationLongDouble2); - ::OutputDebugString(durationStr2); } // remove key messages accumulated so far From 8267c2504afc8b1c871fdcc930682e89831c7f56 Mon Sep 17 00:00:00 2001 From: Florian M Date: Sun, 6 Apr 2025 13:06:13 +0200 Subject: [PATCH 18/20] re-preload after clear due to transparencyChanged; allow overwriting numbers in keymap --- src/JPEGView/Config/KeyMap-README.html | 2 +- src/JPEGView/JPEGProvider.cpp | 55 ++++++-------------------- src/JPEGView/JPEGProvider.h | 6 +-- src/JPEGView/MainDlg.cpp | 10 +++-- src/JPEGView/TimerEventIDs.h | 3 +- 5 files changed, 24 insertions(+), 52 deletions(-) diff --git a/src/JPEGView/Config/KeyMap-README.html b/src/JPEGView/Config/KeyMap-README.html index a0817c7c..00b9d113 100644 --- a/src/JPEGView/Config/KeyMap-README.html +++ b/src/JPEGView/Config/KeyMap-README.html @@ -501,7 +501,7 @@

  • F1 for help
  • Alt+F4 for exit
  • -
  • 0..9 for slide show
  • +
  • 0..9 with Shift/Ctrl for slide show

diff --git a/src/JPEGView/JPEGProvider.cpp b/src/JPEGView/JPEGProvider.cpp index 50476454..87c0450f 100644 --- a/src/JPEGView/JPEGProvider.cpp +++ b/src/JPEGView/JPEGProvider.cpp @@ -107,7 +107,7 @@ CJPEGImage* CJPEGProvider::RequestImage(CFileList* pFileList, EReadAheadDirectio } // cleanup stuff no longer used - RemoveUnusedImages(pFileList, eDirection, pRequest); + RemoveUnusedImages(pFileList, eDirection, false, pRequest); MarkOldestRequestsAsInactive(); // check if we shall start new requests (don't start another request if we are short of memory!) @@ -246,10 +246,11 @@ std::list> CJPEGProvider::GetReadAheadFileList(CFileLis return filesWithFrameIndex; } -void CJPEGProvider::StartNewPreloadRequestBundle(CFileList* pFileList, EReadAheadDirection eDirection, const CProcessParams & processParams, COLORREF colorTransparency, CImageRequest* pLastReadyRequest) { +void CJPEGProvider::StartNewPreloadRequestBundle(CFileList* pFileList, EReadAheadDirection eDirection, const CProcessParams & processParams, COLORREF colorTransparency, void* pLastReadyRequestRaw) { if (pFileList == NULL) { return; } + CImageRequest* pLastReadyRequest = reinterpret_cast(pLastReadyRequestRaw); ::OutputDebugString(_T("StartNewRequestBundle\n")); @@ -319,7 +320,7 @@ CImageLoadThread* CJPEGProvider::SearchThreadForNewRequest(void) { } -void CJPEGProvider::RemoveUnusedImages(CFileList* pFileList, EReadAheadDirection eDirection, void* pLastReadyRequestRaw) { +void CJPEGProvider::RemoveUnusedImages(CFileList* pFileList, EReadAheadDirection eDirection, bool removeAll, void* pLastReadyRequestRaw) { ::OutputDebugString(_T("RemoveUnused\n")); CImageRequest* pLastReadyRequest = reinterpret_cast(pLastReadyRequestRaw); int keepExtraPerDirection = 4; @@ -338,11 +339,13 @@ void CJPEGProvider::RemoveUnusedImages(CFileList* pFileList, EReadAheadDirection } } bool isCurrentImage = pLastReadyRequest != NULL && ((*iter)->FileName == pLastReadyRequest->FileName && (*iter)->FrameIndex == pLastReadyRequest->FrameIndex); - if (!isInReadAheadRange && !isCurrentImage && !(*iter)->InUse && !(*iter)->IsActive) { - ::OutputDebugString(_T("Deleting from cache: ")); ::OutputDebugString((*iter)->FileName); ::OutputDebugString(_T("\n")); - DeleteElementAt(iter); - bRemoved = true; - break; + if (!isCurrentImage && !(*iter)->InUse) { + if (removeAll || (!isInReadAheadRange && !isCurrentImage && !(*iter)->IsActive)) { + ::OutputDebugString(_T("Deleting from cache: ")); ::OutputDebugString((*iter)->FileName); ::OutputDebugString(_T("\n")); + DeleteElementAt(iter); + bRemoved = true; + break; + } } } } while (bRemoved); // repeat until no element was removed anymore @@ -372,42 +375,6 @@ void CJPEGProvider::MarkOldestRequestsAsInactive() { } } -void CJPEGProvider::RemoveUnusedImages(bool bRemoveAlsoActiveRequests, bool bRemoveAll) { - bool bRemoved = false; - int nTimeStampToRemove = -2; - do { - bRemoved = false; - int nSmallestTimeStamp = INT_MAX; - std::list::iterator iter; - for (iter = m_requestList.begin( ); iter != m_requestList.end( ); iter++ ) { - if ((*iter)->InUse == false && (*iter)->Ready && ((*iter)->IsActive == false || bRemoveAlsoActiveRequests || IsDestructivelyProcessed((*iter)->Image))) { - // search element with smallest timestamp - if ((*iter)->AccessTimeStamp < nSmallestTimeStamp) { - nSmallestTimeStamp = (*iter)->AccessTimeStamp; - } - // remove the readahead images - if we get here with read ahead, the strategy was wrong and - // the read ahead image is not used. - if ((*iter)->AccessTimeStamp == nTimeStampToRemove || IsDestructivelyProcessed((*iter)->Image) || bRemoveAll) { - ::OutputDebugString(_T("Deleting from cache: ")); ::OutputDebugString((*iter)->FileName); ::OutputDebugString(_T("\n")); - DeleteElementAt(iter); - bRemoved = true; - break; - } - } - } - nTimeStampToRemove = -2; - // Make one buffer free for next readahead (except when bRemoveAlsoActiveRequests) - int nMaxListSize = bRemoveAlsoActiveRequests ? (unsigned int)m_nNumBuffers : (unsigned int)m_nNumBuffers - 1; - if (m_requestList.size() > (unsigned int)nMaxListSize) { - // remove element with smallest timestamp - if (nSmallestTimeStamp < INT_MAX) { - bRemoved = true; - nTimeStampToRemove = nSmallestTimeStamp; - } - } - } while (bRemoved); // repeat until no element could be removed anymore -} - void CJPEGProvider::DeleteElementAt(std::list::iterator iteratorAt) { delete (*iteratorAt)->Image; delete *iteratorAt; diff --git a/src/JPEGView/JPEGProvider.h b/src/JPEGView/JPEGProvider.h index fe9eeb56..5207c8df 100644 --- a/src/JPEGView/JPEGProvider.h +++ b/src/JPEGView/JPEGProvider.h @@ -69,10 +69,11 @@ class CJPEGProvider // message was received. void OnImageLoadCompleted(int nHandle); - void RemoveUnusedImages(bool bRemoveAlsoReadAhead, bool bRemoveAll = false); - void RemoveUnusedImages(CFileList* pFileList, EReadAheadDirection eDirection, void* pLastReadyRequest); + void RemoveUnusedImages(CFileList* pFileList, EReadAheadDirection eDirection, bool removeAll, void* pLastReadyRequest); void MarkOldestRequestsAsInactive(); + void StartNewPreloadRequestBundle(CFileList* pFileList, EReadAheadDirection eDirection, const CProcessParams& processParams, COLORREF colorTransparency, void* pLastReadyRequest); + private: // stores a request for loading and processing a JPEG image struct CImageRequest { @@ -125,7 +126,6 @@ class CJPEGProvider CImageLoadThread* SearchThreadForNewRequest(void); CImageRequest* StartRequestAndWaitUntilReady(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency); CImageRequest* StartNewRequest(LPCTSTR sFileName, int nFrameIndex, const CProcessParams & processParams, COLORREF colorTransparency); - void StartNewPreloadRequestBundle(CFileList* pFileList, EReadAheadDirection eDirection, const CProcessParams & processParams, COLORREF colorTransparency, CImageRequest* pLastReadyRequest); std::list> CJPEGProvider::GetReadAheadFileList(CFileList* pFileList, EReadAheadDirection eDirection, CImageRequest* pLastReadyRequest, int extraPerDirection = 0); CImageRequest* FindRequest(LPCTSTR strFileName, int nFrameIndex); void DeleteElementAt(std::list::iterator iteratorAt); // also deletes the request and the image in the request diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index 0be2d973..b19d2416 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -985,7 +985,7 @@ LRESULT CMainDlg::OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOO bHandled = true; m_pFileList->SetNavigationMode(Helpers::NM_LoopSubDirectories); GotoImage(POS_Next); - } else if (wParam >= '1' && wParam <= '9' && (!bShift || bCtrl)) { + } else if (wParam >= '1' && wParam <= '9' && (bShift || bCtrl)) { // Start the slideshow bHandled = true; int nValue = (int)wParam - '1' + 1; @@ -2144,13 +2144,14 @@ void CMainDlg::ExecuteCommand(int nCommand) { void CMainDlg::ToggleFullScreen(CSettingsProvider& sp) { m_bFullScreenMode = !m_bFullScreenMode; m_dZoomAtResizeStart = 1.0; - if (sp.ColorTransparency(true) != sp.ColorTransparency(false)) { + bool colorTransparencyChanged = sp.ColorTransparency(true) != sp.ColorTransparency(false); + if (colorTransparencyChanged) { if (m_pCurrentImage->SourceOriginalChannels() == 4) { // Image has alpha channel. Reload it to blend it with changed ColorTransparency. ReloadImage(false); } // Clear cache because the other images may have alpha so they need to be re-blended too. - m_pJPEGProvider->RemoveUnusedImages(true, true); + m_pJPEGProvider->RemoveUnusedImages(m_pFileList, CJPEGProvider::FORWARD, true, nullptr); } if (!m_bFullScreenMode) { @@ -2200,6 +2201,9 @@ void CMainDlg::ToggleFullScreen(CSettingsProvider& sp) { m_dZoom = -1; StartLowQTimer(ZOOM_TIMEOUT); this->SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_FRAMECHANGED); + if (colorTransparencyChanged) { + m_pJPEGProvider->StartNewPreloadRequestBundle(m_pFileList, CJPEGProvider::FORWARD, CreateProcessParams(!m_bFullScreenMode), sp.ColorTransparency(m_bFullScreenMode), nullptr); + } } // Setting window styles have gotten out of hand with the addition of no title bar diff --git a/src/JPEGView/TimerEventIDs.h b/src/JPEGView/TimerEventIDs.h index 0b095b72..910042df 100644 --- a/src/JPEGView/TimerEventIDs.h +++ b/src/JPEGView/TimerEventIDs.h @@ -8,4 +8,5 @@ #define NAVPANEL_ANI_TIMER_EVENT_ID 6 // animation timer for navigation panel #define NAVPANEL_START_ANI_TIMER_EVENT_ID 7 // animation start timer for navigation panel #define IPPANEL_TIMER_EVENT_ID 8 // to show image processing panel in window mode -#define ANIMATION_TIMER_EVENT_ID 9 // GIF animation timer ID \ No newline at end of file +#define ANIMATION_TIMER_EVENT_ID 9 // GIF animation timer ID +#define SHOW_LOADING_TIMER_EVENT_ID 10 // Loading indicator timer ID \ No newline at end of file From 792338f79959e85e4145b07b6110636de3ca9720 Mon Sep 17 00:00:00 2001 From: Florian M Date: Sun, 6 Apr 2025 14:20:13 +0200 Subject: [PATCH 19/20] add visualStudio UpgradeLog --- src/UpgradeLog.htm | 275 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 src/UpgradeLog.htm diff --git a/src/UpgradeLog.htm b/src/UpgradeLog.htm new file mode 100644 index 00000000..a5bd6c4c --- /dev/null +++ b/src/UpgradeLog.htm @@ -0,0 +1,275 @@ + + + + Migration Report +

+ Migration Report -

\ No newline at end of file From ddcf437a80c77ed3ba91d1b87b3c9448052a892b Mon Sep 17 00:00:00 2001 From: Florian M Date: Tue, 8 Apr 2025 17:02:54 +0200 Subject: [PATCH 20/20] show zoom label in fit-to-screen zoom-in case --- src/JPEGView/MainDlg.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/JPEGView/MainDlg.cpp b/src/JPEGView/MainDlg.cpp index b19d2416..bfe84bb5 100644 --- a/src/JPEGView/MainDlg.cpp +++ b/src/JPEGView/MainDlg.cpp @@ -1788,6 +1788,9 @@ void CMainDlg::ExecuteCommand(int nCommand) { else { // The image is smaller than the screen. Always go to 100%, unless we’re already there. if (abs(m_dZoom - 1.0) < 0.01) { ResetZoomToFitScreen(nCommand == IDM_TOGGLE_FILL_WITH_CROP_100_PERCENTS, true, true); + // ResetToFit does not usually show timer. In this zoom-in case let’s show it. + m_bInZooming = true; + StartLowQTimer(ZOOM_TIMEOUT); } else { ResetZoomTo100Percents(m_bMouseOn);