From 5dc9406fd0d0fe8328cfaa3f72e42caf283f1bb8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Kotas?=
Date: Tue, 23 Sep 2025 10:06:24 +0200
Subject: [PATCH 01/14] gitignore
---
.gitignore | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 96 insertions(+)
diff --git a/.gitignore b/.gitignore
index b8373ae1b..2e7fd5e8e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,99 @@ Thumbs.db
/src/plugins/automation/generated/salamander_h.h
/src/plugins/automation/generated/salamander_i.c
/src/plugins/automation/generated/salamander_p.c
+/src/plugins/7zip/vcxproj/7ZA/salamander/Debug_x64/plugins/7zip
+/src/plugins/7zip/vcxproj/7ZA/salamander/Release_x64/plugins/7zip
+/src/plugins/7zip/vcxproj/7zwrapper/salamander/Debug_x64/plugins/7zip
+/src/plugins/7zip/vcxproj/7zwrapper/salamander/Release_x64/plugins/7zip
+/src/plugins/7zip/vcxproj/salamander/Debug_x64/plugins/7zip
+/src/plugins/7zip/vcxproj/salamander/Release_x64/plugins/7zip
+/src/plugins/automation/vcxproj/salamander/Debug_x64/plugins/automation
+/src/plugins/automation/vcxproj/salamander/Release_x64/plugins/automation
+/src/plugins/checksum/vcxproj/salamander/Debug_x64/plugins/checksum
+/src/plugins/checksum/vcxproj/salamander/Release_x64/plugins/checksum
+/src/plugins/checkver/vcxproj/salamander/Debug_x64/plugins/checkver
+/src/plugins/checkver/vcxproj/salamander/Release_x64/plugins/checkver
+/src/plugins/dbviewer/vcxproj/salamander/Debug_x64/plugins/dbviewer
+/src/plugins/dbviewer/vcxproj/salamander/Release_x64/plugins/dbviewer
+/src/plugins/demomenu/vcxproj/salamander/Debug_x64/plugins/demomenu
+/src/plugins/demomenu/vcxproj/salamander/Release_x64/plugins/demomenu
+/src/plugins/demoplug/vcxproj/salamander/Debug_x64/plugins/demoplug
+/src/plugins/demoplug/vcxproj/salamander/Release_x64/plugins/demoplug
+/src/plugins/demoview/vcxproj/salamander/Debug_x64/plugins/demoview
+/src/plugins/demoview/vcxproj/salamander/Release_x64/plugins/demoview
+/src/plugins/diskmap/vcxproj/salamander/Debug_x64/plugins/diskmap
+/src/plugins/diskmap/vcxproj/salamander/Release_x64/plugins/diskmap
+/src/plugins/filecomp/vcxproj/fcremote/salamander/Debug_x64/plugins/filecomp
+/src/plugins/filecomp/vcxproj/fcremote/salamander/Release_x64/plugins/filecomp
+/src/plugins/filecomp/vcxproj/salamander/Debug_x64/plugins/filecomp
+/src/plugins/filecomp/vcxproj/salamander/Release_x64/plugins/filecomp
+/src/plugins/folders/vcxproj/salamander/Debug_x64/plugins/folders
+/src/plugins/folders/vcxproj/salamander/Release_x64/plugins/folders
+/src/plugins/ftp/vcxproj/salamander/Debug_x64/plugins/ftp
+/src/plugins/ftp/vcxproj/salamander/Release_x64/plugins/ftp
+/src/plugins/ieviewer/vcxproj/salamander/Debug_x64/plugins/ieviewer
+/src/plugins/ieviewer/vcxproj/salamander/Release_x64/plugins/ieviewer
+/src/plugins/mmviewer/vcxproj/salamander/Debug_x64/plugins/mmviewer
+/src/plugins/mmviewer/vcxproj/salamander/Release_x64/plugins/mmviewer
+/src/plugins/nethood/vcxproj/salamander/Debug_x64/plugins/nethood
+/src/plugins/nethood/vcxproj/salamander/Release_x64/plugins/nethood
+/src/plugins/pak/vcxproj/salamander/Debug_x64/plugins/pak
+/src/plugins/pak/vcxproj/salamander/Release_x64/plugins/pak
+/src/plugins/peviewer/vcxproj/salamander/Debug_x64/plugins/peviewer
+/src/plugins/peviewer/vcxproj/salamander/Release_x64/plugins/peviewer
+/src/plugins/pictview/vcxproj/exif/salamander/Debug_x64/plugins/pictview
+/src/plugins/pictview/vcxproj/exif/salamander/Release_x64/plugins/pictview
+/src/plugins/pictview/vcxproj/salamander/Debug_x64/plugins/pictview
+/src/plugins/pictview/vcxproj/salamander/Release_x64/plugins/pictview
+/src/plugins/portables/vcxproj/salamander/Debug_x64/plugins/portables
+/src/plugins/portables/vcxproj/salamander/Release_x64/plugins/portables
+/src/plugins/regedt/vcxproj/salamander/Debug_x64/plugins/regedt
+/src/plugins/regedt/vcxproj/salamander/Release_x64/plugins/regedt
+/src/plugins/renamer/vcxproj/salamander/Debug_x64/plugins/renamer
+/src/plugins/renamer/vcxproj/salamander/Release_x64/plugins/renamer
+/src/plugins/splitcbn/vcxproj/salamander/Debug_x64/plugins/splitcbn
+/src/plugins/splitcbn/vcxproj/salamander/Release_x64/plugins/splitcbn
+/src/plugins/tar/vcxproj/salamander/Debug_x64/plugins/tar
+/src/plugins/tar/vcxproj/salamander/Release_x64/plugins/tar
+/src/plugins/unarj/vcxproj/salamander/Debug_x64/plugins/unarj
+/src/plugins/unarj/vcxproj/salamander/Release_x64/plugins/unarj
+/src/plugins/uncab/vcxproj/salamander/Debug_x64/plugins/uncab
+/src/plugins/uncab/vcxproj/salamander/Release_x64/plugins/uncab
+/src/plugins/unchm/vcxproj/chmlib/salamander/Debug_x64/plugins/unchm
+/src/plugins/unchm/vcxproj/chmlib/salamander/Release_x64/plugins/unchm
+/src/plugins/unchm/vcxproj/salamander/Debug_x64/plugins/unchm
+/src/plugins/unchm/vcxproj/salamander/Release_x64/plugins/unchm
+/src/plugins/undelete/vcxproj/salamander/Debug_x64/plugins/undelete
+/src/plugins/undelete/vcxproj/salamander/Release_x64/plugins/undelete
+/src/plugins/unfat/vcxproj/salamander/Debug_x64/plugins/unfat
+/src/plugins/unfat/vcxproj/salamander/Release_x64/plugins/unfat
+/src/plugins/uniso/vcxproj/salamander/Debug_x64/plugins/uniso
+/src/plugins/uniso/vcxproj/salamander/Release_x64/plugins/uniso
+/src/plugins/unlha/vcxproj/salamander/Debug_x64/plugins/unlha
+/src/plugins/unlha/vcxproj/salamander/Release_x64/plugins/unlha
+/src/plugins/unmime/vcxproj/salamander/Debug_x64/plugins/unmime
+/src/plugins/unmime/vcxproj/salamander/Release_x64/plugins/unmime
+/src/plugins/unole/vcxproj/salamander/Debug_x64/plugins/unole
+/src/plugins/unole/vcxproj/salamander/Release_x64/plugins/unole
+/src/plugins/unrar/vcxproj/salamander/Debug_x64/plugins/unrar
+/src/plugins/unrar/vcxproj/salamander/Release_x64/plugins/unrar
+/src/plugins/wmobile/vcxproj/salamander/Debug_x64/plugins/wmobile
+/src/plugins/wmobile/vcxproj/salamander/Release_x64/plugins/wmobile
+/src/plugins/zip/vcxproj/salamander/Debug_x64/plugins/zip
+/src/plugins/zip/vcxproj/salamander/Release_x64/plugins/zip
+/src/plugins/zip/vcxproj/zip2sfx/salamander/Debug_x86/plugins/zip/zip2sfx/Intermediate/microsoft/STL
+/src/plugins/zip/vcxproj/zip2sfx/salamander/Release_x86/plugins/zip/zip2sfx/Intermediate/microsoft/STL
+/src/vcxproj/salamander/Debug_x64
+/src/vcxproj/salamander/Release_x64
+/src/vcxproj/salmon/salamander/Debug_x64
+/src/vcxproj/salmon/salamander/Release_x64
+/src/vcxproj/salopen/salamander/Debug_x64/plugins/Intermediate/salopen/Intermediate/microsoft/STL
+/src/vcxproj/salopen/salamander/Release_x64/plugins/Intermediate/salopen/Intermediate/microsoft/STL
+/src/vcxproj/salspawn/salamander/Debug_x64/plugins/Intermediate/salspawn/Intermediate/microsoft/STL
+/src/vcxproj/salspawn/salamander/Release_x64/plugins/Intermediate/salspawn/Intermediate/microsoft/STL
+/src/vcxproj/shellext/salamander/Debug_x64/plugins/Intermediate/salextx64/Intermediate/microsoft/STL
+/src/vcxproj/shellext/salamander/Debug_x86/plugins/Intermediate/salextx86/Intermediate/microsoft/STL
+/src/vcxproj/shellext/salamander/Release_x64/plugins/Intermediate/salextx64/Intermediate/microsoft/STL
+/src/vcxproj/shellext/salamander/Release_x86/plugins/Intermediate/salextx86/Intermediate/microsoft/STL
+/src/vcxproj/sqlite/salamander/Debug_x64
+/src/vcxproj/sqlite/salamander/Release_x64
From 9a8e8ec269735c8b4e6cefdc40457ad79af58c04 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Kotas?=
Date: Sun, 5 Oct 2025 19:35:45 +0200
Subject: [PATCH 02/14] gitignore
---
.gitignore | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 112 insertions(+)
diff --git a/.gitignore b/.gitignore
index 2e7fd5e8e..23a8cb2ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -107,3 +107,115 @@ Thumbs.db
/src/vcxproj/shellext/salamander/Release_x86/plugins/Intermediate/salextx86/Intermediate/microsoft/STL
/src/vcxproj/sqlite/salamander/Debug_x64
/src/vcxproj/sqlite/salamander/Release_x64
+
+src/plugins/7zip/vcxproj/7ZA/salamander/Release_x86/
+
+src/plugins/7zip/vcxproj/7zwrapper/salamander/Release_x86/
+
+src/plugins/7zip/vcxproj/salamander/Release_x86/
+
+src/plugins/automation/vcxproj/salamander/Release_x86/
+
+src/plugins/checksum/vcxproj/salamander/Release_x86/
+
+src/plugins/checkver/vcxproj/salamander/Release_x86/
+
+src/plugins/dbviewer/vcxproj/salamander/Release_x86/
+
+src/plugins/demomenu/vcxproj/salamander/Release_x86/
+
+src/plugins/demoplug/vcxproj/salamander/Release_x86/
+
+src/plugins/demoview/vcxproj/salamander/Release_x86/
+
+src/plugins/diskmap/vcxproj/salamander/Release_x86/
+
+src/plugins/filecomp/vcxproj/fcremote/salamander/Release_x86/
+
+src/plugins/filecomp/vcxproj/salamander/Release_x86/
+
+src/plugins/folders/vcxproj/salamander/Release_x86/
+
+src/plugins/ftp/vcxproj/salamander/Release_x86/
+
+src/plugins/ieviewer/vcxproj/salamander/Release_x86/
+
+src/plugins/mmviewer/vcxproj/salamander/Release_x86/
+
+src/plugins/nethood/vcxproj/salamander/Release_x86/
+
+src/plugins/pak/vcxproj/salamander/Release_x86/
+
+src/plugins/peviewer/vcxproj/salamander/Release_x86/
+
+src/plugins/pictview/vcxproj/exif/salamander/Release_x86/
+
+src/plugins/pictview/vcxproj/salamander/Release_x86/
+
+src/plugins/portables/vcxproj/salamander/Release_x86/
+
+src/plugins/regedt/vcxproj/salamander/Release_x86/
+
+src/plugins/renamer/vcxproj/salamander/Release_x86/
+
+src/plugins/splitcbn/vcxproj/salamander/Release_x86/
+
+src/plugins/tar/vcxproj/salamander/Release_x86/
+
+src/plugins/unarj/vcxproj/salamander/Release_x86/
+
+src/plugins/uncab/vcxproj/salamander/Release_x86/
+
+src/plugins/unchm/vcxproj/chmlib/salamander/Release_x86/
+
+src/plugins/unchm/vcxproj/salamander/Release_x86/
+
+src/plugins/undelete/vcxproj/salamander/Release_x86/
+
+src/plugins/unfat/vcxproj/salamander/Release_x86/
+
+src/plugins/uniso/vcxproj/salamander/Release_x86/
+
+src/plugins/unlha/vcxproj/salamander/Release_x86/
+
+src/plugins/unmime/vcxproj/salamander/Release_x86/
+
+src/plugins/unole/vcxproj/salamander/Release_x86/
+
+src/plugins/unrar/vcxproj/salamander/Release_x86/
+
+src/plugins/wmobile/vcxproj/salamander/Release_x86/
+
+src/plugins/zip/vcxproj/salamander/Release_x86/
+
+src/vcxproj/salmon/salamander/Release_x86/
+
+src/vcxproj/salopen/salamander/Release_x86/
+
+src/vcxproj/salspawn/salamander/Release_x86/
+
+src/vcxproj/sqlite/salamander/Release_x86/
+
+src/vcxproj/salamander/Release_x86/
+
+src/plugins/webview2renderviewer/managed/obj/
+
+src/plugins/webview2renderviewer/managed/bin/
+
+src/plugins/textviewer/managed/bin/
+
+src/plugins/textviewer/managed/obj/
+
+src/plugins/samandarin/managed/bin/
+
+src/plugins/samandarin/managed/obj/
+
+src/plugins/jsonviewer/managed/bin/
+
+src/plugins/jsonviewer/managed/obj/
+
+src/plugins/csdemo/managed/obj/
+
+build/
+
+output/
From 5c1e1a2f4f4dc30920e401780f7f1fe8d066044b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Kotas?=
Date: Mon, 6 Oct 2025 15:54:41 +0200
Subject: [PATCH 03/14] feat(pictview): replace PVW32Cnv loader with WIC
backend
#187
---
src/plugins/pictview/PVEXEWrapper.cpp | 660 -----
src/plugins/pictview/PVEXEWrapper.h | 13 -
src/plugins/pictview/PVEnvelope.cpp | 180 --
src/plugins/pictview/PVMessage.cpp | 35 -
src/plugins/pictview/PVMessage.h | 532 ----
src/plugins/pictview/PVMessageEnvelope.cpp | 971 -------
src/plugins/pictview/PVMessageWrapper.cpp | 934 ------
src/plugins/pictview/dialogs.cpp | 82 +-
src/plugins/pictview/exif/exif.c | 185 +-
src/plugins/pictview/exif/exif.def | 6 +-
src/plugins/pictview/exif/exif.h | 18 +-
.../help/hh/pictview/introduction_intro.htm | 10 +-
src/plugins/pictview/lang/lang.rc | 12 +-
src/plugins/pictview/lang/lang.rc2 | 6 +-
src/plugins/pictview/lib/PVW32DLL.h | 2 +-
src/plugins/pictview/lib/readme.txt | 10 +-
src/plugins/pictview/pictview.cpp | 642 ++++-
src/plugins/pictview/pictview.h | 47 +-
src/plugins/pictview/pictview.rh2 | 2 +-
src/plugins/pictview/precomp.h | 33 +-
src/plugins/pictview/pvw32cnv.rh2 | 2 +-
src/plugins/pictview/render1.cpp | 78 +-
src/plugins/pictview/renderer.h | 2 +-
src/plugins/pictview/salpvenv.rc | 36 -
src/plugins/pictview/salpvenv.xml | 46 -
src/plugins/pictview/saveas.cpp | 2 +-
src/plugins/pictview/thumbs.cpp | 110 +-
src/plugins/pictview/vcxproj/pictview.vcxproj | 14 +-
.../pictview/vcxproj/pictview.vcxproj.filters | 13 +-
src/plugins/pictview/vcxproj/salpvenv.vcxproj | 101 -
.../pictview/vcxproj/salpvenv_base.props | 32 -
.../pictview/vcxproj/salpvenv_debug.props | 24 -
.../pictview/vcxproj/salpvenv_release.props | 31 -
src/plugins/pictview/versinfo.rh2 | 6 +-
src/plugins/pictview/wic/WicBackend.cpp | 2541 +++++++++++++++++
src/plugins/pictview/wic/WicBackend.h | 130 +
src/plugins/shared/baseaddr_x64.txt | 2 -
src/plugins/shared/baseaddr_x86.txt | 2 -
src/vcxproj/salamand.sln | 22 +-
translations/chinesesimplified/pictview.slt | 66 +-
translations/czech/pictview.slt | 64 +-
translations/dutch/pictview.slt | 64 +-
translations/french/pictview.slt | 66 +-
translations/german/pictview.slt | 66 +-
translations/hungarian/pictview.slt | 66 +-
translations/romanian/pictview.slt | 66 +-
translations/russian/pictview.slt | 66 +-
translations/slovak/pictview.slt | 64 +-
translations/spanish/pictview.slt | 64 +-
49 files changed, 4111 insertions(+), 4115 deletions(-)
delete mode 100644 src/plugins/pictview/PVEXEWrapper.cpp
delete mode 100644 src/plugins/pictview/PVEXEWrapper.h
delete mode 100644 src/plugins/pictview/PVEnvelope.cpp
delete mode 100644 src/plugins/pictview/PVMessage.cpp
delete mode 100644 src/plugins/pictview/PVMessage.h
delete mode 100644 src/plugins/pictview/PVMessageEnvelope.cpp
delete mode 100644 src/plugins/pictview/PVMessageWrapper.cpp
delete mode 100644 src/plugins/pictview/salpvenv.rc
delete mode 100644 src/plugins/pictview/salpvenv.xml
delete mode 100644 src/plugins/pictview/vcxproj/salpvenv.vcxproj
delete mode 100644 src/plugins/pictview/vcxproj/salpvenv_base.props
delete mode 100644 src/plugins/pictview/vcxproj/salpvenv_debug.props
delete mode 100644 src/plugins/pictview/vcxproj/salpvenv_release.props
create mode 100644 src/plugins/pictview/wic/WicBackend.cpp
create mode 100644 src/plugins/pictview/wic/WicBackend.h
diff --git a/src/plugins/pictview/PVEXEWrapper.cpp b/src/plugins/pictview/PVEXEWrapper.cpp
deleted file mode 100644
index 5eff79b00..000000000
--- a/src/plugins/pictview/PVEXEWrapper.cpp
+++ /dev/null
@@ -1,660 +0,0 @@
-// SPDX-FileCopyrightText: 2023 Open Salamander Authors
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-/* This wrapper implements stubs for the functions exported by PVW32Cnv.dll
- * and found in struct CPVW32DLL. These stubs call a hidden remote (32-bit) process
- * SalPVEnv.exe that actually makes the calls to a real PVW32Cnv.dll.
- * This remote process is called the Envelope.
- * Shared memory (File mapping) is used for the inter-process communication.
- * The name of the file mapping is based on the AS.exe ProcessID and a unique call ID.
- * The calls are signalled by a unique Event object whose name is based on the same ID's.
- * Each call uses its own shared memory and event to enable proper multi-threading.
- *
- * This wrapper is enabled by the PICTVIEW_DLL_IN_SEPARATE_PROCESS macro.
- * It supports both 32-bit and 64-bit builds of AS.
- *
- * NOTE: Work in progress.
- */
-#include "precomp.h"
-
-#ifdef PICTVIEW_DLL_IN_SEPARATE_PROCESS
-
-#include "PVEXEWrapper.h"
-#include "PVMessage.h"
-#include "PixelAccess.h"
-#include "Thumbnailer.h"
-
-CPVWrapper PVWrapper;
-
-static PVCODE WINAPI PVReadImage2Stub(LPPVHandle Img, HDC PaintDC, RECT* pDRect, TProgressProc Progress, void* AppSpecific, int ImageIndex);
-static PVCODE WINAPI PVCloseImageStub(LPPVHandle Img);
-static PVCODE WINAPI PVDrawImageStub(LPPVHandle Img, HDC PaintDC, int X, int Y, LPRECT rect);
-static const char* WINAPI PVGetErrorTextStub(DWORD ErrorCode);
-static PVCODE WINAPI PVOpenImageExStub(LPPVHandle* Img, LPPVOpenImageExInfo pOpenExInfo, LPPVImageInfo pImgInfo, int Size);
-static PVCODE WINAPI PVSetBkHandleStub(LPPVHandle Img, COLORREF BkColor);
-static DWORD WINAPI PVGetDLLVersionStub(void);
-static PVCODE WINAPI PVSetStretchParametersStub(LPPVHandle Img, DWORD Width, DWORD Height, DWORD Mode);
-static PVCODE WINAPI PVLoadFromClipboardStub(LPPVHandle* Img, LPPVImageInfo pImgInfo, int Size);
-static PVCODE WINAPI PVGetImageInfoStub(LPPVHandle Img, LPPVImageInfo pImgInfo, int Size, int ImageIndex);
-static PVCODE WINAPI PVSetParamStub(LPPVHandle Img);
-static PVCODE WINAPI PVGetHandles2Stub(LPPVHandle Img, LPPVImageHandles* pHandles);
-static PVCODE WINAPI PVSaveImageStub(LPPVHandle Img, const char* OutFName, LPPVSaveImageInfo pSii, TProgressProc Progress, void* AppSpecific, int ImageIndex);
-static PVCODE WINAPI PVChangeImageStub(LPPVHandle Img, DWORD Flags);
-static DWORD WINAPI PVIsOutCombSupportedStub(int Fmt, int Compr, int Colors, int ColorModel);
-static PVCODE WINAPI PVReadImageSequenceStub(LPPVHandle Img, LPPVImageSequence* ppSeq);
-static PVCODE WINAPI PVCropImageStub(LPPVHandle Img, int Left, int Top, int Width, int Height);
-static bool GetRGBAtCursorStub(LPPVHandle Img, DWORD Colors, int x, int y, RGBQUAD* pRGB, int* pIndex);
-static PVCODE CalculateHistogramStub(LPPVHandle Img, const LPPVImageInfo pvii, LPDWORD luminosity, LPDWORD red, LPDWORD green, LPDWORD blue, LPDWORD rgb);
-static PVCODE CreateThumbnailStub(LPPVHandle Img, LPPVSaveImageInfo pSii, int imageIndex, DWORD imgWidth, DWORD imgHeight,
- int thumbWidth, int thumbHeight, CSalamanderThumbnailMakerAbstract* thumbMaker, DWORD thumbFlags, TProgressProc progressProc, void* progressProcArg);
-static PVCODE SimplifyImageSequenceStub(LPPVHandle hPVImage, HDC dc, int ScreenWidth, int ScreenHeight, LPPVImageSequence& pSeq, const COLORREF& bgColor);
-
-struct CPVW32DLL PVEXEProcStubs = {
- PVReadImage2Stub,
- PVCloseImageStub,
- PVDrawImageStub,
- PVGetErrorTextStub,
- PVOpenImageExStub,
- PVSetBkHandleStub,
- PVGetDLLVersionStub,
- PVSetStretchParametersStub,
- PVLoadFromClipboardStub,
- PVGetImageInfoStub,
- PVSetParamStub,
- PVGetHandles2Stub,
- PVSaveImageStub,
- PVChangeImageStub,
- PVIsOutCombSupportedStub,
- PVReadImageSequenceStub,
- PVCropImageStub,
- GetRGBAtCursorStub,
- CalculateHistogramStub,
- CreateThumbnailStub,
- SimplifyImageSequenceStub};
-
-BOOL InitPVEXEWrapper(HWND hParentWnd, LPCTSTR pPluginFolder)
-{
- PVW32DLL = PVEXEProcStubs;
-
- _tcscpy(PVWrapper.EnvelopePath, pPluginFolder);
- _tcscat(PVWrapper.EnvelopePath, _T("\\SalPVEnv.exe"));
- _sntprintf(PVWrapper.MutexName, SizeOf(PVWrapper.MutexName), _T("PVEXE_%08X"), GetCurrentProcessId());
- PVWrapper.hMutex = CreateMutex(NULL, TRUE, PVWrapper.MutexName);
- _ASSERTE(GetLastError() != ERROR_ALREADY_EXISTS);
- if (!PVWrapper.hMutex)
- {
- return FALSE;
- }
-
- // NOTE: the name of the exe in the Command line is not important. It just must be something.
- _sntprintf(PVWrapper.CommandLine, SizeOf(PVWrapper.CommandLine), _T("SalPVEnv.exe %s"), PVWrapper.MutexName);
- if (!LoadEnvelope())
- {
- CloseHandle(PVWrapper.hMutex);
- PVWrapper.hMutex = NULL;
- return FALSE;
- }
-
- return TRUE;
-}
-
-void ReleasePVEXEWrapper()
-{
- if (PVWrapper.hMutex != NULL)
- {
- CloseHandle(PVWrapper.hMutex); // This will make the Envelope make suicide
- PVWrapper.hMutex = NULL;
- }
-}
-
-// ========================= Stubs to PVW32Cnv.dll =========================
-
-PVCODE WINAPI PVReadImage2Stub(LPPVHandle Img, HDC PaintDC, RECT* pDRect, TProgressProc Progress, void* AppSpecific, int ImageIndex)
-{
- PVMessage_ReadImage msg(Img, ImageIndex, Progress != NULL);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec(PaintDC, pDRect, Progress, AppSpecific))
- {
- PVCODE ret = msg.GetResultCode();
- if (((PVC_OK == ret) || (PVC_CANCELED == ret)) && PaintDC)
- {
- PVDrawImageStub(Img, PaintDC, pDRect->left, pDRect->top, pDRect);
- }
- return ret;
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-PVCODE WINAPI PVCloseImageStub(LPPVHandle Img)
-{
- if (!Img)
- {
- return PVC_OK;
- }
- PVMessage_CloseImage msg(Img);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec())
- {
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-PVCODE WINAPI PVDrawImageStub(LPPVHandle Img, HDC PaintDC, int X, int Y, LPRECT rect)
-{
- PVMessage_DrawImage msg(Img, PaintDC, X, Y, rect);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec(PaintDC))
- {
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-const char* WINAPI PVGetErrorTextStub(DWORD ErrorCode)
-{
- // FIXME: make this thread-safe.
- // FIXME: implement providing the string from Salamander, when needed
- // (PVW32Cnv.dll doesn't contain most strings anymore)
- // FIXME: consult the default error text with Altap
- static char errorStr[512];
-
- if (ErrorCode == PVC_ENVELOPE_NOT_LOADED)
- return "Could not load PictView process.";
-
- PVMessage_GetErrorText msg(ErrorCode);
-
- if (!msg.IsInited())
- return "Could not load PictView process.";
-
- if (msg.Exec())
- {
- strcpy(errorStr, msg.GetErrorText());
- return errorStr;
- }
-
- return "Could not load PictView process.";
-}
-
-PVCODE WINAPI PVOpenImageExStub(LPPVHandle* Img, LPPVOpenImageExInfo pOpenExInfo, LPPVImageInfo pImgInfo, int Size)
-{
- PVMessage_OpenImageEx msg(pOpenExInfo);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec(pImgInfo))
- {
- *Img = msg.GetPVHandle();
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-PVCODE WINAPI PVSetBkHandleStub(LPPVHandle Img, COLORREF BkColor)
-{
- PVMessage_SetBkHandle msg(Img, BkColor);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec())
- {
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-DWORD WINAPI PVGetDLLVersionStub(void)
-{
- PVMessage_GetDLLVersion msg;
- DWORD version;
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec(version))
- {
- return version;
- }
-
- return 0;
-}
-
-PVCODE WINAPI PVSetStretchParametersStub(LPPVHandle Img, DWORD Width, DWORD Height, DWORD Mode)
-{
- PVMessage_SetStretchParameters msg(Img, Width, Height, Mode);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec())
- {
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-PVCODE WINAPI PVLoadFromClipboardStub(LPPVHandle* Img, LPPVImageInfo pImgInfo, int Size)
-{
- PVMessage_LoadFromClipboard msg;
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec(pImgInfo))
- {
- *Img = msg.GetPVHandle();
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-PVCODE WINAPI PVGetImageInfoStub(LPPVHandle Img, LPPVImageInfo pImgInfo, int Size, int ImageIndex)
-{
- PVMessage_GetImageInfo msg(Img, ImageIndex);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec(pImgInfo))
- {
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-PVCODE WINAPI PVSetParamStub(LPPVHandle Img)
-{
- // This is not needed in this environment
- return PVC_OK;
-}
-
-PVCODE WINAPI PVGetHandles2Stub(LPPVHandle Img, LPPVImageHandles* pHandles)
-{
- // This is not needed in this environment
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-PVCODE WINAPI PVSaveImageStub(LPPVHandle Img, const char* OutFName, LPPVSaveImageInfo pSii, TProgressProc Progress, void* AppSpecific, int ImageIndex)
-{
- PVMessage_SaveImage msg(Img, OutFName, pSii, ImageIndex, Progress != NULL);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec(Progress, AppSpecific))
- {
- PVCODE ret = msg.GetResultCode();
-
- if (ret != PVC_OK)
- return ret;
-
- // Send the output to user-defined output funcs, if specified
- if (pSii->Flags & PVSF_USERDEFINED_OUTPUT)
- {
- PVMessage_SaveImage::LPPVMessageSaveImage pHdr = (PVMessage_SaveImage::LPPVMessageSaveImage)msg.GetData();
- HANDLE hFileMap;
- LPBYTE pBuffer;
-
- hFileMap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
- 0, pHdr->OutFileMapSize, pHdr->FileName);
- if (!hFileMap)
- {
- return PVC_OOM;
- }
- pBuffer = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, pHdr->OutFileMapSize);
- if (!pBuffer)
- {
- CloseHandle(hFileMap);
- return PVC_OOM;
- }
- ret = (pSii->WriteFunc(AppSpecific, pBuffer, pHdr->OutFileMapSize) == pHdr->OutFileMapSize) ? PVC_OK : PVC_WRITING_ERROR;
- UnmapViewOfFile(pBuffer);
- CloseHandle(hFileMap);
- PVMessage_CloseHandle(pHdr->InternalFileMapHandle).Exec();
- }
- return ret;
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-PVCODE WINAPI PVChangeImageStub(LPPVHandle Img, DWORD Flags)
-{
- PVMessage_ChangeImage msg(Img, Flags);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec())
- {
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-DWORD WINAPI PVIsOutCombSupportedStub(int Fmt, int Compr, int Colors, int ColorModel)
-{
- PVMessage_IsOutCombSupported msg(Fmt, Compr, Colors, ColorModel);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec())
- {
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-PVCODE WINAPI PVReadImageSequenceStub(LPPVHandle Img, LPPVImageSequence* ppSeq)
-{
- PVMessage_ReadImageSequence msg(Img);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec())
- {
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-PVCODE WINAPI PVCropImageStub(LPPVHandle Img, int Left, int Top, int Width, int Height)
-{
- PVMessage_CropImage msg(Img, Left, Top, Width, Height);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec())
- {
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-bool GetRGBAtCursorStub(LPPVHandle Img, DWORD Colors, int x, int y, RGBQUAD* pRGB, int* pIndex)
-{
- PVMessage_GetRGBAtCursor msg(Img, Colors, x, y);
-
- if (!msg.IsInited())
- return false;
-
- if (msg.Exec())
- {
- *pRGB = *msg.GetRGB();
- *pIndex = msg.GetIndex();
- return msg.GetResultCode() == PVC_OK;
- }
-
- return false;
-}
-
-PVCODE CalculateHistogramStub(LPPVHandle Img, const LPPVImageInfo pvii, LPDWORD luminosity, LPDWORD red, LPDWORD green, LPDWORD blue, LPDWORD rgb)
-{
- PVMessage_CalculateHistogram msg(Img, pvii);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec())
- {
- msg.GetResults(luminosity, red, green, blue, rgb);
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-PVCODE CreateThumbnailStub(LPPVHandle Img, LPPVSaveImageInfo pSii, int imageIndex, DWORD imgWidth, DWORD imgHeight,
- int thumbWidth, int thumbHeight, CSalamanderThumbnailMakerAbstract* thumbMaker, DWORD thumbFlags,
- TProgressProc progressProc, void* progressProcArg)
-{
- CSalamanderThumbnailMaker::CalculateThumbnailSize(imgWidth, imgHeight, thumbWidth, thumbHeight, thumbWidth, thumbHeight);
- PVMessage_CreateThumbnail msg(Img, pSii, imageIndex, imgWidth, imgHeight, thumbWidth, thumbHeight);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (thumbMaker->SetParameters(thumbWidth, thumbHeight, thumbFlags))
- {
- if (msg.Exec(progressProc, progressProcArg))
- {
- if ((msg.GetResultCode() == PVC_OK) || (msg.GetResultCode() == PVC_CANCELED))
- {
- thumbMaker->ProcessBuffer(msg.GetPixelData(), thumbHeight);
- }
- }
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-PVCODE SimplifyImageSequenceStub(LPPVHandle Img, HDC dc, int ScreenWidth, int ScreenHeight, LPPVImageSequence& pSeq, const COLORREF& bgColor)
-{
- PVMessage_SimplifyImageSequence msg(Img, ScreenWidth, ScreenHeight, bgColor);
-
- if (!msg.IsInited())
- return PVC_ENVELOPE_NOT_LOADED;
-
- if (msg.Exec())
- {
- if (msg.GetResultCode() == PVC_OK)
- {
- pSeq = msg.GetImageSequence();
- }
- return msg.GetResultCode();
- }
-
- return PVC_ENVELOPE_NOT_LOADED;
-}
-
-// ========================= LoadEnvelope =========================
-bool LoadEnvelope()
-{
- STARTUPINFO si;
-
- _ASSERTE(PVWrapper.hMutex != NULL);
- if (!PVWrapper.hMutex)
- {
- return false;
- }
-
- // FIXME: The rest of this func may need synchronization via a critical section
- if (PVWrapper.pi.hProcess)
- {
- // FIXME: check that the envelope process is still alive
- return true;
- }
- memset(&si, 0, sizeof(si));
- memset(&PVWrapper.pi, 0, sizeof(PVWrapper.pi));
- si.cb = sizeof(STARTUPINFO);
- if (!CreateProcess(PVWrapper.EnvelopePath, PVWrapper.CommandLine, NULL, NULL,
- FALSE, 0, NULL, NULL, &si, &PVWrapper.pi))
- {
- return false;
- }
-
- WaitForInputIdle(PVWrapper.pi.hProcess, 5000);
-
- // Feed the localized texts from our language dll to the envelope
- PVMessage_InitTexts msg;
-
- if (!msg.IsInited() || !msg.Exec())
- {
- return false;
- }
-
- return true;
-}
-
-PVMessage_InitTexts::PVMessage_InitTexts()
-{
- char *buf = NULL, *ptr = buf;
- size_t alloced = 0;
- char str[5000];
-
- for (int i = 0; i <= 999; i++)
- {
- int size = LoadString(HLanguage, IDS_DLL + i, str, 5000);
- if (size == 0)
- str[0] = 0; // String not found in resources
- size_t len = size + 1;
- if (ptr - buf + len > alloced)
- {
- alloced += 8192;
- char* newbuf = (char*)realloc(buf, alloced);
- if (!newbuf)
- {
- break; // What's wrong?
- }
- ptr = newbuf + (ptr - buf);
- buf = newbuf;
- }
- strcpy(ptr, str);
- ptr += len;
- }
- if (!Init(PVMSG_InitTexts, ptr - buf + sizeof(PVMessageInitTexts) - sizeof(PVMessageHeader), NULL))
- {
- free(buf);
- return;
- }
-
- LPPVMessageInitTexts pHdr = (LPPVMessageInitTexts)pData;
- pHdr->TextsCount = 999;
- memcpy(pHdr->Texts, buf, ptr - buf);
- free(buf);
-}
-
-// The following is needed to keep the linker happy in debug builds
-bool PVMessage_InitTexts::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_GetErrorText::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_OpenImageEx::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_GetImageInfo::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_ReadImage::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_CloseImage::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_DrawImage::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_SaveImage::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_LoadFromClipboard::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_ReadImageSequence::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_SetBkHandle::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_GetDLLVersion::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_SetStretchParameters::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_ChangeImage::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_IsOutCombSupported::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_CropImage::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_GetRGBAtCursor::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_CalculateHistogram::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_CreateThumbnail::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_SimplifyImageSequence::HandleRequest()
-{
- return false;
-}
-
-bool PVMessage_CloseHandle::HandleRequest()
-{
- return false;
-}
-
-#endif
diff --git a/src/plugins/pictview/PVEXEWrapper.h b/src/plugins/pictview/PVEXEWrapper.h
deleted file mode 100644
index 1529c9ad2..000000000
--- a/src/plugins/pictview/PVEXEWrapper.h
+++ /dev/null
@@ -1,13 +0,0 @@
-// SPDX-FileCopyrightText: 2023 Open Salamander Authors
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#ifdef PICTVIEW_DLL_IN_SEPARATE_PROCESS
-
-#include "pictview.h"
-
-BOOL InitPVEXEWrapper(HWND hParentWnd, LPCTSTR pPluginFolder);
-void ReleasePVEXEWrapper();
-
-#endif
diff --git a/src/plugins/pictview/PVEnvelope.cpp b/src/plugins/pictview/PVEnvelope.cpp
deleted file mode 100644
index 620d1e3f0..000000000
--- a/src/plugins/pictview/PVEnvelope.cpp
+++ /dev/null
@@ -1,180 +0,0 @@
-// SPDX-FileCopyrightText: 2023 Open Salamander Authors
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-#include
-#include
-#include
-
-#include "PVMessage.h"
-#include "PixelAccess.h"
-#include "Thumbnailer.h"
-
-#define SizeOf(x) (sizeof(x) / sizeof(x[0]))
-
-extern "C" WINUSERAPI PVCODE WINAPI PVSetParam(const char*(WINAPI* GetExtTextProc)(int msgID));
-
-extern const char* Strings[450];
-extern char* StringsBuf;
-extern CSalamanderThumbnailMaker Thumbnailer;
-
-TCHAR SalamanderMutex[64];
-DWORD MainThreadID;
-
-CPVWrapper PVWrapper;
-
-DWORD WINAPI CheckThreadProc(LPVOID)
-{
- for (;;)
- {
- HANDLE hMutex = OpenMutex(SYNCHRONIZE, FALSE, SalamanderMutex);
- if (!hMutex)
- {
- // Hmmm, Salamander died
- break;
- }
- CloseHandle(hMutex);
- Sleep(1000);
- }
-
- PostThreadMessage(MainThreadID, WM_QUIT, 0, 0);
-
- return TRUE;
-}
-
-// This callback provides strings coming from the Salamander process to PVW32Cnv.dll
-// Salamander provides translated messages, this allows using single PVW32Cnv.dll
-const char* WINAPI GetExtTextProc(int msgID)
-{
- if ((msgID >= 0) && (msgID <= SizeOf(Strings)))
- {
- const char* ret = Strings[msgID];
- return ret ? ret : "";
- }
- return "";
-}
-
-// ****************************************************************************
-// EnableExceptionsOn64
-//
-
-// We want to learn about SEH exceptions even on x64 Windows 7 SP1 and later
-// http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/
-// http://connect.microsoft.com/VisualStudio/feedback/details/550944/hardware-exceptions-on-x64-machines-are-silently-caught-in-wndproc-messages
-// http://support.microsoft.com/kb/976038
-void EnableExceptionsOn64()
-{
- typedef BOOL(WINAPI * FSetProcessUserModeExceptionPolicy)(DWORD dwFlags);
- typedef BOOL(WINAPI * FGetProcessUserModeExceptionPolicy)(LPDWORD dwFlags);
- typedef BOOL(WINAPI * FIsWow64Process)(HANDLE, PBOOL);
-#define PROCESS_CALLBACK_FILTER_ENABLED 0x1
-
- HINSTANCE hDLL = LoadLibrary("KERNEL32.DLL");
- if (hDLL != NULL)
- {
- FIsWow64Process isWow64 = (FIsWow64Process)GetProcAddress(hDLL, "IsWow64Process"); // Min: XP SP2
- FSetProcessUserModeExceptionPolicy set = (FSetProcessUserModeExceptionPolicy)GetProcAddress(hDLL, "SetProcessUserModeExceptionPolicy"); // Min: Vista with hotfix
- FGetProcessUserModeExceptionPolicy get = (FGetProcessUserModeExceptionPolicy)GetProcAddress(hDLL, "GetProcessUserModeExceptionPolicy"); // Min: Vista with hotfix
- if (isWow64 != NULL && set != NULL && get != NULL)
- {
- BOOL bIsWow64;
- if (isWow64(GetCurrentProcess(), &bIsWow64) && bIsWow64)
- {
- DWORD dwFlags;
- if (get(&dwFlags))
- set(dwFlags & ~PROCESS_CALLBACK_FILTER_ENABLED);
- }
- }
- FreeLibrary(hDLL);
- }
-}
-
-#ifdef _CONSOLE
-int _tmain(int argc, TCHAR* argv[])
-#else
-int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPTSTR lpCmdLine, int /*nCmdShow*/)
-#endif
-{
- DWORD CheckThreadID;
- HANDLE hCheckThread;
- HANDLE hEvent;
- TCHAR eventName[32];
- MSG msg;
-
- EnableExceptionsOn64();
-
- // I do not want any critical errors such as "no disk in drive A:"
- SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS);
-
-#ifdef _CONSOLE
- if (argc != 2)
- {
-#else
- if (*lpCmdLine == 0)
- {
-#endif
- return -1;
- }
- /* printf("Cmdline arg #: %d\n", argc);
- for (int i = 0; i < argc; i++)
- printf("Cmdline arg: %s\n", argv[i]);*/
-
-#ifdef _CONSOLE
- _tcsncpy_s(SalamanderMutex, argv[1], _TRUNCATE);
-#else
- _tcsncpy_s(SalamanderMutex, lpCmdLine, _TRUNCATE);
-#endif
-
- // PVSetParam unlocks PVW32Cnv.dll for Salamander and install custom text provider
- memset(Strings, 0, sizeof(Strings));
- PVSetParam(GetExtTextProc);
-
- MainThreadID = GetCurrentThreadId();
-
- // Make thread to check whether Salamander is still alive
- hCheckThread = CreateThread(NULL, 0, CheckThreadProc, NULL, 0, &CheckThreadID);
-
- // Main message loop
- while (GetMessage(&msg, NULL, 0, 0) > 0)
- {
- switch (msg.message)
- {
- case WM_QUIT:
- return 0;
-
- case WM_USER:
-#ifdef _CONSOLE
- printf("Received WM_USER\n");
-#endif
-
- _sntprintf(eventName, SizeOf(eventName), _T("%s_ev%x"), SalamanderMutex, msg.lParam);
- hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, eventName);
- _ASSERTE(hEvent);
- PVMessage::HandleMessage(msg.wParam, msg.lParam, hEvent);
-
- SetEvent(hEvent);
- CloseHandle(hEvent);
- break;
-
- case WM_USER + 1:
- // Handle messages that don't need to send response, e.g. PVMSG_CloseHandle
-#ifdef _CONSOLE
- printf("Received WM_USER+1\n");
-#endif
- PVMessage::HandlePostedMessage(msg.wParam, msg.lParam);
- break;
-
- default:
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
-
- // This should be done when the application quits
- if (StringsBuf)
- {
- free(StringsBuf);
- }
- Thumbnailer.Clear(-1);
- return 0;
-}
diff --git a/src/plugins/pictview/PVMessage.cpp b/src/plugins/pictview/PVMessage.cpp
deleted file mode 100644
index e7e980f14..000000000
--- a/src/plugins/pictview/PVMessage.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-// SPDX-FileCopyrightText: 2023 Open Salamander Authors
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "precomp.h"
-
-#include "PVMessage.h"
-
-PVMessage::PVMessage() : pData(NULL), hFileMap(NULL), pWImg(NULL)
-{
-}
-
-PVMessage::~PVMessage()
-{
- // NOTE: ownership of pData not taken when hFileMap is NULL
- if (hFileMap)
- {
- if (pData)
- UnmapViewOfFile(pData);
- CloseHandle(hFileMap);
- }
-}
-
-LPBYTE PVMessage::GetData()
-{
- return (LPBYTE)pData;
-}
-
-PVCODE PVMessage::GetResultCode()
-{
- if (pData)
- {
- return ((LPPVMessageHeader)pData)->ResultCode;
- }
- return PVC_ENVELOPE_NOT_LOADED;
-}
diff --git a/src/plugins/pictview/PVMessage.h b/src/plugins/pictview/PVMessage.h
deleted file mode 100644
index 262a8bc9b..000000000
--- a/src/plugins/pictview/PVMessage.h
+++ /dev/null
@@ -1,532 +0,0 @@
-// SPDX-FileCopyrightText: 2023 Open Salamander Authors
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "lib/pvw32dll.h"
-
-#define PVC_ENVELOPE_NOT_LOADED ((PVCODE) - 123)
-
-struct CPVWrapper
-{
- TCHAR EnvelopePath[_MAX_PATH];
- TCHAR CommandLine[128];
- PROCESS_INFORMATION pi; // Remote PV Envelope process
- TCHAR MutexName[32]; // Name of the mutex hMutex
- HANDLE hMutex; // Used to signal to the Envelope we are still alive to prevent zombies when AS dies
- DWORD MessageID; // Each message sent to the Envelope has its unique ID
-};
-
-extern CPVWrapper PVWrapper;
-
-// PVMessage sends a single message to the PV Envelope process and waits for the answer.
-// It is for one time use, instead of using single instance for the entire lifetime of AS.
-// of AS because most PV functions are thread-safe.
-// FIXME: ensure each PVMessage request or LPPVHandle indeed creates extra thread in the remote process.
-class PVMessage
-{
-protected:
- enum ePVMSG
- {
- PVMSG_GetDLLVersion,
- PVMSG_CloseImage,
- PVMSG_DrawImage,
- PVMSG_GetErrorText,
- PVMSG_SetBkHandle,
- PVMSG_SetStretchParameters,
- PVMSG_ChangeImage,
- PVMSG_IsOutCombSupported,
- PVMSG_CropImage,
- PVMSG_OpenImageEx,
- PVMSG_GetImageInfo,
- PVMSG_ReadImage,
- PVMSG_ReadImageWithoutProgress,
- PVMSG_SaveImage,
- PVMSG_SaveImageWithoutProgress,
- PVMSG_LoadFromClipboard,
- PVMSG_ReadImageSequence,
- PVMSG_GetRGBAtCursor,
- PVMSG_CalculateHistogram,
- PVMSG_CreateThumbnail,
- PVMSG_SimplifyImageSequence,
- // Internal messages for communication SPL -> Envelope, not stubbing PVW32Cnv.dll
- PVMSG_InitTexts,
- PVMSG_CloseHandle
- };
-
- typedef struct PVMessageHeader
- { // Header at the beginning of pData
- DWORD cbSize; // Including this header
- DWORD Type;
- DWORD PVHandle;
- PVCODE ResultCode;
- } PVMessageHeader, *LPPVMessageHeader;
-
-public:
- PVMessage(ePVMSG type, size_t dataSize, LPPVHandle pvHandle = NULL);
- PVMessage();
- PVMessage(LPPVMessageHeader pHdr);
-
- virtual ~PVMessage();
-
- static bool HandleMessage(DWORD dataSize, DWORD id, HANDLE hEvent);
- static bool HandlePostedMessage(DWORD message, DWORD data);
-
- bool IsInited();
- bool Exec();
- virtual bool HandleRequest() { return false; }
- LPBYTE GetData(); // Returns pointer to data shared with the Envelope process
- PVCODE GetResultCode();
- LPPVHandle GetPVHandle();
- class PVWrapperImageHandle* GetWImg() { return pWImg; }
-
-protected:
- HANDLE hFileMap;
- struct PVMessageHeader* pData; // Obtained via MapViewOfFile(hFileMap)
- DWORD iID;
- class PVWrapperImageHandle* pWImg;
-
- typedef struct PVImageInfoStruct
- {
- DWORD cbSize;
- DWORD Width, Height;
- DWORD BytesPerLine;
- DWORD FileSize;
- DWORD Colors;
- DWORD Format;
- DWORD Flags;
- DWORD ColorModel;
- DWORD NumOfImages;
- DWORD CurrentImage;
- char Info1[PV_MAX_INFO_LEN], Info2[PV_MAX_INFO_LEN], Info3[PV_MAX_INFO_LEN];
- DWORD StretchedWidth;
- DWORD StretchedHeight;
- DWORD StretchMode;
- DWORD HorDPI;
- DWORD VerDPI;
- bool bFSI; // True when FSI is filled and valid
- PVFormatSpecificInfo FSI;
- DWORD Compression;
- DWORD TotalBitDepth;
- DWORD CommentSize;
- char Comment[256];
- } PVImageInfoStruct, *LPPVImageInfoStruct;
-
- bool Init(ePVMSG type, size_t dataSize, LPPVHandle pvHandle);
-
- bool MarshalImageInfo(LPPVImageInfo pInInfo, LPPVImageInfoStruct pOutInfo);
- bool UnmarshalImageInfo(LPPVImageInfoStruct pInInfo, LPPVImageInfo pOutInfo);
-
- static PVMessage* GetPVMessage(LPPVMessageHeader pHdr, HANDLE hEvent);
-};
-
-// Internal message used to feed translated strings from Salamander.exe to Enveloper
-class PVMessage_InitTexts : public PVMessage
-{
-public:
- PVMessage_InitTexts();
- PVMessage_InitTexts(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool HandleRequest();
-
-private:
- typedef struct PVMessageInitTexts : PVMessageHeader
- {
- int TextsCount;
- char Texts[1];
- } PVMessageInitTexts, *LPPVMessageInitTexts;
-};
-
-class PVMessage_GetErrorText : public PVMessage
-{
-public:
- PVMessage_GetErrorText(DWORD ErrorCode);
- PVMessage_GetErrorText(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool HandleRequest();
- const char* GetErrorText();
-
-private:
- typedef struct PVMessageGetErrorText : PVMessageHeader
- {
- DWORD ErrorCode;
- char ErrorText[512];
- } PVMessageGetErrorText, *LPPVMessageGetErrorText;
-};
-
-class PVMessage_OpenImageEx : public PVMessage
-{
-public:
- PVMessage_OpenImageEx(LPPVOpenImageExInfo pOpenExInfo);
- PVMessage_OpenImageEx(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool IsInited();
- bool Exec(LPPVImageInfo pImgInfo);
- bool HandleRequest();
-
-private:
- typedef struct PVMessageOpenImageEx : PVMessageHeader
- {
- // PVOpenImageExInfo
- DWORD Flags;
- char FileName[260]; // Contains FileMap name when Flags contains PVOF_ATTACH_TO_HANDLE
- DWORD DataSize; // Contains size of FileMap data when Flags contains PVOF_ATTACH_TO_HANDLE
- // PVImageInfo
- PVImageInfoStruct ImageInfo;
- } PVMessageOpenImageEx, *LPPVMessageOpenImageEx;
-};
-
-class PVMessage_GetImageInfo : public PVMessage
-{
-public:
- PVMessage_GetImageInfo(LPPVHandle pvHandle, int ImageIndex);
- PVMessage_GetImageInfo(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool Exec(LPPVImageInfo pImgInfo);
- bool HandleRequest();
-
-private:
- typedef struct PVMessageGetImageInfo : PVMessageHeader
- {
- int ImageIndex;
- PVImageInfoStruct ImageInfo;
- } PVMessageGetImageInfo, *LPPVMessageGetImageInfo;
-};
-
-class PVMessageWithProgress : public PVMessage
-{
-public:
- PVMessageWithProgress(ePVMSG type, size_t dataSize, LPPVHandle pvHandle = NULL);
- PVMessageWithProgress(LPPVMessageHeader pHdr, HANDLE Event) : PVMessage(pHdr), hEvent(Event) {};
- bool Exec(TProgressProc Progress, void* AppSpecific);
- BOOL HandleProgress(int done);
-
-protected:
- typedef enum PVState_ReadImage
- {
- PVState_Started,
- PVState_Progressing,
- PVState_Cancelled,
- PVState_Finished
- } PVState_ReadImage;
- typedef struct PVMessageWithProgressHeader : PVMessageHeader
- {
- LONG State; // typecast to PVState_ReadImage; LONG used for InterlockedExchange
- int ProgressValue;
- } PVMessageWithProgressHeader, *LPPVMessageWithProgressHeader;
- HANDLE hEvent;
-};
-
-class PVMessage_ReadImage : public PVMessageWithProgress
-{
-public:
- PVMessage_ReadImage(LPPVHandle pvHandle, int ImageIndex, bool bProgress);
- PVMessage_ReadImage(LPPVMessageHeader pHdr, HANDLE hEvent) : PVMessageWithProgress(pHdr, hEvent) {};
- bool Exec(HDC PaintDC, RECT* pDRect, TProgressProc Progress, void* AppSpecific);
- bool HandleRequest();
-
-private:
- typedef struct PVMessageReadImage : PVMessageWithProgressHeader
- {
- int ImageIndex;
- } PVMessageReadImage, *LPPVMessageReadImage;
-};
-
-class PVMessage_CloseImage : public PVMessage
-{
-public:
- PVMessage_CloseImage(LPPVHandle pvHandle);
- PVMessage_CloseImage(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- ~PVMessage_CloseImage();
- bool HandleRequest();
-};
-
-class PVMessage_DrawImage : public PVMessage
-{
-public:
- PVMessage_DrawImage(LPPVHandle pvHandle, HDC PaintDC, int X, int Y, LPRECT pDrawRect);
- PVMessage_DrawImage(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool Exec(HDC PaintDC);
- bool HandleRequest();
-
-private:
- typedef struct PVMessageDrawImage : PVMessageHeader
- {
- DWORD BitsPerPixel;
- DWORD X, Y;
- RECT DrawRect;
- char SharedMemoryName[16];
- DWORD InternalFileMapHandle; // Envelope's handle to the shared memory object
- } PVMessageDrawImage, *LPPVMessageDrawImage;
-};
-
-class PVMessage_SaveImage : public PVMessageWithProgress
-{
-public:
- PVMessage_SaveImage(LPPVHandle pvHandle, const char* FileName, LPPVSaveImageInfo pSii, int ImageIndex, bool bProgress);
- PVMessage_SaveImage(LPPVMessageHeader pHdr, HANDLE hEvent) : PVMessageWithProgress(pHdr, hEvent) {};
- bool HandleRequest();
-
-private:
- typedef struct PVMessageSaveImage : PVMessageWithProgressHeader
- {
- DWORD cbSize;
- DWORD Format;
- DWORD Compression;
- DWORD Colors;
- DWORD ColorModel;
- DWORD Flags;
- DWORD Width; /* No scaling applied if Width or Height is zero */
- DWORD Height;
- DWORD HorDPI;
- DWORD VerDPI;
- DWORD CommentSize;
- char Comment[256];
- char FormatSpecificInfo[256];
- DWORD CropLeft; /* Cropping applied before scaling */
- DWORD CropTop;
- DWORD CropWidth; /* Crop size */
- DWORD CropHeight; /* No cropping applied if CropWidth or CropHeight is zero */
- struct
- {
- BYTE Flags; /* PVTF_XXX flags */
- union
- {
- BYTE Index; /* Used only when Flags is PVTF_INDEX */
- struct
- {
- BYTE Red, Green, Blue; /* Used only when Flags is PVTF_RGB */
- } RGB;
- } Value;
- } Transp;
- int ImageIndex;
- // When Flags contains PVSF_USERDEFINED_OUTPUT, FileName contains shared memory name,
- // its size is in OutFileMapSize and InternalFileMapHandle contains Envelope's HANDLE to it.
- char FileName[260];
- DWORD OutFileMapSize;
- DWORD InternalFileMapHandle;
- } PVMessageSaveImage, *LPPVMessageSaveImage;
- friend PVCODE WINAPI PVSaveImageStub(LPPVHandle, const char*, LPPVSaveImageInfo, TProgressProc, void*, int);
-};
-
-class PVMessage_LoadFromClipboard : public PVMessage
-{
-public:
- PVMessage_LoadFromClipboard();
- PVMessage_LoadFromClipboard(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool Exec(LPPVImageInfo pImgInfo);
- bool HandleRequest();
-
-private:
- typedef struct PVMessageLoadFromClipboard : PVMessageHeader
- {
- PVImageInfoStruct ImageInfo;
- } PVMessageLoadFromClipboard, *LPPVMessageLoadFromClipboard;
-};
-
-class PVMessage_ReadImageSequence : public PVMessage
-{
-public:
- PVMessage_ReadImageSequence(LPPVHandle pvHandle);
- PVMessage_ReadImageSequence(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool Exec();
- bool HandleRequest();
-
-private:
- typedef struct PVMessageReadImageSequence : PVMessageHeader
- {
- DWORD NumberOfFrames;
- } PVMessageReadImageSequence, *LPPVMessageReadImageSequence;
-};
-
-class PVMessage_SetBkHandle : public PVMessage
-{
-public:
- PVMessage_SetBkHandle(LPPVHandle pvHandle, COLORREF bkColor);
- PVMessage_SetBkHandle(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool HandleRequest();
-
-private:
- typedef struct PVMessageSetBkHandle : PVMessageHeader
- {
- COLORREF BackgroundColor;
- } PVMessageSetBkHandle, *LPPVMessageSetBkHandle;
-};
-
-class PVMessage_GetDLLVersion : public PVMessage
-{
-public:
- PVMessage_GetDLLVersion();
- PVMessage_GetDLLVersion(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool Exec(DWORD& Version);
- bool HandleRequest();
-
-private:
- typedef struct PVMessageGetDLLVersion : PVMessageHeader
- {
- DWORD DLLVersion;
- } PVMessageGetDLLVersion, *LPPVMessageGetDLLVersion;
-};
-
-class PVMessage_SetStretchParameters : public PVMessage
-{
-public:
- PVMessage_SetStretchParameters(LPPVHandle pvHandle, DWORD Width, DWORD Height, DWORD Mode);
- PVMessage_SetStretchParameters(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool HandleRequest();
-
-private:
- typedef struct PVMessageSetStretchParameters : PVMessageHeader
- {
- DWORD Width;
- DWORD Height;
- DWORD Mode;
- } PVMessageSetStretchParameters, *LPPVMessageSetStretchParameters;
-};
-
-class PVMessage_ChangeImage : public PVMessage
-{
-public:
- PVMessage_ChangeImage(LPPVHandle pvHandle, DWORD Flags);
- PVMessage_ChangeImage(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool HandleRequest();
-
-private:
- typedef struct PVMessageChangeImage : PVMessageHeader
- {
- DWORD Flags;
- } PVMessageChangeImage, *LPPVMessageChangeImage;
-};
-
-class PVMessage_IsOutCombSupported : public PVMessage
-{
-public:
- PVMessage_IsOutCombSupported(int Fmt, int Compr, int Colors, int ColorModel);
- PVMessage_IsOutCombSupported(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool HandleRequest();
-
-private:
- typedef struct PVMessageIsOutCombSupported : PVMessageHeader
- {
- int Fmt;
- int Compr;
- int Colors;
- int ColorModel;
- } PVMessageIsOutCombSupported, *LPPVMessageIsOutCombSupported;
-};
-
-class PVMessage_CropImage : public PVMessage
-{
-public:
- PVMessage_CropImage(LPPVHandle pvHandle, int Left, int Top, int Width, int Height);
- PVMessage_CropImage(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool HandleRequest();
-
-private:
- typedef struct PVMessageCropImage : PVMessageHeader
- {
- int Left;
- int Top;
- int Width;
- int Height;
- } PVMessageCropImage, *LPPVMessageCropImage;
-};
-
-class PVMessage_GetRGBAtCursor : public PVMessage
-{
-public:
- PVMessage_GetRGBAtCursor(LPPVHandle pvHandle, DWORD Colors, int x, int y);
- PVMessage_GetRGBAtCursor(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool HandleRequest();
-
- RGBQUAD* GetRGB();
- int GetIndex();
-
-private:
- typedef struct PVMessageGetRGBAtCursor : PVMessageHeader
- {
- DWORD Colors; // Helper to speed things a little up
- int X;
- int Y;
- RGBQUAD RGB;
- int Index;
- } PVMessageGetRGBAtCursor, *LPPVMessageGetRGBAtCursor;
-};
-
-class PVMessage_CalculateHistogram : public PVMessage
-{
-public:
- PVMessage_CalculateHistogram(LPPVHandle pvHandle, const PVImageInfo* pvii);
- PVMessage_CalculateHistogram(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool HandleRequest();
-
- void GetResults(LPDWORD luminosity, LPDWORD red, LPDWORD green, LPDWORD blue, LPDWORD rgb);
-
-private:
- typedef struct PVMessageCalculateHistogram : PVMessageHeader
- {
- DWORD Colors; // Helper to speed things a little up
- DWORD Width;
- DWORD Height;
- DWORD BytesPerLine;
- DWORD luminosity[256];
- DWORD red[256];
- DWORD green[256];
- DWORD blue[256];
- DWORD rgb[256];
- } PVMessageCalculateHistogram, *LPPVMessageCalculateHistogram;
-};
-
-class PVMessage_CreateThumbnail : public PVMessageWithProgress
-{
-public:
- PVMessage_CreateThumbnail(LPPVHandle pvHandle, LPPVSaveImageInfo pSii, int ImageIndex, DWORD imgWidth, DWORD imgHeight, DWORD thumbWidth, DWORD thumbHeight);
- PVMessage_CreateThumbnail(LPPVMessageHeader pHdr, HANDLE hEvent) : PVMessageWithProgress(pHdr, hEvent) {};
- bool HandleRequest();
- LPBYTE GetPixelData();
-
-private:
- typedef struct PVMessageCreateThumbnail : PVMessageWithProgressHeader
- {
- DWORD ThumbWidth; // Target thumbnail width
- DWORD ThumbHeight; // Target thumbnail height
- DWORD ImageWidth; // Original image width
- DWORD ImageHeight; // Original image height
- DWORD Flags;
- int ImageIndex;
- BYTE Buffer[1];
- } PVMessageCreateThumbnail, *LPPVMessageCreateThumbnail;
-};
-
-class PVMessage_SimplifyImageSequence : public PVMessage
-{
-public:
- PVMessage_SimplifyImageSequence(LPPVHandle pvHandle, DWORD ScreenWidth, DWORD ScreenHeight, const COLORREF& bgColor);
- PVMessage_SimplifyImageSequence(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool Exec();
- bool HandleRequest();
- LPPVImageSequence GetImageSequence();
-
-private:
- typedef struct PVMessageSimplifyImageSequence : PVMessageHeader
- {
- DWORD ScreenWidth;
- DWORD ScreenHeight;
- DWORD BitsPerPixel;
- COLORREF BackgroundColor;
- char SharedMemoryName[16];
- DWORD InternalFileMapHandle; // Envelope's handle to the shared memory object
- DWORD Delay[1]; // Size of the array is NumberOfFrames
- } PVMessageSimplifyImageSequence, *LPPVMessageSimplifyImageSequence;
-};
-
-// Internal message, used to return Win32 HANDLE to Envelope to be closed as no longer used
-class PVMessage_CloseHandle : public PVMessage
-{
-public:
- PVMessage_CloseHandle(DWORD Handle);
- PVMessage_CloseHandle(LPPVMessageHeader pHdr) : PVMessage(pHdr) {};
- bool Exec();
- bool HandleRequest();
-
-private:
- DWORD HandleToBeClosed;
-};
-
-bool LoadEnvelope();
diff --git a/src/plugins/pictview/PVMessageEnvelope.cpp b/src/plugins/pictview/PVMessageEnvelope.cpp
deleted file mode 100644
index 292d8c77e..000000000
--- a/src/plugins/pictview/PVMessageEnvelope.cpp
+++ /dev/null
@@ -1,971 +0,0 @@
-// SPDX-FileCopyrightText: 2023 Open Salamander Authors
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "precomp.h"
-
-#include "PVMessage.h"
-#include "PixelAccess.h"
-#include "Thumbnailer.h"
-
-class PVWrapperImageHandle
-{
-public:
- PVWrapperImageHandle();
- PVWrapperImageHandle(LPBYTE aBuffer, DWORD aSize);
- ~PVWrapperImageHandle();
- void FreeSharedMemory();
- void FreeBuffer();
-
- LPPVHandle PVHandle;
- LPPVImageSequence PVImageSequence;
- // The following 4 items are used when transfering file via shared memory
- HANDLE hFileMap;
- LPBYTE pBuffer; // Buffer with file content
- LPBYTE pCur; // Current position inside pBuffer
- DWORD Size; // Size of pBuffer
-};
-
-struct PVThumbnailerInfo
-{
- CSalamanderThumbnailMaker* pThumbnailer;
- DWORD BytesPerLine;
- PVMessageWithProgress* pMsg;
-};
-
-typedef PVWrapperImageHandle* LPPVWrapperImageHandle;
-
-extern TCHAR SalamanderMutex[64];
-extern DWORD MainThreadID;
-
-const char* Strings[450];
-char* StringsBuf;
-CSalamanderThumbnailMaker Thumbnailer;
-
-static bool CreateSharedMemoryForBuffer(LPBYTE pInBuffer, DWORD dataSize, char* fileMapName, DWORD& outFileMap);
-
-PVMessage::PVMessage(LPPVMessageHeader pHdr) : pData(pHdr), hFileMap(NULL), pWImg((LPPVWrapperImageHandle)(pHdr->PVHandle))
-{
-}
-
-bool PVMessage::MarshalImageInfo(LPPVImageInfo pInInfo, LPPVImageInfoStruct pOutInfo)
-{
- pOutInfo->Width = pInInfo->Width;
- pOutInfo->Height = pInInfo->Height;
- pOutInfo->BytesPerLine = pInInfo->BytesPerLine;
- pOutInfo->FileSize = pInInfo->FileSize;
- pOutInfo->Colors = pInInfo->Colors;
- pOutInfo->Format = pInInfo->Format;
- pOutInfo->Flags = pInInfo->Flags;
- pOutInfo->ColorModel = pInInfo->ColorModel;
- pOutInfo->NumOfImages = pInInfo->NumOfImages;
- pOutInfo->CurrentImage = pInInfo->CurrentImage;
- // Quick fix to avoid asserts because of unterminated (although correctly clipped) strings in strcpy below translated to strcpy_s
- pInInfo->Info1[sizeof(pInInfo->Info1) - 1] = 0;
- pInInfo->Info2[sizeof(pInInfo->Info2) - 1] = 0;
- pInInfo->Info3[sizeof(pInInfo->Info3) - 1] = 0;
- strcpy(pOutInfo->Info1, pInInfo->Info1);
- strcpy(pOutInfo->Info2, pInInfo->Info2);
- strcpy(pOutInfo->Info3, pInInfo->Info3);
- pOutInfo->StretchedWidth = pInInfo->StretchedWidth;
- pOutInfo->StretchedHeight = pInInfo->StretchedHeight;
- pOutInfo->StretchMode = pInInfo->StretchMode;
- pOutInfo->HorDPI = pInInfo->HorDPI;
- pOutInfo->VerDPI = pInInfo->VerDPI;
- if (pInInfo->FSI)
- {
- pOutInfo->bFSI = true;
- memcpy(&pOutInfo->FSI, pInInfo->FSI, sizeof(PVFormatSpecificInfo));
- }
- else
- {
- pOutInfo->bFSI = false;
- }
- pOutInfo->Compression = pInInfo->Compression;
- pOutInfo->TotalBitDepth = pInInfo->TotalBitDepth;
- pOutInfo->CommentSize = min(sizeof(pOutInfo->Comment), pInInfo->CommentSize);
- memcpy(pOutInfo->Comment, pInInfo->Comment, pOutInfo->CommentSize);
- return true;
-}
-
-PVMessage* PVMessage::GetPVMessage(LPPVMessageHeader pHdr, HANDLE hEvent)
-{
- switch (pHdr->Type)
- {
- case PVMSG_GetDLLVersion:
- return new PVMessage_GetDLLVersion(pHdr);
- case PVMSG_CloseImage:
- return new PVMessage_CloseImage(pHdr);
- case PVMSG_DrawImage:
- return new PVMessage_DrawImage(pHdr);
- case PVMSG_GetErrorText:
- return new PVMessage_GetErrorText(pHdr);
- case PVMSG_SetBkHandle:
- return new PVMessage_SetBkHandle(pHdr);
- case PVMSG_SetStretchParameters:
- return new PVMessage_SetStretchParameters(pHdr);
- case PVMSG_ChangeImage:
- return new PVMessage_ChangeImage(pHdr);
- case PVMSG_IsOutCombSupported:
- return new PVMessage_IsOutCombSupported(pHdr);
- case PVMSG_CropImage:
- return new PVMessage_CropImage(pHdr);
- case PVMSG_OpenImageEx:
- return new PVMessage_OpenImageEx(pHdr);
- case PVMSG_GetImageInfo:
- return new PVMessage_GetImageInfo(pHdr);
- case PVMSG_ReadImage:
- case PVMSG_ReadImageWithoutProgress:
- return new PVMessage_ReadImage(pHdr, hEvent);
- case PVMSG_SaveImage:
- case PVMSG_SaveImageWithoutProgress:
- return new PVMessage_SaveImage(pHdr, hEvent);
- case PVMSG_LoadFromClipboard:
- return new PVMessage_LoadFromClipboard(pHdr);
- case PVMSG_ReadImageSequence:
- return new PVMessage_ReadImageSequence(pHdr);
- case PVMSG_GetRGBAtCursor:
- return new PVMessage_GetRGBAtCursor(pHdr);
- case PVMSG_CalculateHistogram:
- return new PVMessage_CalculateHistogram(pHdr);
- case PVMSG_CreateThumbnail:
- return new PVMessage_CreateThumbnail(pHdr, hEvent);
- case PVMSG_SimplifyImageSequence:
- return new PVMessage_SimplifyImageSequence(pHdr);
- case PVMSG_InitTexts:
- return new PVMessage_InitTexts(pHdr);
- default:
- // Unsupported/unknown message
- _ASSERTE(pHdr->Type == PVMSG_InitTexts);
- return NULL;
- }
-}
-
-bool PVMessage::HandleMessage(DWORD dataSize, DWORD id, HANDLE hEvent)
-{
- TCHAR fileMapName[48];
- LPPVMessageHeader pHdr;
- HANDLE hFileMap;
-
- _sntprintf(fileMapName, SizeOf(fileMapName), _T("%s_%d"), SalamanderMutex, id);
- hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
- 0, dataSize, fileMapName);
- if (!hFileMap)
- {
- return false;
- }
- pHdr = (LPPVMessageHeader)MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, dataSize);
- if (!pHdr)
- {
- CloseHandle(hFileMap);
- return false;
- }
- _ASSERTE(pHdr->cbSize == dataSize);
- PVMessage* pMessage = GetPVMessage(pHdr, hEvent);
- if (pMessage)
- {
- pMessage->HandleRequest();
- delete pMessage;
- }
-
- UnmapViewOfFile(pHdr);
- CloseHandle(hFileMap);
-
- return true;
-}
-
-bool PVMessage::HandlePostedMessage(DWORD message, DWORD data)
-{
- switch (message)
- {
- case PVMSG_CloseHandle:
- CloseHandle((HANDLE)data);
- break;
- default:
- return false;
- }
- return true;
-}
-
-BOOL PVMessageWithProgress::HandleProgress(int done)
-{
- LPPVMessageWithProgressHeader pHdr = (LPPVMessageWithProgressHeader)pData;
- pHdr->ProgressValue = done;
- PulseEvent(hEvent);
- return pHdr->State == PVState_Cancelled;
-}
-
-bool PVMessage_InitTexts::HandleRequest()
-{
- LPPVMessageInitTexts pHdr = (LPPVMessageInitTexts)pData;
-
- if (!pHdr)
- {
- return false;
- }
- // This message should be sent just once....
- _ASSERTE(!StringsBuf);
- if (StringsBuf)
- {
- return false;
- }
- // Duplioate the entire buffer
- int size = pHdr->cbSize - sizeof(PVMessage) - sizeof(pHdr->TextsCount);
- StringsBuf = (char*)malloc(size);
- if (!StringsBuf)
- {
- return false;
- }
- memcpy(StringsBuf, pHdr->Texts, size);
- // Init strings pointers to point inside the buffer
- const char* str = StringsBuf;
- for (int i = 0; i < min((int)(SizeOf(Strings)), pHdr->TextsCount); i++)
- {
- Strings[i] = str;
- str += strlen(str) + 1;
- }
- pHdr->ResultCode = PVC_OK;
- return true;
-}
-
-bool PVMessage_GetErrorText::HandleRequest()
-{
- LPPVMessageGetErrorText pHdr = (LPPVMessageGetErrorText)pData;
-
- if (!pHdr)
- {
- return false;
- }
- pHdr->ResultCode = PVC_OK;
- strncpy_s(pHdr->ErrorText, PVGetErrorText(pHdr->ErrorCode), _TRUNCATE);
- return true;
-}
-
-DWORD WINAPI MySeekFunc(void* AppSpecific, LONG NewPos, int Origin)
-{
- LPPVWrapperImageHandle pReadInfo = (LPPVWrapperImageHandle)AppSpecific;
-
- switch (Origin)
- {
- case FILE_BEGIN:
- pReadInfo->pCur = pReadInfo->pBuffer + min((DWORD)NewPos, pReadInfo->Size);
- break;
- case FILE_CURRENT:
- pReadInfo->pCur += min((DWORD)NewPos, pReadInfo->Size - (pReadInfo->pCur - pReadInfo->pBuffer));
- break;
- case FILE_END:
- pReadInfo->pCur = pReadInfo->pBuffer + pReadInfo->Size - min((DWORD)NewPos, pReadInfo->Size);
- break;
- }
- return pReadInfo->pCur - pReadInfo->pBuffer;
-}
-
-DWORD WINAPI MyMessageSeekFunc(void* AppSpecific, LONG NewPos, int Origin)
-{
- return MySeekFunc(((PVMessage*)AppSpecific)->GetWImg(), NewPos, Origin);
-}
-
-DWORD WINAPI MyDummySeekFunc(void* AppSpecific, LONG NewPos, int Origin)
-{
- return 0;
-}
-
-DWORD WINAPI MyReadFunc(void* AppSpecific, void* pData, DWORD Size)
-{
- LPPVWrapperImageHandle pReadInfo = (LPPVWrapperImageHandle)AppSpecific;
-
- DWORD ret = min(Size, pReadInfo->Size - (pReadInfo->pCur - pReadInfo->pBuffer));
- memcpy(pData, pReadInfo->pCur, ret);
- pReadInfo->pCur += ret;
- return ret;
-}
-
-DWORD WINAPI MyMessageReadFunc(void* AppSpecific, void* pData, DWORD Size)
-{
- return MyReadFunc(((PVMessage*)AppSpecific)->GetWImg(), pData, Size);
-}
-
-DWORD WINAPI MyWriteFunc(void* AppSpecific, void* pData, DWORD Size)
-{
- LPPVWrapperImageHandle pWriteInfo = (LPPVWrapperImageHandle)AppSpecific;
-
- DWORD ret = min(Size, pWriteInfo->Size - (pWriteInfo->pCur - pWriteInfo->pBuffer));
- memcpy(pWriteInfo->pCur, pData, ret);
- pWriteInfo->pCur += ret;
- return ret;
-}
-
-DWORD WINAPI MyAcumulateWriteFunc(void* AppSpecific, void* pData, DWORD Size)
-{
- PVMessage_SaveImage* pSIMessage = (PVMessage_SaveImage*)AppSpecific;
- LPPVWrapperImageHandle pWriteInfo = pSIMessage->GetWImg();
- DWORD pos = pWriteInfo->pCur - pWriteInfo->pBuffer;
-
- if (pos + Size > pWriteInfo->Size)
- {
- pWriteInfo->pBuffer = (LPBYTE)realloc(pWriteInfo->pBuffer, pos + Size);
- pWriteInfo->pCur = pWriteInfo->pBuffer + pos;
- }
- memcpy(pWriteInfo->pCur, pData, Size);
- pWriteInfo->pCur += Size;
- if ((int)pWriteInfo->Size < pWriteInfo->pCur - pWriteInfo->pBuffer)
- {
- pWriteInfo->Size = pWriteInfo->pCur - pWriteInfo->pBuffer;
- }
- return Size;
-}
-
-DWORD WINAPI MyThumbWriteFunc(void* AppSpecific, void* pData, DWORD Size)
-{
- PVThumbnailerInfo* pThumbInfo = (PVThumbnailerInfo*)AppSpecific;
-
- if (pThumbInfo->pMsg->HandleProgress(0))
- return 0;
- return pThumbInfo->pThumbnailer->ProcessBuffer(pData, Size / pThumbInfo->BytesPerLine) * Size;
-}
-
-BOOL WINAPI ThumbProgressProc(int done, void* data)
-{
- PVThumbnailerInfo* pThumbInfo = (PVThumbnailerInfo*)data;
-
- if (pThumbInfo)
- {
- return pThumbInfo->pMsg->HandleProgress(done);
- }
- return FALSE;
-}
-
-bool PVMessage_OpenImageEx::HandleRequest()
-{
- PVOpenImageExInfo OpenImageInfo;
- PVImageInfo ImageInfo;
- LPPVMessageOpenImageEx pHdr = (LPPVMessageOpenImageEx)pData;
-
- if (!pHdr)
- {
- return false;
- }
-
- pWImg = new PVWrapperImageHandle();
- if (!pWImg)
- {
- pHdr->ResultCode = PVC_OOM;
- return true; // Message handled
- }
-
- memset(&OpenImageInfo, 0, sizeof(OpenImageInfo));
- OpenImageInfo.cbSize = sizeof(OpenImageInfo);
- OpenImageInfo.Flags = pHdr->Flags & ~(PVOF_ATTACH_TO_HANDLE | PVOF_USERDEFINED_INPUT);
- if (pHdr->Flags & (PVOF_ATTACH_TO_HANDLE | PVOF_USERDEFINED_INPUT))
- {
- pWImg->hFileMap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, pHdr->DataSize, pHdr->FileName);
- if (!pWImg->hFileMap)
- {
- pHdr->ResultCode = PVC_NO_FILES_FOUND;
- delete pWImg;
- pWImg = NULL;
- return true; // Message handled
- }
- pWImg->pBuffer = pWImg->pCur = (LPBYTE)MapViewOfFile(pWImg->hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, pHdr->DataSize);
- OpenImageInfo.DataSize = pWImg->Size = pHdr->DataSize;
- OpenImageInfo.Handle = pWImg;
- OpenImageInfo.ReadFunc = MyReadFunc;
- OpenImageInfo.SeekFunc = MySeekFunc;
- OpenImageInfo.Flags |= PVOF_USERDEFINED_INPUT;
- }
- else
- {
- OpenImageInfo.FileName = pHdr->FileName;
- }
- pHdr->ResultCode = PVOpenImageEx(&pWImg->PVHandle, &OpenImageInfo, &ImageInfo, sizeof(ImageInfo));
- if (pHdr->ResultCode == PVC_OK)
- {
- MarshalImageInfo(&ImageInfo, &pHdr->ImageInfo);
- pHdr->PVHandle = (DWORD)pWImg;
- }
- else
- {
- delete pWImg;
- pWImg = NULL;
- }
- return true;
-}
-
-bool PVMessage_GetImageInfo::HandleRequest()
-{
- PVImageInfo ImageInfo;
- LPPVMessageGetImageInfo pHdr = (LPPVMessageGetImageInfo)pData;
-
- if (!pHdr)
- {
- return false;
- }
- pHdr->ResultCode = PVGetImageInfo(pWImg ? pWImg->PVHandle : NULL, &ImageInfo, sizeof(ImageInfo), pHdr->ImageIndex);
- if (pHdr->ResultCode == PVC_OK)
- {
- MarshalImageInfo(&ImageInfo, &pHdr->ImageInfo);
- }
- return true;
-}
-
-PVMessage_CloseImage::~PVMessage_CloseImage()
-{
- delete pWImg;
-}
-
-bool PVMessage_CloseImage::HandleRequest()
-{
- LPPVMessageHeader pHdr = (LPPVMessageHeader)pData;
-
- if (!pHdr)
- {
- return false;
- }
-
- pHdr->ResultCode = PVCloseImage(pWImg ? pWImg->PVHandle : NULL);
- return true;
-}
-
-bool PVMessage_DrawImage::HandleRequest()
-{
- LPPVMessageDrawImage pHdr = (LPPVMessageDrawImage)pData;
-
- if (!pHdr)
- {
- return false;
- }
-
- HDC hDC = GetDC(NULL), hMemDC;
- HBITMAP hOldBmp, hDIB;
- BITMAPINFO bi;
- LPVOID pvBits = NULL;
- DWORD hFMap;
- DWORD paintWidth = pHdr->DrawRect.right - pHdr->DrawRect.left;
- DWORD paintHeight = pHdr->DrawRect.bottom - pHdr->DrawRect.top;
- DWORD frameSize = (((paintWidth * pHdr->BitsPerPixel + 31) >> 3) & ~3) * paintHeight;
-
- if (!CreateSharedMemoryForBuffer(NULL, frameSize, pHdr->SharedMemoryName, hFMap))
- {
- pHdr->ResultCode = PVC_OOM;
- return true;
- }
- pHdr->InternalFileMapHandle = hFMap;
-
- memset(&bi, 0, sizeof(bi));
- bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
- bi.bmiHeader.biWidth = paintWidth;
- bi.bmiHeader.biHeight = paintHeight;
- bi.bmiHeader.biPlanes = 1;
- bi.bmiHeader.biBitCount = (WORD)pHdr->BitsPerPixel;
-
- hDIB = CreateDIBSection(hDC, &bi, DIB_RGB_COLORS, &pvBits, (HANDLE)hFMap, 0);
-
- hMemDC = CreateCompatibleDC(hDC);
- hOldBmp = (HBITMAP)SelectObject(hMemDC, hDIB);
- int X = (int)pHdr->X - pHdr->DrawRect.left;
- int Y = (int)pHdr->Y - pHdr->DrawRect.top;
- RECT rect;
- rect.right = paintWidth;
- rect.bottom = paintHeight;
- rect.left = rect.top = 0;
- pHdr->ResultCode = PVDrawImage(pWImg ? pWImg->PVHandle : NULL, hMemDC, X, Y, &rect);
- SelectObject(hMemDC, hOldBmp);
- DeleteObject(hDIB);
- DeleteDC(hMemDC);
-
- ReleaseDC(NULL, hDC);
- return true;
-}
-
-BOOL WINAPI ProgressProc(int done, void* data)
-{
- PVMessageWithProgress* pMsg = (PVMessageWithProgress*)data;
-
- if (pMsg)
- {
- return pMsg->HandleProgress(done);
- }
- return FALSE;
-}
-
-bool PVMessage_ReadImage::HandleRequest()
-{
- LPPVMessageReadImage pHdr = (LPPVMessageReadImage)pData;
-
- if (!pHdr)
- {
- return false;
- }
- pHdr->State = PVState_Progressing;
- pHdr->ProgressValue = 0;
- // FIXME: support drawing on-the-fly
- pHdr->ResultCode = PVReadImage2(pWImg ? pWImg->PVHandle : NULL, NULL, NULL,
- pHdr->Type == PVMSG_ReadImage ? ProgressProc : NULL, this, pHdr->ImageIndex);
- // Image in shared memory no longer needed -> free it
- if (pWImg)
- pWImg->FreeSharedMemory();
- InterlockedExchange(&pHdr->State, PVState_Finished);
- return true;
-}
-
-bool PVMessage_SaveImage::HandleRequest()
-{
- LPPVMessageSaveImage pHdr = (LPPVMessageSaveImage)pData;
- PVSaveImageInfo SaveImageInfo;
-
- if (!pHdr)
- {
- return false;
- }
-
- pHdr->State = PVState_Progressing;
- pHdr->ProgressValue = 0;
-
- memset(&SaveImageInfo, 0, sizeof(SaveImageInfo));
- SaveImageInfo.cbSize = sizeof(SaveImageInfo);
- SaveImageInfo.Format = pHdr->Format;
- SaveImageInfo.Compression = pHdr->Compression;
- SaveImageInfo.Colors = pHdr->Colors;
- SaveImageInfo.ColorModel = pHdr->ColorModel;
- SaveImageInfo.Flags = pHdr->Flags;
- SaveImageInfo.Width = pHdr->Width;
- SaveImageInfo.Height = pHdr->Height;
- SaveImageInfo.HorDPI = pHdr->HorDPI;
- SaveImageInfo.VerDPI = pHdr->VerDPI;
- _ASSERTE(sizeof(pHdr->FormatSpecificInfo) == sizeof(SaveImageInfo.Misc));
- memcpy(&SaveImageInfo.Misc, pHdr->FormatSpecificInfo, sizeof(SaveImageInfo.Misc));
- SaveImageInfo.CropLeft = pHdr->CropLeft;
- SaveImageInfo.CropTop = pHdr->CropTop;
- SaveImageInfo.CropWidth = pHdr->CropWidth;
- SaveImageInfo.CropHeight = pHdr->CropHeight;
- _ASSERTE(sizeof(pHdr->Transp) == sizeof(SaveImageInfo.Transp));
- memcpy(&SaveImageInfo.Transp, &pHdr->Transp, sizeof(SaveImageInfo.Transp));
- if (SaveImageInfo.CommentSize)
- {
- SaveImageInfo.CommentSize = pHdr->CommentSize;
- SaveImageInfo.Comment = pHdr->Comment;
- }
- if (SaveImageInfo.Flags & PVSF_USERDEFINED_OUTPUT)
- {
- SaveImageInfo.WriteFunc = MyAcumulateWriteFunc;
- SaveImageInfo.SeekFunc = MyMessageSeekFunc;
- SaveImageInfo.ReadFunc = MyMessageReadFunc;
- }
-
- pHdr->ResultCode = PVSaveImage(pWImg ? pWImg->PVHandle : NULL, !(SaveImageInfo.Flags & PVSF_USERDEFINED_OUTPUT) ? pHdr->FileName : NULL,
- &SaveImageInfo, pHdr->Type == PVMSG_SaveImage ? ProgressProc : NULL, this, pHdr->ImageIndex);
- InterlockedExchange(&pHdr->State, PVState_Finished);
- if ((pHdr->ResultCode == PVC_OK) && (SaveImageInfo.Flags & PVSF_USERDEFINED_OUTPUT))
- {
- // When the save succeeded and the are user-defined output funcs, we send the back in a shared memory
- pHdr->OutFileMapSize = pWImg->Size;
- bool ret = CreateSharedMemoryForBuffer(pWImg->pBuffer, pWImg->Size, pHdr->FileName, pHdr->InternalFileMapHandle);
- // The shared memory will be freed by the wrapper via the CloseHandle message
- pWImg->FreeBuffer();
- }
- return true;
-}
-
-bool PVMessage_LoadFromClipboard::HandleRequest()
-{
- PVImageInfo ImageInfo;
- LPPVMessageLoadFromClipboard pHdr = (LPPVMessageLoadFromClipboard)pData;
-
- if (!pHdr)
- {
- return false;
- }
- pWImg = new PVWrapperImageHandle();
- if (!pWImg)
- {
- pHdr->ResultCode = PVC_OOM;
- return true; // Message handled
- }
- pHdr->ResultCode = PVLoadFromClipboard(&pWImg->PVHandle, &ImageInfo, sizeof(ImageInfo));
- if (pHdr->ResultCode == PVC_OK)
- {
- MarshalImageInfo(&ImageInfo, &pHdr->ImageInfo);
- pHdr->PVHandle = (DWORD)pWImg;
- }
- else
- {
- delete pWImg;
- pWImg = NULL;
- }
- return true;
-}
-
-bool PVMessage_ReadImageSequence::HandleRequest()
-{
- LPPVMessageReadImageSequence pHdr = (LPPVMessageReadImageSequence)pData;
-
- if (!pHdr)
- {
- return false;
- }
-
- if (!pWImg)
- pHdr->ResultCode = PVC_INVALID_HANDLE;
- else
- {
- pHdr->ResultCode = PVReadImageSequence(pWImg->PVHandle, &pWImg->PVImageSequence);
- // Image in shared memory no longer needed -> free it
- pWImg->FreeSharedMemory();
- }
- if (PVC_OK == pHdr->ResultCode)
- {
- LPPVImageSequence pSeq = pWImg->PVImageSequence;
- pHdr->NumberOfFrames = 0;
- while (pSeq)
- {
- pSeq = pSeq->pNext;
- pHdr->NumberOfFrames++;
- }
- }
- return true;
-}
-
-bool PVMessage_SetBkHandle::HandleRequest()
-{
- LPPVMessageSetBkHandle pHdr = (LPPVMessageSetBkHandle)pData;
-
- if (!pHdr)
- {
- return false;
- }
- // NOTE: The Salamander version of PVW32Cnv.dll expects COLORREF, not HGDIOBJ
- pHdr->ResultCode = PVSetBkHandle(pWImg ? pWImg->PVHandle : NULL, (HGDIOBJ)pHdr->BackgroundColor);
- return true;
-}
-
-bool PVMessage_GetDLLVersion::HandleRequest()
-{
- LPPVMessageGetDLLVersion pHdr = (LPPVMessageGetDLLVersion)pData;
-
- if (!pHdr)
- {
- return false;
- }
- pHdr->DLLVersion = PVGetDLLVersion();
- pHdr->ResultCode = PVC_OK;
- return true;
-}
-
-bool PVMessage_SetStretchParameters::HandleRequest()
-{
- LPPVMessageSetStretchParameters pHdr = (LPPVMessageSetStretchParameters)pData;
-
- if (!pHdr)
- {
- return false;
- }
- pHdr->ResultCode = PVSetStretchParameters(pWImg ? pWImg->PVHandle : NULL, pHdr->Width, pHdr->Height, pHdr->Mode);
- return true;
-}
-
-bool PVMessage_ChangeImage::HandleRequest()
-{
- LPPVMessageChangeImage pHdr = (LPPVMessageChangeImage)pData;
-
- if (!pHdr)
- {
- return false;
- }
- pHdr->ResultCode = PVChangeImage(pWImg ? pWImg->PVHandle : NULL, pHdr->Flags);
- return true;
-}
-
-bool PVMessage_IsOutCombSupported::HandleRequest()
-{
- LPPVMessageIsOutCombSupported pHdr = (LPPVMessageIsOutCombSupported)pData;
-
- if (!pHdr)
- {
- return false;
- }
- pHdr->ResultCode = PVIsOutCombSupported(pHdr->Fmt, pHdr->Compr, pHdr->Colors, pHdr->ColorModel);
- return true;
-}
-
-bool PVMessage_CropImage::HandleRequest()
-{
- LPPVMessageCropImage pHdr = (LPPVMessageCropImage)pData;
-
- if (!pHdr)
- {
- return false;
- }
- pHdr->ResultCode = PVCropImage(pWImg ? pWImg->PVHandle : NULL, pHdr->Left, pHdr->Top, pHdr->Width, pHdr->Height);
- return true;
-}
-
-bool PVMessage_GetRGBAtCursor::HandleRequest()
-{
- LPPVMessageGetRGBAtCursor pHdr = (LPPVMessageGetRGBAtCursor)pData;
-
- if (!pHdr)
- {
- return false;
- }
- pHdr->ResultCode = GetRGBAtCursor(pWImg ? pWImg->PVHandle : NULL, pHdr->Colors, pHdr->X, pHdr->Y, &pHdr->RGB, &pHdr->Index) ? PVC_OK : PVC_UNSUP_FILE_TYPE;
- return true;
-}
-
-bool PVMessage_CalculateHistogram::HandleRequest()
-{
- LPPVMessageCalculateHistogram pHdr = (LPPVMessageCalculateHistogram)pData;
- PVImageInfo pvii;
-
- if (!pHdr)
- {
- return false;
- }
- // Provide just the information needed/used, nothing more
- memset(&pvii, 0, sizeof(pvii));
- pvii.Colors = pHdr->Colors;
- pvii.Width = pHdr->Width;
- pvii.Height = pHdr->Height;
- pvii.BytesPerLine = pHdr->BytesPerLine;
- pHdr->ResultCode = CalculateHistogram(pWImg ? pWImg->PVHandle : NULL, &pvii, pHdr->luminosity, pHdr->red, pHdr->green, pHdr->blue, pHdr->rgb);
- return true;
-}
-
-bool PVMessage_CreateThumbnail::HandleRequest()
-{
- LPPVMessageCreateThumbnail pHdr = (LPPVMessageCreateThumbnail)pData;
- PVSaveImageInfo SaveImageInfo;
- PVThumbnailerInfo ti;
-
- if (!pHdr)
- {
- return false;
- }
-
- pHdr->State = PVState_Progressing;
- pHdr->ProgressValue = 0;
-
- Thumbnailer.Clear(max(pHdr->ThumbWidth, pHdr->ThumbHeight));
-
- if (!Thumbnailer.SetParameters(pHdr->ImageWidth, pHdr->ImageHeight, 0))
- {
- pHdr->ResultCode = PVC_OOM;
- return true;
- }
-
- // Provide just the information needed/used, nothing more
- memset(&SaveImageInfo, 0, sizeof(SaveImageInfo));
- SaveImageInfo.cbSize = sizeof(SaveImageInfo);
- SaveImageInfo.Format = PVF_RAW;
- SaveImageInfo.Compression = PVCS_NO_COMPRESSION;
- SaveImageInfo.Colors = PV_COLOR_TC32;
- SaveImageInfo.ColorModel = PVCM_RGB;
- SaveImageInfo.Flags = pHdr->Flags | PVSF_USERDEFINED_OUTPUT;
- SaveImageInfo.WriteFunc = MyThumbWriteFunc;
- SaveImageInfo.SeekFunc = MyDummySeekFunc;
- ti.pThumbnailer = &Thumbnailer;
- ti.BytesPerLine = pHdr->ImageWidth * 4;
- ti.pMsg = this;
- pHdr->ResultCode = PVSaveImage(pWImg ? pWImg->PVHandle : NULL, NULL, &SaveImageInfo, ThumbProgressProc, &ti, pHdr->ImageIndex);
- if ((pHdr->ResultCode == PVC_OK) || ((pHdr->ResultCode == PVC_WRITING_ERROR) && (pHdr->State != PVState_Cancelled)))
- {
- pHdr->ResultCode = PVC_OK;
- Thumbnailer.HandleIncompleteImages();
- memcpy(pHdr->Buffer, Thumbnailer.GetThumbnailBuffer(), pHdr->ThumbWidth * pHdr->ThumbHeight * 4);
- }
- InterlockedExchange(&pHdr->State, PVState_Finished);
- return true;
-}
-
-bool PVMessage_SimplifyImageSequence::HandleRequest()
-{
- LPPVMessageSimplifyImageSequence pHdr = (LPPVMessageSimplifyImageSequence)pData;
- LPPVImageSequence pSeq = pWImg->PVImageSequence;
- HDC hMemDC, hMemDC2, hTempDC = GetDC(NULL);
- HBITMAP hMemBmp, hOldBmp, hTargetBmp;
- DWORD PutType;
- RECT rct, rctFull;
- HBRUSH hBr;
- int dispMethod = PVDM_UNDEFINED;
- BITMAPINFO bi;
- LPVOID pvBits = NULL;
- int nFrames, nFrame;
- int nFrameSize = ((pHdr->ScreenWidth * 3 + 3) & ~3) * pHdr->ScreenHeight;
- DWORD hFMap;
-
- // Get the number of frames and create shared memory object accomodating all pixels of all frames
- nFrames = 0;
- while (pSeq)
- {
- pSeq = pSeq->pNext;
- nFrames++;
- }
-
- if (!CreateSharedMemoryForBuffer(NULL, nFrameSize * nFrames, pHdr->SharedMemoryName, hFMap))
- {
- pHdr->ResultCode = PVC_OOM;
- return true;
- }
- pHdr->InternalFileMapHandle = hFMap;
-
- memset(&bi, 0, sizeof(bi));
- bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
- bi.bmiHeader.biWidth = pHdr->ScreenWidth;
- bi.bmiHeader.biHeight = pHdr->ScreenHeight;
- bi.bmiHeader.biPlanes = 1;
- pHdr->BitsPerPixel = bi.bmiHeader.biBitCount = 24;
-
- hMemBmp = CreateCompatibleBitmap(hTempDC, pHdr->ScreenWidth, pHdr->ScreenHeight);
- hMemDC = CreateCompatibleDC(hTempDC);
- SelectObject(hMemDC, hMemBmp);
-
- rct.left = rct.top = 0;
- rct.right = pHdr->ScreenWidth;
- rct.bottom = pHdr->ScreenHeight;
- rctFull = rct;
-
- // hBr = CreateSolidBrush(pvii.FSI->GIF.BgColor);
- hBr = CreateSolidBrush(pHdr->BackgroundColor);
- FillRect(hMemDC, &rct, hBr); // image area
-
- hMemDC2 = CreateCompatibleDC(hMemDC);
- pSeq = pWImg->PVImageSequence;
- nFrame = 0;
- while (pSeq)
- {
- // The GIF89a spec doesn't say anything
- // GIF Construction Set help say dispMethod should be applied AFTER
- // MSIE, NN, Irfan do apply it AFTER
- // XnView (and PictView until 2004.05.24) applies it BEFORE
- // 2009.12.08: odrazka.gif: fill only the area of the subimage
- if ((dispMethod == PVDM_BACKGROUND) || (dispMethod == PVDM_PREVIOUS) /*|| (dispMethod == PVDM_UNDEFINED)*/)
- {
- FillRect(hMemDC, &rct, hBr);
- }
- dispMethod = pSeq->DisposalMethod;
- rct = pSeq->Rect;
- rct.right -= rct.left;
- rct.bottom -= rct.top;
- if (pSeq->TransparentHandle)
- {
- hOldBmp = (HBITMAP)SelectObject(hMemDC2, pSeq->TransparentHandle);
- BitBlt(hMemDC, rct.left, rct.top, rct.right, rct.bottom, hMemDC2, 0, 0, SRCAND);
- SelectObject(hMemDC2, hOldBmp);
- PutType = SRCINVERT;
- DeleteObject(pSeq->TransparentHandle);
- pSeq->TransparentHandle = NULL;
- }
- else
- {
- PutType = SRCCOPY;
- }
- hOldBmp = (HBITMAP)SelectObject(hMemDC2, pSeq->ImgHandle);
- BitBlt(hMemDC, rct.left, rct.top, rct.right, rct.bottom, hMemDC2, 0, 0, PutType);
-
- // hTargetBmp = CreateCompatibleBitmap(hTempDC, pHdr->ScreenWidth, pHdr->ScreenHeight);
- hTargetBmp = CreateDIBSection(hTempDC, &bi, DIB_RGB_COLORS, &pvBits, (HANDLE)hFMap, nFrame * nFrameSize);
-
- SelectObject(hMemDC2, hTargetBmp);
- BitBlt(hMemDC2, 0, 0, pHdr->ScreenWidth, pHdr->ScreenHeight, hMemDC, 0, 0, SRCCOPY);
- rct = pSeq->Rect;
- pSeq->Rect.left = pSeq->Rect.top = 0;
- pSeq->Rect.right = pHdr->ScreenWidth;
- pSeq->Rect.bottom = pHdr->ScreenHeight;
- pHdr->Delay[nFrame] = pSeq->Delay;
- /* hTargetBmp = CreateCompatibleBitmap(dc, rct.right, rct.bottom);
- SelectObject(hMemDC2, hTargetBmp);
- BitBlt(hMemDC2, 0, 0, rct.right, rct.bottom, hMemDC, rct.left, rct.top, SRCCOPY);*/
- SelectObject(hMemDC2, hOldBmp);
- // Delete the DIB Section, we no longer need it, only the pixels in the hFMap object
- DeleteObject(hTargetBmp);
-
- pSeq = pSeq->pNext;
- nFrame++;
- }
- DeleteDC(hMemDC2);
- DeleteObject(hBr);
- DeleteDC(hMemDC);
- DeleteObject(hMemBmp);
-
- ReleaseDC(NULL, hTempDC);
- pHdr->ResultCode = PVC_OK;
- return true;
-}
-
-// ====================== PVWrapperImageHandle ======================
-PVWrapperImageHandle::PVWrapperImageHandle()
-{
- PVHandle = NULL;
- PVImageSequence = NULL;
- hFileMap = NULL;
- pBuffer = pCur = NULL;
- Size = 0;
-}
-
-PVWrapperImageHandle::PVWrapperImageHandle(LPBYTE aBuffer, DWORD aSize)
-{
- PVHandle = NULL;
- PVImageSequence = NULL;
- hFileMap = NULL;
- pBuffer = pCur = aBuffer;
- Size = aSize;
-}
-
-PVWrapperImageHandle::~PVWrapperImageHandle()
-{
- FreeSharedMemory();
- // pBuffer is not NULL when hFileMap was NULL, can occur after wrong use of PVMessage_SaveImage
- if (pBuffer)
- {
- free(pBuffer);
- }
-}
-
-void PVWrapperImageHandle::FreeSharedMemory()
-{
- if (hFileMap)
- {
- UnmapViewOfFile(pBuffer);
- CloseHandle(hFileMap);
- hFileMap = NULL;
- pBuffer = pCur = NULL;
- }
-}
-
-void PVWrapperImageHandle::FreeBuffer()
-{
- if (pBuffer)
- {
- free(pBuffer);
- pBuffer = pCur = NULL;
- Size = 0;
- }
-}
-
-// Creates a file mapping for the given buffer
-bool CreateSharedMemoryForBuffer(LPBYTE pInBuffer, DWORD dataSize, char* fileMapName, DWORD& outFileMap)
-{
- HANDLE hFileMap;
- static int id = 0;
-
- sprintf(fileMapName, "%d_env%d", MainThreadID, id++);
- hFileMap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
- 0, dataSize, fileMapName);
- if (!hFileMap)
- {
- return false;
- }
- if (pInBuffer)
- {
- LPBYTE pBuffer = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, dataSize);
-
- if (!pBuffer)
- {
- CloseHandle(hFileMap);
- return false;
- }
- memcpy(pBuffer, pInBuffer, dataSize);
- UnmapViewOfFile(pBuffer);
- }
- outFileMap = (DWORD)hFileMap;
-
- return true;
-}
diff --git a/src/plugins/pictview/PVMessageWrapper.cpp b/src/plugins/pictview/PVMessageWrapper.cpp
deleted file mode 100644
index 1a9d41d5d..000000000
--- a/src/plugins/pictview/PVMessageWrapper.cpp
+++ /dev/null
@@ -1,934 +0,0 @@
-// SPDX-FileCopyrightText: 2023 Open Salamander Authors
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "precomp.h"
-
-#ifdef PICTVIEW_DLL_IN_SEPARATE_PROCESS
-
-#include "pictview.h"
-#include "PVMessage.h"
-
-// PVWrapperImageHandle wraps LPPVHandle for use by the Wrapper
-class PVWrapperImageHandle
-{
-public:
- PVWrapperImageHandle(DWORD Img);
- ~PVWrapperImageHandle();
-
- bool CreateSharedMemoryForHBitmap(HBITMAP hBitmap, char* pSharedMemoryName, int maxSharedMemoryName, DWORD* pDataSize);
- bool CreateSharedMemoryForMemoryBlock(LPBYTE pBuffer, DWORD dataSize, char* pSharedMemoryName, int maxSharedMemoryName);
- void FreeSharedMemory();
-
- HANDLE hEnvelopeProcess;
- DWORD hPVHandle; // This is LPPVHandle passed from the Envelope to the Wrapper
- HANDLE hFileMap; // Memory used to pass data via PVOF_ATTACH_TO_HANDLE
- DWORD iFileMapID; // ID used to determine file mapping name
- char* Comment;
- LPPVFormatSpecificInfo FSI;
- LPPVImageSequence PVImageSequence; // Allocated by the wrapper
- DWORD NumberOfFrames;
- HANDLE hImageSequenceFileMap;
-};
-
-typedef PVWrapperImageHandle* LPPVWrapperImageHandle;
-
-PVMessage::PVMessage(ePVMSG type, size_t dataSize, LPPVHandle pvHandle)
- : pData(NULL), hFileMap(NULL), pWImg(NULL)
-{
- Init(type, dataSize, pvHandle);
-}
-
-bool PVMessage::Init(ePVMSG type, size_t dataSize, LPPVHandle pvHandle)
-{
- TCHAR fileMapName[48];
- LPPVMessageHeader pHdr;
-
- pWImg = (LPPVWrapperImageHandle)pvHandle;
- iID = PVWrapper.MessageID++;
- _sntprintf(fileMapName, SizeOf(fileMapName), _T("%s_%d"), PVWrapper.MutexName, iID);
- hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
- 0, (DWORD)(dataSize + sizeof(PVMessageHeader)), fileMapName);
- if (!hFileMap)
- {
- return false;
- }
- pData = (LPPVMessageHeader)MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, dataSize + sizeof(PVMessageHeader));
- if (!pData)
- {
- return false;
- }
- memset(pData, 0, dataSize + sizeof(PVMessageHeader));
- pHdr = (LPPVMessageHeader)pData;
- pHdr->cbSize = (DWORD)(dataSize + sizeof(PVMessageHeader));
- pHdr->Type = type;
- pHdr->PVHandle = pWImg ? pWImg->hPVHandle : NULL;
- pHdr->ResultCode = PVC_ENVELOPE_NOT_LOADED;
- return true;
-}
-
-bool PVMessage::IsInited()
-{
- if (!hFileMap || !pData)
- return false;
- if (!LoadEnvelope())
- return false;
- return true;
-}
-
-bool PVMessage::Exec()
-{
- TCHAR eventName[32];
- HANDLE hEvent;
-
- _sntprintf(eventName, SizeOf(eventName), _T("%s_ev%x"), PVWrapper.MutexName, iID);
- hEvent = CreateEvent(NULL, FALSE, FALSE, eventName);
- if (!hEvent)
- {
- return false;
- }
- PostThreadMessage(PVWrapper.pi.dwThreadId, WM_USER, pData->cbSize, iID);
- HANDLE handles[2] = {hEvent, PVWrapper.pi.hProcess};
- DWORD ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
- CloseHandle(hEvent);
- if (ret == 1)
- {
- // The remote process died
- return false;
- }
- return ret == 0;
-}
-
-LPPVHandle PVMessage::GetPVHandle()
-{
- return pWImg;
-}
-
-bool PVMessage::UnmarshalImageInfo(LPPVImageInfoStruct pInInfo, LPPVImageInfo pOutInfo)
-{
- pOutInfo->cbSize = sizeof(PVImageInfo);
- pOutInfo->Width = pInInfo->Width;
- pOutInfo->Height = pInInfo->Height;
- pOutInfo->BytesPerLine = pInInfo->BytesPerLine;
- pOutInfo->FileSize = pInInfo->FileSize;
- pOutInfo->Colors = pInInfo->Colors;
- pOutInfo->Format = pInInfo->Format;
- pOutInfo->Flags = pInInfo->Flags;
- pOutInfo->ColorModel = pInInfo->ColorModel;
- pOutInfo->NumOfImages = pInInfo->NumOfImages;
- pOutInfo->CurrentImage = pInInfo->CurrentImage;
- strcpy(pOutInfo->Info1, pInInfo->Info1);
- strcpy(pOutInfo->Info2, pInInfo->Info2);
- strcpy(pOutInfo->Info3, pInInfo->Info3);
- pOutInfo->StretchedWidth = pInInfo->StretchedWidth;
- pOutInfo->StretchedHeight = pInInfo->StretchedHeight;
- pOutInfo->StretchMode = pInInfo->StretchMode;
- pOutInfo->HorDPI = pInInfo->HorDPI;
- pOutInfo->VerDPI = pInInfo->VerDPI;
- pOutInfo->FSI = NULL;
- if (pInInfo->bFSI)
- {
- pOutInfo->FSI = pWImg->FSI = (LPPVFormatSpecificInfo)malloc(sizeof(PVFormatSpecificInfo));
- if (pOutInfo->FSI)
- {
- memcpy(pOutInfo->FSI, &pInInfo->FSI, sizeof(PVFormatSpecificInfo));
- }
- }
- pOutInfo->Compression = pInInfo->Compression;
- pOutInfo->TotalBitDepth = pInInfo->TotalBitDepth;
- pOutInfo->CommentSize = 0;
- pOutInfo->Comment = NULL;
- if (pInInfo->CommentSize)
- {
- pOutInfo->Comment = pWImg->Comment = (char*)malloc(pInInfo->CommentSize);
- if (pOutInfo->Comment)
- {
- pOutInfo->CommentSize = pInInfo->CommentSize;
- memcpy(pOutInfo->Comment, pInInfo->Comment, pInInfo->CommentSize);
- }
- }
- return true;
-}
-
-// ========================= PVMessageWithProgress =========================
-PVMessageWithProgress::PVMessageWithProgress(ePVMSG type, size_t dataSize, LPPVHandle pvHandle) : PVMessage(type, dataSize, pvHandle)
-{
- LPPVMessageWithProgressHeader pHdr = (LPPVMessageWithProgressHeader)pData;
-
- if (!pHdr)
- {
- return;
- }
- pHdr->State = PVState_Started;
- pHdr->ProgressValue = 0;
-}
-
-bool PVMessageWithProgress::Exec(TProgressProc Progress, void* AppSpecific)
-{
- if (!Progress)
- {
- // No progress function specified -> process quickly
- return PVMessage::Exec();
- }
- TCHAR eventName[32];
- HANDLE hEvt;
-
- _sntprintf(eventName, SizeOf(eventName), _T("%s_ev%x"), PVWrapper.MutexName, iID);
- hEvt = CreateEvent(NULL, FALSE, FALSE, eventName);
- if (!hEvt)
- {
- return false;
- }
- PostThreadMessage(PVWrapper.pi.dwThreadId, WM_USER, pData->cbSize, iID);
- HANDLE handles[2] = {hEvt, PVWrapper.pi.hProcess};
- LPPVMessageWithProgressHeader pHdr = (LPPVMessageWithProgressHeader)pData;
- DWORD ret = -1;
- // Receive progress messages as long as State is not PVState_Finished
- for (;;)
- {
- ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
- if (ret == 0)
- {
- if (pHdr->State == PVState_Progressing)
- {
- // Forward progress update and check for cancellatiom
- if (Progress(pHdr->ProgressValue, AppSpecific))
- {
- LONG state = InterlockedExchange(&pHdr->State, PVState_Cancelled);
- if (state == PVState_Finished)
- {
- // Envelope finished in the meantime -> restore the Finished state to avoid infinite loop
- pHdr->State = PVState_Finished;
- }
- }
- continue;
- }
- if (pHdr->State == PVState_Cancelled)
- {
- continue;
- }
- }
- break;
- }
- CloseHandle(hEvt);
- if (ret == 1)
- {
- // The remote process died
- return false;
- }
- return ret == 0;
-}
-
-// ========================= PVMessage_GetErrorText =========================
-PVMessage_GetErrorText::PVMessage_GetErrorText(DWORD ErrorCode)
- : PVMessage(PVMSG_GetErrorText, sizeof(PVMessageGetErrorText))
-{
- LPPVMessageGetErrorText pHdr = (LPPVMessageGetErrorText)pData;
-
- if (!pHdr)
- {
- return;
- }
- pHdr->ErrorCode = ErrorCode;
-}
-
-const char* PVMessage_GetErrorText::GetErrorText()
-{
- LPPVMessageGetErrorText pHdr = (LPPVMessageGetErrorText)pData;
-
- if (!pHdr)
- {
- return "";
- }
- return pHdr->ErrorText;
-}
-
-// ========================= PVMessage_OpenImageEx =========================
-PVMessage_OpenImageEx::PVMessage_OpenImageEx(LPPVOpenImageExInfo pOpenExInfo)
- : PVMessage()
-{
- LPPVMessageOpenImageEx pHdr;
-
- pWImg = new PVWrapperImageHandle(NULL);
- if (!pWImg)
- {
- return;
- }
-
- Init(PVMSG_OpenImageEx, sizeof(PVMessageOpenImageEx), pWImg);
- pHdr = (LPPVMessageOpenImageEx)pData;
- if (!pHdr)
- {
- return;
- }
-
- pHdr->Flags = pOpenExInfo->Flags;
- if (pOpenExInfo->Flags & PVOF_ATTACH_TO_HANDLE)
- {
- pWImg->CreateSharedMemoryForHBitmap((HBITMAP)pOpenExInfo->Handle, pHdr->FileName, sizeof(pHdr->FileName), &pHdr->DataSize);
- }
- else if (pOpenExInfo->Flags & PVOF_USERDEFINED_INPUT)
- {
- psReadMemFuncData rmfd = (psReadMemFuncData)pOpenExInfo->Handle;
- _ASSERTE(rmfd && !rmfd->Pos);
- pHdr->DataSize = rmfd->Size;
- pWImg->CreateSharedMemoryForMemoryBlock((LPBYTE)rmfd->Buffer, rmfd->Size, pHdr->FileName, sizeof(pHdr->FileName));
- }
- else
- {
- strcpy(pHdr->FileName, pOpenExInfo->FileName);
- }
-}
-
-bool PVMessage_OpenImageEx::IsInited()
-{
- // We do not support custom Read and Seek functions for now
- LPPVMessageOpenImageEx pHdr = (LPPVMessageOpenImageEx)pData;
-
- if (!pData || (!*pHdr->FileName && !(pHdr->Flags & PVOF_ATTACH_TO_HANDLE)))
- {
- return false;
- }
- return PVMessage::IsInited();
-}
-
-bool PVMessage_OpenImageEx::Exec(LPPVImageInfo pImgInfo)
-{
- if (!PVMessage::Exec())
- {
- return false;
- }
- if (GetResultCode() == PVC_OK)
- {
- pWImg->hPVHandle = (((LPPVMessageHeader)pData)->PVHandle);
- return UnmarshalImageInfo(&((LPPVMessageOpenImageEx)pData)->ImageInfo, pImgInfo);
- }
- return true;
-}
-
-// ========================= PVMessage_GetImageInfo =========================
-PVMessage_GetImageInfo::PVMessage_GetImageInfo(LPPVHandle pvHandle, int ImageIndex)
- : PVMessage(PVMSG_GetImageInfo, sizeof(PVMessageGetImageInfo), pvHandle)
-{
- LPPVMessageGetImageInfo pHdr = (LPPVMessageGetImageInfo)pData;
-
- if (!pHdr)
- {
- return;
- }
- pHdr->ImageIndex = ImageIndex;
-}
-
-bool PVMessage_GetImageInfo::Exec(LPPVImageInfo pImgInfo)
-{
- if (!PVMessage::Exec())
- {
- return false;
- }
- return UnmarshalImageInfo(&((LPPVMessageGetImageInfo)pData)->ImageInfo, pImgInfo);
-}
-
-// ========================= PVMessage_ReadImage =========================
-PVMessage_ReadImage::PVMessage_ReadImage(LPPVHandle pvHandle, int ImageIndex, bool bProgress)
- : PVMessageWithProgress(bProgress ? PVMSG_ReadImage : PVMSG_ReadImageWithoutProgress, sizeof(PVMessageReadImage), pvHandle)
-{
- LPPVMessageReadImage pHdr = (LPPVMessageReadImage)pData;
-
- if (!pHdr)
- {
- return;
- }
- pHdr->ImageIndex = ImageIndex;
-}
-
-bool PVMessage_ReadImage::Exec(HDC PaintDC, RECT* pDRect, TProgressProc Progress, void* AppSpecific)
-{
- // FIXME: Paint during loading
- return PVMessageWithProgress::Exec(Progress, AppSpecific);
-}
-
-// ========================= PVMessage_CloseImage =========================
-PVMessage_CloseImage::PVMessage_CloseImage(LPPVHandle pvHandle)
- : PVMessage(PVMSG_CloseImage, sizeof(PVMessage), pvHandle)
-{
-}
-
-PVMessage_CloseImage::~PVMessage_CloseImage()
-{
- delete pWImg;
-}
-
-// ========================= PVMessage_DrawImage =========================
-PVMessage_DrawImage::PVMessage_DrawImage(LPPVHandle pvHandle, HDC PaintDC, int X, int Y, LPRECT pDrawRect)
- : PVMessage(PVMSG_DrawImage, sizeof(PVMessage), pvHandle)
-{
- LPPVMessageDrawImage pHdr = (LPPVMessageDrawImage)pData;
-
- if (!pHdr)
- {
- return;
- }
- pHdr->X = X;
- pHdr->Y = Y;
- pHdr->DrawRect = *pDrawRect;
- if (PaintDC)
- {
- pHdr->BitsPerPixel = GetDeviceCaps(PaintDC, BITSPIXEL);
- if (pHdr->BitsPerPixel < 15)
- {
- // 8-bit displays no longer supported...
- pHdr->BitsPerPixel = 24;
- }
- }
- else
- {
- pHdr->BitsPerPixel = 24;
- }
-}
-
-bool PVMessage_DrawImage::Exec(HDC PaintDC)
-{
- if (!PVMessage::Exec())
- {
- return false;
- }
- if (GetResultCode() == PVC_OK)
- {
- LPPVMessageDrawImage pHdr = (LPPVMessageDrawImage)pData;
- DWORD paintWidth = pHdr->DrawRect.right - pHdr->DrawRect.left;
- DWORD paintHeight = pHdr->DrawRect.bottom - pHdr->DrawRect.top;
- DWORD frameSize = (((paintWidth * pHdr->BitsPerPixel + 31) >> 3) & ~3) * paintHeight;
- HANDLE hFMap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, frameSize, pHdr->SharedMemoryName);
-
- // The envelope no longer needs to keep the shared memory object -> free it
- PVMessage_CloseHandle(pHdr->InternalFileMapHandle).Exec();
-
- if (!hFMap)
- {
- pHdr->ResultCode = PVC_OOM;
- return false;
- }
-
- BITMAPINFO bi;
- LPVOID pvBits = NULL;
- HBITMAP hDIB;
-
- memset(&bi, 0, sizeof(bi));
- bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
- bi.bmiHeader.biWidth = paintWidth;
- bi.bmiHeader.biHeight = paintHeight;
- bi.bmiHeader.biPlanes = 1;
- bi.bmiHeader.biBitCount = (WORD)pHdr->BitsPerPixel;
-
- hDIB = CreateDIBSection(PaintDC, &bi, DIB_RGB_COLORS, &pvBits, hFMap, 0);
- if (hDIB)
- {
- HDC hMemDC = CreateCompatibleDC(PaintDC);
- HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, hDIB);
-
- BitBlt(PaintDC, pHdr->DrawRect.left, pHdr->DrawRect.top,
- paintWidth, paintHeight, hMemDC, 0, 0, SRCCOPY);
- SelectObject(hMemDC, hOldBmp);
- DeleteDC(hMemDC);
- DeleteObject(hDIB);
- }
- CloseHandle(hFMap);
- }
- return true;
-}
-
-// ========================= PVMessage_SaveImage =========================
-PVMessage_SaveImage::PVMessage_SaveImage(LPPVHandle pvHandle, const char* FileName, LPPVSaveImageInfo pSii, int ImageIndex, bool bProgress)
- : PVMessageWithProgress(bProgress ? PVMSG_SaveImage : PVMSG_SaveImageWithoutProgress, sizeof(PVMessageSaveImage), pvHandle)
-{
- LPPVMessageSaveImage pHdr = (LPPVMessageSaveImage)pData;
-
- if (!pHdr)
- {
- return;
- }
-
- pHdr->cbSize = pSii->cbSize;
- pHdr->Format = pSii->Format;
- pHdr->Compression = pSii->Compression;
- pHdr->Colors = pSii->Colors;
- pHdr->ColorModel = pSii->ColorModel;
- pHdr->Flags = pSii->Flags;
- pHdr->Width = pSii->Width;
- pHdr->Height = pSii->Height;
- pHdr->HorDPI = pSii->HorDPI;
- pHdr->VerDPI = pSii->VerDPI;
- _ASSERTE(sizeof(pHdr->FormatSpecificInfo) == sizeof(pSii->Misc));
- memcpy(pHdr->FormatSpecificInfo, &pSii->Misc, sizeof(pSii->Misc));
- pHdr->CropLeft = pSii->CropLeft;
- pHdr->CropTop = pSii->CropTop;
- pHdr->CropWidth = pSii->CropWidth;
- pHdr->CropHeight = pSii->CropHeight;
- _ASSERTE(sizeof(pHdr->Transp) == sizeof(pSii->Transp));
- memcpy(&pHdr->Transp, &pSii->Transp, sizeof(pSii->Transp));
- pHdr->ImageIndex = ImageIndex;
- strcpy(pHdr->FileName, FileName ? FileName : "");
- _ASSERTE(pHdr->CommentSize <= sizeof(pHdr->Comment));
- pHdr->CommentSize = min(pSii->CommentSize, sizeof(pHdr->Comment));
- memcpy(pHdr->Comment, pSii->Comment, pHdr->CommentSize);
-}
-
-// ========================= PVMessage_LoadFromClipboard =========================
-PVMessage_LoadFromClipboard::PVMessage_LoadFromClipboard()
- : PVMessage(PVMSG_LoadFromClipboard, sizeof(PVMessageLoadFromClipboard))
-{
- pWImg = new PVWrapperImageHandle(NULL);
-}
-
-bool PVMessage_LoadFromClipboard::Exec(LPPVImageInfo pImgInfo)
-{
- if (!PVMessage::Exec())
- {
- return false;
- }
- pWImg->hPVHandle = (((LPPVMessageHeader)pData)->PVHandle);
- return UnmarshalImageInfo(&((LPPVMessageLoadFromClipboard)pData)->ImageInfo, pImgInfo);
-}
-
-// ========================= PVMessage_ReadImageSequence =========================
-PVMessage_ReadImageSequence::PVMessage_ReadImageSequence(LPPVHandle pvHandle)
- : PVMessage(PVMSG_ReadImageSequence, sizeof(PVMessageReadImageSequence), pvHandle)
-{
-}
-
-bool PVMessage_ReadImageSequence::Exec()
-{
- if (!PVMessage::Exec())
- {
- return false;
- }
- LPPVMessageReadImageSequence pHdr = (LPPVMessageReadImageSequence)pData;
- if (PVC_OK == GetResultCode())
- {
- pWImg->NumberOfFrames = pHdr->NumberOfFrames;
- }
- return true;
-}
-
-// ========================= PVMessage_SetBkHandle =========================
-PVMessage_SetBkHandle::PVMessage_SetBkHandle(LPPVHandle pvHandle, COLORREF bkColor)
- : PVMessage(PVMSG_SetBkHandle, sizeof(PVMessageSetBkHandle), pvHandle)
-{
- LPPVMessageSetBkHandle pHdr = (LPPVMessageSetBkHandle)pData;
-
- if (!pHdr)
- {
- return;
- }
-
- pHdr->BackgroundColor = bkColor;
-}
-
-// ========================= PVMessage_GetDLLVersion =========================
-PVMessage_GetDLLVersion::PVMessage_GetDLLVersion()
- : PVMessage(PVMSG_GetDLLVersion, sizeof(PVMessageGetDLLVersion))
-{
-}
-
-bool PVMessage_GetDLLVersion::Exec(DWORD& Version)
-{
- if (!PVMessage::Exec())
- {
- return false;
- }
- Version = ((LPPVMessageGetDLLVersion)pData)->DLLVersion;
-
- return true;
-}
-
-// ========================= PVMessage_ChangeImage =========================
-PVMessage_SetStretchParameters::PVMessage_SetStretchParameters(LPPVHandle pvHandle, DWORD Width, DWORD Height, DWORD Mode)
- : PVMessage(PVMSG_SetStretchParameters, sizeof(PVMessageSetStretchParameters), pvHandle)
-{
- LPPVMessageSetStretchParameters pHdr = (LPPVMessageSetStretchParameters)pData;
-
- if (!pHdr)
- {
- return;
- }
-
- pHdr->Width = Width;
- pHdr->Height = Height;
- pHdr->Mode = Mode;
-}
-
-// ========================= PVMessage_ChangeImage =========================
-PVMessage_ChangeImage::PVMessage_ChangeImage(LPPVHandle pvHandle, DWORD Flags)
- : PVMessage(PVMSG_ChangeImage, sizeof(PVMessageChangeImage), pvHandle)
-{
- LPPVMessageChangeImage pHdr = (LPPVMessageChangeImage)pData;
-
- if (!pHdr)
- {
- return;
- }
- pHdr->Flags = Flags;
-}
-
-// ========================= PVMessage_IsOutCombSupported =========================
-PVMessage_IsOutCombSupported::PVMessage_IsOutCombSupported(int Fmt, int Compr, int Colors, int ColorModel)
- : PVMessage(PVMSG_IsOutCombSupported, sizeof(PVMessageIsOutCombSupported))
-{
- LPPVMessageIsOutCombSupported pHdr = (LPPVMessageIsOutCombSupported)pData;
-
- if (!pHdr)
- {
- return;
- }
- pHdr->Fmt = Fmt;
- pHdr->Compr = Compr;
- pHdr->Colors = Colors;
- pHdr->ColorModel = ColorModel;
-}
-
-// ========================= PVMessage_CropImage =========================
-PVMessage_CropImage::PVMessage_CropImage(LPPVHandle pvHandle, int Left, int Top, int Width, int Height)
- : PVMessage(PVMSG_CropImage, sizeof(PVMessageCropImage), pvHandle)
-{
- LPPVMessageCropImage pHdr = (LPPVMessageCropImage)pData;
-
- if (!pHdr)
- {
- return;
- }
- pHdr->Left = Left;
- pHdr->Top = Top;
- pHdr->Width = Width;
- pHdr->Height = Height;
-}
-
-// ========================= PVMessage_GetRGBAtCursor =========================
-PVMessage_GetRGBAtCursor::PVMessage_GetRGBAtCursor(LPPVHandle pvHandle, DWORD Colors, int x, int y)
- : PVMessage(PVMSG_GetRGBAtCursor, sizeof(PVMessageGetRGBAtCursor), pvHandle)
-{
- LPPVMessageGetRGBAtCursor pHdr = (LPPVMessageGetRGBAtCursor)pData;
-
- if (!pHdr)
- {
- return;
- }
- pHdr->Colors = Colors;
- pHdr->X = x;
- pHdr->Y = y;
-}
-
-RGBQUAD* PVMessage_GetRGBAtCursor::GetRGB()
-{
- return &((LPPVMessageGetRGBAtCursor)pData)->RGB;
-}
-
-int PVMessage_GetRGBAtCursor::GetIndex()
-{
- return ((LPPVMessageGetRGBAtCursor)pData)->Index;
-}
-
-// ========================= PVMessage_CalculateHistogram =========================
-PVMessage_CalculateHistogram::PVMessage_CalculateHistogram(LPPVHandle pvHandle, const PVImageInfo* pvii)
- : PVMessage(PVMSG_CalculateHistogram, sizeof(PVMessageCalculateHistogram), pvHandle)
-{
- LPPVMessageCalculateHistogram pHdr = (LPPVMessageCalculateHistogram)pData;
-
- if (!pHdr)
- {
- return;
- }
- pHdr->Colors = pvii->Colors;
- pHdr->Width = pvii->Width;
- pHdr->Height = pvii->Height;
- pHdr->BytesPerLine = pvii->BytesPerLine;
-}
-
-void PVMessage_CalculateHistogram::GetResults(LPDWORD luminosity, LPDWORD red, LPDWORD green, LPDWORD blue, LPDWORD rgb)
-{
- LPPVMessageCalculateHistogram pHdr = (LPPVMessageCalculateHistogram)pData;
-
- memcpy(luminosity, pHdr->luminosity, sizeof(pHdr->luminosity));
- memcpy(red, pHdr->red, sizeof(pHdr->red));
- memcpy(green, pHdr->green, sizeof(pHdr->green));
- memcpy(blue, pHdr->blue, sizeof(pHdr->blue));
- memcpy(rgb, pHdr->rgb, sizeof(pHdr->rgb));
-}
-
-// ========================= PVMessage_CreateThumbnail =========================
-PVMessage_CreateThumbnail::PVMessage_CreateThumbnail(LPPVHandle pvHandle, LPPVSaveImageInfo pSii,
- int ImageIndex, DWORD imgWidth, DWORD imgHeight, DWORD thumbWidth, DWORD thumbHeight)
- : PVMessageWithProgress(PVMSG_CreateThumbnail, sizeof(PVMessageCreateThumbnail) + thumbWidth * thumbHeight * 4, pvHandle)
-{
- LPPVMessageCreateThumbnail pHdr = (LPPVMessageCreateThumbnail)pData;
-
- if (!pHdr)
- {
- return;
- }
- pHdr->Flags = pSii->Flags;
- pHdr->ImageWidth = imgWidth;
- pHdr->ImageHeight = imgHeight;
- pHdr->ThumbWidth = thumbWidth;
- pHdr->ThumbHeight = thumbHeight;
- pHdr->ImageIndex = ImageIndex;
-}
-
-LPBYTE PVMessage_CreateThumbnail::GetPixelData()
-{
- return ((LPPVMessageCreateThumbnail)pData)->Buffer;
-}
-
-// ========================= PVMessage_SimplifyImageSequence =========================
-PVMessage_SimplifyImageSequence::PVMessage_SimplifyImageSequence(LPPVHandle pvHandle, DWORD ScreenWidth, DWORD ScreenHeight, const COLORREF& bgColor)
- : PVMessage(PVMSG_SimplifyImageSequence, sizeof(PVMessageSimplifyImageSequence) + sizeof(DWORD) * ((LPPVWrapperImageHandle)pvHandle)->NumberOfFrames, pvHandle)
-{
- LPPVMessageSimplifyImageSequence pHdr = (LPPVMessageSimplifyImageSequence)pData;
-
- if (!pHdr)
- {
- return;
- }
- pHdr->ScreenWidth = ScreenWidth;
- pHdr->ScreenHeight = ScreenHeight;
- pHdr->BackgroundColor = bgColor;
-}
-
-bool PVMessage_SimplifyImageSequence::Exec()
-{
- if (!PVMessage::Exec())
- {
- return false;
- }
- if (GetResultCode() == PVC_OK)
- {
- LPPVMessageSimplifyImageSequence pHdr = (LPPVMessageSimplifyImageSequence)pData;
- DWORD frameSize = (((pHdr->ScreenWidth * pHdr->BitsPerPixel + 31) >> 3) & ~3) * pHdr->ScreenHeight;
-
- pWImg->hImageSequenceFileMap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, pWImg->NumberOfFrames * frameSize, pHdr->SharedMemoryName);
- // The envelope no longer needs to keep the shared memory object -> free it
- PVMessage_CloseHandle(pHdr->InternalFileMapHandle).Exec();
-
- if (!pWImg->hImageSequenceFileMap)
- {
- return false;
- }
-
- LPPVImageSequence* pCurFrame = &pWImg->PVImageSequence;
- BITMAPINFO bi;
- LPVOID pvBits = NULL;
-
- memset(&bi, 0, sizeof(bi));
- bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
- bi.bmiHeader.biWidth = pHdr->ScreenWidth;
- bi.bmiHeader.biHeight = pHdr->ScreenHeight;
- bi.bmiHeader.biPlanes = 1;
- bi.bmiHeader.biBitCount = (WORD)pHdr->BitsPerPixel;
-
- for (DWORD nFrame = 0; nFrame < pWImg->NumberOfFrames; nFrame++)
- {
- LPPVImageSequence pSeq = (LPPVImageSequence)calloc(1, sizeof(PVImageSequence));
- if (pSeq)
- {
- pSeq->Rect.right = pHdr->ScreenWidth;
- pSeq->Rect.bottom = pHdr->ScreenHeight;
- pSeq->Delay = pHdr->Delay[nFrame];
- pSeq->ImgHandle = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &pvBits,
- pWImg->hImageSequenceFileMap, nFrame * frameSize);
- *pCurFrame = pSeq;
- pCurFrame = &(*pCurFrame)->pNext;
- }
- }
- }
- return true;
-}
-
-LPPVImageSequence PVMessage_SimplifyImageSequence::GetImageSequence()
-{
- if (pWImg)
- {
- return pWImg->PVImageSequence;
- }
- return NULL;
-}
-
-// ========================= PVMessage_CloseHandle =========================
-PVMessage_CloseHandle::PVMessage_CloseHandle(DWORD Handle) : PVMessage()
-{
- HandleToBeClosed = Handle;
-}
-
-bool PVMessage_CloseHandle::Exec()
-{
- PostThreadMessage(PVWrapper.pi.dwThreadId, WM_USER + 1, PVMSG_CloseHandle, HandleToBeClosed);
- return true;
-}
-
-// ========================= PVWrapperImageHandle =========================
-PVWrapperImageHandle::PVWrapperImageHandle(DWORD Img)
- : hEnvelopeProcess(PVWrapper.pi.hProcess), hPVHandle(Img), hFileMap(NULL), Comment(NULL), FSI(NULL),
- PVImageSequence(NULL), hImageSequenceFileMap(NULL)
-{
-}
-
-PVWrapperImageHandle::~PVWrapperImageHandle()
-{
- if (Comment)
- free(Comment);
- if (FSI)
- free(FSI);
- if (PVImageSequence)
- {
- while (PVImageSequence)
- {
- LPPVImageSequence pSeq = PVImageSequence;
-
- PVImageSequence = pSeq->pNext;
- // Transparent bitmaps are merged by the envelope and not propagated to the wrapper
- _ASSERTE(pSeq->ImgHandle && !pSeq->TransparentHandle);
- DeleteObject(pSeq->ImgHandle);
- free(pSeq);
- }
- }
- if (hImageSequenceFileMap)
- {
- CloseHandle(hImageSequenceFileMap);
- }
- FreeSharedMemory();
-}
-
-void PVWrapperImageHandle::FreeSharedMemory()
-{
- if (hFileMap)
- {
- CloseHandle(hFileMap);
- hFileMap = NULL;
- }
-}
-
-bool PVWrapperImageHandle::CreateSharedMemoryForHBitmap(HBITMAP hBitmap, char* pSharedMemoryName, int maxSharedMemoryName, DWORD* pDataSize)
-{
- BITMAPINFO bmpHeader;
- HDC hDC = GetDC(NULL);
-#pragma pack(push, 1)
- typedef struct
- {
- WORD Magic;
- DWORD Size, Reserved;
- DWORD OfsDataInFile;
- BITMAPINFO Header;
- RGBQUAD Palette[255]; // Color 0 is already in Header
- } Bitmap, *LPBitmap;
- LPBitmap pBitmap;
-#pragma pack(pop)
-
- *pDataSize = 2 + 4 + 4 + 4 + sizeof(bmpHeader.bmiHeader);
- bmpHeader.bmiHeader.biSize = sizeof(bmpHeader.bmiHeader);
- bmpHeader.bmiHeader.biBitCount = 0;
- if (!GetDIBits(hDC, hBitmap, 0, 0, NULL, &bmpHeader, DIB_RGB_COLORS))
- {
- ReleaseDC(NULL, hDC);
- return false;
- }
- if (bmpHeader.bmiHeader.biBitCount <= 8)
- {
- *pDataSize += 4 * (1 << bmpHeader.bmiHeader.biBitCount);
- }
- if ((bmpHeader.bmiHeader.biBitCount == 15) | (bmpHeader.bmiHeader.biBitCount == 16) || (bmpHeader.bmiHeader.biBitCount == 32))
- {
- *pDataSize += 3 * 4;
- }
- *pDataSize += bmpHeader.bmiHeader.biSizeImage;
-
- iFileMapID = PVWrapper.MessageID++;
- _snprintf_s(pSharedMemoryName, maxSharedMemoryName, _TRUNCATE, "%s_img%d", PVWrapper.MutexName, iFileMapID);
- hFileMap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, *pDataSize, pSharedMemoryName);
- if (!hFileMap)
- {
- ReleaseDC(NULL, hDC);
- return false;
- }
- pBitmap = (LPBitmap)MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, *pDataSize);
- if (!pBitmap)
- {
- CloseHandle(hFileMap);
- hFileMap = NULL;
- ReleaseDC(NULL, hDC);
- return false;
- }
-
- pBitmap->Magic = 'MB';
- pBitmap->Size = pBitmap->Reserved = 0;
- pBitmap->OfsDataInFile = 2 + 4 + 4 + 4 + bmpHeader.bmiHeader.biSize;
- memcpy(&pBitmap->Header, &bmpHeader, sizeof(bmpHeader));
- if (bmpHeader.bmiHeader.biBitCount <= 8)
- {
- pBitmap->OfsDataInFile += 4 * (1 << bmpHeader.bmiHeader.biBitCount);
- // Will this work?
- GetDIBits(hDC, hBitmap, 0, 0, NULL, &pBitmap->Header, DIB_RGB_COLORS);
- }
- if ((bmpHeader.bmiHeader.biBitCount == 15) || (bmpHeader.bmiHeader.biBitCount == 16))
- {
- pBitmap->OfsDataInFile += 3 * 4;
- *(LPDWORD)(&pBitmap->Header.bmiColors[0]) /*bV4RedMask*/ = 0xf800;
- *(LPDWORD)(&pBitmap->Header.bmiColors[1]) /*bV4GreenMask*/ = 0x07e0;
- *(LPDWORD)(&pBitmap->Header.bmiColors[2]) /*bV4BlueMask*/ = 0x001f;
- HDC DC2 = CreateCompatibleDC(hDC);
- HBITMAP hBmp = (HBITMAP)SelectObject(DC2, hBitmap);
- COLORREF oldClr = GetPixel(DC2, 0, 0);
- COLORREF newClr = SetPixel(DC2, 0, 0, 0xFFF8FF);
- // NT4 15bit: newClr is FFFFFF
- // NT4 16bit: newClr is FFFBFF (?)
- // W95 15bit: newClr is FFF8FF (?)
- // W95 16bit: newClr is FFF8FF
- if (newClr == 0xFFF8FF)
- {
- // Running Win95
- newClr = SetPixel(DC2, 0, 0, 0xFFFcFF);
- }
- if (newClr == 0xFFFFFF)
- {
- // NT: 5-bit Green was converted to 8-bit green -> it's a 15bit bitmap! (proper scaling-rounding)}
- // W95: 6-bit Green was converted to 8-bit green -> it's a 15bit bitmap! (no scaling-rounding}
- *(LPDWORD)(&pBitmap->Header.bmiColors[0]) /*bV4RedMask*/ = 0x7c00;
- *(LPDWORD)(&pBitmap->Header.bmiColors[1]) /*bV4GreenMask*/ = 0x03e0;
- }
- SetPixel(DC2, 0, 0, oldClr); // restore the previous color}
- SelectObject(DC2, hBmp);
- DeleteDC(DC2);
- }
- if (bmpHeader.bmiHeader.biBitCount == 32)
- {
- pBitmap->OfsDataInFile += 3 * 4;
- *(LPDWORD)(&pBitmap->Header.bmiColors[0]) /*bV4RedMask*/ = 0xff0000;
- *(LPDWORD)(&pBitmap->Header.bmiColors[1]) /*bV4GreenMask*/ = 0xff00;
- *(LPDWORD)(&pBitmap->Header.bmiColors[2]) /*bV4BlueMask*/ = 0xff;
- }
- // Finally, get the pixels data
- GetDIBits(hDC, hBitmap, 0, bmpHeader.bmiHeader.biHeight, (LPBYTE)pBitmap + pBitmap->OfsDataInFile, &pBitmap->Header, DIB_RGB_COLORS);
- ReleaseDC(NULL, hDC);
- UnmapViewOfFile(pBitmap);
- return true;
-}
-
-bool PVWrapperImageHandle::CreateSharedMemoryForMemoryBlock(LPBYTE pBuffer, DWORD dataSize, char* pSharedMemoryName, int maxSharedMemoryName)
-{
- iFileMapID = PVWrapper.MessageID++;
-
- _snprintf_s(pSharedMemoryName, maxSharedMemoryName, _TRUNCATE, "%s_img%d", PVWrapper.MutexName, iFileMapID);
- hFileMap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, dataSize, pSharedMemoryName);
- if (!hFileMap)
- {
- return false;
- }
- LPBYTE pData = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, dataSize);
- if (!pData)
- {
- CloseHandle(hFileMap);
- hFileMap = NULL;
- return false;
- }
- memcpy(pData, pBuffer, dataSize);
- UnmapViewOfFile(pData);
- return true;
-}
-
-#endif
diff --git a/src/plugins/pictview/dialogs.cpp b/src/plugins/pictview/dialogs.cpp
index e7e299264..5f5be63bf 100644
--- a/src/plugins/pictview/dialogs.cpp
+++ b/src/plugins/pictview/dialogs.cpp
@@ -124,7 +124,7 @@ CAboutDialog::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
GetDlgItemText(HWindow, IDC_ABOUT_PVW32, buff, 1000);
wsprintf(buff2, buff, PVW32DLL.Version);
SetDlgItemText(HWindow, IDC_ABOUT_PVW32, buff2);
- // PVW32 will be bold
+ // Highlight the backend line in bold
SalamanderGUI->AttachStaticText(HWindow, IDC_ABOUT_PVW32, STF_BOLD);
hl = SalamanderGUI->AttachHyperLink(HWindow, IDC_ABOUT_EMAIL, STF_UNDERLINE | STF_HYPERLINK_COLOR);
@@ -1592,7 +1592,13 @@ CExifDialog::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
EXIFGETVERSION getVer = (EXIFGETVERSION)GetProcAddress(EXIFLibrary, "EXIFGetVersion");
EXIFGETINFO getInfo = (EXIFGETINFO)GetProcAddress(EXIFLibrary, "EXIFGetInfo");
- if (getVer != NULL && getInfo != NULL)
+ EXIFGETINFOW getInfoW = NULL;
+ EXIFGETINFOFROMDATA getInfoFromData =
+ (EXIFGETINFOFROMDATA)GetProcAddress(EXIFLibrary, "EXIFGetInfoFromData");
+#ifdef _UNICODE
+ getInfoW = (EXIFGETINFOW)GetProcAddress(EXIFLibrary, "EXIFGetInfoW");
+#endif
+ if (getVer != NULL && (getInfo != NULL || getInfoW != NULL))
{
DWORD exifVer = getVer();
CALL_STACK_MESSAGE2("EXIFGetInfo() EXIF.DLL version %u", exifVer);
@@ -1624,15 +1630,75 @@ CExifDialog::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
}
else
{
-#ifdef _UNICODE
- char FileNameA[_MAX_PATH];
+ BOOL gotExif = FALSE;
+ CExifFileBuffer exifBuffer;
+ bool bufferLoaded = false;
- WideCharToMultiByte(CP_ACP, 0, FileName, -1, FileNameA, sizeof(FileNameA), NULL, NULL);
- FileNameA[sizeof(FileNameA) - 1] = 0;
- getInfo(FileNameA, 0, ExifEnumProc, (LPARAM)this);
+ if (getInfoFromData)
+ {
+ bufferLoaded = exifBuffer.LoadFromFile(FileName);
+ if (bufferLoaded)
+ {
+ int previousCount = Items.Count;
+ gotExif = getInfoFromData(exifBuffer.GetExifData(),
+ exifBuffer.GetExifSize(),
+ ExifEnumProc,
+ (LPARAM)this);
+ if (gotExif && Items.Count == previousCount)
+ {
+ gotExif = FALSE;
+ }
+ }
+ }
+#ifdef _UNICODE
+ if (!gotExif && getInfoW)
+ {
+ int previousCount = Items.Count;
+ gotExif = getInfoW(FileName, 0, ExifEnumProc, (LPARAM)this);
+ if (gotExif && Items.Count == previousCount)
+ {
+ gotExif = FALSE;
+ }
+ }
+#endif
+ if (!gotExif && getInfo)
+ {
+#ifdef _UNICODE
+ CExifAnsiPath ansiPath;
+ if (ansiPath.PrepareFromFile(FileName))
+ {
+ int previousCount = Items.Count;
+ gotExif = getInfo(ansiPath.GetPath(), 0, ExifEnumProc, (LPARAM)this);
+ if (gotExif && Items.Count == previousCount)
+ {
+ gotExif = FALSE;
+ }
+ }
#else
- getInfo(FileName, 0, ExifEnumProc, (LPARAM)this);
+ int previousCount = Items.Count;
+ gotExif = getInfo(FileName, 0, ExifEnumProc, (LPARAM)this);
+ if (gotExif && Items.Count == previousCount)
+ {
+ gotExif = FALSE;
+ }
#endif
+ }
+ if (!gotExif && getInfoFromData && !bufferLoaded)
+ {
+ bufferLoaded = exifBuffer.LoadFromFile(FileName);
+ if (bufferLoaded)
+ {
+ int previousCount = Items.Count;
+ gotExif = getInfoFromData(exifBuffer.GetExifData(),
+ exifBuffer.GetExifSize(),
+ ExifEnumProc,
+ (LPARAM)this);
+ if (gotExif && Items.Count == previousCount)
+ {
+ gotExif = FALSE;
+ }
+ }
+ }
}
DisableNotification = FALSE;
FillListView();
diff --git a/src/plugins/pictview/exif/exif.c b/src/plugins/pictview/exif/exif.c
index 1c67d3bd9..1cb87aa98 100644
--- a/src/plugins/pictview/exif/exif.c
+++ b/src/plugins/pictview/exif/exif.c
@@ -1,9 +1,11 @@
-// SPDX-FileCopyrightText: 2023 Open Salamander Authors
+// SPDX-FileCopyrightText: 2023 Open Salamander Authors
// SPDX-License-Identifier: GPL-2.0-or-later
#include
#include
#include
+#include
+#include
#include
#include "exif.h"
@@ -12,6 +14,10 @@
#include
#include
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
#pragma warning(push)
#pragma warning(disable : 4267) // FIXME_X64 - warning temporarily suppressed, fix it
#pragma warning(disable : 4133) // FIXME_X64 - warning temporarily suppressed, fix it
@@ -63,34 +69,18 @@ show_ifd(ExifContent* content, void* data)
exif_content_foreach_entry(content, show_entry, data);
}
-DWORD WINAPI
-EXIFGetVersion()
+static BOOL
+enumerate_exif_data(ExifData* ed, EXIFENUMPROC enumFunc, LPARAM lParam)
{
- return EXIF_DLL_VERSION;
-}
+ if (!ed)
+ return FALSE;
-BOOL WINAPI
-EXIFGetInfo(const char* fileName, int dataLen, EXIFENUMPROC enumFunc, LPARAM lParam)
-{
- ExifData* ed;
- ExifMnoteData* md;
+ ExifMnoteData* md = exif_data_get_mnote_data(ed);
struct CEnumData data;
data.EnumFunc = enumFunc;
data.LParam = lParam;
- if (dataLen)
- {
- ed = exif_data_new_from_data(fileName, dataLen);
- }
- else
- {
- ed = exif_data_new_from_file(fileName);
- }
- if (ed == NULL)
- return FALSE;
- md = exif_data_get_mnote_data(ed);
-
exif_data_foreach_content(ed, show_ifd, &data);
if (md)
@@ -129,11 +119,131 @@ EXIFGetInfo(const char* fileName, int dataLen, EXIFENUMPROC enumFunc, LPARAM lPa
}
}
}
+
exif_data_unref(ed);
return TRUE;
}
+static wchar_t*
+duplicate_extended_length_path(const wchar_t* path)
+{
+ if (!path || !*path)
+ return NULL;
+
+ if ((wcslen(path) >= 4) && (wcsncmp(path, L"\\\\?\\", 4) == 0))
+ return _wcsdup(path);
+
+ size_t length = wcslen(path);
+ if (length < MAX_PATH)
+ return _wcsdup(path);
+
+ if ((length >= 2) && (wcsncmp(path, L"\\\\", 2) == 0))
+ {
+ const wchar_t prefix[] = L"\\\\?\\UNC\\";
+ size_t prefixLen = ARRAYSIZE(prefix) - 1;
+ size_t total = prefixLen + length - 2 + 1;
+ wchar_t* extended = (wchar_t*)malloc(total * sizeof(wchar_t));
+ if (!extended)
+ return NULL;
+ wcscpy(extended, prefix);
+ wcscat(extended, path + 2);
+ return extended;
+ }
+
+ const wchar_t prefix[] = L"\\\\?\\";
+ size_t prefixLen = ARRAYSIZE(prefix) - 1;
+ size_t total = prefixLen + length + 1;
+ wchar_t* extended = (wchar_t*)malloc(total * sizeof(wchar_t));
+ if (!extended)
+ return NULL;
+
+ wcscpy(extended, prefix);
+ wcscat(extended, path);
+ return extended;
+}
+
+static ExifData*
+exif_data_new_from_file_w(const wchar_t* fileName)
+{
+ if (!fileName)
+ return NULL;
+
+ ExifLoader* loader = exif_loader_new();
+ if (!loader)
+ return NULL;
+
+ wchar_t* extended = duplicate_extended_length_path(fileName);
+ const wchar_t* pathToOpen = extended ? extended : fileName;
+
+ HANDLE file = CreateFileW(pathToOpen,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL);
+ if (extended)
+ free(extended);
+
+ if (file == INVALID_HANDLE_VALUE)
+ {
+ exif_loader_unref(loader);
+ return NULL;
+ }
+
+ unsigned char buffer[1024];
+ DWORD bytesRead = 0;
+ while (ReadFile(file, buffer, sizeof(buffer), &bytesRead, NULL) && bytesRead > 0)
+ {
+ if (!exif_loader_write(loader, buffer, bytesRead))
+ break;
+ }
+
+ CloseHandle(file);
+
+ ExifData* ed = exif_loader_get_data(loader);
+ exif_loader_unref(loader);
+ return ed;
+}
+
+DWORD WINAPI
+EXIFGetVersion()
+{
+ return EXIF_DLL_VERSION;
+}
+
+BOOL WINAPI
+EXIFGetInfo(const char* fileName, int dataLen, EXIFENUMPROC enumFunc, LPARAM lParam)
+{
+ if (dataLen)
+ {
+ return enumerate_exif_data(exif_data_new_from_data(fileName, dataLen), enumFunc, lParam);
+ }
+ else
+ {
+ return enumerate_exif_data(exif_data_new_from_file(fileName), enumFunc, lParam);
+ }
+}
+
+BOOL WINAPI
+EXIFGetInfoW(const wchar_t* fileName, int dataLen, EXIFENUMPROC enumFunc, LPARAM lParam)
+{
+ if (dataLen)
+ return FALSE;
+
+ return enumerate_exif_data(exif_data_new_from_file_w(fileName), enumFunc, lParam);
+}
+
+BOOL WINAPI
+EXIFGetInfoFromData(const unsigned char* data, unsigned int dataLen, EXIFENUMPROC enumFunc, LPARAM lParam)
+{
+ if (!data || !dataLen)
+ return FALSE;
+
+ return enumerate_exif_data(exif_data_new_from_data(data, dataLen), enumFunc, lParam);
+}
+
// Loads exif data without forced fixup of entries
ExifData* get_exif_data_no_fixups(const char* fileName)
{
@@ -346,17 +456,14 @@ static void orient_enum_ifd(ExifContent* content, void* data)
exif_content_foreach_entry(content, orient_enum_entry, data);
}
-BOOL WINAPI EXIFGetOrientationInfo(const char* fileName, PThumbExifInfo pInfo)
+static BOOL
+populate_orientation_info(ExifData* ed, PThumbExifInfo pInfo)
{
- ExifData* ed;
+ if (!ed)
+ return FALSE;
pInfo->Orient = pInfo->flags = 0;
- ed = exif_data_new_from_file(fileName);
-
- if (ed == NULL)
- return FALSE;
-
exif_data_foreach_content(ed, orient_enum_ifd, pInfo);
exif_data_unref(ed);
@@ -364,4 +471,24 @@ BOOL WINAPI EXIFGetOrientationInfo(const char* fileName, PThumbExifInfo pInfo)
return TRUE;
}
+BOOL WINAPI EXIFGetOrientationInfo(const char* fileName, PThumbExifInfo pInfo)
+{
+ return populate_orientation_info(exif_data_new_from_file(fileName), pInfo);
+}
+
+BOOL WINAPI EXIFGetOrientationInfoW(const wchar_t* fileName, PThumbExifInfo pInfo)
+{
+ return populate_orientation_info(exif_data_new_from_file_w(fileName), pInfo);
+}
+
+BOOL WINAPI EXIFGetOrientationInfoFromData(const unsigned char* buffer,
+ unsigned int dataLen,
+ PThumbExifInfo pInfo)
+{
+ if (!buffer || !dataLen)
+ return FALSE;
+
+ return populate_orientation_info(exif_data_new_from_data(buffer, dataLen), pInfo);
+}
+
#pragma warning(pop) // FIXME_X64 - warning temporarily suppressed, fix it
diff --git a/src/plugins/pictview/exif/exif.def b/src/plugins/pictview/exif/exif.def
index 39c6ecbdc..33c2b6ee0 100644
--- a/src/plugins/pictview/exif/exif.def
+++ b/src/plugins/pictview/exif/exif.def
@@ -1,9 +1,13 @@
LIBRARY EXIF.DLL
-VERSION 8.0
+VERSION 9.0
EXPORTS EXIFGetVersion
EXPORTS EXIFGetInfo
+EXPORTS EXIFGetInfoW
+EXPORTS EXIFGetInfoFromData
EXPORTS EXIFGetOrientationInfo
+EXPORTS EXIFGetOrientationInfoW
+EXPORTS EXIFGetOrientationInfoFromData
EXPORTS EXIFReplaceThumbnail
EXPORTS EXIFInitTranslations
EXPORTS ConvertUTF8ToUCS2
diff --git a/src/plugins/pictview/exif/exif.h b/src/plugins/pictview/exif/exif.h
index 33b0238f8..878efb176 100644
--- a/src/plugins/pictview/exif/exif.h
+++ b/src/plugins/pictview/exif/exif.h
@@ -9,7 +9,7 @@
// Version 6: 2007.05.15: ConvertUTF8ToUCS2 is exported
// NOTE: the version also needs to be increased in the exif.def file
-#define EXIF_DLL_VERSION 8
+#define EXIF_DLL_VERSION 9
#ifndef RC_INVOKED
@@ -38,6 +38,16 @@ typedef BOOL(WINAPI* EXIFGETINFO)(const char* fileName,
EXIFENUMPROC enumFunc,
LPARAM lParam);
+typedef BOOL(WINAPI* EXIFGETINFOFROMDATA)(const unsigned char* data,
+ unsigned int dataLen,
+ EXIFENUMPROC enumFunc,
+ LPARAM lParam);
+
+typedef BOOL(WINAPI* EXIFGETINFOW)(const wchar_t* fileName,
+ int dataLen,
+ EXIFENUMPROC enumFunc,
+ LPARAM lParam);
+
typedef BOOL(WINAPI* EXIFREPLACETHUMBNAIL)(char* fileName, char* newFile,
unsigned char* pData, int size);
@@ -62,6 +72,12 @@ typedef struct _thumbExifInfo
typedef BOOL(WINAPI* EXIFGETORIENTATIONINFO)(const char* filename, PThumbExifInfo pInfo);
+typedef BOOL(WINAPI* EXIFGETORIENTATIONINFOW)(const wchar_t* filename, PThumbExifInfo pInfo);
+
+typedef BOOL(WINAPI* EXIFGETORIENTATIONINFOFROMDATA)(const unsigned char* data,
+ unsigned int dataLen,
+ PThumbExifInfo pInfo);
+
#ifdef __cplusplus
extern "C"
{
diff --git a/src/plugins/pictview/help/hh/pictview/introduction_intro.htm b/src/plugins/pictview/help/hh/pictview/introduction_intro.htm
index 8f435ad49..67aaf7beb 100644
--- a/src/plugins/pictview/help/hh/pictview/introduction_intro.htm
+++ b/src/plugins/pictview/help/hh/pictview/introduction_intro.htm
@@ -14,11 +14,11 @@
Getting Started with PictView Plugin
The PictView plugin is an image viewer, converter, and thumbnailer. It supports more than
-60 file formats . PictView plugin is based on the
-famous freeware PictView
-(for DOS and Windows) image manipulation program and it utilizes
-PVW32Cnv.DLL ,
-an image rendering library that can be licensed for use by third-party programs.
+60 file formats and relies on the
+Windows Imaging Component (WIC) built into Microsoft Windows for decoding and encoding
+image data. The original freeware PictView
+application inspired this plugin, but the closed-source PVW32Cnv.dll backend has been replaced
+with the WIC imaging services that ship with the operating system.
See File Viewer, Menu Extension and Thumbnail Loader sections in
Using Plugins
diff --git a/src/plugins/pictview/lang/lang.rc b/src/plugins/pictview/lang/lang.rc
index 9bbacd99d..a25f491fb 100644
--- a/src/plugins/pictview/lang/lang.rc
+++ b/src/plugins/pictview/lang/lang.rc
@@ -77,13 +77,13 @@ BEGIN
ICON "",IDC_ABOUT_ICON,11,9,20,20
LTEXT "PictView %s - Picture Viewer for Open Salamander",IDC_ABOUT_TITLE,46,9,288,8
LTEXT "",IDC_ABOUT_COPYRIGHT,46,19,288,8,SS_NOPREFIX
- LTEXT "This product is based on library",IDC_STATIC_2,46,40,123,8
- LTEXT "%hs - Image Processing Engine",IDC_ABOUT_PVW32,46,51,277,8
- LTEXT "Copyright © 1994-2023 Jan Patera",IDC_STATIC_3,46,62,198,8
+ LTEXT "This plugin uses the following imaging backend:",IDC_STATIC_2,46,40,190,8
+ LTEXT "%hs",IDC_ABOUT_PVW32,46,51,277,8
+ LTEXT "Powered by Microsoft Windows Imaging Component",IDC_STATIC_3,46,62,220,8
LTEXT "Email:",IDC_STATIC_6,46,81,30,8
- LTEXT "support@pictview.com",IDC_ABOUT_EMAIL,99,81,85,8,WS_TABSTOP
- LTEXT "Home page:",IDC_STATIC_7,46,92,50,8
- LTEXT "www.pictview.com/salamander",IDC_ABOUT_WWW,99,92,122,8,WS_TABSTOP
+ LTEXT "support@opensalamander.org",IDC_ABOUT_EMAIL,99,81,120,8,WS_TABSTOP
+ LTEXT "Project site:",IDC_STATIC_7,46,92,50,8
+ LTEXT "www.opensalamander.org",IDC_ABOUT_WWW,99,92,122,8,WS_TABSTOP
CONTROL "",IDC_STATIC_5,"Static",SS_ETCHEDHORZ | WS_GROUP,2,108,339,1
END
diff --git a/src/plugins/pictview/lang/lang.rc2 b/src/plugins/pictview/lang/lang.rc2
index c31286b90..8ca99026e 100644
--- a/src/plugins/pictview/lang/lang.rc2
+++ b/src/plugins/pictview/lang/lang.rc2
@@ -459,7 +459,7 @@ STRINGTABLE
IDS_NCOLORS_CMYK, "CMYK"
IDS_NCOLORS_LAB, "LAB"
- IDS_ERROR_CALLING_PVW32_DLL, "Error calling PVW32Cnv.DLL"
+ IDS_ERROR_CALLING_PVW32_DLL, "Error calling Windows Imaging Component (WIC)."
IDS_CLIPBOARD_TITLE, ""
IDS_CAPTURE_TITLE, ""
IDS_SCAN_TITLE, ""
@@ -701,8 +701,8 @@ STRINGTABLE
IDS_HIST_TIP_BLUE, "Blue (B)"
IDS_HIST_TIP_RGBSUM, "RGB (A)"
IDS_HIST_TIP_OTHERCHANELS, "Show Other Channels (O)"
- IDS_DLL_NOTFOUND, "Unable to load image processing library PVW32Cnv.dll."
- IDS_DLL_WRONG_VERSION, "The wrong version of PVW32Cnv.dll was found."
+ IDS_DLL_NOTFOUND, "Unable to initialize the Windows Imaging Component (WIC) imaging engine."
+ IDS_DLL_WRONG_VERSION, "An incompatible Windows Imaging Component (WIC) version was found."
IDS_SELECT_REGION, "Please select a region first."
IDS_SAVE_ERR_EXISTS_OVERWRITE, "File ""%s"" already exists.\nDo you want to replace it?"
diff --git a/src/plugins/pictview/lib/PVW32DLL.h b/src/plugins/pictview/lib/PVW32DLL.h
index 34040a70c..949c8604a 100644
--- a/src/plugins/pictview/lib/PVW32DLL.h
+++ b/src/plugins/pictview/lib/PVW32DLL.h
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*****************************************************************************
- * PVW32DLL.H - C Interface to PVW32.DLL and PVW32Cnv.DLL
+ * PVW32DLL.H - Compatibility interface mirroring the legacy PVW32Cnv API
*
* Version 1.56
*
diff --git a/src/plugins/pictview/lib/readme.txt b/src/plugins/pictview/lib/readme.txt
index a72d1de98..95c538436 100644
--- a/src/plugins/pictview/lib/readme.txt
+++ b/src/plugins/pictview/lib/readme.txt
@@ -1,7 +1,5 @@
-11/2023
+10/2025
-We have removed PVW32Cnv.lib because its source code is not available, which violates the GPL license.
-
-TODO: Replace the PictView engine with WIC or another library.
-
-If you want to compile the PictView project, contact us for PVW32Cnv.lib and PVW32Cnv.dll.
\ No newline at end of file
+The closed-source PVW32Cnv backend has been fully replaced by an in-tree implementation that
+uses the Windows Imaging Component (WIC) APIs available on modern versions of Windows. The
+legacy PVW32Cnv.lib import library is no longer required to build the plugin.
\ No newline at end of file
diff --git a/src/plugins/pictview/pictview.cpp b/src/plugins/pictview/pictview.cpp
index d7fb3652b..82ae5050e 100644
--- a/src/plugins/pictview/pictview.cpp
+++ b/src/plugins/pictview/pictview.cpp
@@ -3,6 +3,12 @@
#include "precomp.h"
+#include
+#include
+#include
+#include
+#include
+
#include "lib/pvw32dll.h"
#include "pictview.h"
#include "dialogs.h"
@@ -18,7 +24,7 @@
#include "pictview.rh2"
#include "lang/lang.rh"
#include "histwnd.h"
-#include "PVEXEWrapper.h"
+#include "wic/WicBackend.h"
#include "PixelAccess.h"
// plugin interface object; its methods are called from Salamander
@@ -449,7 +455,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
DLLInstance = hinstDLL;
}
- if (fdwReason == DLL_PROCESS_DETACH) // shutdown (unload) PVW32.SPL
+ if (fdwReason == DLL_PROCESS_DETACH) // shutdown (unload) PictView.spl
{
if (EXIFLibrary != NULL)
{
@@ -457,7 +463,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
EXIFLibrary = NULL;
}
if (PVW32DLL.Handle != NULL)
- FreeLibrary(PVW32DLL.Handle); // release PVW32.DLL as well
+ FreeLibrary(PVW32DLL.Handle); // release the imaging backend module as well
if (G.HAccel != NULL)
DestroyAcceleratorTable(G.HAccel);
if (G.CaptureAtomID != 0)
@@ -527,7 +533,7 @@ CPluginInterfaceAbstract* WINAPI SalamanderPluginEntry(CSalamanderPluginEntryAbs
_T("PictView") /* do not translate! */);
// set the plugin home page URL
- salamander->SetPluginHomePageURL("www.pictview.com/salamander");
+ salamander->SetPluginHomePageURL("www.opensalamander.org");
// If we crash inside pictview.spl, this message box will be displayed
// and the happy recipient of the images will be Honza Patera.
@@ -535,7 +541,7 @@ CPluginInterfaceAbstract* WINAPI SalamanderPluginEntry(CSalamanderPluginEntryAbs
TCHAR exceptInfo[512];
lstrcpyn(exceptInfo, LoadStr(IDS_EXCEPT_INFO1), SizeOf(exceptInfo));
_tcsncat(exceptInfo, LoadStr(IDS_EXCEPT_INFO2), SizeOf(exceptInfo) - _tcslen(exceptInfo));
- SalamanderGeneral->SetPluginBugReportInfo(exceptInfo, "support@pictview.com");
+ SalamanderGeneral->SetPluginBugReportInfo(exceptInfo, "support@opensalamander.org");
return &PluginInterface;
}
@@ -1358,59 +1364,12 @@ void WINAPI HTMLHelpCallback(HWND hWindow, UINT helpID)
BOOL LoadPictViewDll(HWND hParentWnd)
{
- TCHAR path[_MAX_PATH];
-
- if (!GetModuleFileName(DLLInstance, path, SizeOf(path)))
- {
- TRACE_E("GetModuleFileName failed");
- return FALSE;
- }
- _tcsrchr(path, '\\')[0] = 0;
-#ifndef PICTVIEW_DLL_IN_SEPARATE_PROCESS
- _tcscat(path, _T("\\PVW32Cnv.dll"));
- PVW32DLL.Handle = LoadLibrary(path); // load PVW32Cnv.dll
- if (!PVW32DLL.Handle)
+ if (!PictView::Wic::Backend::Instance().Populate(PVW32DLL))
{
- TRACE_E("LoadLibrary(PVW32Cnv.dll) failed");
- SalamanderGeneral->SalMessageBox(hParentWnd, LoadStr(IDS_DLL_NOTFOUND),
- LoadStr(IDS_ERRORTITLE), MB_ICONSTOP | MB_OK);
- return FALSE;
- }
- PVW32DLL.PVReadImage2 = (TPVReadImage2)GetProcAddress(PVW32DLL.Handle, "PVReadImage2");
- PVW32DLL.PVCloseImage = (TPVCloseImage)GetProcAddress(PVW32DLL.Handle, "PVCloseImage");
- PVW32DLL.PVDrawImage = (TPVDrawImage)GetProcAddress(PVW32DLL.Handle, "PVDrawImage");
- PVW32DLL.PVGetErrorText = (TPVGetErrorText)GetProcAddress(PVW32DLL.Handle, "PVGetErrorText");
- PVW32DLL.PVOpenImageEx = (TPVOpenImageEx)GetProcAddress(PVW32DLL.Handle, "PVOpenImageEx");
- PVW32DLL.PVSetBkHandle = (TPVSetBkHandle)GetProcAddress(PVW32DLL.Handle, "PVSetBkHandle");
- PVW32DLL.PVGetDLLVersion = (TPVGetDLLVersion)GetProcAddress(PVW32DLL.Handle, "PVGetDLLVersion");
- PVW32DLL.PVSetStretchParameters = (TPVSetStretchParameters)GetProcAddress(PVW32DLL.Handle, "PVSetStretchParameters");
- PVW32DLL.PVLoadFromClipboard = (TPVLoadFromClipboard)GetProcAddress(PVW32DLL.Handle, "PVLoadFromClipboard");
- PVW32DLL.PVGetImageInfo = (TPVGetImageInfo)GetProcAddress(PVW32DLL.Handle, "PVGetImageInfo");
- PVW32DLL.PVSetParam = (TPVSetParam)GetProcAddress(PVW32DLL.Handle, "PVSetParam");
- PVW32DLL.PVGetHandles2 = (TPVGetHandles2)GetProcAddress(PVW32DLL.Handle, "PVGetHandles2");
- PVW32DLL.PVSaveImage = (TPVSaveImage)GetProcAddress(PVW32DLL.Handle, "PVSaveImage");
- PVW32DLL.PVChangeImage = (TPVChangeImage)GetProcAddress(PVW32DLL.Handle, "PVChangeImage");
- PVW32DLL.PVIsOutCombSupported = (TPVIsOutCombSupported)GetProcAddress(PVW32DLL.Handle, "PVIsOutCombSupported");
- PVW32DLL.PVReadImageSequence = (TPVReadImageSequence)GetProcAddress(PVW32DLL.Handle, "PVReadImageSequence");
- PVW32DLL.PVCropImage = (TPVCropImage)GetProcAddress(PVW32DLL.Handle, "PVCropImage");
- PVW32DLL.GetRGBAtCursor = GetRGBAtCursor;
- PVW32DLL.CalculateHistogram = CalculateHistogram;
- PVW32DLL.CreateThumbnail = CreateThumbnail;
- PVW32DLL.SimplifyImageSequence = SimplifyImageSequence;
-
- if (!PVW32DLL.PVReadImage2 || !PVW32DLL.PVIsOutCombSupported || !PVW32DLL.PVChangeImage || !PVW32DLL.PVSetParam || !PVW32DLL.PVGetHandles2 || !PVW32DLL.PVCropImage)
- {
- TRACE_E("PVW32Cnv was not compiled for Salamander or an old version was found");
- SalamanderGeneral->SalMessageBox(hParentWnd, LoadStr(IDS_DLL_WRONG_VERSION),
- LoadStr(IDS_ERRORTITLE), MB_ICONSTOP | MB_OK);
+ SalamanderGeneral->SalMessageBox(hParentWnd, LoadStr(IDS_DLL_NOTFOUND), LoadStr(IDS_ERRORTITLE),
+ MB_ICONSTOP | MB_OK);
return FALSE;
}
-#else // PICTVIEW_DLL_IN_SEPARATE_PROCESS
- if (!InitPVEXEWrapper(hParentWnd, path))
- {
- return FALSE;
- }
-#endif // PICTVIEW_DLL_IN_SEPARATE_PROCESS
return TRUE;
}
@@ -1459,7 +1418,8 @@ BOOL InitViewer(HWND hParentWnd)
}
i = PVW32DLL.PVGetDLLVersion();
- sprintf(PVW32DLL.Version, "PVW32Cnv.dll %d.%#02d.%d", i >> 16, i & 255, (i >> 8) & 255);
+ _snprintf_s(PVW32DLL.Version, SizeOf(PVW32DLL.Version), _TRUNCATE, "WIC backend %u.%02u",
+ static_cast(PV_VERSION_MAJOR(i)), static_cast(PV_VERSION_MINOR(i)));
PVW32DLL.PVSetParam(GetExtText);
@@ -1541,11 +1501,571 @@ BOOL InitEXIF(HWND hParent, BOOL bSilent)
return TRUE;
}
-void ReleaseViewer()
+bool ConvertPathToExifEncoding(LPCTSTR path, char* buffer, int bufferSize)
+{
+#ifdef _UNICODE
+ if (buffer == NULL || bufferSize <= 0)
+ return false;
+
+ buffer[0] = '\0';
+
+ if (path == NULL)
+ return false;
+
+ BOOL usedDefaultChar = FALSE;
+ int result = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, path, -1, buffer, bufferSize, NULL, &usedDefaultChar);
+ if (result > 0 && !usedDefaultChar)
+ return true;
+
+ TCHAR shortPath[MAX_PATH];
+ DWORD shortLen = GetShortPathName(path, shortPath, SizeOf(shortPath));
+ if (shortLen > 0 && shortLen < SizeOf(shortPath))
+ {
+ usedDefaultChar = FALSE;
+ result = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortPath, -1, buffer, bufferSize, NULL, &usedDefaultChar);
+ if (result > 0 && !usedDefaultChar)
+ return true;
+ }
+
+ WideCharToMultiByte(CP_ACP, 0, path, -1, buffer, bufferSize, NULL, NULL);
+ return false;
+#else
+ if (buffer == NULL || bufferSize <= 0)
+ return false;
+
+ if (path == NULL)
+ {
+ buffer[0] = '\0';
+ return false;
+ }
+
+ lstrcpyn(buffer, path, bufferSize);
+ return true;
+#endif
+}
+
+#ifdef _UNICODE
+static std::wstring BuildExtendedLengthPath(LPCTSTR path)
+{
+ if (path == NULL || *path == 0)
+ return std::wstring();
+
+ if (_tcsncmp(path, _T("\\\\?\\"), 4) == 0)
+ return std::wstring(path);
+
+ size_t length = _tcslen(path);
+ if (length < MAX_PATH)
+ return std::wstring();
+
+ if (_tcsncmp(path, _T("\\\\"), 2) == 0)
+ return std::wstring(_T("\\\\?\\UNC\\")) + (path + 2);
+
+ return std::wstring(_T("\\\\?\\")) + path;
+}
+#else
+static std::wstring BuildExtendedLengthPathWide(const wchar_t* path)
+{
+ if (path == NULL || *path == L'\0')
+ return std::wstring();
+
+ if (wcsncmp(path, L"\\\\?\\", 4) == 0)
+ return std::wstring(path);
+
+ size_t length = wcslen(path);
+ if (length < MAX_PATH)
+ return std::wstring();
+
+ if (wcsncmp(path, L"\\\\", 2) == 0)
+ return std::wstring(L"\\\\?\\UNC\\") + (path + 2);
+
+ return std::wstring(L"\\\\?\\") + path;
+}
+#endif
+
+namespace
+{
+ bool ReadExact(HANDLE file, void* buffer, DWORD bytes)
+ {
+ if (bytes == 0)
+ return true;
+
+ BYTE* out = static_cast(buffer);
+ DWORD remaining = bytes;
+ while (remaining > 0)
+ {
+ DWORD chunk = 0;
+ if (!ReadFile(file, out, remaining, &chunk, NULL) || chunk == 0)
+ return false;
+
+ out += chunk;
+ remaining -= chunk;
+ }
+
+ return true;
+ }
+
+ bool SkipBytes(HANDLE file, size_t bytes)
+ {
+ LARGE_INTEGER distance;
+ distance.QuadPart = static_cast(bytes);
+ return SetFilePointerEx(file, distance, NULL, FILE_CURRENT) != 0;
+ }
+
+ bool LoadExifFromJpegStream(HANDLE file,
+ size_t maxBytes,
+ std::vector& buffer,
+ const unsigned char*& exifData,
+ unsigned int& exifSize)
+ {
+ LARGE_INTEGER zero = {};
+ if (!SetFilePointerEx(file, zero, NULL, FILE_BEGIN))
+ return false;
+
+ BYTE header[2];
+ DWORD read = 0;
+ if (!ReadFile(file, header, 2, &read, NULL) || read != 2)
+ return false;
+
+ if (header[0] != 0xFF || header[1] != 0xD8)
+ return false;
+
+ buffer.clear();
+
+ for (;;)
+ {
+ BYTE prefix = 0;
+ do
+ {
+ if (!ReadFile(file, &prefix, 1, &read, NULL) || read != 1)
+ return false;
+ } while (prefix != 0xFF);
+
+ BYTE marker = 0;
+ do
+ {
+ if (!ReadFile(file, &marker, 1, &read, NULL) || read != 1)
+ return false;
+ } while (marker == 0xFF);
+
+ if (marker == 0x00)
+ continue;
+ if (marker == 0xD8)
+ continue;
+ if (marker == 0xD9 || marker == 0xDA)
+ break;
+ if (marker == 0x01)
+ continue;
+ if (marker >= 0xD0 && marker <= 0xD7)
+ continue;
+
+ BYTE lengthBytes[2];
+ if (!ReadFile(file, lengthBytes, 2, &read, NULL) || read != 2)
+ return false;
+
+ unsigned int segmentLength = (lengthBytes[0] << 8) | lengthBytes[1];
+ if (segmentLength < 2)
+ return false;
+
+ unsigned int payloadLength = segmentLength - 2;
+
+ if (marker == 0xE1)
+ {
+ if (payloadLength < 6)
+ {
+ if (!SkipBytes(file, payloadLength))
+ return false;
+ continue;
+ }
+
+ if (payloadLength > maxBytes)
+ {
+ if (!SkipBytes(file, payloadLength))
+ return false;
+ continue;
+ }
+
+ buffer.resize(payloadLength);
+ if (!ReadExact(file, buffer.data(), payloadLength))
+ {
+ buffer.clear();
+ return false;
+ }
+
+ if (memcmp(buffer.data(), "Exif\0\0", 6) == 0)
+ {
+ exifData = buffer.data();
+ exifSize = payloadLength;
+ return true;
+ }
+
+ buffer.clear();
+ continue;
+ }
+
+ if (!SkipBytes(file, payloadLength))
+ return false;
+ }
+
+ buffer.clear();
+ return false;
+ }
+
+ bool LoadExifByScanning(HANDLE file,
+ size_t maxBytes,
+ std::vector& buffer,
+ const unsigned char*& exifData,
+ unsigned int& exifSize)
+ {
+ LARGE_INTEGER zero = {};
+ if (!SetFilePointerEx(file, zero, NULL, FILE_BEGIN))
+ return false;
+
+ buffer.clear();
+
+ const size_t chunkSize = 64 * 1024;
+ size_t totalRead = 0;
+ size_t searchStart = 0;
+ const unsigned char signature[] = {'E', 'x', 'i', 'f', 0, 0};
+ size_t foundOffset = SIZE_MAX;
+ size_t declaredLength = 0;
+ bool haveDeclaredLength = false;
+
+ while (totalRead < maxBytes)
+ {
+ size_t toRead = chunkSize;
+ if (toRead > maxBytes - totalRead)
+ toRead = maxBytes - totalRead;
+ if (toRead == 0)
+ break;
+
+ size_t start = buffer.size();
+ buffer.resize(start + toRead);
+
+ DWORD bytesRead = 0;
+ if (!ReadFile(file, &buffer[start], static_cast(toRead), &bytesRead, NULL))
+ {
+ buffer.resize(start);
+ return false;
+ }
+
+ if (bytesRead == 0)
+ {
+ buffer.resize(start);
+ break;
+ }
+
+ buffer.resize(start + bytesRead);
+ totalRead += bytesRead;
+
+ size_t searchLimit = 0;
+ if (buffer.size() >= sizeof(signature))
+ searchLimit = buffer.size() - sizeof(signature) + 1;
+
+ for (size_t i = searchStart; i < searchLimit; i++)
+ {
+ if (memcmp(&buffer[i], signature, sizeof(signature)) == 0)
+ {
+ foundOffset = i;
+ if (i >= 4 && buffer[i - 4] == 0xFF && buffer[i - 3] == 0xE1)
+ {
+ size_t declared = (static_cast(buffer[i - 2]) << 8) | buffer[i - 1];
+ if (declared >= 2)
+ {
+ declaredLength = declared - 2; // payload length excludes the length bytes themselves
+ haveDeclaredLength = true;
+ }
+ else
+ {
+ declaredLength = 0;
+ haveDeclaredLength = false;
+ }
+ }
+ else
+ {
+ declaredLength = 0;
+ haveDeclaredLength = false;
+ }
+ break;
+ }
+ }
+
+ if (foundOffset != SIZE_MAX)
+ break;
+
+ searchStart = (buffer.size() > 5) ? buffer.size() - 5 : 0;
+
+ if (bytesRead < toRead)
+ break;
+ }
+
+ if (foundOffset == SIZE_MAX)
+ {
+ buffer.clear();
+ return false;
+ }
+
+ if (haveDeclaredLength && declaredLength > 0)
+ {
+ size_t required = foundOffset + declaredLength;
+ while (buffer.size() < required && totalRead < maxBytes)
+ {
+ size_t toRead = chunkSize;
+ if (toRead > maxBytes - totalRead)
+ toRead = maxBytes - totalRead;
+ if (toRead == 0)
+ break;
+
+ size_t start = buffer.size();
+ buffer.resize(start + toRead);
+
+ DWORD bytesRead = 0;
+ if (!ReadFile(file, &buffer[start], static_cast(toRead), &bytesRead, NULL))
+ {
+ buffer.resize(start);
+ break;
+ }
+
+ if (bytesRead == 0)
+ {
+ buffer.resize(start);
+ break;
+ }
+
+ buffer.resize(start + bytesRead);
+ totalRead += bytesRead;
+
+ if (bytesRead < toRead)
+ break;
+ }
+ }
+
+ size_t available = buffer.size() - foundOffset;
+ size_t finalLength = available;
+ if (haveDeclaredLength)
+ {
+ if (declaredLength > available)
+ {
+ buffer.clear();
+ return false;
+ }
+ finalLength = declaredLength;
+ }
+
+ if (finalLength == 0)
+ {
+ buffer.clear();
+ return false;
+ }
+
+ if (finalLength > static_cast(std::numeric_limits::max()))
+ finalLength = std::numeric_limits::max();
+
+ exifData = &buffer[foundOffset];
+ exifSize = static_cast(finalLength);
+ return exifSize != 0;
+ }
+}
+
+CExifAnsiPath::CExifAnsiPath()
{
-#ifdef PICTVIEW_DLL_IN_SEPARATE_PROCESS
- ReleasePVEXEWrapper();
+ Path[0] = '\0';
+#ifdef _UNICODE
+ TempFile[0] = '\0';
+ UsingTempCopy = false;
#endif
+}
+
+CExifAnsiPath::~CExifAnsiPath()
+{
+#ifdef _UNICODE
+ if (UsingTempCopy && TempFile[0])
+ {
+ std::wstring extendedTemp = BuildExtendedLengthPath(TempFile);
+ LPCTSTR deletePath = extendedTemp.empty() ? TempFile : extendedTemp.c_str();
+ DeleteFile(deletePath);
+ TempFile[0] = '\0';
+ UsingTempCopy = false;
+ }
+#endif
+}
+
+bool CExifAnsiPath::PrepareFromFile(LPCTSTR sourcePath)
+{
+ Path[0] = '\0';
+#ifdef _UNICODE
+ if (UsingTempCopy && TempFile[0])
+ {
+ std::wstring extendedTemp = BuildExtendedLengthPath(TempFile);
+ LPCTSTR deletePath = extendedTemp.empty() ? TempFile : extendedTemp.c_str();
+ DeleteFile(deletePath);
+ }
+ UsingTempCopy = false;
+ TempFile[0] = '\0';
+#endif
+
+ if (sourcePath == NULL)
+ return false;
+
+ if (ConvertPathToExifEncoding(sourcePath, Path, sizeof(Path)))
+ return true;
+
+#ifdef _UNICODE
+ TCHAR tempDir[MAX_PATH];
+ if (!GetTempPath(SizeOf(tempDir), tempDir))
+ return false;
+
+ TCHAR tempFile[MAX_PATH];
+ if (!GetTempFileName(tempDir, _T("pvx"), 0, tempFile))
+ return false;
+
+ std::wstring sourceExtended = BuildExtendedLengthPath(sourcePath);
+ LPCTSTR copySource = sourceExtended.empty() ? sourcePath : sourceExtended.c_str();
+ std::wstring tempExtended = BuildExtendedLengthPath(tempFile);
+ LPCTSTR copyDest = tempExtended.empty() ? tempFile : tempExtended.c_str();
+
+ if (!CopyFile(copySource, copyDest, FALSE))
+ {
+ DeleteFile(copyDest);
+ return false;
+ }
+
+ if (!ConvertPathToExifEncoding(tempFile, Path, sizeof(Path)))
+ {
+ DeleteFile(copyDest);
+ return false;
+ }
+
+ lstrcpyn(TempFile, tempFile, SizeOf(TempFile));
+ UsingTempCopy = true;
+ return true;
+#else
+ return false;
+#endif
+}
+
+CExifFileBuffer::CExifFileBuffer()
+{
+ ExifData = NULL;
+ ExifSize = 0;
+}
+
+bool CExifFileBuffer::LoadFromFile(LPCTSTR sourcePath, size_t maxBytes)
+{
+ ExifData = NULL;
+ ExifSize = 0;
+ Buffer.clear();
+
+ if (!sourcePath || maxBytes == 0)
+ return false;
+
+ HANDLE file;
+#ifdef _UNICODE
+ std::wstring extendedPath = BuildExtendedLengthPath(sourcePath);
+ LPCTSTR openPath = extendedPath.empty() ? sourcePath : extendedPath.c_str();
+ file = CreateFileW(openPath,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL);
+#else
+ file = CreateFileA(sourcePath,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL);
+#endif
+ if (file == INVALID_HANDLE_VALUE)
+ return false;
+
+ const unsigned char* data = NULL;
+ unsigned int size = 0;
+
+ bool success = LoadExifFromJpegStream(file, maxBytes, Buffer, data, size);
+ if (!success)
+ {
+ success = LoadExifByScanning(file, maxBytes, Buffer, data, size);
+ }
+
+ CloseHandle(file);
+
+ if (!success)
+ {
+ Buffer.clear();
+ return false;
+ }
+
+ ExifData = data;
+ ExifSize = size;
+ return true;
+}
+
+#ifndef _UNICODE
+bool CExifFileBuffer::LoadFromWideFile(const wchar_t* sourcePath, size_t maxBytes)
+{
+ ExifData = NULL;
+ ExifSize = 0;
+ Buffer.clear();
+
+ if (!sourcePath || maxBytes == 0)
+ return false;
+
+ std::wstring extendedPath = BuildExtendedLengthPathWide(sourcePath);
+ LPCWSTR openPath = extendedPath.empty() ? sourcePath : extendedPath.c_str();
+
+ HANDLE file = CreateFileW(openPath,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL);
+ if (file == INVALID_HANDLE_VALUE)
+ return false;
+
+ const unsigned char* data = NULL;
+ unsigned int size = 0;
+
+ bool success = LoadExifFromJpegStream(file, maxBytes, Buffer, data, size);
+ if (!success)
+ {
+ success = LoadExifByScanning(file, maxBytes, Buffer, data, size);
+ }
+
+ CloseHandle(file);
+
+ if (!success)
+ {
+ Buffer.clear();
+ return false;
+ }
+
+ ExifData = data;
+ ExifSize = size;
+ return true;
+}
+#endif
+
+bool CExifFileBuffer::HasExifData() const
+{
+ return ExifData != NULL && ExifSize != 0;
+}
+
+const unsigned char* CExifFileBuffer::GetExifData() const
+{
+ return ExifData;
+}
+
+unsigned int CExifFileBuffer::GetExifSize() const
+{
+ return ExifSize;
+}
+
+void ReleaseViewer()
+{
if (!UnregisterClass(TIP_WINDOW_CLASSNAME, DLLInstance))
TRACE_E("UnregisterClass(TIP_WINDOW_CLASSNAME) has failed");
ReleaseWinLib(DLLInstance);
@@ -1639,7 +2159,7 @@ class CViewerThread : public CThread
/*
unsigned WINAPI ViewerThreadBody(void *param)
{
- CALL_STACK_MESSAGE3(_T("ViewerThreadBody() PictView.spl %s %hs"), VERSINFO_VERSION, PVW32DLL.Version);
+ CALL_STACK_MESSAGE3(_T("ViewerThreadBody() PictView.spl %s backend %hs"), VERSINFO_VERSION, PVW32DLL.Version);
SetThreadNameInVCAndTrace(PLUGIN_NAME_EN);
TRACE_I("Begin");
RECT r;
@@ -1758,7 +2278,7 @@ unsigned __stdcall ViewerThread(void *param)
unsigned
CViewerThread::Body()
{
- CALL_STACK_MESSAGE3(_T("ViewerThreadBody() PictView.spl %s %hs"), VERSINFO_VERSION, PVW32DLL.Version);
+ CALL_STACK_MESSAGE3(_T("ViewerThreadBody() PictView.spl %s backend %hs"), VERSINFO_VERSION, PVW32DLL.Version);
SetThreadNameInVCAndTrace(PLUGIN_NAME_EN);
TRACE_I("Begin");
diff --git a/src/plugins/pictview/pictview.h b/src/plugins/pictview/pictview.h
index f20d591f1..14ca3d884 100644
--- a/src/plugins/pictview/pictview.h
+++ b/src/plugins/pictview/pictview.h
@@ -25,7 +25,7 @@ typedef DWORD(WINAPI* TPVIsOutCombSupported)(int Fmt, int Compr, int Colors, int
typedef PVCODE(WINAPI* TPVReadImageSequence)(LPPVHandle Img, LPPVImageSequence* ppSeq);
typedef PVCODE(WINAPI* TPVCropImage)(LPPVHandle Img, int Left, int Top, int Width, int Height);
-// Internal functions, not directly imported from PVW32Cnv.dll
+// Internal helper entry points provided by the in-process imaging backend
typedef bool (*TPVGetRGBAtCursor)(LPPVHandle Img, DWORD Colors, int x, int y, RGBQUAD* pRGB, int* pIndex);
typedef PVCODE (*TPVCalculateHistogram)(LPPVHandle PVHandle, const LPPVImageInfo pvii, LPDWORD luminosity, LPDWORD red, LPDWORD green, LPDWORD blue, LPDWORD rgb);
typedef PVCODE (*TPVCreateThumbnail)(LPPVHandle hPVImage, LPPVSaveImageInfo pSii, int imageIndex, DWORD imgWidth, DWORD imgHeight,
@@ -60,7 +60,7 @@ struct CPVW32DLL
TPVCalculateHistogram CalculateHistogram;
TPVCreateThumbnail CreateThumbnail;
TPVSimplifyImageSequence SimplifyImageSequence;
- HMODULE Handle; // handle of the opened PVW32Cnv.DLL library
+ HMODULE Handle; // handle of the active imaging backend module
char Version[28]; // initialized together with Handle in DllMain on DLL_PROCESS_ATTACH
};
@@ -451,6 +451,49 @@ WCHAR* LoadStrW(int resID);
BOOL InitViewer(HWND hParentWnd);
void ReleaseViewer();
BOOL InitEXIF(HWND hParent, BOOL bSilent);
+bool ConvertPathToExifEncoding(LPCTSTR path, char* buffer, int bufferSize);
+
+#ifndef RC_INVOKED
+#include
+#endif
+
+class CExifAnsiPath
+{
+public:
+ CExifAnsiPath();
+ ~CExifAnsiPath();
+
+ bool PrepareFromFile(LPCTSTR sourcePath);
+ const char* GetPath() const { return Path; }
+
+private:
+ char Path[_MAX_PATH];
+#ifdef _UNICODE
+ TCHAR TempFile[MAX_PATH];
+ bool UsingTempCopy;
+#endif
+};
+
+#ifndef RC_INVOKED
+class CExifFileBuffer
+{
+public:
+ CExifFileBuffer();
+
+ bool LoadFromFile(LPCTSTR sourcePath, size_t maxBytes = 16 * 1024 * 1024);
+#ifndef _UNICODE
+ bool LoadFromWideFile(const wchar_t* sourcePath, size_t maxBytes = 16 * 1024 * 1024);
+#endif
+ bool HasExifData() const;
+ const unsigned char* GetExifData() const;
+ unsigned int GetExifSize() const;
+
+private:
+ std::vector Buffer;
+ const unsigned char* ExifData;
+ unsigned int ExifSize;
+};
+#endif
void OnConfiguration(HWND hParent);
BOOL MultipleMonitors(RECT* boundingRect);
diff --git a/src/plugins/pictview/pictview.rh2 b/src/plugins/pictview/pictview.rh2
index f4e318fec..d7d90f3b5 100644
--- a/src/plugins/pictview/pictview.rh2
+++ b/src/plugins/pictview/pictview.rh2
@@ -462,7 +462,7 @@
#define IDS_EXCEPT_INFO1 2306
#define IDS_EXCEPT_INFO2 2307
-// Texts for PVW32Cnv.dll - reserved IDs 4000-4999 !!!
+// Legacy PVW32Cnv.dll text identifiers reserved for translator compatibility (4000-4999)
#define IDS_DLL 4000
// IDcka stranek v html helpu
diff --git a/src/plugins/pictview/precomp.h b/src/plugins/pictview/precomp.h
index dd4851d52..aa46eb6d3 100644
--- a/src/plugins/pictview/precomp.h
+++ b/src/plugins/pictview/precomp.h
@@ -3,11 +3,16 @@
#pragma once
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
//#define WIN32_LEAN_AND_MEAN // exclude rarely-used stuff from Windows headers
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -20,7 +25,6 @@
#endif
#ifdef _WIN64
-#define PICTVIEW_DLL_IN_SEPARATE_PROCESS // the x64 version of PictView uses the 32-bit pvw32cnv.dll via IPC (inter-process communication) with salpvenv.exe
#define ENABLE_WIA // the x64 version of PictView uses WIA 1.0 for scanning
#else // _WIN64
#define ENABLE_TWAIN32 // the x86 version of PictView uses TWAIN 1.x for scanning (which internally also supports WIA)
@@ -57,11 +61,32 @@
#define GET_X_LPARAM(x) LOWORD(x)
#endif
-#ifndef INT32
-#define INT32 int
-#define UINT32 unsigned int
+#ifdef min
+#undef min
+#endif
+
+#ifdef max
+#undef max
#endif
+template
+inline constexpr auto min(T a, U b) -> typename std::common_type::type
+{
+ using R = typename std::common_type::type;
+ const R ra = static_cast(a);
+ const R rb = static_cast(b);
+ return (rb < ra) ? rb : ra;
+}
+
+template
+inline constexpr auto max(T a, U b) -> typename std::common_type::type
+{
+ using R = typename std::common_type::type;
+ const R ra = static_cast(a);
+ const R rb = static_cast(b);
+ return (ra < rb) ? rb : ra;
+}
+
#ifndef SetWindowLongPtr
// compiling on VC6 w/o reasonably new SDK
#define SetWindowLongPtr SetWindowLong
diff --git a/src/plugins/pictview/pvw32cnv.rh2 b/src/plugins/pictview/pvw32cnv.rh2
index d5804a9b0..0e1cf1481 100644
--- a/src/plugins/pictview/pvw32cnv.rh2
+++ b/src/plugins/pictview/pvw32cnv.rh2
@@ -1,6 +1,6 @@
// Petr: tento soubor existuje jen pro generovani symbols\plugins\pictview\symbols.inc davkou tools\export_translator\export_inc.py
-// Petr: pridal jsem IDS_PVW32Cnv_XXXX, aby nervala quiet-validace v Translatoru
+// Legacy PVW32Cnv identifiers kept for translator validation compatibility
#define IDS_PVW32Cnv_4003 4003
#define IDS_PVW32Cnv_4004 4004
#define IDS_PVW32Cnv_4006 4006
diff --git a/src/plugins/pictview/render1.cpp b/src/plugins/pictview/render1.cpp
index 476a4befd..bb8a4f932 100644
--- a/src/plugins/pictview/render1.cpp
+++ b/src/plugins/pictview/render1.cpp
@@ -1583,7 +1583,7 @@ LRESULT CRendererWindow::OnPaint()
LoadingDC = dc;
XStartLoading = XStart;
YStartLoading = YStart;
- if (pvii.Flags & PVFF_IMAGESEQUENCE)
+ if ((pvii.Flags & PVFF_IMAGESEQUENCE) && pvii.Format == PVF_GIF)
{
code = PVW32DLL.PVReadImageSequence(PVHandle, &PVSequence);
if (code == PVC_OK)
@@ -1609,13 +1609,80 @@ LRESULT CRendererWindow::OnPaint()
(pvii.Flags & PVFF_EXIF) && G.AutoRotate && InitEXIF(NULL, TRUE))
{
EXIFGETORIENTATIONINFO getInfo = (EXIFGETORIENTATIONINFO)GetProcAddress(EXIFLibrary, "EXIFGetOrientationInfo");
+#ifdef _UNICODE
+ EXIFGETORIENTATIONINFOW getInfoW = (EXIFGETORIENTATIONINFOW)GetProcAddress(EXIFLibrary, "EXIFGetOrientationInfoW");
+#endif
+ EXIFGETORIENTATIONINFOFROMDATA getInfoFromData =
+ (EXIFGETORIENTATIONINFOFROMDATA)GetProcAddress(EXIFLibrary, "EXIFGetOrientationInfoFromData");
+
+ CExifFileBuffer exifBuffer;
+ bool bufferLoaded = false;
+
+ SThumbExifInfo info;
+ ZeroMemory(&info, sizeof(info));
+ BOOL gotInfo = FALSE;
+
+ if (getInfoFromData)
+ {
+ bufferLoaded = exifBuffer.LoadFromFile(FileName);
+ if (bufferLoaded)
+ {
+ gotInfo = getInfoFromData(exifBuffer.GetExifData(), exifBuffer.GetExifSize(), &info);
+ if (!gotInfo || !(info.flags & TEI_ORIENT))
+ {
+ gotInfo = FALSE;
+ ZeroMemory(&info, sizeof(info));
+ }
+ }
+ }
+
+#ifdef _UNICODE
+ if (!gotInfo && getInfoW)
+ {
+ gotInfo = getInfoW(FileName, &info);
+ if (gotInfo && !(info.flags & TEI_ORIENT))
+ {
+ gotInfo = FALSE;
+ ZeroMemory(&info, sizeof(info));
+ }
+ }
+#endif
+ if (!gotInfo && getInfo)
+ {
+#ifdef _UNICODE
+ CExifAnsiPath ansiPath;
+ if (ansiPath.PrepareFromFile(FileName))
+ {
+ gotInfo = getInfo(ansiPath.GetPath(), &info);
+ }
+#else
+ gotInfo = getInfo(FileName, &info);
+#endif
+ if (gotInfo && !(info.flags & TEI_ORIENT))
+ {
+ gotInfo = FALSE;
+ ZeroMemory(&info, sizeof(info));
+ }
+ }
+
+ if (!gotInfo && getInfoFromData && !bufferLoaded)
+ {
+ bufferLoaded = exifBuffer.LoadFromFile(FileName);
+ if (bufferLoaded)
+ {
+ gotInfo = getInfoFromData(exifBuffer.GetExifData(), exifBuffer.GetExifSize(), &info);
+ if (!gotInfo || !(info.flags & TEI_ORIENT))
+ {
+ gotInfo = FALSE;
+ ZeroMemory(&info, sizeof(info));
+ }
+ }
+ }
- if (getInfo)
+ if (gotInfo)
{
- SThumbExifInfo info;
int cmd;
- getInfo(FileName, &info);
if ((info.flags & (TEI_WIDTH | TEI_HEIGHT)) == (TEI_WIDTH | TEI_HEIGHT))
{
if (((DWORD)info.Width != pvii.Width) || ((DWORD)info.Height != pvii.Height) || (info.Width < info.Height))
@@ -1645,7 +1712,6 @@ LRESULT CRendererWindow::OnPaint()
cmd = 0;
switch (info.Orient)
{
- // case 1/*normal case*/"
case 2:
cmd = CMD_MIRROR_HOR;
break;
@@ -2458,7 +2524,7 @@ LRESULT CRendererWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
HScroll(SB_THUMBPOSITION, GetScrollPos(HWindow, SB_HORZ) + pt.x - r.right / 2);
VScroll(SB_THUMBPOSITION, GetScrollPos(HWindow, SB_VERT) + pt.y - r.bottom / 2);
// NT4: Desktop icons may get repainted as a response to this
- // LockWindowUpdate(NULL) if a zoomed image is cached by PVW32Cnv.dll
+ // LockWindowUpdate(NULL) if a zoomed image is cached by the backend
LockWindowUpdate(NULL);
}
}
diff --git a/src/plugins/pictview/renderer.h b/src/plugins/pictview/renderer.h
index 2e2cbd1f5..41fd032ef 100644
--- a/src/plugins/pictview/renderer.h
+++ b/src/plugins/pictview/renderer.h
@@ -87,7 +87,7 @@ class CRendererWindow : public CWindow
public:
CViewerWindow* Viewer;
- // variables for PVW32
+ // variables for the imaging backend
LPPVHandle PVHandle; // image handle
LPPVImageSequence PVSequence, PVCurImgInSeq;
BOOL ImageLoaded;
diff --git a/src/plugins/pictview/salpvenv.rc b/src/plugins/pictview/salpvenv.rc
deleted file mode 100644
index 619b21538..000000000
--- a/src/plugins/pictview/salpvenv.rc
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifdef APSTUDIO_INVOKED
-#error this file is not editable by Microsoft Visual C++
-#endif //APSTUDIO_INVOKED
-
-#include "..\shared\spl_vers.h"
-
-CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "salpvenv.xml"
-
-1 VERSIONINFO
- FILEVERSION 1,0,0,VERSINFO_BUILDNUMBER
- PRODUCTVERSION VERSINFO_SALAMANDER_MAJOR,VERSINFO_SALAMANDER_MINORA,VERSINFO_SALAMANDER_MINORB,VERSINFO_BUILDNUMBER
- FILEFLAGSMASK 0x0L
- FILEFLAGS 0x0L
- FILEOS 0x4L
- FILETYPE 0x1L // VFT_APP=0x1
- FILESUBTYPE 0x0L
-{
- BLOCK "StringFileInfo"
- {
- BLOCK "040904b0"
- {
- VALUE "CompanyName", "Open Salamander\0"
- VALUE "FileDescription", "Envelope for 32-bit PVW32Cnv.dll\0"
- VALUE "FileVersion", "1.0\0"
- VALUE "InternalName", "SalPVEnv\0"
- VALUE "OriginalFilename", "salpvenv.exe\0"
- VALUE "LegalCopyright", "Copyright \251 2012-2023 Open Salamander Authors\0"
- VALUE "ProductVersion", VERSINFO_SALAMANDER_VERSION "\0"
- VALUE "ProductName", "Open Salamander\0"
- }
- }
- BLOCK "VarFileInfo"
- {
- VALUE "Translation", 0x0409, 0x04b0
- }
-}
diff --git a/src/plugins/pictview/salpvenv.xml b/src/plugins/pictview/salpvenv.xml
deleted file mode 100644
index 83eb7f965..000000000
--- a/src/plugins/pictview/salpvenv.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-Envelope for 32-bit PVW32Cnv.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
\ No newline at end of file
diff --git a/src/plugins/pictview/saveas.cpp b/src/plugins/pictview/saveas.cpp
index 2211dd5e2..0d2735b1f 100644
--- a/src/plugins/pictview/saveas.cpp
+++ b/src/plugins/pictview/saveas.cpp
@@ -985,7 +985,7 @@ int CRendererWindow::SaveImage(LPCTSTR fileName, DWORD format, SAVEAS_INFO_PTR p
#endif
if (fMirrorHor)
{
- // Patch: PVW32Cnv.dll will mirror the image in memory
+ // Legacy patch: the original backend mirrored the image in memory
sii.Flags ^= PVSF_FLIP_HOR;
fMirrorHor = FALSE;
PVW32DLL.PVSetStretchParameters(PVHandle, XStretchedRange,
diff --git a/src/plugins/pictview/thumbs.cpp b/src/plugins/pictview/thumbs.cpp
index b0da26ed2..e8c831701 100644
--- a/src/plugins/pictview/thumbs.cpp
+++ b/src/plugins/pictview/thumbs.cpp
@@ -825,6 +825,10 @@ BOOL CPluginInterfaceForThumbLoader::LoadThumbnail(LPCTSTR filename, int thumbWi
unsigned char* thumbData;
DWORD pictureFlags = 0;
+ char filenameA[_MAX_PATH];
+ bool hasExactExifPath = ConvertPathToExifEncoding(filename, filenameA, sizeof(filenameA));
+ const char* filenameForExif = filenameA;
+
pvoi.DataSize = ExtractWinThumbnail(filename, &thumbData);
for (;;)
{
@@ -832,16 +836,7 @@ BOOL CPluginInterfaceForThumbLoader::LoadThumbnail(LPCTSTR filename, int thumbWi
/* PVFF_FAST: Discard/do not alloc all unnecessary info */
// pvoi.Flags = PVFF_FAST | (G.IgnoreThumbnails ? 0 : PVOF_THUMBNAIL);
pvoi.Flags = PVFF_FAST | (G.IgnoreThumbnails ? 0 : (fastThumbnail ? PVOF_THUMBNAIL : 0));
-
-#ifdef _UNICODE
- char filenameA[_MAX_PATH];
-
- WideCharToMultiByte(CP_ACP, 0, filename, -1, filenameA, sizeof(filenameA), NULL, NULL);
- filenameA[sizeof(filenameA) - 1] = 0;
- pvoi.FileName = filenameA;
-#else
- pvoi.FileName = filename;
-#endif
+ pvoi.FileName = filenameForExif;
if (pvoi.DataSize)
{
pvoi.Flags |= PVOF_USERDEFINED_INPUT;
@@ -909,11 +904,100 @@ BOOL CPluginInterfaceForThumbLoader::LoadThumbnail(LPCTSTR filename, int thumbWi
if (EXIFLibrary)
{
EXIFGETORIENTATIONINFO getInfo = (EXIFGETORIENTATIONINFO)GetProcAddress(EXIFLibrary, "EXIFGetOrientationInfo");
- if (getInfo)
+#ifdef _UNICODE
+ EXIFGETORIENTATIONINFOW getInfoW = (EXIFGETORIENTATIONINFOW)GetProcAddress(EXIFLibrary, "EXIFGetOrientationInfoW");
+#endif
+ EXIFGETORIENTATIONINFOFROMDATA getInfoFromData =
+ (EXIFGETORIENTATIONINFOFROMDATA)GetProcAddress(EXIFLibrary, "EXIFGetOrientationInfoFromData");
+ CExifFileBuffer exifBuffer;
+ bool bufferLoaded = false;
+ SThumbExifInfo info;
+ ZeroMemory(&info, sizeof(info));
+ BOOL gotInfo = FALSE;
+
+#ifdef _UNICODE
+ if (getInfoFromData)
{
- SThumbExifInfo info;
+ bufferLoaded = exifBuffer.LoadFromFile(filename);
+ if (bufferLoaded)
+ {
+ gotInfo = getInfoFromData(exifBuffer.GetExifData(), exifBuffer.GetExifSize(), &info);
+ if (!gotInfo || !(info.flags & TEI_ORIENT))
+ {
+ gotInfo = FALSE;
+ ZeroMemory(&info, sizeof(info));
+ }
+ }
+ }
- getInfo(filename, &info);
+ if (!gotInfo && getInfoW)
+ {
+ gotInfo = getInfoW(filename, &info);
+ if (gotInfo && !(info.flags & TEI_ORIENT))
+ {
+ gotInfo = FALSE;
+ ZeroMemory(&info, sizeof(info));
+ }
+ }
+ if (!gotInfo && getInfo)
+ {
+ if (hasExactExifPath)
+ {
+ gotInfo = getInfo(filenameForExif, &info);
+ }
+ else
+ {
+ CExifAnsiPath ansiPath;
+ if (ansiPath.PrepareFromFile(filename))
+ {
+ gotInfo = getInfo(ansiPath.GetPath(), &info);
+ }
+ }
+ if (gotInfo && !(info.flags & TEI_ORIENT))
+ {
+ gotInfo = FALSE;
+ ZeroMemory(&info, sizeof(info));
+ }
+ }
+#else
+ if (getInfoFromData)
+ {
+ bufferLoaded = exifBuffer.LoadFromFile(filename);
+ if (bufferLoaded)
+ {
+ gotInfo = getInfoFromData(exifBuffer.GetExifData(), exifBuffer.GetExifSize(), &info);
+ if (!gotInfo || !(info.flags & TEI_ORIENT))
+ {
+ gotInfo = FALSE;
+ ZeroMemory(&info, sizeof(info));
+ }
+ }
+ }
+ if (!gotInfo && getInfo)
+ {
+ gotInfo = getInfo(filename, &info);
+ if (gotInfo && !(info.flags & TEI_ORIENT))
+ {
+ gotInfo = FALSE;
+ ZeroMemory(&info, sizeof(info));
+ }
+ }
+#endif
+ if (!gotInfo && getInfoFromData && !bufferLoaded)
+ {
+ bufferLoaded = exifBuffer.LoadFromFile(filename);
+ if (bufferLoaded)
+ {
+ gotInfo = getInfoFromData(exifBuffer.GetExifData(), exifBuffer.GetExifSize(), &info);
+ if (!gotInfo || !(info.flags & TEI_ORIENT))
+ {
+ gotInfo = FALSE;
+ ZeroMemory(&info, sizeof(info));
+ }
+ }
+ }
+ if (gotInfo)
+ {
if ((info.flags & (TEI_WIDTH | TEI_HEIGHT)) == (TEI_WIDTH | TEI_HEIGHT))
{
if (((DWORD)info.Width != pvii.Width) || ((DWORD)info.Height != pvii.Height) || (info.Width < info.Height))
diff --git a/src/plugins/pictview/vcxproj/pictview.vcxproj b/src/plugins/pictview/vcxproj/pictview.vcxproj
index ec856bc07..e3b926530 100644
--- a/src/plugins/pictview/vcxproj/pictview.vcxproj
+++ b/src/plugins/pictview/vcxproj/pictview.vcxproj
@@ -145,11 +145,7 @@
-
-
-
-
-
+
@@ -211,9 +207,7 @@
-
-
-
+
@@ -255,10 +249,6 @@
{bc2e9ea5-ebc4-4fdf-b064-e2bbbbcba1d3}
false
-
- {17cf5e05-f29c-4e5d-ba41-83254e342e0b}
- false
-
diff --git a/src/plugins/pictview/vcxproj/pictview.vcxproj.filters b/src/plugins/pictview/vcxproj/pictview.vcxproj.filters
index b05cd16ea..10146b6c8 100644
--- a/src/plugins/pictview/vcxproj/pictview.vcxproj.filters
+++ b/src/plugins/pictview/vcxproj/pictview.vcxproj.filters
@@ -33,13 +33,7 @@
cpp
-
- cpp
-
-
- cpp
-
-
+
cpp
@@ -98,10 +92,7 @@
h
-
- h
-
-
+
h
diff --git a/src/plugins/pictview/vcxproj/salpvenv.vcxproj b/src/plugins/pictview/vcxproj/salpvenv.vcxproj
deleted file mode 100644
index c83a3719f..000000000
--- a/src/plugins/pictview/vcxproj/salpvenv.vcxproj
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
-
- Debug
- Win32
-
-
- Release
- Win32
-
-
-
- 16.0
- {17CF5E05-F29C-4E5D-BA41-83254E342E0B}
- salpvenv
- 10.0
-
-
-
-
- false
- v143
- Application
-
-
- true
- v143
- Application
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- stdcpplatest
-
-
-
-
- stdcpplatest
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/plugins/pictview/vcxproj/salpvenv_base.props b/src/plugins/pictview/vcxproj/salpvenv_base.props
deleted file mode 100644
index 83c710bd2..000000000
--- a/src/plugins/pictview/vcxproj/salpvenv_base.props
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
- $(OPENSAL_BUILD_DIR)salamander\$(Configuration)_x64\plugins\pictview\
- $(OutDir)Intermediate\SalPVEnv_x86\
- false
- false
-
-
-
- ..\..\shared;%(AdditionalIncludeDirectories)
- WIN32;WINVER=0x0601;_WIN32_WINNT=0x0601;_WIN32_IE=0x0800;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT;BUILD_ENVELOPE;%(PreprocessorDefinitions)
- false
- Level3
-
-
- false
-
-
- false
- true
- Windows
-
-
- WINVER=0x0601;%(PreprocessorDefinitions)
- 0x0409
-
-
-
diff --git a/src/plugins/pictview/vcxproj/salpvenv_debug.props b/src/plugins/pictview/vcxproj/salpvenv_debug.props
deleted file mode 100644
index 8ad746179..000000000
--- a/src/plugins/pictview/vcxproj/salpvenv_debug.props
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
- true
-
-
-
- Disabled
- _DEBUG;%(PreprocessorDefinitions)
- MultiThreadedDebug
-
-
- false
- ..\..\shared\baseaddr_$(ShortPlatform).txt,$(ProjectName)
-
-
- _DEBUG;%(PreprocessorDefinitions)
-
-
-
\ No newline at end of file
diff --git a/src/plugins/pictview/vcxproj/salpvenv_release.props b/src/plugins/pictview/vcxproj/salpvenv_release.props
deleted file mode 100644
index e54017471..000000000
--- a/src/plugins/pictview/vcxproj/salpvenv_release.props
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
- false
-
-
-
- MaxSpeed
- true
- NDEBUG;%(PreprocessorDefinitions)
- true
- MultiThreaded
- true
-
-
- true
- true
- true
- UseLinkTimeCodeGeneration
-
-
- NDEBUG;%(PreprocessorDefinitions)
-
-
- call ..\..\..\..\tools\codesign\sign_with_retry.cmd "$(TargetPath)"
-
-
-
diff --git a/src/plugins/pictview/versinfo.rh2 b/src/plugins/pictview/versinfo.rh2
index 553a53e33..7ac645d5f 100644
--- a/src/plugins/pictview/versinfo.rh2
+++ b/src/plugins/pictview/versinfo.rh2
@@ -10,8 +10,8 @@
// look at shared\versinfo.rc
#define VERSINFO_MAJOR 2
-#define VERSINFO_MINORA 1
-#define VERSINFO_MINORB 3
+#define VERSINFO_MINORA 2
+#define VERSINFO_MINORB 0
#include "spl_vers.h" // get version & build numbers
@@ -31,7 +31,7 @@
#endif
// for SLG translators
-#define VERSINFO_SLG_WEB "www.altap.cz"
+#define VERSINFO_SLG_WEB "www.opensalamander.org"
#define VERSINFO_SLG_COMMENT "English version"
#endif // __PICTVIEW_VERSINFO_RH2
diff --git a/src/plugins/pictview/wic/WicBackend.cpp b/src/plugins/pictview/wic/WicBackend.cpp
new file mode 100644
index 000000000..701603b0a
--- /dev/null
+++ b/src/plugins/pictview/wic/WicBackend.cpp
@@ -0,0 +1,2541 @@
+// SPDX-FileCopyrightText: 2024 Open Salamander Authors
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "precomp.h"
+#include "WicBackend.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "../Thumbnailer.h"
+
+#pragma comment(lib, "windowscodecs.lib")
+#pragma comment(lib, "shlwapi.lib")
+
+using namespace std::string_literals;
+
+namespace PictView::Wic
+{
+namespace
+{
+constexpr DWORD kBackendVersion = PV_VERSION_156;
+constexpr UINT kBytesPerPixel = 4;
+constexpr UINT kMaxGdiDimension = static_cast(std::numeric_limits::max());
+
+HRESULT AllocatePixelStorage(FrameData& frame, UINT width, UINT height);
+HRESULT FinalizeDecodedFrame(FrameData& frame);
+PVCODE PopulateImageInfo(ImageHandle& handle, LPPVImageInfo info, DWORD bufferSize, bool hasPreviousImage,
+ DWORD previousImageIndex, int currentImage);
+
+std::mutex g_errorMutex;
+std::unordered_map g_errorTexts = {
+ {PVC_OK, "OK"},
+ {PVC_CANNOT_OPEN_FILE, "Unable to open image."},
+ {PVC_UNSUP_FILE_TYPE, "Image format is not supported by the WIC backend."},
+ {PVC_UNSUP_OUT_PARAMS, "Requested output parameters are not supported by the WIC backend."},
+ {PVC_OUT_OF_MEMORY, "Out of memory."},
+ {PVC_INVALID_DIMENSIONS, "Requested dimensions are invalid."},
+ {PVC_CANCELED, "Operation canceled."},
+ {PVC_GDI_ERROR, "A GDI call failed."},
+ {PVC_READING_ERROR, "The image file appears to be corrupt or unreadable."},
+ {PVC_WRITING_ERROR, "The image could not be written."},
+ {PVC_UNEXPECTED_EOF, "The image data ended unexpectedly."},
+};
+
+bool PathLooksLikeExif(LPCWSTR path)
+{
+ if (!path)
+ {
+ return false;
+ }
+ if (wcsstr(path, L"exif") != nullptr)
+ {
+ return true;
+ }
+ if (wcsstr(path, L"{ushort=34665}") != nullptr)
+ {
+ return true;
+ }
+ return false;
+}
+
+bool QueryReaderContainsExif(IWICMetadataQueryReader* query)
+{
+ if (!query)
+ {
+ return false;
+ }
+ static const wchar_t* kProbePaths[] = {
+ L"/ifd/exif:ExifVersion",
+ L"/ifd/{ushort=34665}",
+ L"/app1/ifd/exif:ExifVersion",
+ L"/app1/{ushort=34665}"
+ };
+ PROPVARIANT value;
+ for (const auto* path : kProbePaths)
+ {
+ PropVariantInit(&value);
+ const HRESULT hr = query->GetMetadataByName(path, &value);
+ PropVariantClear(&value);
+ if (SUCCEEDED(hr))
+ {
+ return true;
+ }
+ }
+
+ PropVariantInit(&value);
+ const HRESULT ifdHr = query->GetMetadataByName(L"/ifd", &value);
+ if (SUCCEEDED(ifdHr))
+ {
+ bool hasExif = true;
+ if (value.vt == VT_UNKNOWN && value.punkVal)
+ {
+ ComPtr nested;
+ if (SUCCEEDED(value.punkVal->QueryInterface(IID_PPV_ARGS(&nested))) && nested)
+ {
+ hasExif = QueryReaderContainsExif(nested.Get());
+ }
+ }
+ PropVariantClear(&value);
+ if (hasExif)
+ {
+ return true;
+ }
+ }
+ else
+ {
+ PropVariantClear(&value);
+ }
+
+ ComPtr names;
+ if (SUCCEEDED(query->GetEnumerator(&names)) && names)
+ {
+ LPOLESTR rawName = nullptr;
+ ULONG fetched = 0;
+ while (names->Next(1, &rawName, &fetched) == S_OK)
+ {
+ if (rawName)
+ {
+ const bool looksLikeExif = PathLooksLikeExif(rawName);
+ if (looksLikeExif)
+ {
+ PROPVARIANT enumValue;
+ PropVariantInit(&enumValue);
+ const HRESULT hr = query->GetMetadataByName(rawName, &enumValue);
+ bool hasExif = false;
+ if (SUCCEEDED(hr))
+ {
+ if (enumValue.vt == VT_UNKNOWN && enumValue.punkVal)
+ {
+ ComPtr nested;
+ if (SUCCEEDED(enumValue.punkVal->QueryInterface(IID_PPV_ARGS(&nested))) && nested)
+ {
+ hasExif = QueryReaderContainsExif(nested.Get());
+ }
+ }
+ else
+ {
+ hasExif = true;
+ }
+ }
+ PropVariantClear(&enumValue);
+ CoTaskMemFree(rawName);
+ rawName = nullptr;
+ if (hasExif)
+ {
+ return true;
+ }
+ }
+ if (rawName)
+ {
+ CoTaskMemFree(rawName);
+ rawName = nullptr;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool ReaderContainsExif(IWICMetadataReader* reader)
+{
+ if (!reader)
+ {
+ return false;
+ }
+ GUID format = {};
+ if (SUCCEEDED(reader->GetMetadataFormat(&format)))
+ {
+ if (format == GUID_MetadataFormatExif || format == GUID_MetadataFormatIfd)
+ {
+ return true;
+ }
+ }
+
+ ComPtr query;
+ if (SUCCEEDED(reader->QueryInterface(IID_PPV_ARGS(&query))) && query)
+ {
+ if (QueryReaderContainsExif(query.Get()))
+ {
+ return true;
+ }
+ }
+
+ ComPtr blockReader;
+ if (SUCCEEDED(reader->QueryInterface(IID_PPV_ARGS(&blockReader))) && blockReader)
+ {
+ UINT count = 0;
+ if (SUCCEEDED(blockReader->GetCount(&count)))
+ {
+ for (UINT i = 0; i < count; ++i)
+ {
+ ComPtr child;
+ if (SUCCEEDED(blockReader->GetReaderByIndex(i, &child)) && child)
+ {
+ if (ReaderContainsExif(child.Get()))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool SourceContainsExif(IUnknown* source)
+{
+ if (!source)
+ {
+ return false;
+ }
+ ComPtr blockReader;
+ if (FAILED(source->QueryInterface(IID_PPV_ARGS(&blockReader))) || !blockReader)
+ {
+ return false;
+ }
+ UINT count = 0;
+ if (FAILED(blockReader->GetCount(&count)))
+ {
+ return false;
+ }
+ for (UINT i = 0; i < count; ++i)
+ {
+ ComPtr reader;
+ if (SUCCEEDED(blockReader->GetReaderByIndex(i, &reader)) && reader)
+ {
+ if (ReaderContainsExif(reader.Get()))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool FrameContainsExif(IWICBitmapFrameDecode* frame)
+{
+ if (!frame)
+ {
+ return false;
+ }
+ if (SourceContainsExif(frame))
+ {
+ return true;
+ }
+ ComPtr query;
+ if (SUCCEEDED(frame->GetMetadataQueryReader(&query)) && query)
+ {
+ if (QueryReaderContainsExif(query.Get()))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TryExtractDelayHundredths(const PROPVARIANT& value, UINT& hundredths)
+{
+ switch (value.vt)
+ {
+ case VT_UI1:
+ hundredths = value.bVal;
+ return true;
+ case VT_UI2:
+ hundredths = value.uiVal;
+ return true;
+ case VT_UI4:
+ hundredths = static_cast(value.ulVal);
+ return true;
+ case VT_UI8:
+ hundredths = static_cast(std::min(value.uhVal.QuadPart,
+ static_cast(std::numeric_limits::max())));
+ return true;
+ case VT_UINT:
+ hundredths = value.uintVal;
+ return true;
+ case VT_R4:
+ hundredths = static_cast(value.fltVal);
+ return true;
+ case VT_R8:
+ hundredths = static_cast(value.dblVal);
+ return true;
+ case (VT_VECTOR | VT_UI1):
+ if (value.caub.cElems > 0 && value.caub.pElems)
+ {
+ hundredths = value.caub.pElems[0];
+ return true;
+ }
+ break;
+ case (VT_VECTOR | VT_UI2):
+ if (value.caui.cElems > 0 && value.caui.pElems)
+ {
+ hundredths = value.caui.pElems[0];
+ return true;
+ }
+ break;
+ case (VT_VECTOR | VT_UI4):
+ if (value.caul.cElems > 0 && value.caul.pElems)
+ {
+ hundredths = static_cast(value.caul.pElems[0]);
+ return true;
+ }
+ break;
+ case (VT_VECTOR | VT_UI8):
+ if (value.cauh.cElems > 0 && value.cauh.pElems)
+ {
+ hundredths = static_cast(std::min(value.cauh.pElems[0].QuadPart,
+ static_cast(std::numeric_limits::max())));
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool TryReadDelayHundredths(IWICMetadataQueryReader* reader, LPCWSTR name, UINT& hundredths)
+{
+ if (!reader || !name)
+ {
+ return false;
+ }
+ PROPVARIANT value;
+ PropVariantInit(&value);
+ const HRESULT hr = reader->GetMetadataByName(name, &value);
+ if (FAILED(hr))
+ {
+ PropVariantClear(&value);
+ return false;
+ }
+ const bool extracted = TryExtractDelayHundredths(value, hundredths);
+ PropVariantClear(&value);
+ return extracted;
+}
+
+DWORD ClampDelayHundredthsToMilliseconds(UINT hundredths)
+{
+ if (hundredths == 0)
+ {
+ hundredths = 10; // default to 100 ms when delay is unspecified
+ }
+ const ULONGLONG delayMs64 = static_cast(hundredths) * 10ull;
+ return delayMs64 > std::numeric_limits::max() ? std::numeric_limits::max()
+ : static_cast(delayMs64);
+}
+
+DWORD GetFrameDelayMilliseconds(IWICBitmapFrameDecode* frame)
+{
+ if (!frame)
+ {
+ return 0;
+ }
+ ComPtr query;
+ if (FAILED(frame->GetMetadataQueryReader(&query)) || !query)
+ {
+ return 0;
+ }
+
+ UINT hundredths = 0;
+ static constexpr const wchar_t* kDelayPaths[] = {
+ L"/grctlext/DelayTime", // GIF frame delay
+ L"/ifd/{ushort=0x5100}", // TIFF/PropertyTagFrameDelay
+ L"/xmp/GIF:DelayTime", // XMP GIF namespace (fallback)
+ L"/xmp/MM:FrameDelay", // Additional XMP metadata some encoders emit
+ L"/xmp/extensibility/Animation/FrameDelay"
+ };
+ for (const auto* path : kDelayPaths)
+ {
+ if (TryReadDelayHundredths(query.Get(), path, hundredths))
+ {
+ return ClampDelayHundredthsToMilliseconds(hundredths);
+ }
+ }
+ return 0;
+}
+
+const char* LookupError(DWORD code)
+{
+ std::lock_guard lock(g_errorMutex);
+ const auto it = g_errorTexts.find(code);
+ if (it != g_errorTexts.end())
+ {
+ return it->second.c_str();
+ }
+ static std::string fallback = "Unknown WIC error.";
+ return fallback.c_str();
+}
+
+ULONGLONG AbsoluteDimension(LONGLONG value)
+{
+ if (value >= 0)
+ {
+ return static_cast(value);
+ }
+ if (value == std::numeric_limits::min())
+ {
+ return static_cast(std::numeric_limits::max()) + 1ull;
+ }
+ return static_cast(-(value + 1)) + 1;
+}
+
+DWORD ClampToDword(ULONGLONG value)
+{
+ return value > static_cast(std::numeric_limits::max())
+ ? std::numeric_limits::max()
+ : static_cast(value);
+}
+
+DWORD QueryFileSize(const std::wstring& path)
+{
+ if (path.empty())
+ {
+ return 0;
+ }
+
+ WIN32_FILE_ATTRIBUTE_DATA attributes{};
+ if (!GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &attributes))
+ {
+ return 0;
+ }
+
+ ULARGE_INTEGER size;
+ size.LowPart = attributes.nFileSizeLow;
+ size.HighPart = attributes.nFileSizeHigh;
+ return ClampToDword(size.QuadPart);
+}
+
+size_t NormalizeFrameIndex(const ImageHandle& handle, int requestedIndex, size_t fallbackIndex = 0)
+{
+ if (handle.frames.empty())
+ {
+ return 0;
+ }
+
+ size_t index = fallbackIndex;
+ if (index >= handle.frames.size())
+ {
+ index = handle.frames.size() - 1;
+ }
+
+ if (requestedIndex >= 0)
+ {
+ index = static_cast(requestedIndex);
+ if (index >= handle.frames.size())
+ {
+ index = handle.frames.size() - 1;
+ }
+ }
+
+ return index;
+}
+
+HRESULT PopulateFrameFromBitmapHandle(FrameData& frame, HBITMAP bitmap)
+{
+ if (!bitmap)
+ {
+ return E_INVALIDARG;
+ }
+
+ DIBSECTION dib{};
+ const int objectSize = GetObjectW(bitmap, sizeof(dib), &dib);
+ if (objectSize == 0)
+ {
+ const DWORD error = GetLastError();
+ return HRESULT_FROM_WIN32(error != 0 ? error : ERROR_INVALID_DATA);
+ }
+
+ const LONG widthLong = dib.dsBm.bmWidth;
+ const LONG heightLong = dib.dsBm.bmHeight;
+ if (widthLong <= 0 || heightLong == 0)
+ {
+ return WINCODEC_ERR_INVALIDPARAMETER;
+ }
+
+ const UINT width = static_cast(widthLong);
+ const UINT height = static_cast(heightLong < 0 ? -heightLong : heightLong);
+
+ HRESULT hr = AllocatePixelStorage(frame, width, height);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ BITMAPINFO bmi{};
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = static_cast(width);
+ bmi.bmiHeader.biHeight = -static_cast(height);
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+
+ HDC dc = CreateCompatibleDC(nullptr);
+ if (!dc)
+ {
+ const DWORD error = GetLastError();
+ frame.pixels.clear();
+ frame.stride = 0;
+ return HRESULT_FROM_WIN32(error != 0 ? error : ERROR_NOT_ENOUGH_MEMORY);
+ }
+
+ HGDIOBJ oldBitmap = SelectObject(dc, bitmap);
+ const int lines = GetDIBits(dc, bitmap, 0, height, frame.pixels.data(), &bmi, DIB_RGB_COLORS);
+ if (oldBitmap)
+ {
+ SelectObject(dc, oldBitmap);
+ }
+ DeleteDC(dc);
+
+ if (lines == 0)
+ {
+ const DWORD error = GetLastError();
+ frame.pixels.clear();
+ frame.stride = 0;
+ return HRESULT_FROM_WIN32(error != 0 ? error : ERROR_INVALID_DATA);
+ }
+
+ if (dib.dsBm.bmBitsPixel < 32)
+ {
+ BYTE* pixels = frame.pixels.data();
+ const size_t pixelCount = static_cast(width) * height;
+ for (size_t i = 0; i < pixelCount; ++i)
+ {
+ pixels[i * 4 + 3] = 255;
+ }
+ }
+
+ hr = FinalizeDecodedFrame(frame);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ return S_OK;
+}
+
+HRESULT AllocateBuffer(std::vector& buffer, size_t size)
+{
+ try
+ {
+ buffer.resize(size);
+ }
+ catch (const std::bad_alloc&)
+ {
+ buffer.clear();
+ return E_OUTOFMEMORY;
+ }
+ return S_OK;
+}
+
+HRESULT AllocatePixelStorage(FrameData& frame, UINT width, UINT height)
+{
+ if (width == 0 || height == 0)
+ {
+ return WINCODEC_ERR_INVALIDPARAMETER;
+ }
+ if (width > kMaxGdiDimension || height > kMaxGdiDimension)
+ {
+ return WINCODEC_ERR_INVALIDPARAMETER;
+ }
+
+ const ULONGLONG stride64 = static_cast(width) * kBytesPerPixel;
+ if (stride64 > std::numeric_limits::max())
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ const ULONGLONG buffer64 = stride64 * static_cast(height);
+ if (height != 0 && buffer64 / height != stride64)
+ {
+ return E_OUTOFMEMORY;
+ }
+ if (buffer64 > static_cast(std::numeric_limits::max()))
+ {
+ return E_OUTOFMEMORY;
+ }
+ if (buffer64 > static_cast(std::numeric_limits::max()))
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ frame.width = width;
+ frame.height = height;
+ frame.stride = static_cast(stride64);
+
+ HRESULT hr = AllocateBuffer(frame.pixels, static_cast(buffer64));
+ if (FAILED(hr))
+ {
+ frame.stride = 0;
+ return hr;
+ }
+ return S_OK;
+}
+
+struct GuidMapping
+{
+ DWORD format;
+ GUID container;
+ GUID pixelFormat;
+};
+
+const GuidMapping kEncoderMappings[] = {
+ {PVF_BMP, GUID_ContainerFormatBmp, GUID_WICPixelFormat32bppBGRA},
+ {PVF_PNG, GUID_ContainerFormatPng, GUID_WICPixelFormat32bppBGRA},
+ {PVF_JPG, GUID_ContainerFormatJpeg, GUID_WICPixelFormat24bppBGR},
+ {PVF_TIFF, GUID_ContainerFormatTiff, GUID_WICPixelFormat32bppBGRA},
+ {PVF_GIF, GUID_ContainerFormatGif, GUID_WICPixelFormat8bppIndexed},
+ {PVF_ICO, GUID_ContainerFormatIco, GUID_WICPixelFormat32bppBGRA},
+};
+
+HRESULT CreateDecoder(Backend& backend, const std::wstring& path, IWICBitmapDecoder** decoder)
+{
+ auto factory = backend.Factory();
+ if (!factory)
+ {
+ return E_POINTER;
+ }
+ return factory->CreateDecoderFromFilename(path.c_str(), nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, decoder);
+}
+
+bool IsIgnorableColorProfileError(HRESULT hr)
+{
+ switch (hr)
+ {
+ case WINCODEC_ERR_UNSUPPORTEDOPERATION:
+ case WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT:
+ case WINCODEC_ERR_PROPERTYNOTSUPPORTED:
+ case WINCODEC_ERR_UNSUPPORTEDVERSION:
+#ifdef WINCODEC_ERR_PROFILENOTASSOCIATED
+ case WINCODEC_ERR_PROFILENOTASSOCIATED:
+#endif
+#ifdef WINCODEC_ERR_PROFILEINVALID
+ case WINCODEC_ERR_PROFILEINVALID:
+#endif
+ case E_NOTIMPL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+HRESULT ApplyEmbeddedColorProfile(ImageHandle& handle, FrameData& frame)
+{
+ if (frame.colorConvertedSource)
+ {
+ return S_OK;
+ }
+
+ IWICImagingFactory* factory = handle.backend->Factory();
+ if (!factory)
+ {
+ return E_POINTER;
+ }
+
+ UINT contextCount = 0;
+ HRESULT hr = frame.frame->GetColorContexts(0, nullptr, &contextCount);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ if (contextCount == 0)
+ {
+ return WINCODEC_ERR_UNSUPPORTEDOPERATION;
+ }
+
+ std::vector> sourceContexts(contextCount);
+ std::vector rawContexts(contextCount);
+ for (UINT i = 0; i < contextCount; ++i)
+ {
+ hr = factory->CreateColorContext(&sourceContexts[i]);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ rawContexts[i] = sourceContexts[i].Get();
+ }
+
+ hr = frame.frame->GetColorContexts(contextCount, rawContexts.data(), &contextCount);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ if (contextCount == 0)
+ {
+ return WINCODEC_ERR_UNSUPPORTEDOPERATION;
+ }
+
+ Microsoft::WRL::ComPtr destinationContext;
+ hr = factory->CreateColorContext(&destinationContext);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ hr = destinationContext->InitializeFromExifColorSpace(0x1); // sRGB
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ Microsoft::WRL::ComPtr transform;
+ static const GUID kCLSID_WICColorTransform =
+ {0xB66F034F, 0xD0E2, 0x40AB, {0xB4, 0x36, 0x6D, 0xE3, 0x9E, 0x32, 0x1A, 0x94}};
+ hr = CoCreateInstance(kCLSID_WICColorTransform, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&transform));
+ if (FAILED(hr))
+ {
+ if (hr == REGDB_E_CLASSNOTREG)
+ {
+ return WINCODEC_ERR_UNSUPPORTEDOPERATION;
+ }
+ return hr;
+ }
+
+ if (rawContexts.empty())
+ {
+ return WINCODEC_ERR_UNSUPPORTEDOPERATION;
+ }
+ IWICColorContext* sourceContext = rawContexts[0];
+ hr = transform->Initialize(frame.frame.Get(), sourceContext, destinationContext.Get(), GUID_WICPixelFormat32bppBGRA);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ hr = transform.As(&frame.colorConvertedSource);
+ return hr;
+}
+
+HRESULT CopyBgraFromSource(FrameData& frame, IWICBitmapSource* source)
+{
+ if (!source)
+ {
+ return E_POINTER;
+ }
+
+ UINT width = 0;
+ UINT height = 0;
+ HRESULT hr = source->GetSize(&width, &height);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ hr = AllocatePixelStorage(frame, width, height);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ WICRect rect{0, 0, static_cast(width), static_cast(height)};
+ const UINT bufferSize = static_cast(frame.pixels.size());
+ hr = source->CopyPixels(&rect, frame.stride, bufferSize, frame.pixels.data());
+ if (FAILED(hr))
+ {
+ frame.pixels.clear();
+ frame.stride = 0;
+ return hr;
+ }
+
+ return S_OK;
+}
+
+HRESULT FinalizeDecodedFrame(FrameData& frame)
+{
+ const size_t lineCount = static_cast(frame.height);
+ if (lineCount > frame.linePointers.max_size())
+ {
+ return E_OUTOFMEMORY;
+ }
+ try
+ {
+ frame.linePointers.resize(lineCount);
+ }
+ catch (const std::bad_alloc&)
+ {
+ frame.linePointers.clear();
+ return E_OUTOFMEMORY;
+ }
+
+ for (UINT y = 0; y < frame.height; ++y)
+ {
+ frame.linePointers[y] = frame.pixels.data() + static_cast(y) * frame.stride;
+ }
+ frame.palette.clear();
+
+ frame.bmi.biSize = sizeof(BITMAPINFOHEADER);
+ frame.bmi.biWidth = static_cast(frame.width);
+ frame.bmi.biHeight = -static_cast(frame.height);
+ frame.bmi.biPlanes = 1;
+ frame.bmi.biBitCount = 32;
+ frame.bmi.biCompression = BI_RGB;
+ const size_t pixelBytes = frame.pixels.size();
+ frame.bmi.biSizeImage = pixelBytes > std::numeric_limits::max() ? 0
+ : static_cast(pixelBytes);
+ frame.bmi.biXPelsPerMeter = 0;
+ frame.bmi.biYPelsPerMeter = 0;
+
+ if (frame.hbitmap)
+ {
+ DeleteObject(frame.hbitmap);
+ frame.hbitmap = nullptr;
+ }
+
+ void* bits = nullptr;
+ BITMAPINFO bmi{};
+ bmi.bmiHeader = frame.bmi;
+ frame.hbitmap = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0);
+ if (!frame.hbitmap)
+ {
+ return E_OUTOFMEMORY;
+ }
+ if (bits && !frame.pixels.empty())
+ {
+ memcpy(bits, frame.pixels.data(), frame.pixels.size());
+ }
+
+ frame.decoded = true;
+ return S_OK;
+}
+
+inline BYTE CombineCmykChannel(BYTE component, BYTE black)
+{
+ const int c = 255 - component;
+ const int k = 255 - black;
+ const int value = c * k + 127;
+ return static_cast(value / 255);
+}
+
+inline BYTE ToByteFromWord(UINT16 value)
+{
+ return static_cast((static_cast(value) + 128u) / 257u);
+}
+
+HRESULT DecodeUnsupportedPixelFormat(FrameData& frame)
+{
+ GUID pixelFormat{};
+ HRESULT hr = frame.frame->GetPixelFormat(&pixelFormat);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ if (pixelFormat == GUID_WICPixelFormat32bppCMYK)
+ {
+ UINT width = 0;
+ UINT height = 0;
+ hr = frame.frame->GetSize(&width, &height);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ const ULONGLONG sourceStride64 = static_cast(width) * 4ull;
+ if (sourceStride64 > std::numeric_limits::max())
+ {
+ return E_OUTOFMEMORY;
+ }
+ const UINT sourceStride = static_cast(sourceStride64);
+ const ULONGLONG sourceSize64 = sourceStride64 * static_cast(height);
+ if (height != 0 && sourceSize64 / height != sourceStride64)
+ {
+ return E_OUTOFMEMORY;
+ }
+ if (sourceSize64 > static_cast(std::numeric_limits::max()) ||
+ sourceSize64 > static_cast(std::numeric_limits::max()))
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ std::vector cmyk;
+ hr = AllocateBuffer(cmyk, static_cast(sourceSize64));
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ WICRect rect{0, 0, static_cast(width), static_cast(height)};
+ hr = frame.frame->CopyPixels(&rect, sourceStride, static_cast(cmyk.size()), cmyk.data());
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ hr = AllocatePixelStorage(frame, width, height);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ for (UINT y = 0; y < frame.height; ++y)
+ {
+ const BYTE* src = cmyk.data() + static_cast(y) * sourceStride;
+ BYTE* dst = frame.pixels.data() + static_cast(y) * frame.stride;
+ for (UINT x = 0; x < frame.width; ++x)
+ {
+ const BYTE c = src[x * 4 + 0];
+ const BYTE m = src[x * 4 + 1];
+ const BYTE yComp = src[x * 4 + 2];
+ const BYTE k = src[x * 4 + 3];
+ dst[x * 4 + 0] = CombineCmykChannel(yComp, k);
+ dst[x * 4 + 1] = CombineCmykChannel(m, k);
+ dst[x * 4 + 2] = CombineCmykChannel(c, k);
+ dst[x * 4 + 3] = 255;
+ }
+ }
+ return S_OK;
+ }
+
+ if (pixelFormat == GUID_WICPixelFormat64bppCMYK)
+ {
+ UINT width = 0;
+ UINT height = 0;
+ hr = frame.frame->GetSize(&width, &height);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ const ULONGLONG sourceStride64 = static_cast(width) * 8ull;
+ if (sourceStride64 > std::numeric_limits::max())
+ {
+ return E_OUTOFMEMORY;
+ }
+ const UINT sourceStride = static_cast(sourceStride64);
+ const ULONGLONG sourceSize64 = sourceStride64 * static_cast(height);
+ if (height != 0 && sourceSize64 / height != sourceStride64)
+ {
+ return E_OUTOFMEMORY;
+ }
+ if (sourceSize64 > static_cast(std::numeric_limits::max()) ||
+ sourceSize64 > static_cast(std::numeric_limits::max()))
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ std::vector cmyk;
+ hr = AllocateBuffer(cmyk, static_cast(sourceSize64));
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ WICRect rect{0, 0, static_cast(width), static_cast(height)};
+ hr = frame.frame->CopyPixels(&rect, sourceStride, static_cast(cmyk.size()), cmyk.data());
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ hr = AllocatePixelStorage(frame, width, height);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ for (UINT y = 0; y < frame.height; ++y)
+ {
+ const UINT16* src = reinterpret_cast(cmyk.data() + static_cast(y) * sourceStride);
+ BYTE* dst = frame.pixels.data() + static_cast(y) * frame.stride;
+ for (UINT x = 0; x < frame.width; ++x)
+ {
+ const BYTE c = ToByteFromWord(src[x * 4 + 0]);
+ const BYTE m = ToByteFromWord(src[x * 4 + 1]);
+ const BYTE yComp = ToByteFromWord(src[x * 4 + 2]);
+ const BYTE k = ToByteFromWord(src[x * 4 + 3]);
+ dst[x * 4 + 0] = CombineCmykChannel(yComp, k);
+ dst[x * 4 + 1] = CombineCmykChannel(m, k);
+ dst[x * 4 + 2] = CombineCmykChannel(c, k);
+ dst[x * 4 + 3] = 255;
+ }
+ }
+ return S_OK;
+ }
+
+ return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT;
+}
+
+bool IsConverterFormatFailure(HRESULT hr)
+{
+ if (hr == WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT)
+ {
+ return true;
+ }
+#if defined(WINCODEC_ERR_INVALIDPARAMETER)
+ if (hr == WINCODEC_ERR_INVALIDPARAMETER)
+ {
+ return true;
+ }
+#endif
+#if defined(WINCODEC_ERR_UNSUPPORTEDOPERATION)
+ if (hr == WINCODEC_ERR_UNSUPPORTEDOPERATION)
+ {
+ return true;
+ }
+#endif
+ return false;
+}
+
+HRESULT EnsureConverter(ImageHandle& handle, size_t index)
+{
+ if (index >= handle.frames.size())
+ {
+ return E_INVALIDARG;
+ }
+ FrameData& frame = handle.frames[index];
+ if (frame.converter)
+ {
+ return S_OK;
+ }
+ IWICImagingFactory* factory = handle.backend->Factory();
+ if (!factory)
+ {
+ return E_FAIL;
+ }
+
+ Microsoft::WRL::ComPtr converter;
+ HRESULT hr = factory->CreateFormatConverter(&converter);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ hr = converter->Initialize(frame.frame.Get(), GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone, nullptr, 0.0,
+ WICBitmapPaletteTypeCustom);
+ if (FAILED(hr) && IsConverterFormatFailure(hr))
+ {
+ HRESULT profileHr = ApplyEmbeddedColorProfile(handle, frame);
+ if (SUCCEEDED(profileHr) && frame.colorConvertedSource)
+ {
+ hr = converter->Initialize(frame.colorConvertedSource.Get(), GUID_WICPixelFormat32bppBGRA,
+ WICBitmapDitherTypeNone, nullptr, 0.0, WICBitmapPaletteTypeCustom);
+ }
+ else if (FAILED(profileHr) && !IsIgnorableColorProfileError(profileHr))
+ {
+ return profileHr;
+ }
+ }
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ frame.converter = converter;
+ return S_OK;
+}
+
+HRESULT DecodeFrame(ImageHandle& handle, size_t index)
+{
+ if (index >= handle.frames.size())
+ {
+ return E_INVALIDARG;
+ }
+ FrameData& frame = handle.frames[index];
+ if (frame.decoded)
+ {
+ return S_OK;
+ }
+
+ HRESULT hr = EnsureConverter(handle, index);
+ if (FAILED(hr))
+ {
+ if (IsConverterFormatFailure(hr))
+ {
+ hr = DecodeUnsupportedPixelFormat(frame);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ hr = FinalizeDecodedFrame(frame);
+ return hr;
+ }
+ return hr;
+ }
+
+ hr = CopyBgraFromSource(frame, frame.converter.Get());
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ return FinalizeDecodedFrame(frame);
+}
+
+PVCODE HResultToPvCode(HRESULT hr)
+{
+ if (SUCCEEDED(hr))
+ {
+ return PVC_OK;
+ }
+ if (hr == E_OUTOFMEMORY
+#if defined(WINCODEC_ERR_OUTOFMEMORY)
+ || hr == WINCODEC_ERR_OUTOFMEMORY
+#endif
+#if defined(WINCODEC_ERR_INSUFFICIENTBUFFER)
+ || hr == WINCODEC_ERR_INSUFFICIENTBUFFER
+#endif
+ )
+ {
+ return PVC_OUT_OF_MEMORY;
+ }
+ if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) ||
+ hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) || hr == STG_E_FILENOTFOUND ||
+ hr == STG_E_ACCESSDENIED)
+ {
+ return PVC_CANNOT_OPEN_FILE;
+ }
+ if (hr == E_INVALIDARG
+#if defined(WINCODEC_ERR_INVALIDPARAMETER)
+ || hr == WINCODEC_ERR_INVALIDPARAMETER
+#endif
+#if defined(WINCODEC_ERR_VALUEOUTOFRANGE)
+ || hr == WINCODEC_ERR_VALUEOUTOFRANGE
+#endif
+ )
+ {
+ return PVC_INVALID_DIMENSIONS;
+ }
+ if (
+#if defined(WINCODEC_ERR_BADHEADER)
+ hr == WINCODEC_ERR_BADHEADER ||
+#endif
+#if defined(WINCODEC_ERR_BADIMAGE)
+ hr == WINCODEC_ERR_BADIMAGE ||
+#endif
+#if defined(WINCODEC_ERR_BADMETADATAHEADER)
+ hr == WINCODEC_ERR_BADMETADATAHEADER ||
+#endif
+#if defined(WINCODEC_ERR_BADSTREAMDATA)
+ hr == WINCODEC_ERR_BADSTREAMDATA ||
+#endif
+#if defined(WINCODEC_ERR_STREAMREAD)
+ hr == WINCODEC_ERR_STREAMREAD ||
+#endif
+#if defined(WINCODEC_ERR_STREAMWRITE)
+ hr == WINCODEC_ERR_STREAMWRITE ||
+#endif
+#if defined(WINCODEC_ERR_STREAMNOTAVAILABLE)
+ hr == WINCODEC_ERR_STREAMNOTAVAILABLE ||
+#endif
+#if defined(WINCODEC_ERR_UNEXPECTEDMETADATAFORM)
+ hr == WINCODEC_ERR_UNEXPECTEDMETADATAFORM ||
+#endif
+#if defined(WINCODEC_ERR_INTERNALERROR)
+ hr == WINCODEC_ERR_INTERNALERROR ||
+#endif
+#if defined(WINCODEC_ERR_INVALIDPROGRESSIVELEVEL)
+ hr == WINCODEC_ERR_INVALIDPROGRESSIVELEVEL ||
+#endif
+#if defined(WINCODEC_ERR_UNSUPPORTEDVERSION)
+ hr == WINCODEC_ERR_UNSUPPORTEDVERSION ||
+#endif
+ hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF) || hr == HRESULT_FROM_WIN32(ERROR_CRC) || hr == E_FAIL)
+ {
+ return (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF)) ? PVC_UNEXPECTED_EOF : PVC_READING_ERROR;
+ }
+ if (hr == WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT
+#if defined(WINCODEC_ERR_COMPONENTNOTFOUND)
+ || hr == WINCODEC_ERR_COMPONENTNOTFOUND
+#endif
+#if defined(WINCODEC_ERR_UNSUPPORTEDOPERATION)
+ || hr == WINCODEC_ERR_UNSUPPORTEDOPERATION
+#endif
+#if defined(WINCODEC_ERR_CODECNOTFOUND)
+ || hr == WINCODEC_ERR_CODECNOTFOUND
+#endif
+#if defined(WINCODEC_ERR_DECODERNOTFOUND)
+ || hr == WINCODEC_ERR_DECODERNOTFOUND
+#endif
+#if defined(WINCODEC_ERR_UNKNOWNIMAGEFORMAT)
+ || hr == WINCODEC_ERR_UNKNOWNIMAGEFORMAT
+#endif
+#if defined(WINCODEC_ERR_PROPERTYNOTSUPPORTED)
+ || hr == WINCODEC_ERR_PROPERTYNOTSUPPORTED
+#endif
+ )
+ {
+ return PVC_UNSUP_FILE_TYPE;
+ }
+ return PVC_EXCEPTION;
+}
+
+std::wstring Utf8ToWide(const char* path)
+{
+ if (!path)
+ {
+ return std::wstring();
+ }
+ int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, nullptr, 0);
+ if (len <= 0)
+ {
+ len = MultiByteToWideChar(CP_ACP, 0, path, -1, nullptr, 0);
+ if (len <= 0)
+ {
+ return std::wstring();
+ }
+ std::wstring wide(static_cast(len), L'\0');
+ MultiByteToWideChar(CP_ACP, 0, path, -1, wide.data(), len);
+ if (!wide.empty() && wide.back() == L'\0')
+ {
+ wide.pop_back();
+ }
+ return wide;
+ }
+ std::wstring wide(static_cast(len), L'\0');
+ MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, wide.data(), len);
+ if (!wide.empty() && wide.back() == L'\0')
+ {
+ wide.pop_back();
+ }
+ return wide;
+}
+
+PVCODE PopulateImageInfo(ImageHandle& handle, LPPVImageInfo info, DWORD bufferSize, bool hasPreviousImage,
+ DWORD previousImageIndex, int currentImage)
+{
+ if (!info)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ if (bufferSize < sizeof(PVImageInfo))
+ {
+ return PVC_INVALID_HANDLE;
+ }
+
+ const DWORD bytesToClear = std::min(bufferSize, static_cast(sizeof(PVImageInfo)));
+ ZeroMemory(info, bytesToClear);
+ info->cbSize = sizeof(PVImageInfo);
+ info->FileSize = handle.baseInfo.FileSize;
+ info->Colors = PV_COLOR_TC32;
+ info->Format = handle.baseInfo.Format;
+ info->Flags = handle.baseInfo.Flags;
+ info->ColorModel = PVCM_RGB;
+ info->NumOfImages = static_cast(handle.frames.size());
+ info->StretchMode = handle.stretchMode;
+ info->TotalBitDepth = 32;
+
+ struct FormatLabel
+ {
+ DWORD format;
+ const char* label;
+ };
+
+ static constexpr FormatLabel kFormatLabels[] = {
+ {PVF_BMP, "BMP"},
+ {PVF_PNG, "PNG"},
+ {PVF_JPG, "JPEG"},
+ {PVF_TIFF, "TIFF"},
+ {PVF_GIF, "GIF"},
+ {PVF_ICO, "ICO"},
+ };
+
+ const char* info1 = "WIC";
+ for (const auto& entry : kFormatLabels)
+ {
+ if (entry.format == info->Format)
+ {
+ info1 = entry.label;
+ break;
+ }
+ }
+ StringCchCopyA(info->Info1, PV_MAX_INFO_LEN, info1);
+
+ if (handle.frames.empty())
+ {
+ info->CurrentImage = 0;
+ info->Width = 0;
+ info->Height = 0;
+ info->BytesPerLine = 0;
+ info->StretchedWidth = 0;
+ info->StretchedHeight = 0;
+ return PVC_OK;
+ }
+
+ size_t fallbackIndex = 0;
+ if (!handle.frames.empty() && hasPreviousImage)
+ {
+ fallbackIndex = std::min(static_cast(previousImageIndex), handle.frames.size() - 1);
+ }
+
+ const size_t normalized = NormalizeFrameIndex(handle, currentImage, fallbackIndex);
+ const FrameData& frame = handle.frames[normalized];
+
+ info->CurrentImage = static_cast(normalized);
+ info->Width = frame.width;
+ info->Height = frame.height;
+ info->BytesPerLine = frame.stride;
+
+ double dpiX = 0.0;
+ double dpiY = 0.0;
+ if (frame.frame)
+ {
+ if (SUCCEEDED(frame.frame->GetResolution(&dpiX, &dpiY)))
+ {
+ auto clampDpi = [](double value) -> DWORD {
+ if (!std::isfinite(value) || value <= 0.0)
+ {
+ return 0;
+ }
+ const double rounded = std::floor(value + 0.5);
+ if (rounded <= 0.0)
+ {
+ return 0;
+ }
+ if (rounded > static_cast(std::numeric_limits::max()))
+ {
+ return std::numeric_limits::max();
+ }
+ return static_cast(rounded);
+ };
+
+ info->HorDPI = clampDpi(dpiX);
+ info->VerDPI = clampDpi(dpiY);
+ }
+ }
+
+ const LONGLONG stretchWidthSigned = handle.stretchWidth ? static_cast(handle.stretchWidth)
+ : static_cast(frame.width);
+ const LONGLONG stretchHeightSigned = handle.stretchHeight ? static_cast(handle.stretchHeight)
+ : static_cast(frame.height);
+ const ULONGLONG stretchWidthAbs = AbsoluteDimension(stretchWidthSigned);
+ const ULONGLONG stretchHeightAbs = AbsoluteDimension(stretchHeightSigned);
+ info->StretchedWidth = stretchWidthAbs > std::numeric_limits::max()
+ ? std::numeric_limits::max()
+ : static_cast(stretchWidthAbs);
+ info->StretchedHeight = stretchHeightAbs > std::numeric_limits::max()
+ ? std::numeric_limits::max()
+ : static_cast(stretchHeightAbs);
+ return PVC_OK;
+}
+
+DWORD MapFormatToPvFormat(const GUID& container)
+{
+ if (container == GUID_ContainerFormatBmp)
+ return PVF_BMP;
+ if (container == GUID_ContainerFormatPng)
+ return PVF_PNG;
+ if (container == GUID_ContainerFormatJpeg)
+ return PVF_JPG;
+ if (container == GUID_ContainerFormatGif)
+ return PVF_GIF;
+ if (container == GUID_ContainerFormatTiff)
+ return PVF_TIFF;
+ if (container == GUID_ContainerFormatIco)
+ return PVF_ICO;
+ return PVF_BMP;
+}
+
+HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& handle)
+{
+ UINT frameCount = 0;
+ handle.baseInfo.Flags = 0;
+
+ HRESULT hr = decoder->GetFrameCount(&frameCount);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ handle.frames.resize(frameCount);
+ bool hasExif = SourceContainsExif(decoder);
+ if (!hasExif)
+ {
+ Microsoft::WRL::ComPtr decoderQuery;
+ if (SUCCEEDED(decoder->GetMetadataQueryReader(&decoderQuery)) && decoderQuery)
+ {
+ hasExif = QueryReaderContainsExif(decoderQuery.Get());
+ }
+ }
+ for (UINT i = 0; i < frameCount; ++i)
+ {
+ FrameData data;
+ hr = decoder->GetFrame(i, &data.frame);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ UINT width = 0;
+ UINT height = 0;
+ hr = data.frame->GetSize(&width, &height);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ if (width == 0 || height == 0)
+ {
+ return WINCODEC_ERR_INVALIDPARAMETER;
+ }
+ data.width = width;
+ data.height = height;
+ data.delayMs = GetFrameDelayMilliseconds(data.frame.Get());
+ if (frameCount > 1 && data.delayMs == 0)
+ {
+ data.delayMs = 100;
+ }
+ if (!hasExif && FrameContainsExif(data.frame.Get()))
+ {
+ hasExif = true;
+ }
+ handle.frames[i] = std::move(data);
+ }
+ GUID container = {};
+ decoder->GetContainerFormat(&container);
+ handle.baseInfo.Format = MapFormatToPvFormat(container);
+ handle.baseInfo.NumOfImages = frameCount;
+ handle.baseInfo.FileSize = QueryFileSize(handle.fileName);
+
+ if (!hasExif && handle.baseInfo.Format == PVF_JPG)
+ {
+ CExifFileBuffer buffer;
+ bool loaded = false;
+#ifdef _UNICODE
+ loaded = buffer.LoadFromFile(handle.fileName.c_str());
+#else
+ loaded = buffer.LoadFromWideFile(handle.fileName.c_str());
+#endif
+ if (loaded)
+ {
+ hasExif = buffer.HasExifData();
+ }
+ }
+ if (hasExif)
+ {
+ handle.baseInfo.Flags |= PVFF_EXIF;
+ }
+ if (frameCount > 1 && handle.baseInfo.Format == PVF_GIF)
+ {
+ handle.baseInfo.Flags |= PVFF_IMAGESEQUENCE;
+ }
+ return S_OK;
+}
+
+PVCODE DrawFrame(ImageHandle& handle, FrameData& frame, HDC dc, int x, int y, LPRECT rect)
+{
+ if (!dc)
+ {
+ return PVC_OK;
+ }
+
+ const LONGLONG stretchWidthSigned = handle.stretchWidth ? static_cast(handle.stretchWidth)
+ : static_cast(frame.width);
+ const LONGLONG stretchHeightSigned = handle.stretchHeight ? static_cast(handle.stretchHeight)
+ : static_cast(frame.height);
+ const ULONGLONG stretchWidthAbs = AbsoluteDimension(stretchWidthSigned);
+ const ULONGLONG stretchHeightAbs = AbsoluteDimension(stretchHeightSigned);
+ if (stretchWidthAbs == 0 || stretchHeightAbs == 0)
+ {
+ return PVC_OK;
+ }
+
+ if (stretchWidthAbs > static_cast(std::numeric_limits::max()) ||
+ stretchHeightAbs > static_cast(std::numeric_limits::max()))
+ {
+ return PVC_INVALID_DIMENSIONS;
+ }
+ if (frame.width > static_cast(std::numeric_limits::max()) ||
+ frame.height > static_cast(std::numeric_limits::max()))
+ {
+ return PVC_INVALID_DIMENSIONS;
+ }
+
+ RECT imageRect;
+ imageRect.left = x;
+ imageRect.top = y;
+ const LONGLONG imageRight = static_cast(x) + static_cast(stretchWidthAbs);
+ const LONGLONG imageBottom = static_cast(y) + static_cast(stretchHeightAbs);
+ if (imageRight > std::numeric_limits::max() || imageRight < std::numeric_limits::min() ||
+ imageBottom > std::numeric_limits::max() || imageBottom < std::numeric_limits::min())
+ {
+ return PVC_INVALID_DIMENSIONS;
+ }
+ imageRect.right = static_cast(imageRight);
+ imageRect.bottom = static_cast(imageBottom);
+
+ RECT clipRect = imageRect;
+ if (rect)
+ {
+ if (!IntersectRect(&clipRect, &imageRect, rect))
+ {
+ return PVC_OK;
+ }
+ }
+
+ int savedState = 0;
+ bool resetClip = false;
+ if (rect)
+ {
+ savedState = SaveDC(dc);
+ if (savedState == 0)
+ {
+ resetClip = true;
+ }
+ const int clipResult = IntersectClipRect(dc, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
+ if (clipResult == ERROR)
+ {
+ if (savedState > 0)
+ {
+ RestoreDC(dc, savedState);
+ }
+ else if (resetClip)
+ {
+ SelectClipRgn(dc, nullptr);
+ }
+ return PVC_GDI_ERROR;
+ }
+ if (clipResult == NULLREGION)
+ {
+ if (savedState > 0)
+ {
+ RestoreDC(dc, savedState);
+ }
+ else if (resetClip)
+ {
+ SelectClipRgn(dc, nullptr);
+ }
+ return PVC_OK;
+ }
+ }
+
+ int previousMode = SetStretchBltMode(dc, handle.stretchMode ? static_cast(handle.stretchMode) : COLORONCOLOR);
+ BITMAPINFO bmi{};
+ bmi.bmiHeader = frame.bmi;
+
+ const int destX = stretchWidthSigned >= 0 ? imageRect.left : imageRect.right - 1;
+ const int destY = stretchHeightSigned >= 0 ? imageRect.top : imageRect.bottom - 1;
+ const int destWidth = stretchWidthSigned >= 0 ? static_cast(stretchWidthAbs)
+ : -static_cast(stretchWidthAbs);
+ const int destHeight = stretchHeightSigned >= 0 ? static_cast(stretchHeightAbs)
+ : -static_cast(stretchHeightAbs);
+
+ const int result = StretchDIBits(dc, destX, destY, destWidth, destHeight, 0, 0, frame.width, frame.height,
+ frame.pixels.data(), &bmi, DIB_RGB_COLORS, SRCCOPY);
+
+ if (previousMode > 0)
+ {
+ SetStretchBltMode(dc, previousMode);
+ }
+ if (savedState > 0)
+ {
+ RestoreDC(dc, savedState);
+ }
+ else if (resetClip)
+ {
+ SelectClipRgn(dc, nullptr);
+ }
+ if (result == GDI_ERROR)
+ {
+ return PVC_GDI_ERROR;
+ }
+ return PVC_OK;
+}
+
+PVCODE CreateSequenceNodes(ImageHandle& handle, LPPVImageSequence* seq)
+{
+ if (!seq)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ *seq = nullptr;
+ LPPVImageSequence head = nullptr;
+ LPPVImageSequence* tail = seq;
+ for (size_t i = 0; i < handle.frames.size(); ++i)
+ {
+ FrameData& frame = handle.frames[i];
+ HRESULT hr = DecodeFrame(handle, i);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ auto node = std::make_unique();
+ node->pNext = nullptr;
+ node->Rect.left = 0;
+ node->Rect.top = 0;
+ node->Rect.right = frame.width;
+ node->Rect.bottom = frame.height;
+ node->Delay = frame.delayMs;
+ node->DisposalMethod = PVDM_UNDEFINED;
+ node->ImgHandle = frame.hbitmap;
+ node->TransparentHandle = nullptr;
+ *tail = node.release();
+ tail = &((*tail)->pNext);
+ }
+ *tail = nullptr;
+ return PVC_OK;
+}
+
+PVCODE SaveFrame(ImageHandle& handle, int imageIndex, const wchar_t* path, const GuidMapping& mapping,
+ LPPVSaveImageInfo info)
+{
+ if (handle.frames.empty())
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ const size_t normalizedIndex = NormalizeFrameIndex(handle, imageIndex, 0);
+ FrameData& frame = handle.frames[normalizedIndex];
+ HRESULT hr = DecodeFrame(handle, normalizedIndex);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ Microsoft::WRL::ComPtr encoder;
+ hr = handle.backend->Factory()->CreateEncoder(mapping.container, nullptr, &encoder);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ Microsoft::WRL::ComPtr stream;
+ hr = handle.backend->Factory()->CreateStream(&stream);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ hr = stream->InitializeFromFilename(path, GENERIC_WRITE);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ hr = encoder->Initialize(stream.Get(), WICBitmapEncoderNoCache);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ Microsoft::WRL::ComPtr frameEncode;
+ Microsoft::WRL::ComPtr bag;
+ hr = encoder->CreateNewFrame(&frameEncode, &bag);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ hr = frameEncode->Initialize(bag.Get());
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ hr = frameEncode->SetSize(frame.width, frame.height);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ GUID pixelFormat = mapping.pixelFormat;
+ hr = frameEncode->SetPixelFormat(&pixelFormat);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ if (pixelFormat != mapping.pixelFormat)
+ {
+ return PVC_UNSUP_FILE_TYPE;
+ }
+
+ if (mapping.pixelFormat == GUID_WICPixelFormat24bppBGR)
+ {
+ const UINT stride = frame.width * 3;
+ std::vector rgb(stride * frame.height);
+ for (UINT y = 0; y < frame.height; ++y)
+ {
+ const BYTE* src = frame.pixels.data() + y * frame.stride;
+ BYTE* dst = rgb.data() + y * stride;
+ for (UINT x = 0; x < frame.width; ++x)
+ {
+ dst[x * 3 + 0] = src[x * 4 + 0];
+ dst[x * 3 + 1] = src[x * 4 + 1];
+ dst[x * 3 + 2] = src[x * 4 + 2];
+ }
+ }
+ hr = frameEncode->WritePixels(frame.height, stride, static_cast(rgb.size()), rgb.data());
+ }
+ else if (mapping.pixelFormat == GUID_WICPixelFormat8bppIndexed)
+ {
+ Microsoft::WRL::ComPtr bitmap;
+ hr = handle.backend->Factory()->CreateBitmapFromMemory(frame.width, frame.height,
+ GUID_WICPixelFormat32bppBGRA, frame.stride,
+ static_cast(frame.pixels.size()),
+ frame.pixels.data(), &bitmap);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ Microsoft::WRL::ComPtr palette;
+ hr = handle.backend->Factory()->CreatePalette(&palette);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ hr = palette->InitializeFromBitmap(bitmap.Get(), 256, FALSE);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ hr = frameEncode->SetPalette(palette.Get());
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ Microsoft::WRL::ComPtr gifConverter;
+ hr = handle.backend->Factory()->CreateFormatConverter(&gifConverter);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ hr = gifConverter->Initialize(bitmap.Get(), GUID_WICPixelFormat8bppIndexed, WICBitmapDitherTypeErrorDiffusion,
+ palette.Get(), 0.0, WICBitmapPaletteTypeCustom);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ const UINT stride = frame.width;
+ std::vector indexed(static_cast(stride) * frame.height);
+ WICRect rect{0, 0, static_cast(frame.width), static_cast(frame.height)};
+ hr = gifConverter->CopyPixels(&rect, stride, static_cast(indexed.size()), indexed.data());
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ hr = frameEncode->WritePixels(frame.height, stride, static_cast(indexed.size()), indexed.data());
+ }
+ else
+ {
+ hr = frameEncode->WritePixels(frame.height, frame.stride, static_cast(frame.pixels.size()), frame.pixels.data());
+ }
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ hr = frameEncode->Commit();
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ hr = encoder->Commit();
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ return PVC_OK;
+}
+
+DWORD MapPixelFormatToColors(const GUID& guid)
+{
+ if (guid == GUID_WICPixelFormat1bppIndexed)
+ return 2;
+ if (guid == GUID_WICPixelFormat4bppIndexed)
+ return 16;
+ if (guid == GUID_WICPixelFormat8bppIndexed)
+ return 256;
+ return 0;
+}
+
+} // namespace
+
+ScopedCoInit::ScopedCoInit()
+ : m_hr(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED))
+ , m_needUninit(false)
+{
+ if (m_hr == RPC_E_CHANGED_MODE)
+ {
+ m_hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+ }
+ if (m_hr == S_OK || m_hr == S_FALSE)
+ {
+ m_needUninit = true;
+ }
+}
+
+ScopedCoInit::~ScopedCoInit()
+{
+ if (m_needUninit)
+ {
+ CoUninitialize();
+ }
+}
+
+Backend::Backend()
+{
+ if (!m_comScope.Succeeded())
+ {
+ return;
+ }
+ auto hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_factory));
+ if (FAILED(hr))
+ {
+ m_factory.Reset();
+ }
+}
+
+Backend& Backend::Instance()
+{
+ static Backend instance;
+ return instance;
+}
+
+bool Backend::Populate(CPVW32DLL& table)
+{
+ if (!m_factory)
+ {
+ return false;
+ }
+
+ table.PVOpenImageEx = &Backend::sPVOpenImageEx;
+ table.PVCloseImage = &Backend::sPVCloseImage;
+ table.PVReadImage2 = &Backend::sPVReadImage2;
+ table.PVDrawImage = &Backend::sPVDrawImage;
+ table.PVGetErrorText = &Backend::sPVGetErrorText;
+ table.PVSetBkHandle = &Backend::sPVSetBkHandle;
+ table.PVGetDLLVersion = &Backend::sPVGetDLLVersion;
+ table.PVSetStretchParameters = &Backend::sPVSetStretchParameters;
+ table.PVLoadFromClipboard = &Backend::sPVLoadFromClipboard;
+ table.PVGetImageInfo = &Backend::sPVGetImageInfo;
+ table.PVSetParam = &Backend::sPVSetParam;
+ table.PVGetHandles2 = &Backend::sPVGetHandles2;
+ table.PVSaveImage = &Backend::sPVSaveImage;
+ table.PVChangeImage = &Backend::sPVChangeImage;
+ table.PVIsOutCombSupported = &Backend::sPVIsOutCombSupported;
+ table.PVReadImageSequence = &Backend::sPVReadImageSequence;
+ table.PVCropImage = &Backend::sPVCropImage;
+ table.GetRGBAtCursor = &Backend::sGetRGBAtCursor;
+ table.CalculateHistogram = &Backend::sCalculateHistogram;
+ table.CreateThumbnail = &Backend::sCreateThumbnail;
+ table.SimplifyImageSequence = &Backend::sSimplifyImageSequence;
+ table.Handle = nullptr;
+ StringCchCopyA(table.Version, SizeOf(table.Version), "WIC 1.0");
+ return true;
+}
+
+ImageHandle* Backend::FromHandle(LPPVHandle handle)
+{
+ return reinterpret_cast(handle);
+}
+
+PVCODE WINAPI Backend::sPVOpenImageEx(LPPVHandle* Img, LPPVOpenImageExInfo pOpenExInfo, LPPVImageInfo pImgInfo, int size)
+{
+ if (!Img || !pOpenExInfo)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ *Img = nullptr;
+ if (!(pOpenExInfo->Flags & PVOF_ATTACH_TO_HANDLE) && !pOpenExInfo->FileName)
+ {
+ return PVC_UNSUP_FILE_TYPE;
+ }
+
+ ScopedCoInit init;
+ if (!init.Succeeded())
+ {
+ return PVC_EXCEPTION;
+ }
+
+ auto& backend = Backend::Instance();
+ auto image = std::make_unique();
+ image->backend = &backend;
+ image->openFlags = pOpenExInfo->Flags;
+
+ if (pOpenExInfo->Flags & PVOF_ATTACH_TO_HANDLE)
+ {
+ HBITMAP bitmap = reinterpret_cast(pOpenExInfo->Handle);
+ if (!bitmap)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+
+ FrameData frame;
+ HRESULT hr = PopulateFrameFromBitmapHandle(frame, bitmap);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ image->frames.push_back(std::move(frame));
+ image->baseInfo.Format = PVF_BMP;
+ image->baseInfo.Flags = 0;
+ image->baseInfo.NumOfImages = 1;
+ image->baseInfo.FileSize = 0;
+ }
+ else
+ {
+ image->fileName = Utf8ToWide(pOpenExInfo->FileName);
+ if (image->fileName.empty())
+ {
+ return PVC_CANNOT_OPEN_FILE;
+ }
+ image->baseInfo.FileSize = QueryFileSize(image->fileName);
+
+ Microsoft::WRL::ComPtr decoder;
+ HRESULT hr = CreateDecoder(backend, image->fileName, &decoder);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ hr = CollectFrames(backend, decoder.Get(), *image);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ }
+
+ if (image->frames.empty())
+ {
+ return PVC_UNSUP_FILE_TYPE;
+ }
+
+ image->baseInfo.NumOfImages = static_cast(image->frames.size());
+
+ HRESULT hr = DecodeFrame(*image, 0);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+
+ if (pImgInfo)
+ {
+ const DWORD bufferSize = size > 0 ? static_cast(size) : 0;
+ PopulateImageInfo(*image, pImgInfo, bufferSize, false, 0, 0);
+ }
+
+ *Img = reinterpret_cast(image.release());
+ return PVC_OK;
+}
+
+PVCODE WINAPI Backend::sPVCloseImage(LPPVHandle Img)
+{
+ auto handle = FromHandle(Img);
+ if (!handle)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ for (auto& frame : handle->frames)
+ {
+ frame.converter.Reset();
+ frame.colorConvertedSource.Reset();
+ if (frame.hbitmap)
+ {
+ DeleteObject(frame.hbitmap);
+ frame.hbitmap = nullptr;
+ }
+ }
+ delete handle;
+ return PVC_OK;
+}
+
+PVCODE WINAPI Backend::sPVReadImage2(LPPVHandle Img, HDC paintDC, RECT* dRect, TProgressProc /*progress*/, void* /*appSpecific*/,
+ int imageIndex)
+{
+ auto handle = FromHandle(Img);
+ if (!handle)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ ScopedCoInit init;
+ if (!init.Succeeded())
+ {
+ return PVC_EXCEPTION;
+ }
+ if (handle->frames.empty())
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ const size_t normalizedIndex = NormalizeFrameIndex(*handle, imageIndex, 0);
+ HRESULT hr = DecodeFrame(*handle, normalizedIndex);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ return DrawFrame(*handle, handle->frames[normalizedIndex], paintDC, dRect ? dRect->left : 0,
+ dRect ? dRect->top : 0, dRect);
+}
+
+PVCODE WINAPI Backend::sPVDrawImage(LPPVHandle Img, HDC paintDC, int x, int y, LPRECT rect)
+{
+ auto handle = FromHandle(Img);
+ if (!handle)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ ScopedCoInit init;
+ if (!init.Succeeded())
+ {
+ return PVC_EXCEPTION;
+ }
+ HRESULT hr = DecodeFrame(*handle, 0);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ return DrawFrame(*handle, handle->frames[0], paintDC, x, y, rect);
+}
+
+const char* WINAPI Backend::sPVGetErrorText(DWORD errorCode)
+{
+ return LookupError(errorCode);
+}
+
+PVCODE WINAPI Backend::sPVSetBkHandle(LPPVHandle Img, COLORREF bkColor)
+{
+ auto handle = FromHandle(Img);
+ if (!handle)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ handle->background = bkColor;
+ return PVC_OK;
+}
+
+DWORD WINAPI Backend::sPVGetDLLVersion()
+{
+ return kBackendVersion;
+}
+
+PVCODE WINAPI Backend::sPVSetStretchParameters(LPPVHandle Img, DWORD width, DWORD height, DWORD mode)
+{
+ auto handle = FromHandle(Img);
+ if (!handle)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ const auto convert = [](DWORD value) -> LONG {
+ if (value == 0 || value == 0x80000000u)
+ {
+ return 0;
+ }
+ const LONG signedValue = static_cast(value);
+ if ((value & 0x80000000u) != 0u)
+ {
+ return signedValue;
+ }
+ if (value > static_cast(std::numeric_limits::max()))
+ {
+ return std::numeric_limits::max();
+ }
+ return signedValue;
+ };
+
+ handle->stretchWidth = convert(width);
+ handle->stretchHeight = convert(height);
+ handle->stretchMode = mode;
+ return PVC_OK;
+}
+
+PVCODE WINAPI Backend::sPVLoadFromClipboard(LPPVHandle* /*Img*/, LPPVImageInfo /*pImgInfo*/, int /*size*/)
+{
+ return PVC_UNSUP_FILE_TYPE;
+}
+
+PVCODE WINAPI Backend::sPVGetImageInfo(LPPVHandle Img, LPPVImageInfo pImgInfo, int size, int imageIndex)
+{
+ auto handle = FromHandle(Img);
+ if (!handle || !pImgInfo)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ ScopedCoInit init;
+ if (!init.Succeeded())
+ {
+ return PVC_EXCEPTION;
+ }
+ const DWORD bufferSize = size > 0 ? static_cast(size) : 0;
+ DWORD previousIndex = 0;
+ bool hasPreviousIndex = false;
+ if (bufferSize >= sizeof(PVImageInfo) && pImgInfo->cbSize == sizeof(PVImageInfo))
+ {
+ previousIndex = pImgInfo->CurrentImage;
+ hasPreviousIndex = true;
+ }
+ size_t fallbackIndex = 0;
+ if (!handle->frames.empty())
+ {
+ const size_t lastIndex = handle->frames.size() - 1;
+ fallbackIndex = std::min(static_cast(previousIndex), lastIndex);
+ }
+ else
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ const size_t normalizedIndex = NormalizeFrameIndex(*handle, imageIndex, fallbackIndex);
+ HRESULT hr = DecodeFrame(*handle, normalizedIndex);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ return PopulateImageInfo(*handle, pImgInfo, bufferSize, hasPreviousIndex, previousIndex, imageIndex);
+}
+
+PVCODE WINAPI Backend::sPVSetParam(LPPVHandle /*Img*/)
+{
+ return PVC_OK;
+}
+
+PVCODE WINAPI Backend::sPVGetHandles2(LPPVHandle Img, LPPVImageHandles* pHandles)
+{
+ auto handle = FromHandle(Img);
+ if (!handle || !pHandles)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ ScopedCoInit init;
+ if (!init.Succeeded())
+ {
+ return PVC_EXCEPTION;
+ }
+ HRESULT hr = DecodeFrame(*handle, 0);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ auto& frame = handle->frames[0];
+ PVImageHandles& handles = handle->handles;
+ ZeroMemory(&handles, sizeof(PVImageHandles));
+ handles.TransparentHandle = frame.hbitmap;
+ handles.TransparentBackgroundHandle = frame.hbitmap;
+ handles.StretchedHandle = frame.hbitmap;
+ handles.StretchedTransparentHandle = frame.hbitmap;
+ handles.Palette = frame.palette.empty() ? nullptr : frame.palette.data();
+ handles.pLines = frame.linePointers.empty() ? nullptr : frame.linePointers.data();
+ *pHandles = &handles;
+ return PVC_OK;
+}
+
+PVCODE WINAPI Backend::sPVSaveImage(LPPVHandle Img, const char* outFileName, LPPVSaveImageInfo pSii, TProgressProc /*progress*/,
+ void* /*appSpecific*/, int imageIndex)
+{
+ auto handle = FromHandle(Img);
+ if (!handle)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ ScopedCoInit init;
+ if (!init.Succeeded())
+ {
+ return PVC_EXCEPTION;
+ }
+ if (!outFileName)
+ {
+ return PVC_UNSUP_OUT_PARAMS;
+ }
+ const auto fileName = Utf8ToWide(outFileName);
+ const GuidMapping* mapping = nullptr;
+ for (const auto& item : kEncoderMappings)
+ {
+ if (item.format == pSii->Format)
+ {
+ mapping = &item;
+ break;
+ }
+ }
+ if (!mapping)
+ {
+ return PVC_UNSUP_OUT_PARAMS;
+ }
+ return SaveFrame(*handle, imageIndex, fileName.c_str(), *mapping, pSii);
+}
+
+PVCODE WINAPI Backend::sPVChangeImage(LPPVHandle Img, DWORD flags)
+{
+ auto handle = FromHandle(Img);
+ if (!handle)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ ScopedCoInit init;
+ if (!init.Succeeded())
+ {
+ return PVC_EXCEPTION;
+ }
+ if (handle->frames.empty())
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ FrameData& frame = handle->frames[0];
+ HRESULT hr = DecodeFrame(*handle, 0);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ frame.converter.Reset();
+ frame.colorConvertedSource.Reset();
+ if (!(flags & (PVCF_ROTATE90CW | PVCF_ROTATE90CCW)))
+ {
+ return PVC_OK;
+ }
+
+ const UINT newWidth = frame.height;
+ const UINT newHeight = frame.width;
+ std::vector rotated(frame.pixels.size());
+ for (UINT y = 0; y < frame.height; ++y)
+ {
+ for (UINT x = 0; x < frame.width; ++x)
+ {
+ BYTE* src = frame.pixels.data() + y * frame.stride + x * 4;
+ UINT dstX = flags & PVCF_ROTATE90CW ? (frame.height - 1 - y) : y;
+ UINT dstY = flags & PVCF_ROTATE90CW ? x : (frame.width - 1 - x);
+ BYTE* dst = rotated.data() + dstY * newWidth * 4 + dstX * 4;
+ memcpy(dst, src, 4);
+ }
+ }
+ frame.width = newWidth;
+ frame.height = newHeight;
+ frame.stride = frame.width * 4;
+ frame.pixels.swap(rotated);
+ HRESULT finalizeHr = FinalizeDecodedFrame(frame);
+ if (FAILED(finalizeHr))
+ {
+ return HResultToPvCode(finalizeHr);
+ }
+ return PVC_OK;
+}
+
+DWORD WINAPI Backend::sPVIsOutCombSupported(int format, int /*compression*/, int /*colors*/, int /*colorModel*/)
+{
+ for (const auto& item : kEncoderMappings)
+ {
+ if (item.format == static_cast(format))
+ {
+ return 0;
+ }
+ }
+ return static_cast(-1);
+}
+
+PVCODE WINAPI Backend::sPVReadImageSequence(LPPVHandle Img, LPPVImageSequence* seq)
+{
+ auto handle = FromHandle(Img);
+ if (!handle)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ ScopedCoInit init;
+ if (!init.Succeeded())
+ {
+ return PVC_EXCEPTION;
+ }
+ return CreateSequenceNodes(*handle, seq);
+}
+
+PVCODE WINAPI Backend::sPVCropImage(LPPVHandle Img, int left, int top, int width, int height)
+{
+ auto handle = FromHandle(Img);
+ if (!handle)
+ {
+ return PVC_INVALID_HANDLE;
+ }
+ ScopedCoInit init;
+ if (!init.Succeeded())
+ {
+ return PVC_EXCEPTION;
+ }
+ FrameData& frame = handle->frames[0];
+ HRESULT hr = DecodeFrame(*handle, 0);
+ if (FAILED(hr))
+ {
+ return HResultToPvCode(hr);
+ }
+ frame.converter.Reset();
+ frame.colorConvertedSource.Reset();
+ if (left < 0 || top < 0 || width <= 0 || height <= 0 || left + width > static_cast(frame.width) ||
+ top + height > static_cast(frame.height))
+ {
+ return PVC_INVALID_DIMENSIONS;
+ }
+ std::vector cropped(static_cast