From 5b6e8e0eb10e8351ebde8e4af3779e2bb5f3cdfc Mon Sep 17 00:00:00 2001 From: Viliam Lejcik Date: Mon, 27 Jan 2025 08:47:47 +0100 Subject: [PATCH 1/8] pictview: added support for viewing .heif and .webp files (experimental) --- src/plugins/pictview/PVMessage.h | 46 +- src/plugins/pictview/PVOverrider.cpp | 434 ++++++++++++++++++ src/plugins/pictview/PVOverrider.h | 9 + src/plugins/pictview/heif.cpp | 137 ++++++ src/plugins/pictview/heif.h | 22 + src/plugins/pictview/histwnd.cpp | 1 - src/plugins/pictview/pictview.cpp | 15 +- src/plugins/pictview/pvtwain.cpp | 1 - src/plugins/pictview/statsbar.cpp | 1 - src/plugins/pictview/vcpkg.json | 7 + src/plugins/pictview/vcxproj/pictview.vcxproj | 10 + .../pictview/vcxproj/pictview.vcxproj.filters | 18 + src/plugins/pictview/webp.cpp | 138 ++++++ src/plugins/pictview/webp.h | 27 ++ src/plugins/pictview/wiawrap.cpp | 1 - 15 files changed, 838 insertions(+), 29 deletions(-) create mode 100644 src/plugins/pictview/PVOverrider.cpp create mode 100644 src/plugins/pictview/PVOverrider.h create mode 100644 src/plugins/pictview/heif.cpp create mode 100644 src/plugins/pictview/heif.h create mode 100644 src/plugins/pictview/vcpkg.json create mode 100644 src/plugins/pictview/webp.cpp create mode 100644 src/plugins/pictview/webp.h diff --git a/src/plugins/pictview/PVMessage.h b/src/plugins/pictview/PVMessage.h index 2e15a6016..262a8bc9b 100644 --- a/src/plugins/pictview/PVMessage.h +++ b/src/plugins/pictview/PVMessage.h @@ -5,7 +5,7 @@ #include "lib/pvw32dll.h" -#define PVC_ENVELOPE_NOT_LOADED ((PVCODE)-123) +#define PVC_ENVELOPE_NOT_LOADED ((PVCODE) - 123) struct CPVWrapper { @@ -125,7 +125,7 @@ class PVMessage_InitTexts : public PVMessage { public: PVMessage_InitTexts(); - PVMessage_InitTexts(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_InitTexts(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool HandleRequest(); private: @@ -140,7 +140,7 @@ class PVMessage_GetErrorText : public PVMessage { public: PVMessage_GetErrorText(DWORD ErrorCode); - PVMessage_GetErrorText(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_GetErrorText(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool HandleRequest(); const char* GetErrorText(); @@ -156,7 +156,7 @@ class PVMessage_OpenImageEx : public PVMessage { public: PVMessage_OpenImageEx(LPPVOpenImageExInfo pOpenExInfo); - PVMessage_OpenImageEx(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_OpenImageEx(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool IsInited(); bool Exec(LPPVImageInfo pImgInfo); bool HandleRequest(); @@ -177,7 +177,7 @@ class PVMessage_GetImageInfo : public PVMessage { public: PVMessage_GetImageInfo(LPPVHandle pvHandle, int ImageIndex); - PVMessage_GetImageInfo(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_GetImageInfo(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool Exec(LPPVImageInfo pImgInfo); bool HandleRequest(); @@ -193,7 +193,7 @@ class PVMessageWithProgress : public PVMessage { public: PVMessageWithProgress(ePVMSG type, size_t dataSize, LPPVHandle pvHandle = NULL); - PVMessageWithProgress(LPPVMessageHeader pHdr, HANDLE Event) : PVMessage(pHdr), hEvent(Event){}; + PVMessageWithProgress(LPPVMessageHeader pHdr, HANDLE Event) : PVMessage(pHdr), hEvent(Event) {}; bool Exec(TProgressProc Progress, void* AppSpecific); BOOL HandleProgress(int done); @@ -217,7 +217,7 @@ class PVMessage_ReadImage : public PVMessageWithProgress { public: PVMessage_ReadImage(LPPVHandle pvHandle, int ImageIndex, bool bProgress); - PVMessage_ReadImage(LPPVMessageHeader pHdr, HANDLE hEvent) : PVMessageWithProgress(pHdr, hEvent){}; + PVMessage_ReadImage(LPPVMessageHeader pHdr, HANDLE hEvent) : PVMessageWithProgress(pHdr, hEvent) {}; bool Exec(HDC PaintDC, RECT* pDRect, TProgressProc Progress, void* AppSpecific); bool HandleRequest(); @@ -232,7 +232,7 @@ class PVMessage_CloseImage : public PVMessage { public: PVMessage_CloseImage(LPPVHandle pvHandle); - PVMessage_CloseImage(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_CloseImage(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; ~PVMessage_CloseImage(); bool HandleRequest(); }; @@ -241,7 +241,7 @@ class PVMessage_DrawImage : public PVMessage { public: PVMessage_DrawImage(LPPVHandle pvHandle, HDC PaintDC, int X, int Y, LPRECT pDrawRect); - PVMessage_DrawImage(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_DrawImage(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool Exec(HDC PaintDC); bool HandleRequest(); @@ -260,7 +260,7 @@ 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){}; + PVMessage_SaveImage(LPPVMessageHeader pHdr, HANDLE hEvent) : PVMessageWithProgress(pHdr, hEvent) {}; bool HandleRequest(); private: @@ -309,7 +309,7 @@ class PVMessage_LoadFromClipboard : public PVMessage { public: PVMessage_LoadFromClipboard(); - PVMessage_LoadFromClipboard(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_LoadFromClipboard(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool Exec(LPPVImageInfo pImgInfo); bool HandleRequest(); @@ -324,7 +324,7 @@ class PVMessage_ReadImageSequence : public PVMessage { public: PVMessage_ReadImageSequence(LPPVHandle pvHandle); - PVMessage_ReadImageSequence(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_ReadImageSequence(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool Exec(); bool HandleRequest(); @@ -339,7 +339,7 @@ class PVMessage_SetBkHandle : public PVMessage { public: PVMessage_SetBkHandle(LPPVHandle pvHandle, COLORREF bkColor); - PVMessage_SetBkHandle(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_SetBkHandle(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool HandleRequest(); private: @@ -353,7 +353,7 @@ class PVMessage_GetDLLVersion : public PVMessage { public: PVMessage_GetDLLVersion(); - PVMessage_GetDLLVersion(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_GetDLLVersion(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool Exec(DWORD& Version); bool HandleRequest(); @@ -368,7 +368,7 @@ class PVMessage_SetStretchParameters : public PVMessage { public: PVMessage_SetStretchParameters(LPPVHandle pvHandle, DWORD Width, DWORD Height, DWORD Mode); - PVMessage_SetStretchParameters(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_SetStretchParameters(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool HandleRequest(); private: @@ -384,7 +384,7 @@ class PVMessage_ChangeImage : public PVMessage { public: PVMessage_ChangeImage(LPPVHandle pvHandle, DWORD Flags); - PVMessage_ChangeImage(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_ChangeImage(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool HandleRequest(); private: @@ -398,7 +398,7 @@ class PVMessage_IsOutCombSupported : public PVMessage { public: PVMessage_IsOutCombSupported(int Fmt, int Compr, int Colors, int ColorModel); - PVMessage_IsOutCombSupported(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_IsOutCombSupported(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool HandleRequest(); private: @@ -415,7 +415,7 @@ class PVMessage_CropImage : public PVMessage { public: PVMessage_CropImage(LPPVHandle pvHandle, int Left, int Top, int Width, int Height); - PVMessage_CropImage(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_CropImage(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool HandleRequest(); private: @@ -432,7 +432,7 @@ class PVMessage_GetRGBAtCursor : public PVMessage { public: PVMessage_GetRGBAtCursor(LPPVHandle pvHandle, DWORD Colors, int x, int y); - PVMessage_GetRGBAtCursor(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_GetRGBAtCursor(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool HandleRequest(); RGBQUAD* GetRGB(); @@ -453,7 +453,7 @@ class PVMessage_CalculateHistogram : public PVMessage { public: PVMessage_CalculateHistogram(LPPVHandle pvHandle, const PVImageInfo* pvii); - PVMessage_CalculateHistogram(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_CalculateHistogram(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool HandleRequest(); void GetResults(LPDWORD luminosity, LPDWORD red, LPDWORD green, LPDWORD blue, LPDWORD rgb); @@ -477,7 +477,7 @@ 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){}; + PVMessage_CreateThumbnail(LPPVMessageHeader pHdr, HANDLE hEvent) : PVMessageWithProgress(pHdr, hEvent) {}; bool HandleRequest(); LPBYTE GetPixelData(); @@ -498,7 +498,7 @@ class PVMessage_SimplifyImageSequence : public PVMessage { public: PVMessage_SimplifyImageSequence(LPPVHandle pvHandle, DWORD ScreenWidth, DWORD ScreenHeight, const COLORREF& bgColor); - PVMessage_SimplifyImageSequence(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_SimplifyImageSequence(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool Exec(); bool HandleRequest(); LPPVImageSequence GetImageSequence(); @@ -521,7 +521,7 @@ class PVMessage_CloseHandle : public PVMessage { public: PVMessage_CloseHandle(DWORD Handle); - PVMessage_CloseHandle(LPPVMessageHeader pHdr) : PVMessage(pHdr){}; + PVMessage_CloseHandle(LPPVMessageHeader pHdr) : PVMessage(pHdr) {}; bool Exec(); bool HandleRequest(); diff --git a/src/plugins/pictview/PVOverrider.cpp b/src/plugins/pictview/PVOverrider.cpp new file mode 100644 index 000000000..8631b1172 --- /dev/null +++ b/src/plugins/pictview/PVOverrider.cpp @@ -0,0 +1,434 @@ +// SPDX-FileCopyrightText: 2023 Open Salamander Authors +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "precomp.h" +#include "PVOverrider.h" +#include "pictview.h" +#include "heif.h" +#include "webp.h" +#include +#include +#include + +namespace +{ + + // original function pointers + CPVW32DLL origPVW32DLL{}; + + enum class HandleType + { + PictView = 0, // the original PictView handle + Heif, + Webp + }; + + struct StretchParams + { + DWORD Width; + DWORD Height; + DWORD Mode; + }; + + // overridden PVHandle + struct Handle + { + HandleType type{HandleType::PictView}; + std::optional bkColor; + std::optional stretch; + std::variant data; + }; + + PVCODE OpenImageEx(LPPVHandle* Img, LPPVOpenImageExInfo pOpenExInfo, LPPVImageInfo pImgInfo, int Size) + { + PVCODE result; + assert(Img); + assert(pOpenExInfo); + assert(pImgInfo); + + // initialize the input handle + *Img = nullptr; + + // create a new handle + auto* handle = new Handle{}; + if (!handle) + return PVC_OOM; + + // additional supported image formats can be loaded only from a file, not from a buffer + if (pOpenExInfo->FileName && !(pOpenExInfo->Flags & PVOF_USERDEFINED_INPUT)) + { + PVImageInfo info; + + { + // try out to load the image as HEIF + handle->type = HandleType::Heif; + handle->data.emplace(); + auto& heif = std::get(handle->data); + result = heif.Open(pOpenExInfo->FileName, info); + if (result == PVC_OK) + { + // image opened successfully + *Img = reinterpret_cast(handle); + *pImgInfo = info; + return PVC_OK; + } + } + + { + // try out to load the image as WebP + handle->type = HandleType::Webp; + handle->data.emplace(); + auto& webp = std::get(handle->data); + result = webp.Open(pOpenExInfo->FileName, info); + if (result == PVC_OK) + { + // image opened successfully + *Img = reinterpret_cast(handle); + *pImgInfo = info; + return PVC_OK; + } + } + } + + // call the original function + handle->type = HandleType::PictView; + handle->data = LPPVHandle{nullptr}; + result = origPVW32DLL.PVOpenImageEx(&std::get(handle->data), pOpenExInfo, pImgInfo, Size); + if (result == PVC_OK) + { + // image opened successfully + *Img = reinterpret_cast(handle); + } + else + { + // error, delete the handle + origPVW32DLL.PVCloseImage(std::get(handle->data)); + delete handle; + } + + return result; + } + + PVCODE _ReadImage(Handle* handle, TProgressProc Progress, void* AppSpecific, int ImageIndex) + { + assert(handle); + assert(handle->type != HandleType::PictView); + // TODO: handle non primary ImageIndex + assert(ImageIndex == 0); + + HBITMAP bmp{}; + PVCODE result; + + switch (handle->type) + { + case HandleType::Heif: + result = std::get(handle->data).Read(bmp, Progress, AppSpecific); + break; + case HandleType::Webp: + result = std::get(handle->data).Read(bmp, Progress, AppSpecific); + break; + default: + assert(0 || "Invalid image type"); + return PVC_INVALID_HANDLE; + } + + if (result == PVC_OK) + { + LPPVHandle tmpHandle{}; + // image loaded successfully, load the bitmap by original PVW32DLL + PVOpenImageExInfo oiei{}; + oiei.cbSize = sizeof(oiei); + oiei.Flags = PVOF_ATTACH_TO_HANDLE; + oiei.Handle = bmp; + PVImageInfo pvii{}; + result = origPVW32DLL.PVOpenImageEx(&tmpHandle, &oiei, &pvii, sizeof(pvii)); + if (result == PVC_OK) + { + // apply background color and stretching if it was set + if (handle->bkColor.has_value()) + { + origPVW32DLL.PVSetBkHandle(tmpHandle, handle->bkColor.value()); + handle->bkColor.reset(); + } + if (handle->stretch.has_value()) + { + const auto& params = handle->stretch.value(); + origPVW32DLL.PVSetStretchParameters(tmpHandle, params.Width, params.Height, params.Mode); + handle->stretch.reset(); + } + // read the image content + result = origPVW32DLL.PVReadImage2(tmpHandle, 0, NULL, Progress, AppSpecific, 0); + } + + if (result == PVC_OK) + { + // success, swap the handles + handle->type = HandleType::PictView; + handle->data = tmpHandle; + } + else + { + origPVW32DLL.PVCloseImage(tmpHandle); + } + } + if (bmp) + DeleteObject(bmp); + return result; + } + + PVCODE ReadImage(LPPVHandle Img, HDC PaintDC, RECT* pDRect, TProgressProc Progress, void* AppSpecific, int ImageIndex) + { + if (!Img) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + { + // read the image content + auto result = _ReadImage(handle, Progress, AppSpecific, ImageIndex); + if (result == PVC_OK) + { + // ensure that the image handle is converted to the PictView type + assert(handle->type == HandleType::PictView); + // announce reading done + if (Progress) + Progress(100, AppSpecific); + // draw the loaded image + origPVW32DLL.PVDrawImage(std::get(handle->data), PaintDC, pDRect->left, pDRect->top, pDRect); + } + return result; + } + // call the original function + return origPVW32DLL.PVReadImage2(std::get(handle->data), PaintDC, pDRect, Progress, AppSpecific, ImageIndex); + } + + PVCODE DrawImage(LPPVHandle Img, HDC PaintDC, int X, int Y, LPRECT rect) + { + if (!Img) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + { + // TODO: draw background during loading + return PVC_INVALID_HANDLE; + } + // call the original function + return origPVW32DLL.PVDrawImage(std::get(handle->data), PaintDC, X, Y, rect); + } + + PVCODE CloseImage(LPPVHandle Img) + { + if (!Img) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + { + delete handle; + return PVC_OK; + } + // call the original function + PVCODE result = origPVW32DLL.PVCloseImage(std::get(handle->data)); + delete handle; + return result; + } + + PVCODE SetBkHandle(LPPVHandle Img, COLORREF BkColor) + { + if (!Img) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + { + // cache the background color + handle->bkColor = BkColor; + return PVC_OK; + } + // call the original function + return origPVW32DLL.PVSetBkHandle(std::get(handle->data), BkColor); + } + + PVCODE GetHandles2(LPPVHandle Img, LPPVImageHandles* pHandles) + { + if (!Img) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + return PVC_INVALID_HANDLE; + // call the original function + return origPVW32DLL.PVGetHandles2(std::get(handle->data), pHandles); + } + + PVCODE GetImageInfo(LPPVHandle Img, LPPVImageInfo pImgInfo, int cbSize, int ImageIndex) + { + if (!Img) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + return PVC_INVALID_HANDLE; + // call the original function + return origPVW32DLL.PVGetImageInfo(std::get(handle->data), pImgInfo, cbSize, ImageIndex); + } + + PVCODE SetStretchParameters(LPPVHandle Img, DWORD Width, DWORD Height, DWORD Mode) + { + if (!Img) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + { + // cache the parameters + handle->stretch = StretchParams{Width, Height, Mode}; + return PVC_OK; + } + // call the original function + return origPVW32DLL.PVSetStretchParameters(std::get(handle->data), Width, Height, Mode); + } + + PVCODE LoadFromClipboard(LPPVHandle* Img, LPPVImageInfo pImgInfo, int cbSize) + { + if (!Img) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + return PVC_INVALID_HANDLE; + // call the original function + return origPVW32DLL.PVLoadFromClipboard(Img, pImgInfo, cbSize); + } + + PVCODE SaveImage(LPPVHandle Img, const char* OutFName, LPPVSaveImageInfo pSii, TProgressProc Progress, void* AppSpecific, int ImageIndex) + { + if (!Img) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + return PVC_INVALID_HANDLE; + // call the original function + return origPVW32DLL.PVSaveImage(std::get(handle->data), OutFName, pSii, Progress, AppSpecific, ImageIndex); + } + + PVCODE ChangeImage(LPPVHandle Img, DWORD Flags) + { + if (!Img) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + return PVC_INVALID_HANDLE; + // call the original function + return origPVW32DLL.PVChangeImage(std::get(handle->data), Flags); + } + + PVCODE ReadImageSequence(LPPVHandle Img, LPPVImageSequence* ppSeq) + { + if (!Img) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + return PVC_INVALID_HANDLE; + // call the original function + return origPVW32DLL.PVReadImageSequence(Img, ppSeq); + } + + PVCODE CropImage(LPPVHandle Img, int Left, int Top, int Width, int Height) + { + if (!Img) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + return PVC_INVALID_HANDLE; + // call the original function + return origPVW32DLL.PVCropImage(std::get(handle->data), Left, Top, Width, Height); + } + + bool _GetRGBAtCursor(LPPVHandle Img, DWORD Colors, int x, int y, RGBQUAD* pRGB, int* pIndex) + { + if (!Img) + return false; + auto* handle = reinterpret_cast(Img); + if (handle->type != HandleType::PictView) + return false; + // call the original function + return origPVW32DLL.GetRGBAtCursor(std::get(handle->data), Colors, x, y, pRGB, pIndex); + } + + PVCODE _CalculateHistogram(LPPVHandle PVHandle, const LPPVImageInfo pvii, LPDWORD luminosity, + LPDWORD red, LPDWORD green, LPDWORD blue, LPDWORD rgb) + { + if (!PVHandle) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(PVHandle); + if (handle->type != HandleType::PictView) + return PVC_INVALID_HANDLE; + // call the original function + return origPVW32DLL.CalculateHistogram(std::get(handle->data), pvii, luminosity, red, green, blue, rgb); + } + + PVCODE _CreateThumbnail(LPPVHandle hPVImage, LPPVSaveImageInfo pSii, int imageIndex, DWORD imgWidth, DWORD imgHeight, + int thumbWidth, int thumbHeight, CSalamanderThumbnailMakerAbstract* thumbMaker, + DWORD thumbFlags, TProgressProc progressProc, void* progressProcArg) + { + if (!hPVImage) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(hPVImage); + if (handle->type != HandleType::PictView) + { + // set the thumbnail size + thumbMaker->SetParameters(thumbWidth, thumbHeight, 0); + + if (_ReadImage(handle, nullptr, nullptr, imageIndex) != PVC_OK) + { + thumbMaker->SetError(); + return PVC_INVALID_HANDLE; + } + if (progressProc) + progressProc(0, progressProcArg); + } + // call the original function + return origPVW32DLL.CreateThumbnail(std::get(handle->data), pSii, imageIndex, imgWidth, imgHeight, thumbWidth, + thumbHeight, thumbMaker, thumbFlags, progressProc, progressProcArg); + } + + PVCODE _SimplifyImageSequence(LPPVHandle hPVImage, HDC dc, int ScreenWidth, int ScreenHeight, LPPVImageSequence& pSeq, + const COLORREF& bgColor) + { + if (!hPVImage) + return PVC_INVALID_HANDLE; + auto* handle = reinterpret_cast(hPVImage); + if (handle->type != HandleType::PictView) + return PVC_INVALID_HANDLE; + // call the original function + return origPVW32DLL.SimplifyImageSequence(std::get(handle->data), dc, ScreenWidth, ScreenHeight, pSeq, bgColor); + } + +} // namespace + +////////////////////////////////////////////////////////////////////////// + +void InitializePvOverrider() +{ + // save the original pointers + origPVW32DLL = PVW32DLL; + + PVW32DLL.PVOpenImageEx = OpenImageEx; + PVW32DLL.PVReadImage2 = ReadImage; + PVW32DLL.PVDrawImage = DrawImage; + PVW32DLL.PVCloseImage = CloseImage; + PVW32DLL.PVSetBkHandle = SetBkHandle; + PVW32DLL.PVGetHandles2 = GetHandles2; + PVW32DLL.PVGetImageInfo = GetImageInfo; + PVW32DLL.PVSetStretchParameters = SetStretchParameters; + PVW32DLL.PVLoadFromClipboard = LoadFromClipboard; + PVW32DLL.PVSaveImage = SaveImage; + PVW32DLL.PVChangeImage = ChangeImage; + PVW32DLL.PVReadImageSequence = ReadImageSequence; + PVW32DLL.PVCropImage = CropImage; + + PVW32DLL.GetRGBAtCursor = _GetRGBAtCursor; + PVW32DLL.CalculateHistogram = _CalculateHistogram; + PVW32DLL.CreateThumbnail = _CreateThumbnail; + PVW32DLL.SimplifyImageSequence = _SimplifyImageSequence; +} + +void UninitializePvOverrider() +{ + // restore the original pointers + PVW32DLL = origPVW32DLL; +} diff --git a/src/plugins/pictview/PVOverrider.h b/src/plugins/pictview/PVOverrider.h new file mode 100644 index 000000000..6b0ab8c77 --- /dev/null +++ b/src/plugins/pictview/PVOverrider.h @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Open Salamander Authors +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +// overrider for the pict-view library, which adds support for viewing new image formats + +void InitializePvOverrider(); +void UninitializePvOverrider(); diff --git a/src/plugins/pictview/heif.cpp b/src/plugins/pictview/heif.cpp new file mode 100644 index 000000000..0e6f3488c --- /dev/null +++ b/src/plugins/pictview/heif.cpp @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: 2023 Open Salamander Authors +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "precomp.h" +#include "heif.h" +#include +#include + +ImageHeif::~ImageHeif() +{ + if (m_image) + heif_image_release(m_image); + if (m_handle) + heif_image_handle_release(m_handle); + if (m_ctx) + heif_context_free(m_ctx); +} + +PVCODE ImageHeif::Open(const char* filename, PVImageInfo& pvii) +{ + pvii = {}; + + // initialize libheif context + m_ctx = heif_context_alloc(); + if (!m_ctx) + return PVC_OUT_OF_MEMORY; + heif_error err = heif_context_read_from_file(m_ctx, filename, nullptr); + if (err.code != heif_error_Ok) + { + switch (err.code) + { + case heif_error_Invalid_input: + return PVC_UNKNOWN_FILE_STRUCT; + case heif_error_Memory_allocation_error: + return PVC_OUT_OF_MEMORY; + default: + return PVC_CANNOT_OPEN_FILE; + } + } + + // get the primary image handle + err = heif_context_get_primary_image_handle(m_ctx, &m_handle); + if (err.code != heif_error_Ok) + return PVC_UNKNOWN_FILE_STRUCT; + + // fill out the image info + pvii.cbSize = sizeof(pvii); + pvii.Format = PVF_BMP; + pvii.Width = heif_image_handle_get_width(m_handle); + pvii.Height = heif_image_handle_get_height(m_handle); + const int luma_bits = heif_image_handle_get_luma_bits_per_pixel(m_handle); + const int chroma_bits = heif_image_handle_get_chroma_bits_per_pixel(m_handle); + pvii.Colors = (1 << luma_bits) * (1 << chroma_bits); + pvii.NumOfImages = 1; + + // compute the number of bytes per line (stride) + const int bytes_per_pixel = (luma_bits + chroma_bits) / 8; // assuming 24-bit RGB format + pvii.BytesPerLine = pvii.Width * bytes_per_pixel; + pvii.Colors = PV_COLOR_TC24; + pvii.ColorModel = PVCM_RGB; + pvii.Compression = PVCS_NO_COMPRESSION; + + // get the file size + if (std::ifstream file(filename, std::ios::binary | std::ios::ate); + file.is_open()) + { + file.seekg(0, std::ios::end); + pvii.FileSize = static_cast(file.tellg()); + } + + strcpy_s(pvii.Info1, "HEIF"); + + return PVC_OK; +} + +PVCODE ImageHeif::Read(HBITMAP& bmp, TProgressProc Progress, void* AppSpecific) +{ + assert(m_ctx); + assert(m_handle); + +#if 0 + // TODO: decoding progress and cancellation doesn't work yet! + heif_decoding_options* options = heif_decoding_options_alloc(); + if (!options) + return PVC_OUT_OF_MEMORY; + + options->version = 1; + options->start_progress = start_progress; + options->on_progress = on_progress; + options->end_progress = end_progress; + options->cancel_decoding = cancel_decoding; + options->progress_user_data = &progress_data; +#endif + + // decode the image + heif_error err = heif_decode_image(m_handle, &m_image, heif_colorspace_RGB, heif_chroma_interleaved_RGB, nullptr); + if (err.code != heif_error_Ok) + return PVC_READING_ERROR; + + // check image bit depth + const int bitDepth = heif_image_get_bits_per_pixel(m_image, heif_channel_interleaved); + if (bitDepth != 24) + return PVC_UNSUP_COLOR_DEPTH; + + // get image dimensions + const int width = heif_image_get_width(m_image, heif_channel_interleaved); + const int height = heif_image_get_height(m_image, heif_channel_interleaved); + + // get image data + int stride{}; + const uint8_t* data = heif_image_get_plane_readonly(m_image, heif_channel_interleaved, &stride); + if (!data) + return PVC_READING_ERROR; + + // convert the decoded image to a bitmap + BITMAPINFO bmi = {0}; + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = -height; // Negative to indicate top-down DIB + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 24; // 24-bit RGB + bmi.bmiHeader.biCompression = BI_RGB; + + HDC hdc = GetDC(nullptr); + void* bits = nullptr; + bmp = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0); + ReleaseDC(nullptr, hdc); + + // copy image data to the DIB + for (int y = 0; y < height; ++y) + { + memcpy(static_cast(bits) + y * width * 3, data + y * stride, width * 3); + } + + // image loaded successfully + return PVC_OK; +} diff --git a/src/plugins/pictview/heif.h b/src/plugins/pictview/heif.h new file mode 100644 index 000000000..50b028c9c --- /dev/null +++ b/src/plugins/pictview/heif.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Open Salamander Authors +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "lib/pvw32dll.h" +#include + +class ImageHeif +{ +public: + ImageHeif() = default; + ~ImageHeif(); + + PVCODE Open(const char* filename, PVImageInfo& pvii); + PVCODE Read(HBITMAP& bmp, TProgressProc Progress, void* AppSpecific); + +private: + heif_context* m_ctx{}; + heif_image_handle* m_handle{}; + heif_image* m_image{}; +}; diff --git a/src/plugins/pictview/histwnd.cpp b/src/plugins/pictview/histwnd.cpp index f2f7886cb..b114f43fb 100644 --- a/src/plugins/pictview/histwnd.cpp +++ b/src/plugins/pictview/histwnd.cpp @@ -3,7 +3,6 @@ #include "precomp.h" -#include "lib\\pvw32dll.h" #include "pictview.h" #include "dialogs.h" #include "histwnd.h" diff --git a/src/plugins/pictview/pictview.cpp b/src/plugins/pictview/pictview.cpp index 874a04cfb..d6962e0c1 100644 --- a/src/plugins/pictview/pictview.cpp +++ b/src/plugins/pictview/pictview.cpp @@ -20,6 +20,7 @@ #include "histwnd.h" #include "PVEXEWrapper.h" #include "PixelAccess.h" +#include "PVOverrider.h" // objekt interfacu pluginu, jeho metody se volaji ze Salamandera CPluginInterface PluginInterface; @@ -71,7 +72,7 @@ BOOL SalamanderRegistered = FALSE; // 22 - Salamander 2.52 B1: Added *.BLP files used by Blizzard Entertainment in World of Wordcraft int ConfigVersion = 0; -#define CURRENT_CONFIG_VERSION 22 +#define CURRENT_CONFIG_VERSION 23 SGlobals G; // inicializovano v InitViewer TDirectArray ExifHighlights(20, 10); @@ -1063,6 +1064,11 @@ void CPluginInterface::Connect(HWND parent, CSalamanderConnectAbstract* salamand salamander->AddViewer("*.blp", TRUE); } + if (ConfigVersion < 23) // Added HEIF and WebP formats + { + salamander->AddViewer("*.heic;*.heif;*.webp", TRUE); + } + /* slouzi pro skript export_mnu.py, ktery generuje salmenu.mnu pro Translator udrzovat synchronizovane s volani salamander->AddMenuItem() dole... MENU_TEMPLATE_ITEM PluginMenu[] = @@ -1113,7 +1119,7 @@ MENU_TEMPLATE_ITEM PluginMenu[] = "*.flc;*.fli;*.gem;*.gif;*.ham;*.hmr;*.hrz;*.icn;*.ico;*.iff;*.img;" "*.cdt;*.cel;*.clp;*.cit;*.cmx;*.cot;*.cpt;*.cur;*.cut;*.dcx;*.dib;" "*.82i;*.83i;*.85i;*.86i;*.89i;*.92i;*.awd;*.bmi;*.bmp;*.cal;*.cdr;" - "*.arw;*.blp;*.cr2;*.dng;*.orf;*.pef"); + "*.arw;*.blp;*.cr2;*.dng;*.orf;*.pef;*.heic;*.heif;*.webp"); } void CPluginInterface::ClearHistory(HWND parent) @@ -1411,6 +1417,9 @@ BOOL LoadPictViewDll(HWND hParentWnd) return FALSE; } #endif // PICTVIEW_DLL_IN_SEPARATE_PROCESS + + // inject the new image file formats support into the viewer + InitializePvOverrider(); return TRUE; } @@ -1543,6 +1552,8 @@ BOOL InitEXIF(HWND hParent, BOOL bSilent) void ReleaseViewer() { + UninitializePvOverrider(); + #ifdef PICTVIEW_DLL_IN_SEPARATE_PROCESS ReleasePVEXEWrapper(); #endif diff --git a/src/plugins/pictview/pvtwain.cpp b/src/plugins/pictview/pvtwain.cpp index 7e716577c..a50de4bdd 100644 --- a/src/plugins/pictview/pvtwain.cpp +++ b/src/plugins/pictview/pvtwain.cpp @@ -5,7 +5,6 @@ #ifdef ENABLE_TWAIN32 -#include "lib/pvw32dll.h" #include "renderer.h" #include "dialogs.h" #include "pictview.h" diff --git a/src/plugins/pictview/statsbar.cpp b/src/plugins/pictview/statsbar.cpp index a15c96c42..a9706bbac 100644 --- a/src/plugins/pictview/statsbar.cpp +++ b/src/plugins/pictview/statsbar.cpp @@ -3,7 +3,6 @@ #include "precomp.h" -#include "lib\\pvw32dll.h" #include "renderer.h" #include "pictview.h" #include "pictview.rh" diff --git a/src/plugins/pictview/vcpkg.json b/src/plugins/pictview/vcpkg.json new file mode 100644 index 000000000..98ad3cc60 --- /dev/null +++ b/src/plugins/pictview/vcpkg.json @@ -0,0 +1,7 @@ +{ + "dependencies": [ + "libheif", + "libwebp" + ], + "builtin-baseline": "6f29f12e82a8293156836ad81cc9bf5af41fe836" +} diff --git a/src/plugins/pictview/vcxproj/pictview.vcxproj b/src/plugins/pictview/vcxproj/pictview.vcxproj index ec856bc07..a2e1017e2 100644 --- a/src/plugins/pictview/vcxproj/pictview.vcxproj +++ b/src/plugins/pictview/vcxproj/pictview.vcxproj @@ -100,6 +100,10 @@ + + true + true + stdcpplatest @@ -131,6 +135,7 @@ + @@ -151,6 +156,7 @@ + @@ -167,6 +173,7 @@ + @@ -201,6 +208,7 @@ + @@ -215,6 +223,7 @@ + @@ -225,6 +234,7 @@ + diff --git a/src/plugins/pictview/vcxproj/pictview.vcxproj.filters b/src/plugins/pictview/vcxproj/pictview.vcxproj.filters index b05cd16ea..3244a623e 100644 --- a/src/plugins/pictview/vcxproj/pictview.vcxproj.filters +++ b/src/plugins/pictview/vcxproj/pictview.vcxproj.filters @@ -81,6 +81,15 @@ cpp + + cpp + + + cpp + + + cpp + @@ -167,6 +176,15 @@ h + + h + + + h + + + h + diff --git a/src/plugins/pictview/webp.cpp b/src/plugins/pictview/webp.cpp new file mode 100644 index 000000000..5afb95006 --- /dev/null +++ b/src/plugins/pictview/webp.cpp @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: 2023 Open Salamander Authors +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "precomp.h" +#include "webp.h" +#include +#include + +ImageWebp::ImageWebp() +{ + WebPInitDecoderConfig(&m_config); +} + +ImageWebp::~ImageWebp() +{ + if (m_idec) + WebPIDelete(m_idec); + WebPFreeDecBuffer(&m_config.output); +} + +PVCODE ImageWebp::Open(const char* filename, PVImageInfo& pvii) +{ + pvii = {}; + + m_file.open(filename, std::ios::binary); + if (!m_file.is_open()) + return PVC_CANNOT_OPEN_FILE; + + // get the file size + m_file.seekg(0, std::ios::end); + m_fileSize = static_cast(m_file.tellg()); + m_file.seekg(0, std::ios::beg); + + // read the first 30 bytes of the file + const size_t HeaderSize = 30; + std::array buffer; + if (!m_file.read(reinterpret_cast(buffer.data()), buffer.size())) + return PVC_READING_ERROR; + m_file.seekg(0, std::ios::beg); + + // get the image features + auto result = WebPGetFeatures(buffer.data(), buffer.size(), &m_config.input); + if (result != VP8_STATUS_OK) + { + switch (result) + { + case VP8_STATUS_OUT_OF_MEMORY: + return PVC_OUT_OF_MEMORY; + case VP8_STATUS_BITSTREAM_ERROR: + return PVC_UNKNOWN_FILE_STRUCT; + default: + return PVC_READING_ERROR; + } + } + + // fill out the image info + pvii.cbSize = sizeof(pvii); + pvii.Format = PVF_BMP; + pvii.Width = m_config.input.width; + pvii.Height = m_config.input.height; + pvii.BytesPerLine = m_config.input.width * 4; // 32-bit RGBA format + pvii.Colors = PV_COLOR_TC32; + pvii.ColorModel = PVCM_RGB; + pvii.Compression = PVCS_NO_COMPRESSION; + pvii.FileSize = static_cast(m_fileSize); + pvii.NumOfImages = 1; + + strcpy_s(pvii.Info1, "WebP"); + + return PVC_OK; +} + +PVCODE ImageWebp::Read(HBITMAP& bmp, TProgressProc Progress, void* AppSpecific) +{ + assert(m_file.is_open()); + + // initialize the decoder + m_config.output.colorspace = MODE_BGRA; + m_idec = WebPIDecode(nullptr, 0, &m_config); + if (!m_idec) + return PVC_EXCEPTION; + + const size_t ChunkSize = 1024; + std::array buffer; + VP8StatusCode result = VP8_STATUS_SUSPENDED; + + // read file by chunks + size_t totalBytesRead = 0; + int progressRound = 0; + while (!m_file.eof() && result == VP8_STATUS_SUSPENDED) + { + m_file.read(reinterpret_cast(buffer.data()), buffer.size()); + if (m_file.bad()) + return PVC_READING_ERROR; + const auto bytesRead = m_file.gcount(); + if (bytesRead == 0) + return PVC_UNEXPECTED_EOF; + totalBytesRead += bytesRead; + + result = WebPIAppend(m_idec, buffer.data(), bytesRead); + + // report progress + if (Progress && (progressRound++ > 10)) + { + progressRound = 0; + const auto progressPercentage = static_cast(totalBytesRead * 100 / m_fileSize); + if (Progress(progressPercentage, AppSpecific)) + return PVC_CANCELED; + } + } + + if (result != VP8_STATUS_OK) + return PVC_READING_ERROR; + + // convert the decoded image to a bitmap + BITMAPINFO bmi = {0}; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = m_config.input.width; + bmi.bmiHeader.biHeight = -m_config.input.height; // Negative height to indicate a top-down DIB + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; // 32-bit BGRA + bmi.bmiHeader.biCompression = BI_RGB; + + HDC hdc = GetDC(nullptr); + if (!hdc) + return PVC_GDI_ERROR; + bmp = CreateDIBitmap(hdc, &bmi.bmiHeader, CBM_INIT, m_config.output.u.RGBA.rgba, + &bmi, DIB_RGB_COLORS); + ReleaseDC(nullptr, hdc); + if (!bmp) + return PVC_GDI_ERROR; + + // reading done + if (Progress) + Progress(100, AppSpecific); + // image loaded successfully + return PVC_OK; +} diff --git a/src/plugins/pictview/webp.h b/src/plugins/pictview/webp.h new file mode 100644 index 000000000..0aa9ac1f8 --- /dev/null +++ b/src/plugins/pictview/webp.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Open Salamander Authors +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "lib/pvw32dll.h" +#include +#include + +class ImageWebp +{ +public: + ImageWebp(); + ~ImageWebp(); + + PVCODE Open(const char* filename, PVImageInfo& pvii); + PVCODE Read(HBITMAP& bmp, TProgressProc Progress, void* AppSpecific); + +private: + void MoveFrom(ImageWebp& other); + + // image decoder + WebPIDecoder* m_idec{nullptr}; + WebPDecoderConfig m_config{}; + std::ifstream m_file; + size_t m_fileSize{}; +}; diff --git a/src/plugins/pictview/wiawrap.cpp b/src/plugins/pictview/wiawrap.cpp index 1035a17c4..5c7cb01a4 100644 --- a/src/plugins/pictview/wiawrap.cpp +++ b/src/plugins/pictview/wiawrap.cpp @@ -59,7 +59,6 @@ void* just_to_show_error = &change_default_value_of_WIN32_WINNT; #include -#include "lib/pvw32dll.h" #include "renderer.h" #include "dialogs.h" #include "pictview.h" From d30251a813613f91aefa151ed3c7d26139053c4d Mon Sep 17 00:00:00 2001 From: Viliam Lejcik Date: Mon, 27 Jan 2025 21:29:57 +0100 Subject: [PATCH 2/8] cast correction --- src/plugins/pictview/webp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/pictview/webp.cpp b/src/plugins/pictview/webp.cpp index 5afb95006..af203676d 100644 --- a/src/plugins/pictview/webp.cpp +++ b/src/plugins/pictview/webp.cpp @@ -92,7 +92,7 @@ PVCODE ImageWebp::Read(HBITMAP& bmp, TProgressProc Progress, void* AppSpecific) m_file.read(reinterpret_cast(buffer.data()), buffer.size()); if (m_file.bad()) return PVC_READING_ERROR; - const auto bytesRead = m_file.gcount(); + const auto bytesRead = static_cast(m_file.gcount()); if (bytesRead == 0) return PVC_UNEXPECTED_EOF; totalBytesRead += bytesRead; From 3cda29eb349e9bb427e0aaaa606370b8a598c33d Mon Sep 17 00:00:00 2001 From: Viliam Lejcik Date: Mon, 27 Jan 2025 21:37:07 +0100 Subject: [PATCH 3/8] a few more improvements for pict-view overrider --- src/plugins/pictview/PVOverrider.cpp | 127 ++++++++++++++++++--------- 1 file changed, 87 insertions(+), 40 deletions(-) diff --git a/src/plugins/pictview/PVOverrider.cpp b/src/plugins/pictview/PVOverrider.cpp index 8631b1172..e01ff6636 100644 --- a/src/plugins/pictview/PVOverrider.cpp +++ b/src/plugins/pictview/PVOverrider.cpp @@ -39,7 +39,11 @@ namespace std::variant data; }; - PVCODE OpenImageEx(LPPVHandle* Img, LPPVOpenImageExInfo pOpenExInfo, LPPVImageInfo pImgInfo, int Size) + // utilities + Handle* _ConvertHandle(LPPVHandle hPVImage); + LPPVHandle _GetHandleForLocalCall(Handle* handle); + + PVCODE WINAPI OpenImageEx(LPPVHandle* Img, LPPVOpenImageExInfo pOpenExInfo, LPPVImageInfo pImgInfo, int Size) { PVCODE result; assert(Img); @@ -109,7 +113,7 @@ namespace return result; } - PVCODE _ReadImage(Handle* handle, TProgressProc Progress, void* AppSpecific, int ImageIndex) + PVCODE WINAPI _ReadImage(Handle* handle, TProgressProc Progress, void* AppSpecific, int ImageIndex) { assert(handle); assert(handle->type != HandleType::PictView); @@ -176,11 +180,11 @@ namespace return result; } - PVCODE ReadImage(LPPVHandle Img, HDC PaintDC, RECT* pDRect, TProgressProc Progress, void* AppSpecific, int ImageIndex) + PVCODE WINAPI ReadImage(LPPVHandle Img, HDC PaintDC, RECT* pDRect, TProgressProc Progress, void* AppSpecific, int ImageIndex) { if (!Img) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(Img); + auto* handle = _ConvertHandle(Img); if (handle->type != HandleType::PictView) { // read the image content @@ -201,11 +205,11 @@ namespace return origPVW32DLL.PVReadImage2(std::get(handle->data), PaintDC, pDRect, Progress, AppSpecific, ImageIndex); } - PVCODE DrawImage(LPPVHandle Img, HDC PaintDC, int X, int Y, LPRECT rect) + PVCODE WINAPI DrawImage(LPPVHandle Img, HDC PaintDC, int X, int Y, LPRECT rect) { if (!Img) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(Img); + auto* handle = _ConvertHandle(Img); if (handle->type != HandleType::PictView) { // TODO: draw background during loading @@ -215,11 +219,11 @@ namespace return origPVW32DLL.PVDrawImage(std::get(handle->data), PaintDC, X, Y, rect); } - PVCODE CloseImage(LPPVHandle Img) + PVCODE WINAPI CloseImage(LPPVHandle Img) { if (!Img) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(Img); + auto* handle = _ConvertHandle(Img); if (handle->type != HandleType::PictView) { delete handle; @@ -231,11 +235,11 @@ namespace return result; } - PVCODE SetBkHandle(LPPVHandle Img, COLORREF BkColor) + PVCODE WINAPI SetBkHandle(LPPVHandle Img, COLORREF BkColor) { if (!Img) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(Img); + auto* handle = _ConvertHandle(Img); if (handle->type != HandleType::PictView) { // cache the background color @@ -246,33 +250,33 @@ namespace return origPVW32DLL.PVSetBkHandle(std::get(handle->data), BkColor); } - PVCODE GetHandles2(LPPVHandle Img, LPPVImageHandles* pHandles) + PVCODE WINAPI GetHandles2(LPPVHandle Img, LPPVImageHandles* pHandles) { if (!Img) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(Img); + auto* handle = _ConvertHandle(Img); if (handle->type != HandleType::PictView) return PVC_INVALID_HANDLE; // call the original function return origPVW32DLL.PVGetHandles2(std::get(handle->data), pHandles); } - PVCODE GetImageInfo(LPPVHandle Img, LPPVImageInfo pImgInfo, int cbSize, int ImageIndex) + PVCODE WINAPI GetImageInfo(LPPVHandle Img, LPPVImageInfo pImgInfo, int cbSize, int ImageIndex) { if (!Img) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(Img); + auto* handle = _ConvertHandle(Img); if (handle->type != HandleType::PictView) return PVC_INVALID_HANDLE; // call the original function return origPVW32DLL.PVGetImageInfo(std::get(handle->data), pImgInfo, cbSize, ImageIndex); } - PVCODE SetStretchParameters(LPPVHandle Img, DWORD Width, DWORD Height, DWORD Mode) + PVCODE WINAPI SetStretchParameters(LPPVHandle Img, DWORD Width, DWORD Height, DWORD Mode) { if (!Img) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(Img); + auto* handle = _ConvertHandle(Img); if (handle->type != HandleType::PictView) { // cache the parameters @@ -283,55 +287,69 @@ namespace return origPVW32DLL.PVSetStretchParameters(std::get(handle->data), Width, Height, Mode); } - PVCODE LoadFromClipboard(LPPVHandle* Img, LPPVImageInfo pImgInfo, int cbSize) + PVCODE WINAPI LoadFromClipboard(LPPVHandle* Img, LPPVImageInfo pImgInfo, int cbSize) { - if (!Img) - return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(Img); - if (handle->type != HandleType::PictView) - return PVC_INVALID_HANDLE; - // call the original function - return origPVW32DLL.PVLoadFromClipboard(Img, pImgInfo, cbSize); + assert(Img); + + // initialize the handle + *Img = nullptr; + + // call the original function with a temporary handle + LPPVHandle tempHandle = nullptr; + auto result = origPVW32DLL.PVLoadFromClipboard(&tempHandle, pImgInfo, cbSize); + if (result != PVC_OK) + return result; + + // create a new handle + auto* handle = new Handle{}; + if (!handle) + return PVC_OOM; + + // image loaded successfully + handle->type = HandleType::PictView; + handle->data = tempHandle; + *Img = reinterpret_cast(handle); + return result; } - PVCODE SaveImage(LPPVHandle Img, const char* OutFName, LPPVSaveImageInfo pSii, TProgressProc Progress, void* AppSpecific, int ImageIndex) + PVCODE WINAPI SaveImage(LPPVHandle Img, const char* OutFName, LPPVSaveImageInfo pSii, TProgressProc Progress, void* AppSpecific, int ImageIndex) { if (!Img) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(Img); + auto* handle = _ConvertHandle(Img); if (handle->type != HandleType::PictView) return PVC_INVALID_HANDLE; // call the original function return origPVW32DLL.PVSaveImage(std::get(handle->data), OutFName, pSii, Progress, AppSpecific, ImageIndex); } - PVCODE ChangeImage(LPPVHandle Img, DWORD Flags) + PVCODE WINAPI ChangeImage(LPPVHandle Img, DWORD Flags) { if (!Img) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(Img); + auto* handle = _ConvertHandle(Img); if (handle->type != HandleType::PictView) return PVC_INVALID_HANDLE; // call the original function return origPVW32DLL.PVChangeImage(std::get(handle->data), Flags); } - PVCODE ReadImageSequence(LPPVHandle Img, LPPVImageSequence* ppSeq) + PVCODE WINAPI ReadImageSequence(LPPVHandle Img, LPPVImageSequence* ppSeq) { if (!Img) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(Img); + auto* handle = _ConvertHandle(Img); if (handle->type != HandleType::PictView) return PVC_INVALID_HANDLE; // call the original function - return origPVW32DLL.PVReadImageSequence(Img, ppSeq); + return origPVW32DLL.PVReadImageSequence(std::get(handle->data), ppSeq); } - PVCODE CropImage(LPPVHandle Img, int Left, int Top, int Width, int Height) + PVCODE WINAPI CropImage(LPPVHandle Img, int Left, int Top, int Width, int Height) { if (!Img) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(Img); + auto* handle = _ConvertHandle(Img); if (handle->type != HandleType::PictView) return PVC_INVALID_HANDLE; // call the original function @@ -342,11 +360,11 @@ namespace { if (!Img) return false; - auto* handle = reinterpret_cast(Img); + auto* handle = _ConvertHandle(Img); if (handle->type != HandleType::PictView) return false; // call the original function - return origPVW32DLL.GetRGBAtCursor(std::get(handle->data), Colors, x, y, pRGB, pIndex); + return origPVW32DLL.GetRGBAtCursor(_GetHandleForLocalCall(handle), Colors, x, y, pRGB, pIndex); } PVCODE _CalculateHistogram(LPPVHandle PVHandle, const LPPVImageInfo pvii, LPDWORD luminosity, @@ -354,11 +372,11 @@ namespace { if (!PVHandle) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(PVHandle); + auto* handle = _ConvertHandle(PVHandle); if (handle->type != HandleType::PictView) return PVC_INVALID_HANDLE; // call the original function - return origPVW32DLL.CalculateHistogram(std::get(handle->data), pvii, luminosity, red, green, blue, rgb); + return origPVW32DLL.CalculateHistogram(_GetHandleForLocalCall(handle), pvii, luminosity, red, green, blue, rgb); } PVCODE _CreateThumbnail(LPPVHandle hPVImage, LPPVSaveImageInfo pSii, int imageIndex, DWORD imgWidth, DWORD imgHeight, @@ -367,7 +385,7 @@ namespace { if (!hPVImage) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(hPVImage); + auto* handle = _ConvertHandle(hPVImage); if (handle->type != HandleType::PictView) { // set the thumbnail size @@ -381,8 +399,9 @@ namespace if (progressProc) progressProc(0, progressProcArg); } + // call the original function - return origPVW32DLL.CreateThumbnail(std::get(handle->data), pSii, imageIndex, imgWidth, imgHeight, thumbWidth, + return origPVW32DLL.CreateThumbnail(_GetHandleForLocalCall(handle), pSii, imageIndex, imgWidth, imgHeight, thumbWidth, thumbHeight, thumbMaker, thumbFlags, progressProc, progressProcArg); } @@ -391,13 +410,41 @@ namespace { if (!hPVImage) return PVC_INVALID_HANDLE; - auto* handle = reinterpret_cast(hPVImage); + auto* handle = _ConvertHandle(hPVImage); if (handle->type != HandleType::PictView) return PVC_INVALID_HANDLE; // call the original function return origPVW32DLL.SimplifyImageSequence(std::get(handle->data), dc, ScreenWidth, ScreenHeight, pSeq, bgColor); } + // utilities + + Handle* _ConvertHandle(LPPVHandle hPVImage) + { + auto* handle = reinterpret_cast(hPVImage); + + // check that the handle type is valid + assert(handle->type == HandleType::PictView || + handle->type == HandleType::Heif || + handle->type == HandleType::Webp); + + return handle; + } + + LPPVHandle _GetHandleForLocalCall(Handle* handle) + { + // HACK: resolve the handle +#ifdef _WIN64 + // on x64, the image handle is processed in a separate process, so it has to be + // of type compatible with pict-view library + return std::get(handle->data); +#else + // on x86, the image handle is just passed into a local function, and later it is passed + // as an argument into a PVW32DLL.PVxxx() call, so the handle is converted there + return handle; +#endif + } + } // namespace ////////////////////////////////////////////////////////////////////////// From 89964753377b3b461e8eed4af18b5fdc9f64e158 Mon Sep 17 00:00:00 2001 From: Viliam Lejcik Date: Mon, 27 Jan 2025 21:40:38 +0100 Subject: [PATCH 4/8] minor cleanup --- src/plugins/pictview/webp.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/pictview/webp.h b/src/plugins/pictview/webp.h index 0aa9ac1f8..d41ea3799 100644 --- a/src/plugins/pictview/webp.h +++ b/src/plugins/pictview/webp.h @@ -17,8 +17,6 @@ class ImageWebp PVCODE Read(HBITMAP& bmp, TProgressProc Progress, void* AppSpecific); private: - void MoveFrom(ImageWebp& other); - // image decoder WebPIDecoder* m_idec{nullptr}; WebPDecoderConfig m_config{}; From c22fae6e4fb17d22727e3e5483d0b5a5f55e4384 Mon Sep 17 00:00:00 2001 From: Viliam Lejcik Date: Mon, 27 Jan 2025 21:59:04 +0100 Subject: [PATCH 5/8] use vcpkg manifest file --- src/plugins/pictview/vcxproj/pictview.vcxproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/pictview/vcxproj/pictview.vcxproj b/src/plugins/pictview/vcxproj/pictview.vcxproj index a2e1017e2..0978fcb11 100644 --- a/src/plugins/pictview/vcxproj/pictview.vcxproj +++ b/src/plugins/pictview/vcxproj/pictview.vcxproj @@ -103,6 +103,7 @@ true true + true From 9cb8ca077bf3e290ebd3720aae3948345d8adc90 Mon Sep 17 00:00:00 2001 From: Viliam Lejcik Date: Mon, 27 Jan 2025 23:06:22 +0100 Subject: [PATCH 6/8] clang-formated PVW32DLL.h --- src/plugins/pictview/lib/PVW32DLL.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/plugins/pictview/lib/PVW32DLL.h b/src/plugins/pictview/lib/PVW32DLL.h index 9c8864cf5..34040a70c 100644 --- a/src/plugins/pictview/lib/PVW32DLL.h +++ b/src/plugins/pictview/lib/PVW32DLL.h @@ -38,20 +38,20 @@ #define PV_VERSION_155 0x10037 #define PV_VERSION_156 0x10038 #define PV_VERSION_MAJOR(x) ((x) >> 16) -#define PV_VERSION_MINOR(x) ((x)&0xFFFF) +#define PV_VERSION_MINOR(x) ((x) & 0xFFFF) #define PV_MAXIMUM_BITMAP_SIZE 0xFFFFFFFF /* Error codes: */ -#define PVC_EXPIRED ((PVCODE)-8) /* Demo version has expired */ -#define PVC_GDI_ERROR ((PVCODE)-7) /* GDI function like SelectObject failed */ -#define PVC_EXCEPTION ((PVCODE)-6) /* Internal error */ -#define PVC_CANCELED ((PVCODE)-5) /* Read canceled - not yet used */ -#define PVC_NO_MORE_IMAGES ((PVCODE)-4) /* No more images in the file */ -#define PVC_OOM ((PVCODE)-3) /* Out of memory */ -#define PVC_SYNC_ERROR ((PVCODE)-2) -#define PVC_INVALID_HANDLE ((PVCODE)-1) +#define PVC_EXPIRED ((PVCODE) - 8) /* Demo version has expired */ +#define PVC_GDI_ERROR ((PVCODE) - 7) /* GDI function like SelectObject failed */ +#define PVC_EXCEPTION ((PVCODE) - 6) /* Internal error */ +#define PVC_CANCELED ((PVCODE) - 5) /* Read canceled - not yet used */ +#define PVC_NO_MORE_IMAGES ((PVCODE) - 4) /* No more images in the file */ +#define PVC_OOM ((PVCODE) - 3) /* Out of memory */ +#define PVC_SYNC_ERROR ((PVCODE) - 2) +#define PVC_INVALID_HANDLE ((PVCODE) - 1) #define PVC_OK 0 #define PVC_UNKNOWN_COMPRESSION 6 #define PVC_OUT_OF_MEMORY 7 From 755f8540cd435ceceb7866637823b5d265777e93 Mon Sep 17 00:00:00 2001 From: Viliam Lejcik Date: Sun, 14 Dec 2025 12:12:38 +0100 Subject: [PATCH 7/8] removed redundant call --- src/plugins/pictview/webp.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/plugins/pictview/webp.cpp b/src/plugins/pictview/webp.cpp index af203676d..c906ff863 100644 --- a/src/plugins/pictview/webp.cpp +++ b/src/plugins/pictview/webp.cpp @@ -130,9 +130,6 @@ PVCODE ImageWebp::Read(HBITMAP& bmp, TProgressProc Progress, void* AppSpecific) if (!bmp) return PVC_GDI_ERROR; - // reading done - if (Progress) - Progress(100, AppSpecific); // image loaded successfully return PVC_OK; } From 5b43ed1341c4f14a3ec65f82d20972d9ff166cf1 Mon Sep 17 00:00:00 2001 From: Viliam Lejcik Date: Sun, 14 Dec 2025 12:13:16 +0100 Subject: [PATCH 8/8] heif: show progress and allow cancellation during picture loading some picture decoders that are supported by libheif ignore the heif_decoding_options, and that we can't get loading progress and we can't cancel the loading procedure! --- src/plugins/pictview/heif.cpp | 58 +++++++++++++++++++++++++++++------ src/plugins/pictview/heif.h | 4 +++ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/plugins/pictview/heif.cpp b/src/plugins/pictview/heif.cpp index 0e6f3488c..ffa90c054 100644 --- a/src/plugins/pictview/heif.cpp +++ b/src/plugins/pictview/heif.cpp @@ -6,6 +6,14 @@ #include #include +struct HeifProgressData +{ + TProgressProc progressProc; + void* appSpecific; + int cancelFlag{false}; + int maxProgress{0}; +}; + ImageHeif::~ImageHeif() { if (m_image) @@ -78,22 +86,26 @@ PVCODE ImageHeif::Read(HBITMAP& bmp, TProgressProc Progress, void* AppSpecific) assert(m_ctx); assert(m_handle); -#if 0 - // TODO: decoding progress and cancellation doesn't work yet! + // decoding progress and cancellation doesn't work yet! heif_decoding_options* options = heif_decoding_options_alloc(); if (!options) return PVC_OUT_OF_MEMORY; - options->version = 1; - options->start_progress = start_progress; - options->on_progress = on_progress; - options->end_progress = end_progress; - options->cancel_decoding = cancel_decoding; + options->version = 6; + options->start_progress = &ImageHeif::StartProgress; + options->on_progress = &ImageHeif::OnProgress; + // options->end_progress = &ImageHeif::EndProgress; + options->cancel_decoding = &ImageHeif::CancelDecoding; + + HeifProgressData progress_data{ + .progressProc = Progress, + .appSpecific = AppSpecific + }; options->progress_user_data = &progress_data; -#endif // decode the image - heif_error err = heif_decode_image(m_handle, &m_image, heif_colorspace_RGB, heif_chroma_interleaved_RGB, nullptr); + heif_error err = heif_decode_image(m_handle, &m_image, heif_colorspace_RGB, heif_chroma_interleaved_RGB, options); + heif_decoding_options_free(options); if (err.code != heif_error_Ok) return PVC_READING_ERROR; @@ -135,3 +147,31 @@ PVCODE ImageHeif::Read(HBITMAP& bmp, TProgressProc Progress, void* AppSpecific) // image loaded successfully return PVC_OK; } + +void ImageHeif::StartProgress(heif_progress_step step, int max_progress, void* user) +{ + if (step != heif_progress_step_total) + return; + auto* data = static_cast(user); + data->maxProgress = max_progress; +} + +void ImageHeif::OnProgress(heif_progress_step step, int progress, void* user) +{ + if (step != heif_progress_step_total) + return; + + auto* data = static_cast(user); + if (data->progressProc && data->maxProgress != 0) + { + const int percent = progress * 100 / data->maxProgress; + if (data->progressProc(percent, data->appSpecific) && data->cancelFlag) + data->cancelFlag = true; + } +} + +int ImageHeif::CancelDecoding(void* user) +{ + const auto* data = static_cast(user); + return data->cancelFlag; +} diff --git a/src/plugins/pictview/heif.h b/src/plugins/pictview/heif.h index 50b028c9c..c4db2290c 100644 --- a/src/plugins/pictview/heif.h +++ b/src/plugins/pictview/heif.h @@ -16,6 +16,10 @@ class ImageHeif PVCODE Read(HBITMAP& bmp, TProgressProc Progress, void* AppSpecific); private: + static void StartProgress(enum heif_progress_step step, int max_progress, void* user); + static void OnProgress(enum heif_progress_step step, int progress, void* user); + static int CancelDecoding(void* user); + heif_context* m_ctx{}; heif_image_handle* m_handle{}; heif_image* m_image{};