Skip to content

Commit

Permalink
Initial support of Lepton JPEGs
Browse files Browse the repository at this point in the history
  • Loading branch information
SergeiVorobyov committed Mar 25, 2023
1 parent e57dd85 commit f471de9
Show file tree
Hide file tree
Showing 15 changed files with 143 additions and 12 deletions.
10 changes: 6 additions & 4 deletions src/JPEGView/FileExtensionsDlg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "HelpersGUI.h"
#include "SettingsProvider.h"
#include "FileExtensionsRegistry.h"
#include "LepLoader.h"

///////////////////////////////////////////////////////////////////////////////////
// Helpers
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/JPEGView/FileExtensionsDlg.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,5 @@ class CFileExtensionsDlg : public CDialogImpl<CFileExtensionsDlg>

void FillFileExtensionsList();
void InsertExtension(LPCTSTR sExtension, LPCTSTR sHint);
void InsertExtensions(LPCTSTR sExtensionList, LPCTSTR sHint);
void InsertExtensions(LPCTSTR sExtensionList, LPCTSTR sHint, LPCTSTR param = NULL);
};
6 changes: 6 additions & 0 deletions src/JPEGView/FileList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "Helpers.h"
#include "DirectoryWatcher.h"
#include "Shlwapi.h"
#include "LepLoader.h"

///////////////////////////////////////////////////////////////////////////////////
// Helpers
Expand Down Expand Up @@ -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;
Expand Down
17 changes: 17 additions & 0 deletions src/JPEGView/Helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
}
}
6 changes: 6 additions & 0 deletions src/JPEGView/Helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
60 changes: 57 additions & 3 deletions src/JPEGView/ImageLoadThread.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@


#include "StdAfx.h"
#include "ImageLoadThread.h"
#include <gdiplus.h>
Expand All @@ -20,6 +20,7 @@
#include "WEBPWrapper.h"
#include "QOIWrapper.h"
#include "MaxImageDef.h"
#include "LepLoader.h"


using namespace Gdiplus;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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];
}
Expand Down
3 changes: 3 additions & 0 deletions src/JPEGView/ImageLoadThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/JPEGView/ImageProcessingTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

Expand Down
2 changes: 2 additions & 0 deletions src/JPEGView/JPEGView.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@
<ClCompile Include="JPEGView.cpp" />
<ClCompile Include="JXLWrapper.cpp" />
<ClCompile Include="KeyMap.cpp" />
<ClCompile Include="LepLoader.cpp" />
<ClCompile Include="LocalDensityCorr.cpp" />
<ClCompile Include="ManageOpenWithDlg.cpp" />
<ClCompile Include="MultiMonitorSupport.cpp" />
Expand Down Expand Up @@ -416,6 +417,7 @@
<ClInclude Include="JPEGProvider.h" />
<ClInclude Include="JXLWrapper.h" />
<ClInclude Include="KeyMap.h" />
<ClInclude Include="LepLoader.h" />
<ClInclude Include="LocalDensityCorr.h" />
<ClInclude Include="ManageOpenWithDlg.h" />
<ClInclude Include="MaxImageDef.h" />
Expand Down
6 changes: 6 additions & 0 deletions src/JPEGView/JPEGView.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@
<ClCompile Include="ICCProfileTransform.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="LepLoader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="BasicProcessing.h">
Expand Down Expand Up @@ -551,6 +554,9 @@
<ClInclude Include="ICCProfileTransform.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LepLoader.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="res\JPEGView.ico">
Expand Down
13 changes: 13 additions & 0 deletions src/JPEGView/LepLoader.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
11 changes: 11 additions & 0 deletions src/JPEGView/LepLoader.h
Original file line number Diff line number Diff line change
@@ -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();
};

8 changes: 4 additions & 4 deletions src/JPEGView/SettingsProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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];
Expand Down
6 changes: 6 additions & 0 deletions src/JPEGView/SettingsProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/JPEGView/stdafx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit f471de9

Please sign in to comment.