From f471de98d33cc8f43a0d007cbfa0b0d78135a541 Mon Sep 17 00:00:00 2001 From: Sergei Vorobyov Date: Wed, 8 Jun 2022 02:26:45 +0300 Subject: [PATCH] Initial support of Lepton JPEGs --- src/JPEGView/FileExtensionsDlg.cpp | 10 +++-- src/JPEGView/FileExtensionsDlg.h | 2 +- src/JPEGView/FileList.cpp | 6 +++ src/JPEGView/Helpers.cpp | 17 ++++++++ src/JPEGView/Helpers.h | 6 +++ src/JPEGView/ImageLoadThread.cpp | 60 +++++++++++++++++++++++++-- src/JPEGView/ImageLoadThread.h | 3 ++ src/JPEGView/ImageProcessingTypes.h | 1 + src/JPEGView/JPEGView.vcxproj | 2 + src/JPEGView/JPEGView.vcxproj.filters | 6 +++ src/JPEGView/LepLoader.cpp | 13 ++++++ src/JPEGView/LepLoader.h | 11 +++++ src/JPEGView/SettingsProvider.cpp | 8 ++-- src/JPEGView/SettingsProvider.h | 6 +++ src/JPEGView/stdafx.h | 4 ++ 15 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 src/JPEGView/LepLoader.cpp create mode 100644 src/JPEGView/LepLoader.h diff --git a/src/JPEGView/FileExtensionsDlg.cpp b/src/JPEGView/FileExtensionsDlg.cpp index 90f31644..7f932ee9 100644 --- a/src/JPEGView/FileExtensionsDlg.cpp +++ b/src/JPEGView/FileExtensionsDlg.cpp @@ -4,6 +4,7 @@ #include "HelpersGUI.h" #include "SettingsProvider.h" #include "FileExtensionsRegistry.h" +#include "LepLoader.h" /////////////////////////////////////////////////////////////////////////////////// // Helpers @@ -293,18 +294,19 @@ void CFileExtensionsDlg::FillFileExtensionsList() { InsertExtension(_T("*.qoi"), FormatHint(CNLS::GetString(_T("%s images")), _T("Quite OK Image"))); InsertExtensions(CSettingsProvider::This().FilesProcessedByWIC(), CNLS::GetString(_T("%s images (processed by Window Imaging Component - WIC)"))); InsertExtensions(CSettingsProvider::This().FileEndingsRAW(), CNLS::GetString(_T("%s camera raw images (embedded JPEGs only)"))); + if (LepLoader::LeptonToolPresent()) + InsertExtensions(CSettingsProvider::This().FilesProcessedByLepton(), CNLS::GetString(_T("%s images")), _T("Dropbox's Lepton")); } -void CFileExtensionsDlg::InsertExtensions(LPCTSTR sExtensionList, LPCTSTR sHint) { +void CFileExtensionsDlg::InsertExtensions(LPCTSTR sExtensionList, LPCTSTR sHint, LPCTSTR param) { int nNumChars = (int)_tcslen(sExtensionList); int nStart = 0; for (int i = 0; i <= nNumChars; i++) { if (sExtensionList[i] == _T(';') || sExtensionList[i] == 0) { CString sExtension(&sExtensionList[nStart], i - nStart); if (sExtension.GetLength() >= 3) { - CString sExtensionUpper(&((LPCTSTR)sExtension)[2]); - sExtensionUpper.MakeUpper(); - InsertExtension(sExtension, FormatHint(sHint, sExtensionUpper)); + CString sExtensionUpper(param ? param : &((LPCTSTR)sExtension)[2]); + InsertExtension(sExtension, FormatHint(sHint, param ? sExtensionUpper : sExtensionUpper.MakeUpper())); } nStart = i + 1; } diff --git a/src/JPEGView/FileExtensionsDlg.h b/src/JPEGView/FileExtensionsDlg.h index 12fc0b0f..e5c1c930 100644 --- a/src/JPEGView/FileExtensionsDlg.h +++ b/src/JPEGView/FileExtensionsDlg.h @@ -44,5 +44,5 @@ class CFileExtensionsDlg : public CDialogImpl void FillFileExtensionsList(); void InsertExtension(LPCTSTR sExtension, LPCTSTR sHint); - void InsertExtensions(LPCTSTR sExtensionList, LPCTSTR sHint); + void InsertExtensions(LPCTSTR sExtensionList, LPCTSTR sHint, LPCTSTR param = NULL); }; diff --git a/src/JPEGView/FileList.cpp b/src/JPEGView/FileList.cpp index 79787a0f..3b9e1d69 100644 --- a/src/JPEGView/FileList.cpp +++ b/src/JPEGView/FileList.cpp @@ -4,6 +4,7 @@ #include "Helpers.h" #include "DirectoryWatcher.h" #include "Shlwapi.h" +#include "LepLoader.h" /////////////////////////////////////////////////////////////////////////////////// // Helpers @@ -182,6 +183,11 @@ static LPCTSTR* GetSupportedFileEndingList() { if (_tcslen(sFileEndingsWIC) > 2 && WICPresentGuarded()) { ParseAndAddFileEndings(sFileEndingsWIC); } + + if (LepLoader::LeptonToolPresent()) { + ParseAndAddFileEndings(CSettingsProvider::This().FilesProcessedByLepton()); + } + ParseAndAddFileEndings(CSettingsProvider::This().FileEndingsRAW()); } return sFileEndings; diff --git a/src/JPEGView/Helpers.cpp b/src/JPEGView/Helpers.cpp index 28018282..2c4c60b4 100644 --- a/src/JPEGView/Helpers.cpp +++ b/src/JPEGView/Helpers.cpp @@ -777,6 +777,8 @@ EImageFormat GetImageFormat(LPCTSTR sFileName) { return IF_WIC; } else if (IsInFileEndingList(CSettingsProvider::This().FileEndingsRAW(), sEnding)) { return IF_CameraRAW; + } else if (IsInFileEndingList(CSettingsProvider::This().FilesProcessedByLepton(), sEnding)) { + return IF_Lepton; } } return IF_Unknown; @@ -963,4 +965,19 @@ int GetWindowsVersion() { return osvi.dwMajorVersion * 100 + osvi.dwMinorVersion; } +CString GetTempPath() { + TCHAR tempPath[MAX_PATH]; + tempPath[0] = 0; + ::GetTempPath(MAX_PATH, tempPath); + return tempPath; +} +GUID GetGuid() { + GUID guid = GUID_NULL; + ::CoCreateGuid(&guid); + return guid; +} +CString GetGuidString() { + CComBSTR guid(Helpers::GetGuid()); + return CString(CW2T(guid)); +} } \ No newline at end of file diff --git a/src/JPEGView/Helpers.h b/src/JPEGView/Helpers.h index e9839f73..5b4c28f8 100644 --- a/src/JPEGView/Helpers.h +++ b/src/JPEGView/Helpers.h @@ -244,6 +244,12 @@ namespace Helpers { // Returns the windows version in the format Major * 100 + Minor, e.g. 602 for Windows 8 int GetWindowsVersion(); + // Returns the sysmem's temporary directory. + CString GetTempPath(); + + GUID GetGuid(); + CString GetGuidString(); + // Conversion class that replaces the | by null character in a string. // Caution: Uses a static buffer and therefore only one string can be replaced concurrently const int MAX_SIZE_REPLACE_PIPE = 512; diff --git a/src/JPEGView/ImageLoadThread.cpp b/src/JPEGView/ImageLoadThread.cpp index fd06f0ed..a91497be 100644 --- a/src/JPEGView/ImageLoadThread.cpp +++ b/src/JPEGView/ImageLoadThread.cpp @@ -1,4 +1,4 @@ - + #include "StdAfx.h" #include "ImageLoadThread.h" #include @@ -20,6 +20,7 @@ #include "WEBPWrapper.h" #include "QOIWrapper.h" #include "MaxImageDef.h" +#include "LepLoader.h" using namespace Gdiplus; @@ -88,6 +89,8 @@ static EImageFormat GetImageFormat(LPCTSTR sFileName) { } else if ((header[0] == 0xff && header[1] == 0x0a) || memcmp(header, "\x00\x00\x00\x0cJXL\x20\x0d\x0a\x87\x0a", 12) == 0) { return IF_JXL; + } else if (header[0] == 0xcf && header[1] == 0x84) { + return IF_Lepton; // Unfortunately, TIFF detection by header bytes is not reliable // A few RAW image formats use TIFF as the container @@ -399,6 +402,10 @@ void CImageLoadThread::ProcessRequest(CRequestBase& request) { DeleteCachedAvifDecoder(); ProcessReadRAWRequest(&rq); break; + case IF_Lepton: + DeleteCachedGDIBitmap(); + ProcessReadLeptonRequest(&rq); + break; case IF_WIC: DeleteCachedGDIBitmap(); DeleteCachedWebpDecoder(); @@ -492,8 +499,12 @@ void CImageLoadThread::DeleteCachedAvifDecoder() { #endif } -void CImageLoadThread::ProcessReadJPEGRequest(CRequest * request) { - HANDLE hFile = ::CreateFile(request->FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); +void CImageLoadThread::ProcessReadJPEGRequest(CRequest* request) { + ProcessReadJPEGRequest(request, request->FileName); +} + +void CImageLoadThread::ProcessReadJPEGRequest(CRequest* request, const CString& fileName) { + HANDLE hFile = ::CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { return; } @@ -1019,6 +1030,49 @@ void CImageLoadThread::ProcessReadGDIPlusRequest(CRequest * request) { } } +void CImageLoadThread::ProcessReadLeptonRequest(CRequest* request) { + static const CString space(_T(" ")); + static const CString quote(_T("\"")); + + // Convert the Lepton image into a temporary JPEG file and read then as a regular JPEG file. + CString guid(Helpers::GetGuidString()); + guid.Replace(_T("-"), _T("")); + guid.Replace(_T("{"), _T("")); + guid.Replace(_T("}"), _T("")); + + CString tempFile = Helpers::GetTempPath() + CW2T(guid); + CString sCommandLine = CSettingsProvider::This().LeptonToolExtraArgs(); + if (!sCommandLine.IsEmpty()) + sCommandLine += space; + + sCommandLine += quote + request->FileName + quote + space + quote + tempFile + quote; + + STARTUPINFO si = { 0 }; + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_UNTRUSTEDSOURCE; + PROCESS_INFORMATION pi = { 0 }; + if (!::CreateProcess(LepLoader::GetToolPath(), sCommandLine.GetBuffer(), + NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT|CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { + sCommandLine.ReleaseBuffer(); + request->Image = NULL; + request->OutOfMemory = request->Image == NULL; + return; + } + + sCommandLine.ReleaseBuffer(); + ::WaitForSingleObject(pi.hProcess, INFINITE); + + ::CloseHandle(pi.hProcess); + ::CloseHandle(pi.hThread); + + // Pass the temporary JPEG file to the routine, but specify the temp file path + // instead of requested LEP file. + ProcessReadJPEGRequest(request, tempFile); + + // Delete the temporary file as it is not needed anymore. + ::DeleteFile(tempFile); +} + static unsigned char* alloc(int sizeInBytes) { return new(std::nothrow) unsigned char[sizeInBytes]; } diff --git a/src/JPEGView/ImageLoadThread.h b/src/JPEGView/ImageLoadThread.h index edcb399e..74180654 100644 --- a/src/JPEGView/ImageLoadThread.h +++ b/src/JPEGView/ImageLoadThread.h @@ -107,6 +107,8 @@ class CImageLoadThread : public CWorkThread void DeleteCachedJxlDecoder(); void DeleteCachedAvifDecoder(); + // Special overload for Lepton files, where actual Lepton file goes via a temporary file. + void ProcessReadJPEGRequest(CRequest* request, const CString& fileName); void ProcessReadJPEGRequest(CRequest * request); void ProcessReadPNGRequest(CRequest * request); void ProcessReadBMPRequest(CRequest * request); @@ -119,6 +121,7 @@ class CImageLoadThread : public CWorkThread void ProcessReadRAWRequest(CRequest * request); void ProcessReadGDIPlusRequest(CRequest * request); void ProcessReadWICRequest(CRequest* request); + void ProcessReadLeptonRequest(CRequest* request); static void SetFileDependentProcessParams(CRequest * request); static bool ProcessImageAfterLoad(CRequest * request); diff --git a/src/JPEGView/ImageProcessingTypes.h b/src/JPEGView/ImageProcessingTypes.h index 7a6f0e6b..add6f682 100644 --- a/src/JPEGView/ImageProcessingTypes.h +++ b/src/JPEGView/ImageProcessingTypes.h @@ -45,6 +45,7 @@ enum EImageFormat { IF_CameraRAW, IF_JPEG_Embedded, // JPEG embedded in another file, e.g. camera raw IF_TGA, + IF_Lepton, IF_Unknown }; diff --git a/src/JPEGView/JPEGView.vcxproj b/src/JPEGView/JPEGView.vcxproj index 690585b3..cda37c5c 100644 --- a/src/JPEGView/JPEGView.vcxproj +++ b/src/JPEGView/JPEGView.vcxproj @@ -327,6 +327,7 @@ + @@ -416,6 +417,7 @@ + diff --git a/src/JPEGView/JPEGView.vcxproj.filters b/src/JPEGView/JPEGView.vcxproj.filters index 30e84d77..ffdf8fe7 100644 --- a/src/JPEGView/JPEGView.vcxproj.filters +++ b/src/JPEGView/JPEGView.vcxproj.filters @@ -285,6 +285,9 @@ Source Files + + Source Files + @@ -551,6 +554,9 @@ Header Files + + Header Files + diff --git a/src/JPEGView/LepLoader.cpp b/src/JPEGView/LepLoader.cpp new file mode 100644 index 00000000..ed2c7d37 --- /dev/null +++ b/src/JPEGView/LepLoader.cpp @@ -0,0 +1,13 @@ +#include "stdafx.h" +#include "LepLoader.h" +#include "SettingsProvider.h" + +bool LepLoader::LeptonToolPresent() +{ + return (::GetFileAttributes(GetToolPath()) != INVALID_FILE_ATTRIBUTES); +} + +CString LepLoader::GetToolPath() +{ + return CString(CSettingsProvider::This().GetEXEPath()) + CSettingsProvider::This().LeptonToolName(); +} diff --git a/src/JPEGView/LepLoader.h b/src/JPEGView/LepLoader.h new file mode 100644 index 00000000..38141e64 --- /dev/null +++ b/src/JPEGView/LepLoader.h @@ -0,0 +1,11 @@ +#pragma once + +class LepLoader +{ +public: + static bool LeptonToolPresent(); + + // Gets the path where the global INI file and the EXE is located + static CString GetToolPath(); +}; + diff --git a/src/JPEGView/SettingsProvider.cpp b/src/JPEGView/SettingsProvider.cpp index b2ffb3ff..9c78ee99 100644 --- a/src/JPEGView/SettingsProvider.cpp +++ b/src/JPEGView/SettingsProvider.cpp @@ -177,6 +177,9 @@ CSettingsProvider::CSettingsProvider(void) { m_nWEBPSaveQuality = GetInt(_T("WEBPSaveQuality"), 85, 0, 100); m_sDefaultSaveFormat = GetString(_T("DefaultSaveFormat"), _T("jpg")); m_sFilesProcessedByWIC = GetString(_T("FilesProcessedByWIC"), _T("*.wdp;*.mdp;*.hdp")); + m_sFilesProcessedByLepton = GetString(_T("FilesProcessedByLepton"), _T("*.lep")); + m_sLeptonToolName = GetString(_T("LeptonToolName"), _T("lepton-avx.exe")); + m_sLeptonToolExtraArgs = GetString(_T("LeptonToolExtraArgs"), _T("-allowprogressive -memory=1024M -threadmemory=128M")); m_sFileEndingsRAW = GetString(_T("FileEndingsRAW"), _T("*.pef;*.dng;*.crw;*.nef;*.cr2;*.mrw;*.rw2;*.orf;*.x3f;*.arw;*.kdc;*.nrw;*.dcr;*.sr2;*.raf")); m_bCreateParamDBEntryOnSave = GetBool(_T("CreateParamDBEntryOnSave"), true); m_bWrapAroundFolder = GetBool(_T("WrapAroundFolder"), true); @@ -285,10 +288,7 @@ CSettingsProvider::CSettingsProvider(void) { m_sWallpaperPath = GetString(_T("WallpaperPath"), _T("%temp%")); if (m_sWallpaperPath == _T("%temp%")) { - TCHAR tempPath[MAX_PATH]; - tempPath[0] = 0; - ::GetTempPath(MAX_PATH, tempPath); - m_sWallpaperPath = tempPath; + m_sWallpaperPath = Helpers::GetTempPath(); } else { TCHAR buffer[MAX_PATH]; diff --git a/src/JPEGView/SettingsProvider.h b/src/JPEGView/SettingsProvider.h index f4406e0b..fc84eb06 100644 --- a/src/JPEGView/SettingsProvider.h +++ b/src/JPEGView/SettingsProvider.h @@ -77,6 +77,9 @@ class CSettingsProvider int WEBPSaveQuality() { return m_nWEBPSaveQuality; } LPCTSTR DefaultSaveFormat() { return m_sDefaultSaveFormat; } LPCTSTR FilesProcessedByWIC() { return m_sFilesProcessedByWIC; } + LPCTSTR FilesProcessedByLepton() { return m_sFilesProcessedByLepton; } + const CString& LeptonToolName() const { return m_sLeptonToolName; } + const CString& LeptonToolExtraArgs() const { return m_sLeptonToolExtraArgs; } LPCTSTR FileEndingsRAW() { return m_sFileEndingsRAW; } void AddTemporaryRAWFileEnding(LPCTSTR sEnding) { m_sFileEndingsRAW += CString(_T(";*.")) + sEnding; } bool CreateParamDBEntryOnSave() { return m_bCreateParamDBEntryOnSave; } @@ -244,6 +247,9 @@ class CSettingsProvider int m_nWEBPSaveQuality; CString m_sDefaultSaveFormat; CString m_sFilesProcessedByWIC; + CString m_sFilesProcessedByLepton; + CString m_sLeptonToolName; + CString m_sLeptonToolExtraArgs; CString m_sFileEndingsRAW; bool m_bCreateParamDBEntryOnSave; bool m_bWrapAroundFolder; diff --git a/src/JPEGView/stdafx.h b/src/JPEGView/stdafx.h index 763bbf71..6decc5a9 100644 --- a/src/JPEGView/stdafx.h +++ b/src/JPEGView/stdafx.h @@ -61,6 +61,10 @@ extern CAppModule _Module; #error _UNICODE symbol must be defined #endif +#ifndef STARTF_UNTRUSTEDSOURCE +#define STARTF_UNTRUSTEDSOURCE 0x00008000 +#endif + #if defined _M_IX86 #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") #elif defined _M_IA64