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(width) * static_cast(height) * 4); + const UINT newStride = width * 4; + for (int y = 0; y < height; ++y) + { + const BYTE* src = frame.pixels.data() + (top + y) * frame.stride + left * 4; + BYTE* dst = cropped.data() + y * newStride; + memcpy(dst, src, newStride); + } + frame.width = static_cast(width); + frame.height = static_cast(height); + frame.stride = newStride; + frame.pixels.swap(cropped); + HRESULT finalizeHr = FinalizeDecodedFrame(frame); + if (FAILED(finalizeHr)) + { + return HResultToPvCode(finalizeHr); + } + return PVC_OK; +} + +bool Backend::sGetRGBAtCursor(LPPVHandle Img, DWORD /*colors*/, int x, int y, RGBQUAD* rgb, int* /*index*/) +{ + auto handle = FromHandle(Img); + if (!handle) + { + return false; + } + ScopedCoInit init; + if (!init.Succeeded()) + { + return false; + } + FrameData& frame = handle->frames[0]; + if (!frame.decoded) + { + if (FAILED(DecodeFrame(*handle, 0))) + { + return false; + } + } + if (x < 0 || y < 0 || x >= static_cast(frame.width) || y >= static_cast(frame.height)) + { + return false; + } + const BYTE* src = frame.pixels.data() + y * frame.stride + x * 4; + if (rgb) + { + rgb->rgbBlue = src[0]; + rgb->rgbGreen = src[1]; + rgb->rgbRed = src[2]; + rgb->rgbReserved = src[3]; + } + return true; +} + +PVCODE Backend::sCalculateHistogram(LPPVHandle Img, const LPPVImageInfo /*info*/, LPDWORD luminosity, LPDWORD red, LPDWORD green, + LPDWORD blue, LPDWORD rgb) +{ + auto handle = FromHandle(Img); + if (!handle) + { + return PVC_INVALID_HANDLE; + } + ScopedCoInit init; + if (!init.Succeeded()) + { + return PVC_EXCEPTION; + } + FrameData& frame = handle->frames[0]; + if (!frame.decoded) + { + if (FAILED(DecodeFrame(*handle, 0))) + { + return PVC_EXCEPTION; + } + } + std::fill(luminosity, luminosity + 256, 0); + std::fill(red, red + 256, 0); + std::fill(green, green + 256, 0); + std::fill(blue, blue + 256, 0); + std::fill(rgb, rgb + 256, 0); + + for (UINT y = 0; y < frame.height; ++y) + { + const BYTE* src = frame.pixels.data() + y * frame.stride; + for (UINT x = 0; x < frame.width; ++x) + { + BYTE b = src[x * 4 + 0]; + BYTE g = src[x * 4 + 1]; + BYTE r = src[x * 4 + 2]; + BYTE l = static_cast((static_cast(r) * 30 + static_cast(g) * 59 + static_cast(b) * 11) / 100); + luminosity[l]++; + red[r]++; + green[g]++; + blue[b]++; + rgb[(r + g + b) / 3]++; + } + } + return PVC_OK; +} + +PVCODE Backend::sCreateThumbnail(LPPVHandle Img, LPPVSaveImageInfo /*sii*/, int imageIndex, DWORD imgWidth, DWORD imgHeight, + int thumbWidth, int thumbHeight, CSalamanderThumbnailMakerAbstract* thumbMaker, + DWORD thumbFlags, TProgressProc progressProc, void* progressProcArg) +{ + auto handle = FromHandle(Img); + if (!handle || !thumbMaker) + { + 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); + } + FrameData& frame = handle->frames[normalizedIndex]; + + int targetWidth = thumbWidth > 0 ? thumbWidth : static_cast(frame.width); + int targetHeight = thumbHeight > 0 ? thumbHeight : static_cast(frame.height); + if (targetWidth <= 0) + { + targetWidth = static_cast(frame.width); + } + if (targetHeight <= 0) + { + targetHeight = static_cast(frame.height); + } + + const auto calculateThumbnailSize = [](int originalWidth, int originalHeight, int maxWidth, int maxHeight, + int& thumbWidth, int& thumbHeight) { + if (originalWidth <= maxWidth && originalHeight <= maxHeight) + { + thumbWidth = originalWidth; + thumbHeight = originalHeight; + return false; + } + + const double aspect = static_cast(originalWidth) / static_cast(originalHeight); + const double bounds = static_cast(maxWidth) / static_cast(maxHeight); + if (bounds < aspect) + { + thumbWidth = maxWidth; + thumbHeight = static_cast(static_cast(maxWidth) / aspect); + } + else + { + thumbHeight = maxHeight; + thumbWidth = static_cast(static_cast(maxHeight) * aspect); + } + + if (thumbWidth < 1) + { + thumbWidth = 1; + } + if (thumbHeight < 1) + { + thumbHeight = 1; + } + return true; + }; + + calculateThumbnailSize(static_cast(imgWidth), static_cast(imgHeight), targetWidth, targetHeight, targetWidth, + targetHeight); + + if (!thumbMaker->SetParameters(targetWidth, targetHeight, thumbFlags)) + { + return PVC_OUT_OF_MEMORY; + } + + const UINT desiredWidth = static_cast(targetWidth); + const UINT desiredHeight = static_cast(targetHeight); + + const BYTE* source = frame.pixels.data(); + std::vector scaled; + if (desiredWidth != frame.width || desiredHeight != frame.height) + { + Microsoft::WRL::ComPtr scaleSource; + if (frame.converter) + { + scaleSource = frame.converter; + } + else + { + 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); + } + scaleSource = bitmap; + } + + Microsoft::WRL::ComPtr scaler; + hr = handle->backend->Factory()->CreateBitmapScaler(&scaler); + if (FAILED(hr)) + { + return HResultToPvCode(hr); + } + hr = scaler->Initialize(scaleSource.Get(), desiredWidth, desiredHeight, WICBitmapInterpolationModeFant); + if (FAILED(hr)) + { + return HResultToPvCode(hr); + } + + scaled.resize(static_cast(desiredWidth) * static_cast(desiredHeight) * 4); + WICRect rect{0, 0, static_cast(desiredWidth), static_cast(desiredHeight)}; + hr = scaler->CopyPixels(&rect, desiredWidth * 4, static_cast(scaled.size()), scaled.data()); + if (FAILED(hr)) + { + return HResultToPvCode(hr); + } + + source = scaled.data(); + } + + if (progressProc && !progressProc(100, progressProcArg)) + { + return PVC_CANCELED; + } + + // The thumbnail maker expects rows in top-down order; feed them in manageable batches + // so it can honour cancellation requests. + const size_t rowBytes = static_cast(desiredWidth) * 4; + int processedRows = 0; + while (processedRows < targetHeight) + { + if (thumbMaker->GetCancelProcessing()) + { + return PVC_CANCELED; + } + + const int batch = std::min(32, targetHeight - processedRows); + BYTE* chunk = const_cast(source + static_cast(processedRows) * rowBytes); + thumbMaker->ProcessBuffer(chunk, batch); + processedRows += batch; + } + return PVC_OK; +} + +PVCODE Backend::sSimplifyImageSequence(LPPVHandle /*Img*/, HDC /*dc*/, int /*screenWidth*/, int /*screenHeight*/, + LPPVImageSequence& /*seq*/, const COLORREF& /*bgColor*/) +{ + return PVC_OK; +} + +} // namespace PictView::Wic diff --git a/src/plugins/pictview/wic/WicBackend.h b/src/plugins/pictview/wic/WicBackend.h new file mode 100644 index 000000000..13877b020 --- /dev/null +++ b/src/plugins/pictview/wic/WicBackend.h @@ -0,0 +1,130 @@ +// SPDX-FileCopyrightText: 2024 Open Salamander Authors +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../lib/PVW32DLL.h" +#include "../pictview.h" + +namespace PictView::Wic +{ +class Backend; + +struct ImageHandle; + +using Microsoft::WRL::ComPtr; + +struct FrameData +{ + ComPtr frame; + ComPtr converter; + ComPtr colorConvertedSource; + UINT width = 0; + UINT height = 0; + UINT stride = 0; + std::vector pixels; + std::vector linePointers; + std::vector palette; + BITMAPINFOHEADER bmi{}; + HBITMAP hbitmap = nullptr; + DWORD delayMs = 0; + bool decoded = false; +}; + +struct ImageHandle +{ + Backend* backend = nullptr; + std::wstring fileName; + DWORD openFlags = 0; + std::vector frames; + LONG stretchWidth = 0; + LONG stretchHeight = 0; + DWORD stretchMode = PV_STRETCH_NO; + COLORREF background = RGB(0, 0, 0); + PVImageInfo baseInfo{}; + PVImageHandles handles{}; +}; + +/** + * Lightweight RAII helper around CoInitialize/CoUninitialize. The viewer + * already initialises COM on most threads, but the WIC backend may also be + * invoked from helper worker threads that do not call into COM yet. We keep + * this helper header-only to avoid the dependency on ATL. + */ +class ScopedCoInit +{ +public: + ScopedCoInit(); + ScopedCoInit(const ScopedCoInit&) = delete; + ScopedCoInit& operator=(const ScopedCoInit&) = delete; + ~ScopedCoInit(); + + bool Succeeded() const + { + return m_hr == S_OK || m_hr == S_FALSE; + } + +private: + HRESULT m_hr; + bool m_needUninit; +}; + +class Backend +{ +public: + Backend(); + Backend(const Backend&) = delete; + Backend& operator=(const Backend&) = delete; + + static Backend& Instance(); + + bool Populate(CPVW32DLL& table); + + IWICImagingFactory* Factory() const { return m_factory.Get(); } + +private: + static PVCODE WINAPI sPVOpenImageEx(LPPVHandle* Img, LPPVOpenImageExInfo pOpenExInfo, LPPVImageInfo pImgInfo, int size); + static PVCODE WINAPI sPVCloseImage(LPPVHandle Img); + static PVCODE WINAPI sPVReadImage2(LPPVHandle Img, HDC paintDC, RECT* dRect, TProgressProc progress, void* appSpecific, + int imageIndex); + static PVCODE WINAPI sPVDrawImage(LPPVHandle Img, HDC paintDC, int x, int y, LPRECT rect); + static const char* WINAPI sPVGetErrorText(DWORD errorCode); + static PVCODE WINAPI sPVSetBkHandle(LPPVHandle Img, COLORREF bkColor); + static DWORD WINAPI sPVGetDLLVersion(); + static PVCODE WINAPI sPVSetStretchParameters(LPPVHandle Img, DWORD width, DWORD height, DWORD mode); + static PVCODE WINAPI sPVLoadFromClipboard(LPPVHandle* Img, LPPVImageInfo pImgInfo, int size); + static PVCODE WINAPI sPVGetImageInfo(LPPVHandle Img, LPPVImageInfo pImgInfo, int size, int imageIndex); + static PVCODE WINAPI sPVSetParam(LPPVHandle Img); + static PVCODE WINAPI sPVGetHandles2(LPPVHandle Img, LPPVImageHandles* pHandles); + static PVCODE WINAPI sPVSaveImage(LPPVHandle Img, const char* outFileName, LPPVSaveImageInfo pSii, TProgressProc progress, + void* appSpecific, int imageIndex); + static PVCODE WINAPI sPVChangeImage(LPPVHandle Img, DWORD flags); + static DWORD WINAPI sPVIsOutCombSupported(int format, int compression, int colors, int colorModel); + static PVCODE WINAPI sPVReadImageSequence(LPPVHandle Img, LPPVImageSequence* seq); + static PVCODE WINAPI sPVCropImage(LPPVHandle Img, int left, int top, int width, int height); + static bool sGetRGBAtCursor(LPPVHandle Img, DWORD colors, int x, int y, RGBQUAD* rgb, int* index); + static PVCODE sCalculateHistogram(LPPVHandle Img, const LPPVImageInfo info, LPDWORD luminosity, LPDWORD red, LPDWORD green, + LPDWORD blue, LPDWORD rgb); + static PVCODE sCreateThumbnail(LPPVHandle Img, LPPVSaveImageInfo sii, int imageIndex, DWORD imgWidth, DWORD imgHeight, + int thumbWidth, int thumbHeight, CSalamanderThumbnailMakerAbstract* thumbMaker, + DWORD thumbFlags, TProgressProc progressProc, void* progressProcArg); + static PVCODE sSimplifyImageSequence(LPPVHandle Img, HDC dc, int screenWidth, int screenHeight, LPPVImageSequence& seq, + const COLORREF& bgColor); + + static ImageHandle* FromHandle(LPPVHandle handle); + + ScopedCoInit m_comScope; + ComPtr m_factory; +}; + +} // namespace PictView::Wic diff --git a/src/plugins/shared/baseaddr_x64.txt b/src/plugins/shared/baseaddr_x64.txt index 256af78f1..e9056afff 100644 --- a/src/plugins/shared/baseaddr_x64.txt +++ b/src/plugins/shared/baseaddr_x64.txt @@ -71,7 +71,6 @@ lang_mmviewer 0x0000010033800000 salrtl9 0x0000010023900000 salrtlp9 0x0000010023b00000 ero_dlls 0x0000010024000000 0x04000000 -pvw32cnv 0x0000010028100000 undelete 0x0000010028200000 lang_undelete 0x0000010038200000 7zip 0x0000010028300000 @@ -91,7 +90,6 @@ lang_nethood 0x0000010038e00000 salmon 0x0000010029000000 fcremote 0x0000010029200000 sqlite 0x0000010029300000 -salpvenv 0x0000010029400000 zip2sfx 0x0000010029500000 salopen 0x0000010029600000 salspawn 0x0000010029700000 diff --git a/src/plugins/shared/baseaddr_x86.txt b/src/plugins/shared/baseaddr_x86.txt index 989f3760f..cc6690540 100644 --- a/src/plugins/shared/baseaddr_x86.txt +++ b/src/plugins/shared/baseaddr_x86.txt @@ -71,7 +71,6 @@ lang_mmviewer 0x33800000 salrtl9 0x23900000 salrtlp9 0x23b00000 ero_dlls 0x24000000 0x04000000 -pvw32cnv 0x28100000 undelete 0x28200000 lang_undelete 0x38200000 7zip 0x28300000 @@ -91,7 +90,6 @@ lang_nethood 0x38e00000 salmon 0x29000000 fcremote 0x29200000 sqlite 0x29300000 -salpvenv 0x29400000 ; dummy - x86 version doesn't exist zip2sfx 0x29500000 salopen 0x29600000 salspawn 0x29700000 diff --git a/src/vcxproj/salamand.sln b/src/vcxproj/salamand.sln index f45010095..ada297509 100644 --- a/src/vcxproj/salamand.sln +++ b/src/vcxproj/salamand.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.9.34310.174 @@ -163,8 +163,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "7zwrapper", "..\plugins\7zi EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fcremote", "..\plugins\filecomp\vcxproj\fcremote\fcremote.vcxproj", "{89CD2440-2844-4C0B-9CDE-1045334E9EC0}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "salpvenv", "..\plugins\pictview\vcxproj\salpvenv.vcxproj", "{17CF5E05-F29C-4E5D-BA41-83254E342E0B}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sqlite", "sqlite\sqlite.vcxproj", "{49D6CEED-804F-4EA6-BD12-2D825E13DAE3}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lang_portables", "..\plugins\portables\vcxproj\lang_portables.vcxproj", "{3F2F1440-A2C9-47F0-BE4E-3E2794BEDF07}" @@ -618,16 +616,18 @@ Global {BDD8F231-0C97-46D1-8DFC-7649741B7028}.Release|x64.Build.0 = Release|x64 {BDD8F231-0C97-46D1-8DFC-7649741B7028}.Utils (Release)|Win32.ActiveCfg = Release|Win32 {BDD8F231-0C97-46D1-8DFC-7649741B7028}.Utils (Release)|x64.ActiveCfg = Release|x64 - {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Debug|Win32.ActiveCfg = Debug|Win32 - {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Debug|Win32.Build.0 = Debug|Win32 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Debug|Win32.ActiveCfg = Debug|x64 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Debug|Win32.Build.0 = Debug|x64 {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Debug|x64.ActiveCfg = Debug|x64 {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Debug|x64.Build.0 = Debug|x64 - {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Release|Win32.ActiveCfg = Release|Win32 - {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Release|Win32.Build.0 = Release|Win32 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Release|Win32.ActiveCfg = Release|x64 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Release|Win32.Build.0 = Release|x64 {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Release|x64.ActiveCfg = Release|x64 {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Release|x64.Build.0 = Release|x64 - {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|Win32.ActiveCfg = Release|Win32 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|Win32.ActiveCfg = Release|x64 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|Win32.Build.0 = Release|x64 {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|x64.ActiveCfg = Release|x64 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|x64.Build.0 = Release|x64 {C3D5458E-BF71-4C7D-AF45-F502A14EB528}.Debug|Win32.ActiveCfg = Debug|Win32 {C3D5458E-BF71-4C7D-AF45-F502A14EB528}.Debug|Win32.Build.0 = Debug|Win32 {C3D5458E-BF71-4C7D-AF45-F502A14EB528}.Debug|x64.ActiveCfg = Debug|x64 @@ -988,12 +988,6 @@ Global {89CD2440-2844-4C0B-9CDE-1045334E9EC0}.Release|x64.Build.0 = Release|x64 {89CD2440-2844-4C0B-9CDE-1045334E9EC0}.Utils (Release)|Win32.ActiveCfg = Release|Win32 {89CD2440-2844-4C0B-9CDE-1045334E9EC0}.Utils (Release)|x64.ActiveCfg = Release|x64 - {17CF5E05-F29C-4E5D-BA41-83254E342E0B}.Debug|Win32.ActiveCfg = Debug|Win32 - {17CF5E05-F29C-4E5D-BA41-83254E342E0B}.Debug|x64.ActiveCfg = Debug|Win32 - {17CF5E05-F29C-4E5D-BA41-83254E342E0B}.Release|Win32.ActiveCfg = Release|Win32 - {17CF5E05-F29C-4E5D-BA41-83254E342E0B}.Release|x64.ActiveCfg = Release|Win32 - {17CF5E05-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|Win32.ActiveCfg = Release|Win32 - {17CF5E05-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|x64.ActiveCfg = Release|Win32 {49D6CEED-804F-4EA6-BD12-2D825E13DAE3}.Debug|Win32.ActiveCfg = Debug|Win32 {49D6CEED-804F-4EA6-BD12-2D825E13DAE3}.Debug|Win32.Build.0 = Debug|Win32 {49D6CEED-804F-4EA6-BD12-2D825E13DAE3}.Debug|x64.ActiveCfg = Debug|x64 diff --git a/translations/chinesesimplified/pictview.slt b/translations/chinesesimplified/pictview.slt index 3bf9c8402..bc79834ef 100644 --- a/translations/chinesesimplified/pictview.slt +++ b/translations/chinesesimplified/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\chinesesimplified\pictview.atp" -TEXTVERSION,"2.13 (x86)" -VERSION,"2,1,3,180" +TEXTVERSION,"2.20 (x64)" +VERSION,"2,2,0,0" [TRANSLATION] LANGID,2052 @@ -15,13 +15,13 @@ COMMENT,"简体中文版" 2013,11,9,20,20,1,"" 2007,46,9,288,8,1,"PictView %s - 用于 Open Salamander 的图像查看器" 2011,46,19,288,8,1,"" -3001,46,40,124,8,1,"此产品基于库" -2008,46,51,277,8,1,"%hs - 图像处理引擎" -3002,46,62,198,8,1,"版权所有 © 1994-2023 Jan Patera" -3005,46,81,23,8,1,"Email:" -2009,71,81,85,8,1,"support@pictview.com" -3006,46,92,22,8,1,"主页:" -2010,71,92,122,8,1,"www.pictview.com/salamander" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +2008,46,51,277,8,1,"%hs" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3005,46,81,30,8,1,"Email:" +2009,99,81,120,8,1,"support@opensalamander.org" +3006,46,92,50,8,1,"Project site:" +2010,99,92,122,8,1,"www.opensalamander.org" 3004,2,108,339,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"简体中文版" 2021,14,17,151,12,1,"和 Open Salamander 的一样(&M)" 2022,14,29,148,12,1,"如果需要就更大,但是不会更小(&L)" 2023,14,41,148,12,1,"跟随需要(&N)" -3001,6,60,256,67,1,"默认缩放" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2024,14,72,151,12,1,"适应完整(无滚动条)(&F)" 2025,14,84,148,12,1,"适应宽度(无水平滚动条)(&W)" 2026,14,96,148,12,1,"原始大小(&O)" @@ -39,7 +39,7 @@ COMMENT,"简体中文版" [DIALOG 2040] 268,150,1,"高级" -3006,6,5,256,69,1,"文件面板中的缩略图" +3006,46,92,50,8,1,"Project site:" 3007,14,18,240,16,1,"一些图像可能包含不正确的缩略图,这些缩略图没有正确旋转或没有反应最新的变更。" 2050,14,35,200,12,1,"忽略存储在图像文件中的原始缩略图(更慢)(&I)" 3008,14,51,241,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"简体中文版" [DIALOG 2070] 268,150,1,"工具" 3000,6,5,256,94,1,"选择工具" -3001,17,18,150,8,1,"当按住 Shift 键时选择的区域:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2071,24,27,203,12,1,"正方形(&S)" 2072,24,40,218,12,1,"&4:3 长方形" 2073,24,53,218,12,1,"&3:2 长方形" 2074,24,66,218,12,1,"&16:9 长方形" 2075,24,79,45,12,1,"其他(&O):" 2076,70,79,17,12,1,"" -3002,89,81,4,8,1,":" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2077,93,79,17,12,1,"" 2078,7,106,167,12,1,"颜色吸管以十六进制挑选颜色(&X)" [DIALOG 2080] 268,150,1,"颜色" 3000,6,5,256,50,1,"窗口模式" -3001,17,19,103,8,1,"背景(&B)" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2082,128,16,27,14,1,"" -3002,17,36,91,8,1,"透明(&T)" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2081,128,33,27,14,1,"" 3003,6,60,256,50,1,"全屏模式" 3004,17,75,103,8,1,"背景(&A)" 2084,128,72,27,14,1,"" -3005,17,92,44,8,1,"透明(&R)" +3005,46,81,30,8,1,"Email:" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"简体中文版" 2103,67,8,100,12,1,"" 3000,7,24,26,8,1,"宽(&W):" 2104,67,22,100,12,1,"" -3001,7,38,24,8,1,"高(&H):" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2105,67,36,100,12,1,"" -3002,7,52,33,8,1,"颜色(&C):" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2106,67,50,100,12,1,"" 3003,7,66,58,8,1,"内存中大小(&S):" 2107,67,64,100,12,1,"" 3004,7,80,49,8,1,"文件大小(&F):" 2108,67,78,100,12,1,"" -3005,7,94,17,8,1,"&DPI:" +3005,46,81,30,8,1,"Email:" 2109,67,92,100,12,1,"" -3006,7,108,33,8,1,"格式(&R):" +3006,46,92,50,8,1,"Project site:" 2110,67,106,100,12,1,"" 3007,7,122,33,8,1,"压缩(&O):" 2111,67,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"简体中文版" 121,58,1,"缩放到" 3000,8,13,49,8,1,"放大率(&M):" 2131,57,11,44,156,1,"" -3001,105,13,9,8,1,"%" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 1,7,37,50,14,1,"确定" 2,63,37,50,14,1,"取消" @@ -143,12 +143,12 @@ COMMENT,"简体中文版" 2157,16,43,61,12,1,"前台窗口(&W)" 2156,16,55,104,12,1,"前台窗口的客户区域(&A) " 2159,16,67,107,12,1,"虚拟屏幕(为多显示器)(&V)" -3001,6,92,167,49,1,"捕捉触发器" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2155,16,106,41,12,1,"热键(&H)" 2153,16,121,50,12,1,"计时器(&T)" 2154,67,106,77,12,1,"" 2151,67,121,21,12,1,"" -3002,92,123,11,8,1,"秒" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2152,8,147,73,12,1,"包括鼠标光标(&I)" 3003,2,167,176,1,1,"" 1,8,172,50,14,1,"捕捉(&C)" @@ -167,7 +167,7 @@ COMMENT,"简体中文版" 3000,5,14,39,8,1,"覆盖文件:" 2181,48,14,286,8,1,"" 2182,48,24,286,8,1,"" -3001,5,37,39,8,1,"用文件:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2183,48,37,286,8,1,"" 2184,48,47,286,8,1,"" 6,86,66,50,14,1,"是(&Y)" @@ -179,9 +179,9 @@ COMMENT,"简体中文版" 2300,5,2,33,8,1,"压缩(&C):" 2301,58,0,170,80,1,"" 2302,234,0,50,14,1,"选项(&O)" -3001,5,26,49,8,1,"颜色深度(&D):" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2303,58,24,70,80,1,"" -3002,5,46,49,8,1,"旋转(&R):" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2304,58,44,70,80,1,"" 3003,5,66,52,8,1,"翻转/镜像(&F):" 2305,58,64,70,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"简体中文版" 2514,232,15,118,8,1,"" 2515,354,12,50,14,1,"设置(&S)..." 3000,225,35,186,62,1," 位置 " -3001,238,49,25,8,1,"左(&L):" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2503,267,47,45,12,1,"" 2504,318,47,66,75,1,"" -3002,238,68,25,8,1,"顶(&T):" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2501,267,66,45,12,1,"" 2502,318,66,66,75,1,"" 2516,267,82,60,12,1,"居中图像(&C)" 3004,225,102,186,100,1," 缩放打印大小 " -3005,231,118,32,8,1,"缩放(&E):" +3005,46,81,30,8,1,"Email:" 2505,267,116,35,12,1,"" -3006,305,118,10,8,1,"%" +3006,46,92,50,8,1,"Project site:" 2506,318,111,84,12,1,"缩放以适应媒体(&F)" 2507,318,123,70,12,1,"保持宽高比(&K)" 3007,231,139,32,8,1,"宽(&W):" @@ -362,7 +362,7 @@ COMMENT,"简体中文版" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"调用 PVW32Cnv.DLL 错误" +1009,1,"Error calling Windows Imaging Component (WIC)" 1010,1,"红: %i, 绿: %i, 蓝: %i" 1011,1,"红: %#02x, 绿: %#02x, 蓝: %#02x" 1012,1,"(%i, %i): 红: %i, 绿: %i, 蓝: %i" @@ -463,8 +463,8 @@ COMMENT,"简体中文版" 1102,1,"显示其他通道(O)" [STRINGTABLE 15] -1109,1,"无法加载图像处理库 PVW32Cnv.dll." -1110,1,"发现错误的 PVW32Cnv.dll 版本。" +1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." +1110,1,"An incompatible Windows Imaging Component (WIC) version was found." 1111,1,"请先选择一个区域。" 1112,1,"默认" 1113,1,"文件 "%s" 已经存在。\n你要替换它吗?" diff --git a/translations/czech/pictview.slt b/translations/czech/pictview.slt index b1c17f50d..01fd63dc7 100644 --- a/translations/czech/pictview.slt +++ b/translations/czech/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\czech\pictview.atp" -TEXTVERSION,"2.13 (x86)" -VERSION,"2,1,3,180" +TEXTVERSION,"2.20 (x64)" +VERSION,"2,2,0,0" [TRANSLATION] LANGID,1029 @@ -15,13 +15,13 @@ COMMENT,"Česká verze" 2013,11,9,20,20,1,"" 2007,46,9,340,8,1,"Prohlížeč PictView %s - Prohlížeč obrázků pro Open Salamander" 2011,46,19,340,8,1,"" -3001,46,40,123,8,1,"Tento produkt využívá knihovnu" -2008,46,51,338,8,1,"%hs - Image Processing Engine" -3002,46,62,262,8,1,"Copyright © 1994-2023 Jan Patera" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +2008,46,51,277,8,1,"%hs" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,115,81,85,8,1,"support@pictview.com" -3006,46,92,66,8,1,"Domovská stránka:" -2010,115,92,122,8,1,"www.pictview.com/cz/salamander" +2009,99,81,120,8,1,"support@opensalamander.org" +3006,46,92,50,8,1,"Project site:" +2010,99,92,122,8,1,"www.opensalamander.org" 3004,2,108,391,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Česká verze" 2021,14,17,151,12,1,"&Stejná jako u Open Salamandera" 2022,14,29,148,12,1,"Vě&tší, je-li potřeba, ale ne menší" 2023,14,41,148,12,1,"D&le potřeby" -3001,6,60,273,67,1,"Výchozí způsob zvětšení" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2024,14,72,151,12,1,"&Umístit do okna (žádné posuvníky)" 2025,14,84,165,12,1,"U&místit na šířku (bez vodorovného posuvníku)" 2026,14,96,148,12,1,"&Původní velikost" @@ -39,7 +39,7 @@ COMMENT,"Česká verze" [DIALOG 2040] 285,150,1,"Rozšířené" -3006,6,5,273,69,1,"Miniatury v souborových panelech" +3006,46,92,50,8,1,"Project site:" 3007,14,18,225,16,1,"Některé soubory s obrázky mohou obsahovat neaktuální miniaturu, která je špatně otočená nebo neodráží poslední úpravy obrázku." 2050,14,35,204,12,1,"&Ignorovat miniatury uložené přímo v souborech (pomalé)" 3008,14,51,257,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Česká verze" [DIALOG 2070] 285,150,1,"Nástroje" 3000,6,5,273,94,1,"Výběr" -3001,17,18,150,8,1,"Oblast vybíraná při stisku klávesy Shift:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2071,24,27,203,12,1,"Čtvere&c" 2072,24,40,218,12,1,"Obdélník &4:3" 2073,24,53,218,12,1,"Obdélník &3:2" 2074,24,66,218,12,1,"Obdélník &16:9" 2075,24,79,35,12,1,"&Jiná:" 2076,60,79,17,12,1,"" -3002,79,81,4,8,1,":" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2077,83,79,17,12,1,"" 2078,7,106,167,12,1,"Ukazovat barvy pipetou he&xadecimálně" [DIALOG 2080] 285,150,1,"Barvy" 3000,6,5,273,50,1,"Zobrazení v okně" -3001,17,19,103,8,1,"&Pozadí" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2082,128,16,27,14,1,"" -3002,17,36,91,8,1,"Průhledná &barva" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2081,128,33,27,14,1,"" 3003,6,60,273,50,1,"Zobrazení přes celou obrazovku" 3004,17,75,103,8,1,"P&ozadí" 2084,128,72,27,14,1,"" -3005,17,92,58,8,1,"Průhledná b&arva" +3005,46,81,30,8,1,"Email:" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Česká verze" 2103,70,8,100,12,1,"" 3000,7,24,49,8,1,"Šíř&ka:" 2104,70,22,100,12,1,"" -3001,7,38,49,8,1,"&Výška:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2105,70,36,100,12,1,"" -3002,7,52,44,8,1,"Počet &barev:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2106,70,50,100,12,1,"" 3003,7,66,61,8,1,"Velikost (&paměť):" 2107,70,64,100,12,1,"" 3004,7,80,52,8,1,"Velikost (d&isk):" 2108,70,78,100,12,1,"" -3005,7,94,50,8,1,"&DPI:" +3005,46,81,30,8,1,"Email:" 2109,70,92,100,12,1,"" -3006,7,108,50,8,1,"&Formát:" +3006,46,92,50,8,1,"Project site:" 2110,70,106,100,12,1,"" 3007,7,122,50,8,1,"K&omprese:" 2111,70,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Česká verze" 121,58,1,"Zvětšení obrázku" 3000,8,13,49,8,1,"&Poměr:" 2131,57,11,44,156,1,"" -3001,105,13,9,8,1,"%" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 1,7,37,50,14,1,"OK" 2,63,37,50,14,1,"Storno" @@ -143,12 +143,12 @@ COMMENT,"Česká verze" 2157,16,43,120,12,1,"&Aktivní okno" 2156,16,55,135,12,1,"&Klientskou oblast aktivního okna " 2159,16,67,148,12,1,"&Virtuální obrazovku (při více monitorech)" -3001,6,92,167,49,1,"Spoušť" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2155,16,106,58,12,1,"&Horká klávesa" 2153,16,121,40,12,1,"Čas&ovač" 2154,75,106,77,12,1,"" 2151,75,121,21,12,1,"" -3002,100,123,33,8,1,"vteřin" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2152,8,147,85,12,1,"Včetně kurzoru &myši" 3003,2,167,166,1,1,"" 1,8,172,50,14,1,"&Sejmout" @@ -167,7 +167,7 @@ COMMENT,"Česká verze" 3000,5,14,78,8,1,"Má se přepsat soubor:" 2181,85,14,286,8,1,"" 2182,85,24,286,8,1,"" -3001,5,37,78,8,1,"tímto souborem:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2183,85,37,286,8,1,"" 2184,85,47,286,8,1,"" 6,104,66,50,14,1,"&Ano" @@ -179,9 +179,9 @@ COMMENT,"Česká verze" 2300,5,2,49,8,1,"&Komprese:" 2301,54,0,170,80,1,"" 2302,230,0,50,14,1,"&Možnosti" -3001,5,26,49,8,1,"Počet &barev:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2303,54,24,70,80,1,"" -3002,5,46,49,8,1,"&Otočení:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2304,54,44,70,80,1,"" 3003,5,66,49,8,1,"&Zrcadlení:" 2305,54,64,70,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Česká verze" 2514,232,15,118,8,1,"" 2515,354,12,50,14,1,"&Nastavení..." 3000,225,35,186,62,1," Umístění na papíře " -3001,234,49,24,8,1,"V&levo:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2503,262,47,45,12,1,"" 2504,313,47,66,75,1,"" -3002,234,68,24,8,1,"&Shora:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2501,262,66,45,12,1,"" 2502,313,66,66,75,1,"" 2516,262,82,60,12,1,"&Centrovat" 3004,225,102,186,100,1," Přizpůsobit velikost " -3005,233,118,25,8,1,"&Poměr:" +3005,46,81,30,8,1,"Email:" 2505,262,116,35,12,1,"" -3006,300,118,10,8,1,"%" +3006,46,92,50,8,1,"Project site:" 2506,313,111,72,12,1,"Přizpůsobit p&apíru" 2507,313,123,86,12,1,"&Zachovat poměr stran" 3007,233,139,25,8,1,"Šíř&ka:" @@ -362,7 +362,7 @@ COMMENT,"Česká verze" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Chyba při volání PVW32Cnv.DLL" +1009,1,"Error calling Windows Imaging Component (WIC)" 1010,1,"Červená: %i, Zelená: %i, Modrá: %i" 1011,1,"Červená: %#02x, Zelená: %#02x, Modrá: %#02x" 1012,1,"(%i, %i): Červená: %i, Zelená: %i, Modrá: %i" @@ -463,8 +463,8 @@ COMMENT,"Česká verze" 1102,1,"Ukázat ostatní kanály (O)" [STRINGTABLE 15] -1109,1,"Nelze najít knihovnu PVW32Cnv.dll pro zpracování obrázků." -1110,1,"Byla nalezena špatná verze knihovny PVW32Cnv.dll." +1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." +1110,1,"An incompatible Windows Imaging Component (WIC) version was found." 1111,1,"Není vybrána žádná oblast." 1112,1,"Výchozí" 1113,1,"Soubor "%s" již existuje.\nMá být přepsán?" diff --git a/translations/dutch/pictview.slt b/translations/dutch/pictview.slt index 930d7b368..767846eb8 100644 --- a/translations/dutch/pictview.slt +++ b/translations/dutch/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\dutch\pictview.atp" -TEXTVERSION,"2.13 (x86)" -VERSION,"2,1,3,180" +TEXTVERSION,"2.20 (x64)" +VERSION,"2,2,0,0" [TRANSLATION] LANGID,1043 @@ -15,13 +15,13 @@ COMMENT,"Nederlandse versie" 2013,11,9,20,20,1,"" 2007,46,9,309,8,1,"PictView %s - Afbeeldingsviewer voor Open Salamander" 2011,46,19,309,8,1,"" -3001,46,40,138,8,1,"Dit product is gebaseerd op bibliotheken" -2008,46,51,298,8,1,"%hs - Image Processing Engine" -3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +2008,46,51,277,8,1,"%hs" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,99,81,85,8,1,"support@pictview.com" -3006,46,92,50,8,1,"Start pagina:" -2010,99,92,122,8,1,"www.pictview.com/salamander" +2009,99,81,120,8,1,"support@opensalamander.org" +3006,46,92,50,8,1,"Project site:" +2010,99,92,122,8,1,"www.opensalamander.org" 3004,2,108,360,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Nederlandse versie" 2021,14,17,151,12,1,"Gelijk aan Altap Sala&mander" 2022,14,29,148,12,1,"&Groter indien nodig maar niet kleiner" 2023,14,41,148,12,1,"Indien &nodig" -3001,6,60,276,67,1,"Standaard zoom" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2024,14,72,151,12,1,"&Passend (geen schuifbalken)" 2025,14,84,173,12,1,"Aanpassen aan &breedte (geen horiz. schuifbalk)" 2026,14,96,148,12,1,"&Werkelijke grootte" @@ -39,7 +39,7 @@ COMMENT,"Nederlandse versie" [DIALOG 2040] 288,150,1,"Geavanceerd" -3006,6,5,276,69,1,"Miniatuurweergave in bestandspanelen" +3006,46,92,50,8,1,"Project site:" 3007,14,18,260,16,1,"Sommige afbeeldingen kunnen een onjuiste miniatuurweergave hebben, die niet juist gedraaid is of niet de laatste wijzigingen weergeeft." 2050,14,35,225,12,1,"&Negeer oorspronkelijke miniaturen in afbeeldingen (langzamer)" 3008,14,51,260,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Nederlandse versie" [DIALOG 2070] 288,150,1,"Gereedschappen" 3000,6,5,276,94,1,"Selectie gereedschap" -3001,17,18,150,8,1,"Gebied selectie bij vasthouden Shift toets:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2071,24,27,203,12,1,"&Vierkant" 2072,24,40,218,12,1,"&4:3 rechthoek" 2073,24,53,218,12,1,"&3:2 rechthoek" 2074,24,66,218,12,1,"&16:9 rechthoek" 2075,24,79,40,12,1,"&Anders:" 2076,67,79,17,12,1,"" -3002,86,81,4,8,1,":" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2077,90,79,17,12,1,"" 2078,7,106,167,12,1,"Pipet kiest kleuren in &hexadecimalen" [DIALOG 2080] 288,150,1,"Kleuren" 3000,6,5,276,50,1,"Venstermodus" -3001,17,19,103,8,1,"&Achtergrond" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2082,128,16,27,14,1,"" -3002,17,36,91,8,1,"&Doorzichtig" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2081,128,33,27,14,1,"" 3003,6,60,276,50,1,"Volledig schermmodus" 3004,17,75,103,8,1,"A&chtergrond" 2084,128,72,27,14,1,"" -3005,17,92,44,8,1,"D&oorzichtig" +3005,46,81,30,8,1,"Email:" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Nederlandse versie" 2103,75,8,90,12,1,"" 3000,7,24,49,8,1,"&Breedte:" 2104,75,22,90,12,1,"" -3001,7,38,49,8,1,"&Hoogte:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2105,75,36,90,12,1,"" -3002,7,52,42,8,1,"&Kleuren:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2106,75,50,90,12,1,"" 3003,7,66,64,8,1,"&Geheugengrootte:" 2107,75,64,90,12,1,"" 3004,7,80,59,8,1,"B&estandsgrootte:" 2108,75,78,90,12,1,"" -3005,7,94,50,8,1,"&DPI:" +3005,46,81,30,8,1,"Email:" 2109,75,92,90,12,1,"" -3006,7,108,50,8,1,"Fo&rmaat:" +3006,46,92,50,8,1,"Project site:" 2110,75,106,90,12,1,"" 3007,7,122,50,8,1,"C&ompressie:" 2111,75,120,90,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Nederlandse versie" 121,58,1,"Zoomen naar" 3000,8,13,49,8,1,"&Vergroting:" 2131,57,11,44,156,1,"" -3001,105,13,9,8,1,"%" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 1,7,37,50,14,1,"OK" 2,63,37,50,14,1,"Annuleren" @@ -143,12 +143,12 @@ COMMENT,"Nederlandse versie" 2157,16,43,120,12,1,"Voo&rgrondvenster" 2156,16,55,135,12,1,"Client&gebied van voorgrondvenster " 2159,16,67,154,12,1,"&Virtueelscherm (voor meerdere schermen)" -3001,6,92,172,49,1,"Trigger" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2155,16,106,44,12,1,"&Sneltoets" 2153,16,121,44,12,1,"&Timer" 2154,65,106,84,12,1,"" 2151,65,121,21,12,1,"" -3002,90,123,33,8,1,"seconden" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2152,8,147,85,12,1,"&Inclusief muiscursor" 3003,2,167,180,1,1,"" 1,11,172,50,14,1,"Vast&leggen" @@ -167,7 +167,7 @@ COMMENT,"Nederlandse versie" 3000,4,14,78,8,1,"Bestand overschrijven:" 2181,86,14,286,8,1,"" 2182,86,24,286,8,1,"" -3001,4,37,78,8,1,"Met bestand:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2183,86,37,286,8,1,"" 2184,86,47,286,8,1,"" 6,105,66,50,14,1,"&Ja" @@ -179,9 +179,9 @@ COMMENT,"Nederlandse versie" 2300,5,2,49,8,1,"&Compressie:" 2301,76,0,170,80,1,"" 2302,252,0,50,14,1,"&Opties" -3001,5,26,49,8,1,"Kleu&rdiepte:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2303,76,24,70,80,1,"" -3002,5,46,49,8,1,"&Draaien:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2304,76,44,70,80,1,"" 3003,5,66,70,8,1,"O&mslaan/Spiegelen:" 2305,76,64,70,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Nederlandse versie" 2514,232,15,131,8,1,"" 2515,367,12,50,14,1,"&Setup..." 3000,225,35,195,62,1," Positie " -3001,240,49,22,8,1,"&Links:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2503,266,47,45,12,1,"" 2504,317,47,66,75,1,"" -3002,244,68,18,8,1,"&Top:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2501,266,66,45,12,1,"" 2502,317,66,66,75,1,"" 2516,266,82,83,12,1,"A&fbeelding centreren" 3004,225,102,195,100,1," Geschaalde afdruk grootte " -3005,235,118,27,8,1,"S&chaal:" +3005,46,81,30,8,1,"Email:" 2505,266,116,35,12,1,"" -3006,304,118,10,8,1,"%" +3006,46,92,50,8,1,"Project site:" 2506,317,111,98,12,1,"Maak &passend aan media" 2507,317,123,78,12,1,"Behoud &verhouding" 3007,231,139,31,8,1,"Br&eedte:" @@ -362,7 +362,7 @@ COMMENT,"Nederlandse versie" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Fout bij aanroep PVW32Cnv.DLL" +1009,1,"Error calling Windows Imaging Component (WIC)" 1010,1,"Rood: %i, Groen: %i, Blauw: %i" 1011,1,"Rood: %#02x, Groen: %#02x, Blauw: %#02x" 1012,1,"(%i, %i): Rood: %i, Groen: %i, Blauw: %i" @@ -463,8 +463,8 @@ COMMENT,"Nederlandse versie" 1102,1,"Andere kanalen weergeven (O)" [STRINGTABLE 15] -1109,1,"Kan de afbeelding verwerkingsbibiliotheek PVW32Cnv.dll niet laden." -1110,1,"De verkeerde versie van PVW32Cnv.dll is gevonden." +1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." +1110,1,"An incompatible Windows Imaging Component (WIC) version was found." 1111,1,"Selecteer eerst een regio alstublieft." 1112,1,"Standaard" 1113,1,"Bestand "%s" bestaat reeds.\nWilt u deze vervangen?" diff --git a/translations/french/pictview.slt b/translations/french/pictview.slt index 6466a39df..5281eb9bc 100644 --- a/translations/french/pictview.slt +++ b/translations/french/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\french\pictview.atp" -TEXTVERSION,"2.13 (x86)" -VERSION,"2,1,3,180" +TEXTVERSION,"2.20 (x64)" +VERSION,"2,2,0,0" [TRANSLATION] LANGID,1036 @@ -15,13 +15,13 @@ COMMENT,"Version française" 2013,11,9,20,20,1,"" 2007,46,9,313,8,1,"PictView %s - Visualiseur d'image pour Open Salamander" 2011,46,19,313,8,1,"" -3001,46,40,132,8,1,"Ce produit est basé sur la bibliothèque" -2008,46,51,302,8,1,"%hs - Image Processing Engine" -3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" -3005,46,81,23,8,1,"Email:" -2009,83,81,85,8,1,"support@pictview.com" -3006,46,92,34,8,1,"Site web:" -2010,83,92,122,8,1,"www.pictview.com/salamander" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +2008,46,51,277,8,1,"%hs" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3005,46,81,30,8,1,"Email:" +2009,99,81,120,8,1,"support@opensalamander.org" +3006,46,92,50,8,1,"Project site:" +2010,99,92,122,8,1,"www.opensalamander.org" 3004,2,108,364,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Version française" 2021,14,17,117,12,1,"Co&mme pour Open Salamander" 2022,14,29,157,12,1,"Plus &grand si nécessaire mais pas plus petit" 2023,14,41,70,12,1,"Selon les &besoins" -3001,6,60,306,67,1,"Zoom par défaut" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2024,14,72,156,12,1,"&Tout remplir (pas de barres de défilement)" 2025,14,84,166,12,1,"Adapté à la &largeur (pas de barre horizontale)" 2026,14,96,64,12,1,"Taille &originelle" @@ -39,7 +39,7 @@ COMMENT,"Version française" [DIALOG 2040] 318,150,1,"Avancé" -3006,6,5,306,69,1,"Miniatures dans les panneaux de fichiers" +3006,46,92,50,8,1,"Project site:" 3007,14,18,240,16,1,"Certaines images peuvent présenter une miniature erronée, mal positionnée ou qui ne présente pas les derniers changements." 2050,14,35,280,12,1,"&Ignorer les miniatures originelles enregistrées dans les fichiers image (plus lent)" 3008,14,51,241,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Version française" [DIALOG 2070] 318,150,1,"Outils" 3000,6,5,306,94,1,"Sélection de l'outil" -3001,17,18,164,8,1,"Zone sélectionnée avec la touche Maj enfoncée:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2071,24,27,203,12,1,"&Carré" 2072,24,40,218,12,1,"rectangle &4:3" 2073,24,53,218,12,1,"rectangle &3:2" 2074,24,66,218,12,1,"rectangle &16:9" 2075,24,79,35,12,1,"&Autre:" 2076,60,79,17,12,1,"" -3002,79,81,4,8,1,":" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2077,83,79,17,12,1,"" 2078,7,106,167,12,1,"La pipette pointe les couleurs en he&xadécimal" [DIALOG 2080] 318,150,1,"Couleurs" 3000,6,5,306,50,1,"Mode fenêtré" -3001,17,19,103,8,1,"&Fond" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2082,128,16,27,14,1,"" -3002,17,36,91,8,1,"&Transparent" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2081,128,33,27,14,1,"" 3003,6,60,306,50,1,"Mode plein écran" 3004,17,75,103,8,1,"F&ond" 2084,128,72,27,14,1,"" -3005,17,92,44,8,1,"T&ransparent" +3005,46,81,30,8,1,"Email:" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Version française" 2103,73,8,100,12,1,"" 3000,7,24,49,8,1,"&Largeur:" 2104,73,22,100,12,1,"" -3001,7,38,49,8,1,"&Hauteur:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2105,73,36,100,12,1,"" -3002,7,52,42,8,1,"&Couleurs:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2106,73,50,100,12,1,"" 3003,7,66,65,8,1,"&Taille en mémoire:" 2107,73,64,100,12,1,"" 3004,7,80,56,8,1,"Taille du &fichier:" 2108,73,78,100,12,1,"" -3005,7,94,50,8,1,"&DPI:" +3005,46,81,30,8,1,"Email:" 2109,73,92,100,12,1,"" -3006,7,108,50,8,1,"Fo&rmat:" +3006,46,92,50,8,1,"Project site:" 2110,73,106,100,12,1,"" 3007,7,122,50,8,1,"C&ompression:" 2111,73,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Version française" 132,58,1,"Zoomer jusque" 3000,8,13,59,8,1,"&Agrandissement:" 2131,68,11,44,156,1,"" -3001,113,13,9,8,1,"%" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 1,13,37,50,14,1,"OK" 2,69,37,50,14,1,"Annuler" @@ -143,12 +143,12 @@ COMMENT,"Version française" 2157,16,43,120,12,1,"&Fenêtre à l'avant-plan" 2156,16,55,141,12,1,"&Zone client de la fenêtre d'avant-plan " 2159,16,67,146,12,1,"Ecran &virtuel (pour moniteurs multiples)" -3001,6,92,167,49,1,"Déclencheur de capture" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2155,16,106,46,12,1,"&Raccourci" 2153,16,121,39,12,1,"&Horloge" 2154,63,106,77,12,1,"" 2151,63,121,21,12,1,"" -3002,88,123,33,8,1,"secondes" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2152,8,147,104,12,1,"&Inclure le curseur de souris" 3003,2,167,176,1,1,"" 1,8,172,50,14,1,"&Capturer" @@ -167,7 +167,7 @@ COMMENT,"Version française" 3000,4,14,61,8,1,"Ecraser le fichier:" 2181,69,14,286,8,1,"" 2182,69,24,286,8,1,"" -3001,4,37,61,8,1,"Par le fichier:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2183,69,37,286,8,1,"" 2184,69,47,286,8,1,"" 6,96,66,50,14,1,"&Oui" @@ -179,9 +179,9 @@ COMMENT,"Version française" 2300,5,2,49,8,1,"&Compression:" 2301,54,0,199,80,1,"" 2302,259,0,50,14,1,"&Options" -3001,5,26,80,8,1,"&Profondeur de couleur:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2303,86,24,81,80,1,"" -3002,5,46,33,8,1,"&Rotation:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2304,86,44,81,80,1,"" 3003,5,66,78,8,1,"Réfle&xion/Mirroir:" 2305,86,64,81,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Version française" 2514,232,15,140,8,1,"" 2515,376,12,50,14,1,"&Réglage..." 3000,225,35,208,62,1," Position " -3001,231,49,31,8,1,"&Gauche:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2503,266,47,45,12,1,"" 2504,317,47,66,75,1,"" -3002,231,68,31,8,1,"&Haut:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2501,266,66,45,12,1,"" 2502,317,66,66,75,1,"" 2516,266,82,64,12,1,"&Centrer l'image" 3004,225,102,208,100,1," Mise à l'échelle de l'impression " -3005,231,118,31,8,1,"&Echelle:" +3005,46,81,30,8,1,"Email:" 2505,266,116,35,12,1,"" -3006,304,118,10,8,1,"%" +3006,46,92,50,8,1,"Project site:" 2506,317,111,71,12,1,"Adapter au &média" 2507,317,123,111,12,1,"C&onserver le rapport d'aspect" 3007,231,139,31,8,1,"&Largeur:" @@ -362,7 +362,7 @@ COMMENT,"Version française" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Erreur d'appel de PVW32Cnv.DLL" +1009,1,"Error calling Windows Imaging Component (WIC)" 1010,1,"Rouge: %i, Vert: %i, Bleu: %i" 1011,1,"Rouge: %#02x, Vert: %#02x, Bleu: %#02x" 1012,1,"(%i, %i): Rouge: %i, Vert: %i, Bleu: %i" @@ -463,8 +463,8 @@ COMMENT,"Version française" 1102,1,"Voir d'autres canaux (O)" [STRINGTABLE 15] -1109,1,"Impossible de charger la bibliothèque de traitement d'image PVW32Cnv.dll." -1110,1,"Une mauvaise version de PVW32Cnv.dll a été trouvée." +1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." +1110,1,"An incompatible Windows Imaging Component (WIC) version was found." 1111,1,"Veuillez d'abord choisir une région." 1112,1,"Par défaut" 1113,1,"Le fichier "%s" existe déjà.\nVoulez-vous le remplacer?" diff --git a/translations/german/pictview.slt b/translations/german/pictview.slt index 95e77790c..038583799 100644 --- a/translations/german/pictview.slt +++ b/translations/german/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\german\pictview.atp" -TEXTVERSION,"2.13 (x86)" -VERSION,"2,1,3,180" +TEXTVERSION,"2.20 (x64)" +VERSION,"2,2,0,0" [TRANSLATION] LANGID,1031 @@ -15,13 +15,13 @@ COMMENT,"Deutsche Version" 2013,11,9,20,20,1,"" 2007,46,9,334,8,1,"PictView %s - Grafikbetrachter-Plugin für Open Salamander" 2011,46,19,334,8,1,"" -3001,46,40,150,8,1,"PictView basiert auf der Programmbibliothek" -2008,46,51,323,8,1,"%hs - Bildverarbeitungseinheit" -3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" -3005,46,81,26,8,1,"E-Mail:" -2009,84,81,85,8,1,"support@pictview.com" -3006,46,92,36,8,1,"Webseite:" -2010,84,92,122,8,1,"www.pictview.com/salamander" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +2008,46,51,277,8,1,"%hs" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3005,46,81,30,8,1,"Email:" +2009,99,81,120,8,1,"support@opensalamander.org" +3006,46,92,50,8,1,"Project site:" +2010,99,92,122,8,1,"www.opensalamander.org" 3004,2,108,385,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Deutsche Version" 2021,14,17,175,12,1,"&Fenstergröße des Open Salamanders verwenden" 2022,14,29,194,12,1,"&Automatische Anpassung - keine Fensterverkleinerung" 2023,14,41,97,12,1,"A&utomatische Anpassung" -3001,6,60,293,67,1,"Zoomeinstellungen" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2024,14,72,224,12,1,"Automatische Anpassung an Fenster&göße (keine Bildlaufleisten)" 2025,14,84,270,12,1,"Automatische Anpassung an Fenster&breite (keine horizontalen Bildlaufleisten)" 2026,14,96,58,12,1,"&Originalgröße" @@ -39,7 +39,7 @@ COMMENT,"Deutsche Version" [DIALOG 2040] 305,150,1,"Erweitert" -3006,6,5,293,69,1,"Miniaturansichten in Dateilisten" +3006,46,92,50,8,1,"Project site:" 3007,14,18,277,16,1,"Einige Grafikdateien können eine fehlerhafte Miniaturansicht enthalten, welche nicht richtig gedreht wurden oder nicht die letzten Änderungen wiederspiegeln." 2050,14,35,233,12,1,"&Interne Miniaturansichten in Grafikdateien ignorieren (langsamer)" 3008,14,51,257,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Deutsche Version" [DIALOG 2070] 305,150,1,"Werkzeuge" 3000,6,5,293,94,1,"Auswahl" -3001,17,18,173,8,1,"Umschalt-Taste aktiviert folgenden Auswahlmodus:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2071,24,27,203,12,1,"&Quadratisch" 2072,24,40,218,12,1,"Rechteckig - Verhältnis &4:3" 2073,24,53,218,12,1,"Rechteckig - Verhältnis &3:2" 2074,24,66,218,12,1,"Rechteckig - Verhältnis &16:9" 2075,24,79,41,12,1,"&Weitere:" 2076,66,79,17,12,1,"" -3002,85,81,4,8,1,":" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2077,89,79,17,12,1,"" 2078,7,106,167,12,1,"He&xadezimale Farbwerte an Pipette anzeigen" [DIALOG 2080] 305,150,1,"Farben" 3000,6,5,293,50,1,"Fenstereinstellungen" -3001,17,19,103,8,1,"&Hintergrund" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2082,128,16,27,14,1,"" -3002,17,36,91,8,1,"&Transparent" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2081,128,33,27,14,1,"" 3003,6,60,293,50,1,"Vollbildeinstellungen" 3004,17,75,103,8,1,"H&intergrund" 2084,128,72,27,14,1,"" -3005,17,92,44,8,1,"T&ransparent" +3005,46,81,30,8,1,"Email:" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Deutsche Version" 2103,67,8,100,12,1,"" 3000,7,24,44,8,1,"Grafik&breite:" 2104,67,22,100,12,1,"" -3001,7,38,42,8,1,"Grafik&höhe:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2105,67,36,100,12,1,"" -3002,7,52,50,8,1,"&Farbenanzahl:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2106,67,50,100,12,1,"" 3003,7,66,44,8,1,"&Grafikgröße:" 2107,67,64,100,12,1,"" 3004,7,80,42,8,1,"&Dateigröße:" 2108,67,78,100,12,1,"" -3005,7,94,58,8,1,"&Auflösung (DPI):" +3005,46,81,30,8,1,"Email:" 2109,67,92,100,12,1,"" -3006,7,108,29,8,1,"Forma&t:" +3006,46,92,50,8,1,"Project site:" 2110,67,106,100,12,1,"" 3007,7,122,57,8,1,"&Komprimierung:" 2111,67,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Deutsche Version" 125,58,1,"Zoomfaktor" 3000,8,13,52,8,1,"&Vergrößerung:" 2131,61,11,44,156,1,"" -3001,109,13,9,8,1,"%" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 1,9,37,50,14,1,"OK" 2,65,37,50,14,1,"Abbrechen" @@ -143,12 +143,12 @@ COMMENT,"Deutsche Version" 2157,16,43,120,12,1,"F&enster im Vordergrund" 2156,16,55,148,12,1,"&Arbeitsbereich des Vordergrundfensters " 2159,16,67,162,12,1,"Virtueller &Bildschirm (bei Mehrschirmbetrieb)" -3001,6,92,182,49,1,"Methode" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2155,16,106,56,12,1,"Tasten&kürzel" 2153,16,121,73,12,1,"A&utomatisch nach" 2154,90,106,92,12,1,"" 2151,90,121,21,12,1,"" -3002,115,123,36,8,1,"Sekunden" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2152,8,147,96,12,1,"&Mauszeiger fotografieren" 3003,2,167,190,1,1,"" 1,12,172,57,14,1,"&Fotografieren" @@ -167,7 +167,7 @@ COMMENT,"Deutsche Version" 3000,6,14,48,8,1,"Überschreibe:" 2181,58,14,286,8,1,"" 2182,58,24,286,8,1,"" -3001,9,37,45,8,1,"mit:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2183,58,37,286,8,1,"" 2184,58,47,286,8,1,"" 6,91,66,50,14,1,"&Ja" @@ -179,9 +179,9 @@ COMMENT,"Deutsche Version" 2300,5,2,57,8,1,"&Komprimierung:" 2301,63,0,186,80,1,"" 2302,255,0,50,14,1,"&Optionen" -3001,5,26,49,8,1,"Farb&tiefe:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2303,63,24,86,80,1,"" -3002,5,46,49,8,1,"&Drehung:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2304,63,44,86,80,1,"" 3003,5,66,55,8,1,"&Spiegeln:" 2305,63,64,86,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Deutsche Version" 2514,232,15,121,8,1,"" 2515,357,12,70,14,1,"&Seite einrichten..." 3000,225,35,209,62,1," Position " -3001,247,49,23,8,1,"&Links:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2503,274,47,45,12,1,"" 2504,325,47,66,75,1,"" -3002,247,68,23,8,1,"&Oben:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2501,274,66,45,12,1,"" 2502,325,66,66,75,1,"" 2516,274,82,60,12,1,"Bild &zentrieren" 3004,225,102,209,100,1," Skalierte Ausgabegröße " -3005,229,118,41,8,1,"S&kalierung:" +3005,46,81,30,8,1,"Email:" 2505,274,116,35,12,1,"" -3006,312,118,10,8,1,"%" +3006,46,92,50,8,1,"Project site:" 2506,325,111,101,12,1,"Auf &Mediengröße skalieren" 2507,325,123,97,12,1,"&Proportionen beibehalten" 3007,229,139,41,8,1,"&Breite:" @@ -362,7 +362,7 @@ COMMENT,"Deutsche Version" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Fehler beim Aufruf der Datei PVW32Cnv.DLL" +1009,1,"Error calling Windows Imaging Component (WIC)" 1010,1,"Rot: %i, Grün: %i, Blau: %i" 1011,1,"Rot: %#02x, Grün: %#02x, Blau: %#02x" 1012,1,"(%i, %i): Rot: %i, Grün: %i, Blau: %i" @@ -463,8 +463,8 @@ COMMENT,"Deutsche Version" 1102,1,"Weitere Kanäle anzeigen (O)" [STRINGTABLE 15] -1109,1,"Die Datei PVW32Cnv.DLL konnte nicht geladen werden." -1110,1,"Datei PVW32Cnv.DLL liegt in einer falschen Version vor." +1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." +1110,1,"An incompatible Windows Imaging Component (WIC) version was found." 1111,1,"Bitte wählen Sie zuerst einen Bereich aus." 1112,1,"Standard" 1113,1,"Die Datei "%s" existiert bereits.\nWollen Sie die Datei ersetzen?" diff --git a/translations/hungarian/pictview.slt b/translations/hungarian/pictview.slt index 7c97c5266..6b9779343 100644 --- a/translations/hungarian/pictview.slt +++ b/translations/hungarian/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\hungarian\pictview.atp" -TEXTVERSION,"2.13 (x86)" -VERSION,"2,1,3,180" +TEXTVERSION,"2.20 (x64)" +VERSION,"2,2,0,0" [TRANSLATION] LANGID,1038 @@ -15,13 +15,13 @@ COMMENT,"Magyar változat" 2013,11,9,20,20,1,"" 2007,46,9,290,8,1,"PictView %s - Képnéző Open Salamander-hez" 2011,46,19,290,8,1,"" -3001,46,40,123,8,1,"A termék alapjául szolgál" -2008,46,51,279,8,1,"%hs - Képfeldolgozó modul" -3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" -3005,46,81,23,8,1,"Email:" -2009,76,81,85,8,1,"support@pictview.com" -3006,46,92,29,8,1,"Honlap:" -2010,76,92,122,8,1,"www.pictview.com/salamander" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +2008,46,51,277,8,1,"%hs" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3005,46,81,30,8,1,"Email:" +2009,99,81,120,8,1,"support@opensalamander.org" +3006,46,92,50,8,1,"Project site:" +2010,99,92,122,8,1,"www.opensalamander.org" 3004,2,108,341,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Magyar változat" 2021,14,17,151,12,1,"&Mint az Open Salamander-é" 2022,14,29,148,12,1,"Nagyobb, ha ke&ll, de kisebb nem" 2023,14,41,148,12,1,"&Amekkora szükséges" -3001,6,60,253,67,1,"Alapértelmezett nagyítás" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2024,14,72,151,12,1,"&Teljes igazítás (nincs görgetősáv)" 2025,14,84,183,12,1,"S&zélességhez igazítás (nincs vízszintes görgetősáv)" 2026,14,96,148,12,1,"&Eredeti méret" @@ -39,7 +39,7 @@ COMMENT,"Magyar változat" [DIALOG 2040] 265,150,1,"Haladó" -3006,6,5,253,69,1,"Bélyegképek a fájl panelekben" +3006,46,92,50,8,1,"Project site:" 3007,14,18,225,16,1,"Néhány kép hibás bélyegképet tartalmaz, melyek nem forgathatók helyesen vagy nem állíthatók vissza az utolsó módosítások." 2050,14,35,214,12,1,"Eredet&i bélyegképek, képbe tárolásának kihagyása (lassabb)" 3008,14,51,237,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Magyar változat" [DIALOG 2070] 265,150,1,"Eszközök" 3000,6,5,253,94,1,"Kijelölő eszköz" -3001,17,18,180,8,1,"A terület a Shift gomb nyomva tartásával jelölhető ki:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2071,24,27,203,12,1,"&Négyzet" 2072,24,40,218,12,1,"&4:3-as téglalap" 2073,24,53,218,12,1,"&3:2-es téglalap" 2074,24,66,218,12,1,"&16:9-es téglalap" 2075,24,79,35,12,1,"&Egyéb:" 2076,60,79,17,12,1,"" -3002,79,81,4,8,1,":" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2077,83,79,17,12,1,"" 2078,7,106,167,12,1,"He&xadecimális színfelvevő eszköz" [DIALOG 2080] 265,150,1,"Színek" 3000,6,5,253,50,1,"Ablak mód" -3001,17,19,103,8,1,"&Háttér" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2082,128,16,27,14,1,"" -3002,17,36,91,8,1,"Át&látszó" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2081,128,33,27,14,1,"" 3003,6,60,253,50,1,"Teljes képernyős mód" 3004,17,75,103,8,1,"Hátté&r" 2084,128,72,27,14,1,"" -3005,17,92,44,8,1,"Á&tlátszó" +3005,46,81,30,8,1,"Email:" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Magyar változat" 2103,65,8,100,12,1,"" 3000,7,24,49,8,1,"S&zéles:" 2104,65,22,100,12,1,"" -3001,7,38,49,8,1,"M&agas:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2105,65,36,100,12,1,"" -3002,7,52,42,8,1,"&Szín:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2106,65,50,100,12,1,"" 3003,7,66,51,8,1,"M&emóriában:" 2107,65,64,100,12,1,"" 3004,7,80,42,8,1,"&Fájlméret:" 2108,65,78,100,12,1,"" -3005,7,94,50,8,1,"&DPI:" +3005,46,81,30,8,1,"Email:" 2109,65,92,100,12,1,"" -3006,7,108,50,8,1,"Fo&rmátum:" +3006,46,92,50,8,1,"Project site:" 2110,65,106,100,12,1,"" 3007,7,122,50,8,1,"&Tömörítés:" 2111,65,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Magyar változat" 121,58,1,"Nagyítás" 3000,8,13,49,8,1,"&Nagyítás:" 2131,57,11,44,156,1,"" -3001,105,13,9,8,1,"%" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 1,7,37,50,14,1,"OK" 2,63,37,50,14,1,"Mégse" @@ -143,12 +143,12 @@ COMMENT,"Magyar változat" 2157,16,43,120,12,1,"A&blak előtere" 2156,16,55,135,12,1,"&Ablak előterének tartalma " 2159,16,67,142,12,1,"&Virtuális képernyő (több monitornál)" -3001,6,92,167,49,1,"Mentési jel" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2155,16,106,42,12,1,"&Gyorsbill" 2153,16,121,35,12,1,"I&dőzítő" 2154,59,106,77,12,1,"" 2151,59,121,21,12,1,"" -3002,84,123,39,8,1,"másodperc" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2152,8,147,85,12,1,"Egérmutató &is" 3003,2,167,166,1,1,"" 1,8,172,50,14,1,"&Mentés" @@ -167,7 +167,7 @@ COMMENT,"Magyar változat" 3000,4,14,54,8,1,"Felülírandó fájl:" 2181,62,14,286,8,1,"" 2182,62,24,286,8,1,"" -3001,4,37,54,8,1,"Ezzel:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2183,62,37,286,8,1,"" 2184,62,47,286,8,1,"" 6,93,66,50,14,1,"&Igen" @@ -179,9 +179,9 @@ COMMENT,"Magyar változat" 2300,5,2,49,8,1,"Tömöríté&s:" 2301,54,0,170,80,1,"" 2302,230,0,55,14,1,"Beállítás&ok" -3001,5,26,49,8,1,"S&zínmélység:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2303,54,24,70,80,1,"" -3002,5,46,49,8,1,"Fo&rgatás:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2304,54,44,70,80,1,"" 3003,5,66,49,8,1,"&Tükrözés:" 2305,54,64,70,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Magyar változat" 2514,232,15,118,8,1,"" 2515,354,12,50,14,1,"Beállítá&s..." 3000,225,35,186,62,1," Pozíció " -3001,233,49,27,8,1,"Ba&l:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2503,264,47,45,12,1,"" 2504,315,47,66,75,1,"" -3002,233,68,27,8,1,"&Fent:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2501,264,66,45,12,1,"" 2502,315,66,66,75,1,"" 2516,264,82,60,12,1,"Ké&p középre" 3004,225,102,186,100,1," Nyomtatási méret " -3005,233,118,27,8,1,"&Nyújt:" +3005,46,81,30,8,1,"Email:" 2505,264,116,35,12,1,"" -3006,302,118,10,8,1,"%" +3006,46,92,50,8,1,"Project site:" 2506,315,111,67,12,1,"N&yújtás" 2507,315,123,84,12,1,"&Képarány megtartása" 3007,233,139,27,8,1,"S&zéles:" @@ -362,7 +362,7 @@ COMMENT,"Magyar változat" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Hibás PVW32Cnv.DLL meghívás" +1009,1,"Error calling Windows Imaging Component (WIC)" 1010,1,"Vörös: %i, Zöld: %i, Kék: %i" 1011,1,"Vörös: %#02x, Zöld: %#02x, Kék: %#02x" 1012,1,"(%i, %i): Vörös: %i, Zöld: %i, Kék: %i" @@ -463,8 +463,8 @@ COMMENT,"Magyar változat" 1102,1,"Többi csatorna (O)" [STRINGTABLE 15] -1109,1,"A képfeldolgozó PVW32Cnv.dll nem tölthető be." -1110,1,"Rossz verziós PVW32Cnv.dll fájl." +1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." +1110,1,"An incompatible Windows Imaging Component (WIC) version was found." 1111,1,"Előbb válasszon egy területet." 1112,1,"Alapbeállítás" 1113,1,"A(z) "%s" fájl már létezik.\nLe akarja cserélni?" diff --git a/translations/romanian/pictview.slt b/translations/romanian/pictview.slt index c59f98123..c79946af7 100644 --- a/translations/romanian/pictview.slt +++ b/translations/romanian/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\romanian\pictview.atp" -TEXTVERSION,"2.13 (x86)" -VERSION,"2,1,3,180" +TEXTVERSION,"2.20 (x64)" +VERSION,"2,2,0,0" [TRANSLATION] LANGID,1048 @@ -15,13 +15,13 @@ COMMENT,"Versiunea romana" 2013,11,9,20,20,1,"" 2007,46,9,322,8,1,"PictView %s - Vizualizator imagini pentru Open Salamander" 2011,46,19,322,8,1,"" -3001,46,40,123,8,1,"Acest produs este bazat pe libraria" -2008,46,51,311,8,1,"%hs - Motor de Procesare a Imaginii" -3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" -3005,46,81,23,8,1,"Email:" -2009,91,81,85,8,1,"support@pictview.com" -3006,46,92,44,8,1,"Home page:" -2010,91,92,122,8,1,"www.pictview.com/salamander" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +2008,46,51,277,8,1,"%hs" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3005,46,81,30,8,1,"Email:" +2009,99,81,120,8,1,"support@opensalamander.org" +3006,46,92,50,8,1,"Project site:" +2010,99,92,122,8,1,"www.opensalamander.org" 3004,2,108,373,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Versiunea romana" 2021,14,17,151,12,1,"La fel ca Open Sala&mander" 2022,14,29,148,12,1,"Mai &larga daca e nevoie dar nu mai mica" 2023,14,41,148,12,1,"Cat e &nevoie" -3001,6,60,243,67,1,"Marire implicita" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2024,14,72,151,12,1,"Toata Imaginea (fara bare de de&filare)" 2025,14,84,161,12,1,"Cat latimea (fara &bara orizontala de defilare)" 2026,14,96,148,12,1,"Marime &originala" @@ -39,7 +39,7 @@ COMMENT,"Versiunea romana" [DIALOG 2040] 255,150,1,"Avansat" -3006,6,5,243,69,1,"Miniatura in panourile de fisiere" +3006,46,92,50,8,1,"Project site:" 3007,14,18,225,16,1,"Unele imagini pot contine o miniatura incorecta, care nu este rotita corect sau care nu reflecta ultimele schimbari." 2050,14,35,222,12,1,"&Ignora miniatura originala stocata in fisierul imagine (mai lent)" 3008,14,51,227,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Versiunea romana" [DIALOG 2070] 255,150,1,"Unelte" 3000,6,5,243,94,1,"Unealta de selectare" -3001,17,18,150,8,1,"Aria selectata cand tii apasat tasta Shift:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2071,24,27,203,12,1,"&Patrat" 2072,24,40,218,12,1,"&4:3 dreptunghi" 2073,24,53,218,12,1,"&3:2 dreptunghi" 2074,24,66,218,12,1,"&16:9 dreptunghi" 2075,24,79,35,12,1,"&Alta:" 2076,60,79,17,12,1,"" -3002,79,81,4,8,1,":" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2077,83,79,17,12,1,"" 2078,7,106,167,12,1,"Pipeta obtine culorile in he&xadecimal" [DIALOG 2080] 255,150,1,"Culori" 3000,6,5,243,50,1,"Mod fereasatra" -3001,17,19,103,8,1,"&Fundal" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2082,128,16,27,14,1,"" -3002,17,36,91,8,1,"&Transparent" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2081,128,33,27,14,1,"" 3003,6,60,243,50,1,"Mod pe tot ecranul" 3004,17,75,103,8,1,"Fund&al" 2084,128,72,27,14,1,"" -3005,17,92,44,8,1,"T&ransparent" +3005,46,81,30,8,1,"Email:" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Versiunea romana" 2103,82,8,100,12,1,"" 3000,7,24,49,8,1,"&Latime:" 2104,82,22,100,12,1,"" -3001,7,38,49,8,1,"&Inaltime:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2105,82,36,100,12,1,"" -3002,7,52,42,8,1,"&Culori:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2106,82,50,100,12,1,"" 3003,7,66,74,8,1,"&Marimea in memorie:" 2107,82,64,100,12,1,"" 3004,7,80,48,8,1,"Marime &fisier:" 2108,82,78,100,12,1,"" -3005,7,94,50,8,1,"&DPI:" +3005,46,81,30,8,1,"Email:" 2109,82,92,100,12,1,"" -3006,7,108,50,8,1,"Fo&rmat:" +3006,46,92,50,8,1,"Project site:" 2110,82,106,100,12,1,"" 3007,7,122,50,8,1,"C&ompresie:" 2111,82,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Versiunea romana" 121,58,1,"Mareste la" 3000,8,13,49,8,1,"&Marire:" 2131,57,11,44,156,1,"" -3001,105,13,9,8,1,"%" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 1,7,37,50,14,1,"OK" 2,63,37,50,14,1,"Anuleaza" @@ -143,12 +143,12 @@ COMMENT,"Versiunea romana" 2157,16,43,120,12,1,"&Fereastra din prim plan" 2156,16,55,146,12,1,"&Aria selectata a fereastrei din prim plan " 2159,16,67,163,12,1,"Ecranul &Virtual (pentru mai multe monitoare)" -3001,6,92,177,49,1,"Declansator Captura" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2155,16,106,38,12,1,"Ta&sta" 2153,16,121,53,12,1,"Ti&mp ramas" 2154,70,106,77,12,1,"" 2151,70,121,21,12,1,"" -3002,95,123,33,8,1,"secunde" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2152,8,147,85,12,1,"I&nclude cursor mouse" 3003,2,167,185,1,1,"" 1,13,172,50,14,1,"&Captura" @@ -167,7 +167,7 @@ COMMENT,"Versiunea romana" 3000,6,14,48,8,1,"Suprascrie:" 2181,58,14,286,8,1,"" 2182,58,24,286,8,1,"" -3001,9,37,45,8,1,"Cu Fisier:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2183,58,37,286,8,1,"" 2184,58,47,286,8,1,"" 6,91,66,50,14,1,"&Da" @@ -179,9 +179,9 @@ COMMENT,"Versiunea romana" 2300,5,2,49,8,1,"&Compresie:" 2301,54,0,170,80,1,"" 2302,230,0,50,14,1,"&Optiuni" -3001,5,26,65,8,1,"A&dancime culoare:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2303,71,24,75,80,1,"" -3002,5,46,61,8,1,"&Rotire:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2304,71,44,75,80,1,"" 3003,5,66,61,8,1,"&Flip/Oglinda:" 2305,71,64,75,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Versiunea romana" 2514,232,15,158,8,1,"" 2515,394,12,50,14,1,"Seta&re..." 3000,225,35,226,62,1," Pozitie " -3001,233,49,28,8,1,"Stan&ga:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2503,265,47,45,12,1,"" 2504,316,47,66,75,1,"" -3002,233,68,28,8,1,"&Sus:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2501,265,66,45,12,1,"" 2502,316,66,66,75,1,"" 2516,265,82,79,12,1,"&Centreaza imaginea" 3004,225,102,226,100,1," Marime Scalata a Imprimarii " -3005,230,118,31,8,1,"Scalar&e:" +3005,46,81,30,8,1,"Email:" 2505,265,116,35,12,1,"" -3006,303,118,10,8,1,"%" +3006,46,92,50,8,1,"Project site:" 2506,316,111,132,12,1,"Mareste sa se p&otriveasca pe media" 2507,316,123,86,12,1,"Mentine ra&port aspect" 3007,230,139,31,8,1,"&Latime:" @@ -362,7 +362,7 @@ COMMENT,"Versiunea romana" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Eroare apelare PVW32Cnv.DLL" +1009,1,"Error calling Windows Imaging Component (WIC)" 1010,1,"Rosu: %i, Verde: %i, Albastru: %i" 1011,1,"Rosu: %#02x, Verde: %#02x, Albastru: %#02x" 1012,1,"(%i, %i): Rosu: %i, Verde: %i, Albastru: %i" @@ -463,8 +463,8 @@ COMMENT,"Versiunea romana" 1102,1,"Arata Alte Canale (O)" [STRINGTABLE 15] -1109,1,"Nu pot incarca libraria de procesare imagine PVW32Cnv.dll." -1110,1,"A fost gasita o versiune gresita a PVW32Cnv.dll." +1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." +1110,1,"An incompatible Windows Imaging Component (WIC) version was found." 1111,1,"Mai intai selecteaza o regiune." 1112,1,"Implicit" 1113,1,"Fisierul "%s" exista deja.\nDoriti sa-l inlocuiti?" diff --git a/translations/russian/pictview.slt b/translations/russian/pictview.slt index 0060a8750..544193a8f 100644 --- a/translations/russian/pictview.slt +++ b/translations/russian/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\russian\pictview.atp" -TEXTVERSION,"2.13 (x86)" -VERSION,"2,1,3,180" +TEXTVERSION,"2.20 (x64)" +VERSION,"2,2,0,0" [TRANSLATION] LANGID,1049 @@ -15,13 +15,13 @@ COMMENT,"Русская версия" 2013,11,9,20,20,1,"" 2007,46,9,314,8,1,"PictView %s - Просмотр изображений Open Salamander" 2011,46,19,314,8,1,"" -3001,46,40,181,8,1,"Используется библиотека обработки изображений" -2008,46,51,303,8,1,"%hs" -3002,46,62,198,8,1,"© 1994-2023 Jan Patera" -3005,46,81,39,8,1,"Эл. почта:" -2009,86,81,85,8,1,"support@pictview.com" -3006,46,92,22,8,1,"Сайт:" -2010,86,92,122,8,1,"www.pictview.com/salamander" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +2008,46,51,277,8,1,"%hs" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3005,46,81,30,8,1,"Email:" +2009,99,81,120,8,1,"support@opensalamander.org" +3006,46,92,50,8,1,"Project site:" +2010,99,92,122,8,1,"www.opensalamander.org" 3004,2,108,365,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Русская версия" 2021,14,17,172,12,1,"&Совпадает с главным окном Open Salamander" 2022,14,29,219,12,1,"&Не меньше главного окна или больше при необходимости" 2023,14,41,148,12,1,"&Автоматический" -3001,6,60,317,67,1,"Масштаб по умолчанию" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2024,14,72,151,12,1,"&Полностью (без прокрутки)" 2025,14,84,169,12,1,"По &ширине (без горизонтальной прокрутки)" 2026,14,96,148,12,1,"&Реальный размер" @@ -39,7 +39,7 @@ COMMENT,"Русская версия" [DIALOG 2040] 329,150,1,"Дополнительно" -3006,6,5,317,69,1,"Эскизы на панелях" +3006,46,92,50,8,1,"Project site:" 3007,14,18,301,16,1,"Изображения могут содержать некорректные эскизы, которые могут быть неправильно повернуты или не соответствовать последней редакции изображения." 2050,14,35,263,12,1,"&Не использовать встроенные эскизы файлов изображений (медленно)" 3008,14,51,301,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Русская версия" [DIALOG 2070] 329,150,1,"Инструменты" 3000,6,5,317,94,1,"Выделение области" -3001,17,18,186,8,1,"При удерживании клавиши Shift выделять область:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2071,24,27,203,12,1,"&Квадрат" 2072,24,40,218,12,1,"Прямоугольник &4:3" 2073,24,53,218,12,1,"Прямоугольник &3:2" 2074,24,66,218,12,1,"Прямоугольник &16:9" 2075,24,79,40,12,1,"&Другой:" 2076,65,79,17,12,1,"" -3002,84,81,4,8,1,":" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2077,88,79,17,12,1,"" 2078,7,106,188,12,1,"Выбирать цвета в &шестнадцатеричных значениях" [DIALOG 2080] 329,150,1,"Цвета" 3000,6,5,317,50,1,"Оконный режим" -3001,17,19,103,8,1,"&Фон" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2082,128,16,27,14,1,"" -3002,17,36,91,8,1,"&Прозрачный" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2081,128,33,27,14,1,"" 3003,6,60,317,50,1,"Полноэкранный режим" 3004,17,75,103,8,1,"Ф&он" 2084,128,72,27,14,1,"" -3005,17,92,46,8,1,"П&розрачный" +3005,46,81,30,8,1,"Email:" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Русская версия" 2103,76,8,154,12,1,"" 3000,7,24,34,8,1,"&Ширина:" 2104,76,22,154,12,1,"" -3001,7,38,30,8,1,"&Высота:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2105,76,36,154,12,1,"" -3002,7,52,56,8,1,"&Глубина цвета:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2106,76,50,154,12,1,"" 3003,7,66,64,8,1,"&Размер в памяти:" 2107,76,64,154,12,1,"" 3004,7,80,55,8,1,"Ра&змер файла:" 2108,76,78,154,12,1,"" -3005,7,94,68,8,1,"Разрешен&ие (DPI):" +3005,46,81,30,8,1,"Email:" 2109,76,92,154,12,1,"" -3006,7,108,32,8,1,"&Формат:" +3006,46,92,50,8,1,"Project site:" 2110,76,106,154,12,1,"" 3007,7,122,31,8,1,"С&жатие:" 2111,76,120,154,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Русская версия" 121,58,1,"Масштаб" 3000,8,13,49,8,1,"&Масштаб:" 2131,57,11,44,156,1,"" -3001,105,13,9,8,1,"%" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 1,7,37,50,14,1,"ОК" 2,63,37,50,14,1,"Отмена" @@ -143,12 +143,12 @@ COMMENT,"Русская версия" 2157,16,43,120,12,1,"Активное &окно" 2156,16,55,141,12,1,"&Клиентская область активного окна " 2159,16,67,165,12,1,"В&иртуальный экран (несколько мониторов)" -3001,6,92,182,49,1,"Запуск" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2155,16,106,78,12,1,"&Сочетание клавиш" 2153,16,121,38,12,1,"&Таймер" 2154,98,106,77,12,1,"" 2151,98,121,21,12,1,"" -3002,123,123,33,8,1,"секунд" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2152,8,147,90,12,1,"Включая к&урсор мыши" 3003,2,167,190,1,1,"" 1,16,172,50,14,1,"&Захват" @@ -167,7 +167,7 @@ COMMENT,"Русская версия" 3000,4,14,59,8,1,"Заменить файл:" 2181,67,14,288,8,1,"" 2182,67,24,288,8,1,"" -3001,4,37,59,8,1,"Файлом:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2183,67,37,288,8,1,"" 2184,67,47,288,8,1,"" 6,96,66,50,14,1,"&Да" @@ -179,9 +179,9 @@ COMMENT,"Русская версия" 2300,5,2,47,8,1,"С&жатие:" 2301,54,0,190,80,1,"" 2302,250,0,53,14,1,"&Свойства" -3001,5,26,55,8,1,"Глубина &цвета:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2303,68,24,90,80,1,"" -3002,5,46,49,8,1,"Пово&рот:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2304,68,44,90,80,1,"" 3003,5,66,62,8,1,"О&тразить:" 2305,68,64,90,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Русская версия" 2514,232,15,132,8,1,"" 2515,370,12,57,14,1,"&Настройка..." 3000,225,35,208,62,1," Положение " -3001,232,49,53,8,1,"&Левое поле:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2503,288,47,45,12,1,"" 2504,339,47,66,75,1,"" -3002,232,68,53,8,1,"&Верхнее поле:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2501,288,66,45,12,1,"" 2502,339,66,66,75,1,"" 2516,288,82,63,12,1,"&Центрировать" 3004,225,102,208,100,1," Размер " -3005,232,118,53,8,1,"&Масштаб:" +3005,46,81,30,8,1,"Email:" 2505,288,116,35,12,1,"" -3006,326,118,10,8,1,"%" +3006,46,92,50,8,1,"Project site:" 2506,339,111,75,12,1,"По размеру л&иста" 2507,339,123,90,12,1,"&Сохранить пропорции" 3007,232,139,53,8,1,"&Ширина:" @@ -362,7 +362,7 @@ COMMENT,"Русская версия" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Ошибка вызова библиотеки "pvw32cnv.dll"" +1009,1,"Error calling Windows Imaging Component (WIC)" 1010,1,"Красный: %i, Зеленый: %i, Синий: %i" 1011,1,"Красный: %#02x, Зеленый: %#02x, Синий: %#02x" 1012,1,"(%i, %i): Красный: %i, Зеленый: %i, Синий: %i" @@ -463,8 +463,8 @@ COMMENT,"Русская версия" 1102,1,"Другие каналы (O)" [STRINGTABLE 15] -1109,1,"Не удается загрузить библиотеку обработки изображений "pvw32cnv.dll"." -1110,1,"Несоответствующая версия библиотеки "pvw32cnv.dll"." +1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." +1110,1,"An incompatible Windows Imaging Component (WIC) version was found." 1111,1,"Пожалуйста, выберите область." 1112,1,"По умолчанию" 1113,1,"Файл "%s" уже существует.\nЗаменить файл?" diff --git a/translations/slovak/pictview.slt b/translations/slovak/pictview.slt index 3fbae3887..490b30dee 100644 --- a/translations/slovak/pictview.slt +++ b/translations/slovak/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\slovak\pictview.atp" -TEXTVERSION,"2.13 (x86)" -VERSION,"2,1,3,180" +TEXTVERSION,"2.20 (x64)" +VERSION,"2,2,0,0" [TRANSLATION] LANGID,1051 @@ -15,13 +15,13 @@ COMMENT,"Slovenská verzia" 2013,11,9,20,20,1,"" 2007,46,9,352,8,1,"Prehliadač PictView %s - Prehliadač obrázkov pre Open Salamander" 2011,46,19,352,8,1,"" -3001,46,40,123,8,1,"Tento produkt využíva knižnicu" -2008,46,51,341,8,1,"%hs - Image Processing Engine" -3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +2008,46,51,277,8,1,"%hs" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,113,81,85,8,1,"support@pictview.com" -3006,46,92,66,8,1,"Domovská stránka:" -2010,113,92,122,8,1,"www.pictview.com/cz/salamander" +2009,99,81,120,8,1,"support@opensalamander.org" +3006,46,92,50,8,1,"Project site:" +2010,99,92,122,8,1,"www.opensalamander.org" 3004,2,108,403,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Slovenská verzia" 2021,14,17,151,12,1,"&Rovnaká ako Open Salamandera" 2022,14,29,148,12,1,"Vä&čšia, ak je potrebné, ale nie menšia" 2023,14,41,148,12,1,"Pod&ľa potreby" -3001,6,60,248,67,1,"Predvolený spôsob zväčšenia" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2024,14,72,151,12,1,"&Umiestniť do okna (žiadne posuvníky)" 2025,14,84,174,12,1,"U&miestniť na šírku (bez vodorovného posuvníka)" 2026,14,96,148,12,1,"&Pôvodná veľkosť" @@ -39,7 +39,7 @@ COMMENT,"Slovenská verzia" [DIALOG 2040] 260,150,1,"Rozšírené" -3006,6,5,248,69,1,"Miniatúry v súborových paneloch" +3006,46,92,50,8,1,"Project site:" 3007,14,18,231,16,1,"Niektoré súbory s obrázkami môžu obsahovať neaktuálnu miniatúru, ktorá je zle otočená alebo nereflektuje posledné úpravy obrázka." 2050,14,35,205,12,1,"&Ignorovať miniatúry uložené priamo v súboroch (pomalé)" 3008,14,51,232,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Slovenská verzia" [DIALOG 2070] 260,150,1,"Nástroje" 3000,6,5,248,94,1,"Výber" -3001,17,18,150,8,1,"Oblasť vyberaná pri stlačení klávesu Shift:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2071,24,27,203,12,1,"Štvore&c" 2072,24,40,218,12,1,"Obdĺžnik &4:3" 2073,24,53,218,12,1,"Obdĺžnik &3:2" 2074,24,66,218,12,1,"Obdĺžnik &16:9" 2075,24,79,35,12,1,"&Iná:" 2076,60,79,17,12,1,"" -3002,79,81,4,8,1,":" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2077,83,79,17,12,1,"" 2078,7,106,167,12,1,"Ukazovať farby pipetou he&xadecimálne" [DIALOG 2080] 260,150,1,"Farby" 3000,6,5,248,50,1,"Zobrazenie v okne" -3001,17,19,103,8,1,"&Pozadie" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2082,128,16,27,14,1,"" -3002,17,36,91,8,1,"Priehľadná &farba" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2081,128,33,27,14,1,"" 3003,6,60,248,50,1,"Zobrazenie cez celú obrazovku" 3004,17,75,103,8,1,"P&ozadie" 2084,128,72,27,14,1,"" -3005,17,92,59,8,1,"Priehľadná f&arba" +3005,46,81,30,8,1,"Email:" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Slovenská verzia" 2103,69,8,100,12,1,"" 3000,7,24,49,8,1,"Šír&ka:" 2104,69,22,100,12,1,"" -3001,7,38,49,8,1,"&Výška:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2105,69,36,100,12,1,"" -3002,7,52,44,8,1,"Počet &farieb:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2106,69,50,100,12,1,"" 3003,7,66,61,8,1,"Veľkosť (&pamäť):" 2107,69,64,100,12,1,"" 3004,7,80,52,8,1,"Veľkosť (d&isk):" 2108,69,78,100,12,1,"" -3005,7,94,50,8,1,"&DPI:" +3005,46,81,30,8,1,"Email:" 2109,69,92,100,12,1,"" -3006,7,108,50,8,1,"Fo&rmát:" +3006,46,92,50,8,1,"Project site:" 2110,69,106,100,12,1,"" 3007,7,122,50,8,1,"K&ompresia:" 2111,69,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Slovenská verzia" 121,58,1,"Zväčšenie obrázku" 3000,8,13,49,8,1,"&Pomer:" 2131,57,11,44,156,1,"" -3001,105,13,9,8,1,"%" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 1,7,37,50,14,1,"OK" 2,63,37,50,14,1,"Zrušiť" @@ -143,12 +143,12 @@ COMMENT,"Slovenská verzia" 2157,16,43,120,12,1,"&Aktívne okno" 2156,16,55,135,12,1,"&Klientsku oblasť aktívneho okna " 2159,16,67,151,12,1,"&Virtuálnu obrazovku (pri viac monitoroch)" -3001,6,92,167,49,1,"Spúšť" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2155,16,106,57,12,1,"&Horúci kláves" 2153,16,121,40,12,1,"Čas&ovač" 2154,74,106,91,12,1,"" 2151,74,121,21,12,1,"" -3002,99,123,33,8,1,"sekúnd" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2152,8,147,85,12,1,"Vrátane kurzoru &myši" 3003,2,167,175,1,1,"" 1,8,172,50,14,1,"&Snímať" @@ -167,7 +167,7 @@ COMMENT,"Slovenská verzia" 3000,6,14,76,8,1,"Má sa prepísať súbor:" 2181,84,14,286,8,1,"" 2182,84,24,286,8,1,"" -3001,6,37,76,8,1,"týmto súborom:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2183,84,37,286,8,1,"" 2184,84,47,286,8,1,"" 6,104,66,50,14,1,"&Áno" @@ -179,9 +179,9 @@ COMMENT,"Slovenská verzia" 2300,5,2,40,8,1,"&Kompresia:" 2301,51,0,172,80,1,"" 2302,229,0,50,14,1,"&Možnosti" -3001,5,26,45,8,1,"Počet &farieb:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2303,51,24,79,80,1,"" -3002,5,46,34,8,1,"&Otočenie:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2304,51,44,79,80,1,"" 3003,5,66,43,8,1,"&Zrkadlenie:" 2305,51,64,79,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Slovenská verzia" 2514,232,15,118,8,1,"" 2515,354,12,60,14,1,"&Nastavenie..." 3000,225,35,196,62,1," Umiestnenie na papieri " -3001,234,49,24,8,1,"V&ľavo:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2503,262,47,45,12,1,"" 2504,313,47,66,75,1,"" -3002,234,68,24,8,1,"Z&hora:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2501,262,66,45,12,1,"" 2502,313,66,66,75,1,"" 2516,262,82,60,12,1,"&Centrovať" 3004,225,102,196,100,1," Prispôsobiť veľkosť " -3005,233,118,25,8,1,"&Pomer:" +3005,46,81,30,8,1,"Email:" 2505,262,116,35,12,1,"" -3006,300,118,10,8,1,"%" +3006,46,92,50,8,1,"Project site:" 2506,313,111,77,12,1,"Prispôsobiť p&apieru" 2507,313,123,87,12,1,"&Zachovať pomer strán" 3007,233,139,25,8,1,"Šír&ka:" @@ -362,7 +362,7 @@ COMMENT,"Slovenská verzia" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Chyba pri volaní PVW32Cnv.DLL" +1009,1,"Error calling Windows Imaging Component (WIC)" 1010,1,"Červená: %i, Zelená: %i, Modrá: %i" 1011,1,"Červená: %#02x, Zelená: %#02x, Modrá: %#02x" 1012,1,"(%i, %i): Červená: %i, Zelená: %i, Modrá: %i" @@ -463,8 +463,8 @@ COMMENT,"Slovenská verzia" 1102,1,"Ukázať ostatné kanály (O)" [STRINGTABLE 15] -1109,1,"Nie je možné nájsť knižnicu PVW32Cnv.dll na spracovanie obrázkov." -1110,1,"Bola nájdená zlá verzia knižnice PVW32Cnv.dll." +1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." +1110,1,"An incompatible Windows Imaging Component (WIC) version was found." 1111,1,"Nie je vybraná žiadna oblasť." 1112,1,"Predvolené" 1113,1,"Súbor "%s" úž existuje.\nMá sa prepísať?" diff --git a/translations/spanish/pictview.slt b/translations/spanish/pictview.slt index 52ac47686..c155c310c 100644 --- a/translations/spanish/pictview.slt +++ b/translations/spanish/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\spanish\pictview.atp" -TEXTVERSION,"2.13 (x86)" -VERSION,"2,1,3,180" +TEXTVERSION,"2.20 (x64)" +VERSION,"2,2,0,0" [TRANSLATION] LANGID,3082 @@ -15,13 +15,13 @@ COMMENT,"Versión en español" 2013,11,9,20,20,1,"" 2007,46,9,308,8,1,"PictView %s - Visor de imágenes para Open Salamander" 2011,46,19,308,8,1,"" -3001,46,40,129,8,1,"Este producto se basa en la biblioteca" -2008,46,51,297,8,1,"%hs - Motor de tratamiento de imágenes" -3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +2008,46,51,277,8,1,"%hs" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,91,81,85,8,1,"support@pictview.com" -3006,46,92,44,8,1,"Página web:" -2010,91,92,122,8,1,"www.pictview.com/salamander" +2009,99,81,120,8,1,"support@opensalamander.org" +3006,46,92,50,8,1,"Project site:" +2010,99,92,122,8,1,"www.opensalamander.org" 3004,2,108,359,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Versión en español" 2021,14,17,151,12,1,"&Como Open Salamander" 2022,14,29,148,12,1,"&Mayor si es necesario pero no menor" 2023,14,41,148,12,1,"&Según sea necesario" -3001,6,60,253,67,1,"Tamaño por defecto" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2024,14,72,151,12,1,"&Ajustar entero (sin barras)" 2025,14,84,148,12,1,"Ajustar al a&ncho (sin barra horizontal)" 2026,14,96,148,12,1,"Tamaño &original" @@ -39,7 +39,7 @@ COMMENT,"Versión en español" [DIALOG 2040] 265,150,1,"Avanzado" -3006,6,5,253,69,1,"Miniaturas en paneles de ficheros" +3006,46,92,50,8,1,"Project site:" 3007,14,18,225,16,1,"Algunas imágenes pueden contener miniatura imprecisa, no rotada adecuadamente o no reflejar los últimos cambios." 2050,14,35,231,12,1,"&Ignorar miniaturas originales incluidas en ficheros imagen (lento)" 3008,14,51,237,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Versión en español" [DIALOG 2070] 265,150,1,"Herramientas" 3000,6,5,253,94,1,"Herramienta seleccionar" -3001,17,18,183,8,1,"Area seleccionada cuando se mantiene pulsada Mays:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2071,24,27,203,12,1,"&Cuadrado" 2072,24,40,218,12,1,"Rectángulo &4:3" 2073,24,53,218,12,1,"Rectángulo &3:2" 2074,24,66,218,12,1,"Rectángulo &16:9" 2075,24,79,30,12,1,"&Otra:" 2076,56,79,17,12,1,"" -3002,75,81,4,8,1,":" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2077,79,79,17,12,1,"" 2078,7,106,167,12,1,"La pipeta recoge colores en he&xadecimal" [DIALOG 2080] 265,150,1,"Colores" 3000,6,5,253,50,1,"Modo ventana" -3001,17,19,103,8,1,"&Fondo" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2082,128,16,27,14,1,"" -3002,17,36,91,8,1,"&Transparente" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2081,128,33,27,14,1,"" 3003,6,60,253,50,1,"Modo pantalla completa" 3004,17,75,103,8,1,"F&ondo" 2084,128,72,27,14,1,"" -3005,17,92,46,8,1,"T&ransparente" +3005,46,81,30,8,1,"Email:" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Versión en español" 2103,72,8,100,12,1,"" 3000,7,24,49,8,1,"&Anchura:" 2104,72,22,100,12,1,"" -3001,7,38,49,8,1,"A<ura:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2105,72,36,100,12,1,"" -3002,7,52,42,8,1,"&Colores:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2106,72,50,100,12,1,"" 3003,7,66,64,8,1,"&Tamaño memoria:" 2107,72,64,100,12,1,"" 3004,7,80,58,8,1,"Tamaño &fichero:" 2108,72,78,100,12,1,"" -3005,7,94,50,8,1,"&DPI:" +3005,46,81,30,8,1,"Email:" 2109,72,92,100,12,1,"" -3006,7,108,50,8,1,"Fo&rmato:" +3006,46,92,50,8,1,"Project site:" 2110,72,106,100,12,1,"" 3007,7,122,50,8,1,"C&ompresión:" 2111,72,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Versión en español" 121,58,1,"Cambiar visualización" 3000,8,13,49,8,1,"&Aumento:" 2131,57,11,44,156,1,"" -3001,105,13,9,8,1,"%" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 1,7,37,50,14,1,"Aceptar" 2,63,37,50,14,1,"Cancelar" @@ -143,12 +143,12 @@ COMMENT,"Versión en español" 2157,16,43,120,12,1,"Ve&ntana en primer plano" 2156,16,55,150,12,1,"&Area cliente de ventana en primer plano " 2159,16,67,142,12,1,"Pantalla &virtual (para varios monitores)" -3001,6,92,167,49,1,"Disparador de captura" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2155,16,106,54,12,1,"&Tecla rápida" 2153,16,121,60,12,1,"T&emporizador" 2154,77,106,77,12,1,"" 2151,77,121,21,12,1,"" -3002,102,123,33,8,1,"segundos" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2152,8,147,89,12,1,"&Incluir cursor del ratón" 3003,3,167,174,1,1,"" 1,8,172,50,14,1,"&Capturar" @@ -167,7 +167,7 @@ COMMENT,"Versión en español" 3000,6,14,48,8,1,"Reemplazar:" 2181,58,14,286,8,1,"" 2182,58,24,286,8,1,"" -3001,9,37,45,8,1,"Con fichero:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2183,58,37,286,8,1,"" 2184,58,47,286,8,1,"" 6,91,66,50,14,1,"&Sí" @@ -179,9 +179,9 @@ COMMENT,"Versión en español" 2300,5,2,49,8,1,"&Compresión:" 2301,54,0,180,80,1,"" 2302,240,0,50,14,1,"&Opciones" -3001,5,26,52,8,1,"&Profund. color:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2303,58,24,80,80,1,"" -3002,5,46,52,8,1,"&Rotación:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2304,58,44,80,80,1,"" 3003,5,66,52,8,1,"&Trasp./espejo:" 2305,58,64,80,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Versión en español" 2514,232,15,126,8,1,"" 2515,362,12,50,14,1,"&Disponer..." 3000,225,35,194,62,1," Posición " -3001,232,49,26,8,1,"&Izda:" +3001,46,40,190,8,1,"This plugin uses the following imaging backend:" 2503,262,47,45,12,1,"" 2504,313,47,66,75,1,"" -3002,232,68,26,8,1,"&Sup:" +3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 2501,262,66,45,12,1,"" 2502,313,66,66,75,1,"" 2516,262,82,65,12,1,"Ce&ntrar imagen" 3004,225,102,194,100,1," Tamaño escalado " -3005,232,118,26,8,1,"&Escala:" +3005,46,81,30,8,1,"Email:" 2505,262,116,35,12,1,"" -3006,300,118,10,8,1,"%" +3006,46,92,50,8,1,"Project site:" 2506,313,111,101,12,1,"Escalar: a&justar al soporte" 2507,313,123,101,12,1,"&Mantener relación aspecto" 3007,232,139,26,8,1,"&Ancho:" @@ -362,7 +362,7 @@ COMMENT,"Versión en español" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Error llamando a PVW32Cnv.DLL" +1009,1,"Error calling Windows Imaging Component (WIC)" 1010,1,"Rojo: %i, Verde: %i, Azul: %i" 1011,1,"Rojo: %#02x, Verde: %#02x, Azul: %#02x" 1012,1,"(%i, %i): Rojo: %i, Verde: %i, Azul: %i" @@ -463,8 +463,8 @@ COMMENT,"Versión en español" 1102,1,"Ver otros canales (O)" [STRINGTABLE 15] -1109,1,"Imposible cargar la biblioteca de procesado de imágenes PVW32Cnv.dll." -1110,1,"Se encontró una versión errónea de PVW32Cnv.dll." +1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." +1110,1,"An incompatible Windows Imaging Component (WIC) version was found." 1111,1,"Seleccione una región primero." 1112,1,"Por defecto" 1113,1,"El fichero "%s" ya existe.\n¿Desea reemplazarlo?" From c0836a269d0745b73758b6ec2f07b0ec6c922913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kotas?= Date: Tue, 7 Oct 2025 01:31:36 +0200 Subject: [PATCH 04/14] revert unwanted changes I overlooked a few changes that the Codex introduced, but I didn't want to change this. --- src/plugins/pictview/lang/lang.rc | 4 ++-- src/plugins/pictview/pictview.cpp | 4 ++-- src/plugins/pictview/versinfo.rh2 | 2 +- src/vcxproj/salamand.sln | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/plugins/pictview/lang/lang.rc b/src/plugins/pictview/lang/lang.rc index a25f491fb..c8c95b2b3 100644 --- a/src/plugins/pictview/lang/lang.rc +++ b/src/plugins/pictview/lang/lang.rc @@ -81,9 +81,9 @@ BEGIN 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@opensalamander.org",IDC_ABOUT_EMAIL,99,81,120,8,WS_TABSTOP + LTEXT "support@pictview.com",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 + LTEXT "www.pictview.com/salamander",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/pictview.cpp b/src/plugins/pictview/pictview.cpp index 82ae5050e..b3a29e019 100644 --- a/src/plugins/pictview/pictview.cpp +++ b/src/plugins/pictview/pictview.cpp @@ -533,7 +533,7 @@ CPluginInterfaceAbstract* WINAPI SalamanderPluginEntry(CSalamanderPluginEntryAbs _T("PictView") /* do not translate! */); // set the plugin home page URL - salamander->SetPluginHomePageURL("www.opensalamander.org"); + salamander->SetPluginHomePageURL("www.pictview.com/salamander"); // If we crash inside pictview.spl, this message box will be displayed // and the happy recipient of the images will be Honza Patera. @@ -541,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@opensalamander.org"); + SalamanderGeneral->SetPluginBugReportInfo(exceptInfo, "support@pictview.com"); return &PluginInterface; } diff --git a/src/plugins/pictview/versinfo.rh2 b/src/plugins/pictview/versinfo.rh2 index 7ac645d5f..bf8c1dd77 100644 --- a/src/plugins/pictview/versinfo.rh2 +++ b/src/plugins/pictview/versinfo.rh2 @@ -31,7 +31,7 @@ #endif // for SLG translators -#define VERSINFO_SLG_WEB "www.opensalamander.org" +#define VERSINFO_SLG_WEB "www.altap.cz" #define VERSINFO_SLG_COMMENT "English version" #endif // __PICTVIEW_VERSINFO_RH2 diff --git a/src/vcxproj/salamand.sln b/src/vcxproj/salamand.sln index ada297509..543edd15d 100644 --- a/src/vcxproj/salamand.sln +++ b/src/vcxproj/salamand.sln @@ -616,16 +616,16 @@ Global {BDD8F231-0C97-46D1-8DFC-7649741B7028}.Release|x64.Build.0 = Release|x64 {BDD8F231-0C97-46D1-8DFC-7649741B7028}.Utils (Release)|Win32.ActiveCfg = Release|Win32 {BDD8F231-0C97-46D1-8DFC-7649741B7028}.Utils (Release)|x64.ActiveCfg = Release|x64 - {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Debug|Win32.ActiveCfg = Debug|x64 - {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Debug|Win32.Build.0 = Debug|x64 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Debug|Win32.ActiveCfg = Debug|Win32 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Debug|Win32.Build.0 = Debug|Win32 {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Debug|x64.ActiveCfg = Debug|x64 {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Debug|x64.Build.0 = Debug|x64 - {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Release|Win32.ActiveCfg = Release|x64 - {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Release|Win32.Build.0 = Release|x64 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Release|Win32.ActiveCfg = Release|Win32 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Release|Win32.Build.0 = Release|Win32 {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Release|x64.ActiveCfg = Release|x64 {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Release|x64.Build.0 = Release|x64 - {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|Win32.ActiveCfg = Release|x64 - {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|Win32.Build.0 = Release|x64 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|Win32.ActiveCfg = Release|Win32 + {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|Win32.Build.0 = Release|Win32 {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|x64.ActiveCfg = Release|x64 {17CF5E04-F29C-4E5D-BA41-83254E342E0B}.Utils (Release)|x64.Build.0 = Release|x64 {C3D5458E-BF71-4C7D-AF45-F502A14EB528}.Debug|Win32.ActiveCfg = Debug|Win32 From dc0599f2a047525b9acfd9bf4e5a580cf55388ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kotas?= Date: Tue, 7 Oct 2025 01:37:02 +0200 Subject: [PATCH 05/14] revert unwanted changes I overlooked a few changes that the Codex introduced, but I didn't want to change this. --- translations/chinesesimplified/pictview.slt | 4 ++-- translations/czech/pictview.slt | 4 ++-- translations/dutch/pictview.slt | 4 ++-- translations/french/pictview.slt | 4 ++-- translations/german/pictview.slt | 4 ++-- translations/hungarian/pictview.slt | 4 ++-- translations/romanian/pictview.slt | 4 ++-- translations/russian/pictview.slt | 4 ++-- translations/slovak/pictview.slt | 4 ++-- translations/spanish/pictview.slt | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/translations/chinesesimplified/pictview.slt b/translations/chinesesimplified/pictview.slt index bc79834ef..82343bf1f 100644 --- a/translations/chinesesimplified/pictview.slt +++ b/translations/chinesesimplified/pictview.slt @@ -19,9 +19,9 @@ COMMENT,"简体中文版" 2008,46,51,277,8,1,"%hs" 3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@opensalamander.org" +2009,99,81,120,8,1,"support@pictview.com" 3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.opensalamander.org" +2010,99,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,339,1,1,"" [DIALOG 2020] diff --git a/translations/czech/pictview.slt b/translations/czech/pictview.slt index 01fd63dc7..17d321108 100644 --- a/translations/czech/pictview.slt +++ b/translations/czech/pictview.slt @@ -19,9 +19,9 @@ COMMENT,"Česká verze" 2008,46,51,277,8,1,"%hs" 3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@opensalamander.org" +2009,99,81,120,8,1,"support@pictview.com" 3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.opensalamander.org" +2010,99,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,391,1,1,"" [DIALOG 2020] diff --git a/translations/dutch/pictview.slt b/translations/dutch/pictview.slt index 767846eb8..334894a8d 100644 --- a/translations/dutch/pictview.slt +++ b/translations/dutch/pictview.slt @@ -19,9 +19,9 @@ COMMENT,"Nederlandse versie" 2008,46,51,277,8,1,"%hs" 3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@opensalamander.org" +2009,99,81,120,8,1,"support@pictview.com" 3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.opensalamander.org" +2010,99,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,360,1,1,"" [DIALOG 2020] diff --git a/translations/french/pictview.slt b/translations/french/pictview.slt index 5281eb9bc..a261cc266 100644 --- a/translations/french/pictview.slt +++ b/translations/french/pictview.slt @@ -19,9 +19,9 @@ COMMENT,"Version française" 2008,46,51,277,8,1,"%hs" 3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@opensalamander.org" +2009,99,81,120,8,1,"support@pictview.com" 3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.opensalamander.org" +2010,99,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,364,1,1,"" [DIALOG 2020] diff --git a/translations/german/pictview.slt b/translations/german/pictview.slt index 038583799..4cfc56187 100644 --- a/translations/german/pictview.slt +++ b/translations/german/pictview.slt @@ -19,9 +19,9 @@ COMMENT,"Deutsche Version" 2008,46,51,277,8,1,"%hs" 3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@opensalamander.org" +2009,99,81,120,8,1,"support@pictview.com" 3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.opensalamander.org" +2010,99,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,385,1,1,"" [DIALOG 2020] diff --git a/translations/hungarian/pictview.slt b/translations/hungarian/pictview.slt index 6b9779343..c3791d953 100644 --- a/translations/hungarian/pictview.slt +++ b/translations/hungarian/pictview.slt @@ -19,9 +19,9 @@ COMMENT,"Magyar változat" 2008,46,51,277,8,1,"%hs" 3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@opensalamander.org" +2009,99,81,120,8,1,"support@pictview.com" 3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.opensalamander.org" +2010,99,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,341,1,1,"" [DIALOG 2020] diff --git a/translations/romanian/pictview.slt b/translations/romanian/pictview.slt index c79946af7..4aa3e3425 100644 --- a/translations/romanian/pictview.slt +++ b/translations/romanian/pictview.slt @@ -19,9 +19,9 @@ COMMENT,"Versiunea romana" 2008,46,51,277,8,1,"%hs" 3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@opensalamander.org" +2009,99,81,120,8,1,"support@pictview.com" 3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.opensalamander.org" +2010,99,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,373,1,1,"" [DIALOG 2020] diff --git a/translations/russian/pictview.slt b/translations/russian/pictview.slt index 544193a8f..174061c28 100644 --- a/translations/russian/pictview.slt +++ b/translations/russian/pictview.slt @@ -19,9 +19,9 @@ COMMENT,"Русская версия" 2008,46,51,277,8,1,"%hs" 3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@opensalamander.org" +2009,99,81,120,8,1,"support@pictview.com" 3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.opensalamander.org" +2010,99,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,365,1,1,"" [DIALOG 2020] diff --git a/translations/slovak/pictview.slt b/translations/slovak/pictview.slt index 490b30dee..a2dc2014a 100644 --- a/translations/slovak/pictview.slt +++ b/translations/slovak/pictview.slt @@ -19,9 +19,9 @@ COMMENT,"Slovenská verzia" 2008,46,51,277,8,1,"%hs" 3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@opensalamander.org" +2009,99,81,120,8,1,"support@pictview.com" 3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.opensalamander.org" +2010,99,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,403,1,1,"" [DIALOG 2020] diff --git a/translations/spanish/pictview.slt b/translations/spanish/pictview.slt index c155c310c..254e9f902 100644 --- a/translations/spanish/pictview.slt +++ b/translations/spanish/pictview.slt @@ -19,9 +19,9 @@ COMMENT,"Versión en español" 2008,46,51,277,8,1,"%hs" 3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@opensalamander.org" +2009,99,81,120,8,1,"support@pictview.com" 3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.opensalamander.org" +2010,99,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,359,1,1,"" [DIALOG 2020] From 6c1ffb2d9854fb10e0916a2adc7653b5dee2114e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kotas?= Date: Tue, 7 Oct 2025 02:07:24 +0200 Subject: [PATCH 06/14] Handle GIF frame rects and disposal metadata in WIC backend --- src/plugins/pictview/wic/WicBackend.cpp | 102 ++++++++++++++++++++++-- src/plugins/pictview/wic/WicBackend.h | 2 + 2 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/plugins/pictview/wic/WicBackend.cpp b/src/plugins/pictview/wic/WicBackend.cpp index 701603b0a..a7fc71525 100644 --- a/src/plugins/pictview/wic/WicBackend.cpp +++ b/src/plugins/pictview/wic/WicBackend.cpp @@ -26,6 +26,7 @@ #pragma comment(lib, "windowscodecs.lib") #pragma comment(lib, "shlwapi.lib") +#pragma comment(lib, "propsys.lib") using namespace std::string_literals; @@ -41,6 +42,9 @@ 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); +bool TryReadUnsignedMetadata(IWICMetadataQueryReader* reader, LPCWSTR name, UINT& value); +DWORD MapGifDisposalToPv(UINT disposal); +LONG ClampUnsignedToLong(ULONGLONG value); std::mutex g_errorMutex; std::unordered_map g_errorTexts = { @@ -351,6 +355,34 @@ bool TryReadDelayHundredths(IWICMetadataQueryReader* reader, LPCWSTR name, UINT& return extracted; } +bool TryReadUnsignedMetadata(IWICMetadataQueryReader* reader, LPCWSTR name, UINT& value) +{ + if (!reader || !name) + { + return false; + } + + PROPVARIANT rawValue; + PropVariantInit(&rawValue); + const HRESULT hr = reader->GetMetadataByName(name, &rawValue); + if (FAILED(hr)) + { + PropVariantClear(&rawValue); + return false; + } + + UINT extracted = 0; + const HRESULT convertHr = PropVariantToUInt32(rawValue, &extracted); + PropVariantClear(&rawValue); + if (FAILED(convertHr)) + { + return false; + } + + value = extracted; + return true; +} + DWORD ClampDelayHundredthsToMilliseconds(UINT hundredths) { if (hundredths == 0) @@ -392,6 +424,21 @@ DWORD GetFrameDelayMilliseconds(IWICBitmapFrameDecode* frame) return 0; } +DWORD MapGifDisposalToPv(UINT disposal) +{ + switch (disposal & 0x7u) + { + case 1: + return PVDM_UNMODIFIED; + case 2: + return PVDM_BACKGROUND; + case 3: + return PVDM_PREVIOUS; + default: + return PVDM_UNDEFINED; + } +} + const char* LookupError(DWORD code) { std::lock_guard lock(g_errorMutex); @@ -417,6 +464,13 @@ ULONGLONG AbsoluteDimension(LONGLONG value) return static_cast(-(value + 1)) + 1; } +LONG ClampUnsignedToLong(ULONGLONG value) +{ + return value > static_cast(std::numeric_limits::max()) + ? std::numeric_limits::max() + : static_cast(value); +} + DWORD ClampToDword(ULONGLONG value) { return value > static_cast(std::numeric_limits::max()) @@ -542,6 +596,12 @@ HRESULT PopulateFrameFromBitmapHandle(FrameData& frame, HBITMAP bitmap) } } + frame.rect.left = 0; + frame.rect.top = 0; + frame.rect.right = ClampUnsignedToLong(static_cast(width)); + frame.rect.bottom = ClampUnsignedToLong(static_cast(height)); + frame.disposal = PVDM_UNDEFINED; + hr = FinalizeDecodedFrame(frame); if (FAILED(hr)) { @@ -1405,6 +1465,41 @@ HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& { data.delayMs = 100; } + data.rect.left = 0; + data.rect.top = 0; + data.rect.right = ClampUnsignedToLong(static_cast(width)); + data.rect.bottom = ClampUnsignedToLong(static_cast(height)); + data.disposal = PVDM_UNDEFINED; + + ULONGLONG left64 = 0; + ULONGLONG top64 = 0; + bool leftSpecified = false; + bool topSpecified = false; + ComPtr frameQuery; + if (SUCCEEDED(data.frame->GetMetadataQueryReader(&frameQuery)) && frameQuery) + { + UINT value = 0; + if (TryReadUnsignedMetadata(frameQuery.Get(), L"/imgdesc/Left", value)) + { + left64 = value; + leftSpecified = true; + data.rect.left = ClampUnsignedToLong(left64); + } + if (TryReadUnsignedMetadata(frameQuery.Get(), L"/imgdesc/Top", value)) + { + top64 = value; + topSpecified = true; + data.rect.top = ClampUnsignedToLong(top64); + } + if (TryReadUnsignedMetadata(frameQuery.Get(), L"/grctlext/Disposal", value)) + { + data.disposal = MapGifDisposalToPv(value); + } + } + const ULONGLONG right64 = (leftSpecified ? left64 : 0ull) + static_cast(data.width); + const ULONGLONG bottom64 = (topSpecified ? top64 : 0ull) + static_cast(data.height); + data.rect.right = ClampUnsignedToLong(right64); + data.rect.bottom = ClampUnsignedToLong(bottom64); if (!hasExif && FrameContainsExif(data.frame.Get())) { hasExif = true; @@ -1581,12 +1676,9 @@ PVCODE CreateSequenceNodes(ImageHandle& handle, LPPVImageSequence* seq) } 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->Rect = frame.rect; node->Delay = frame.delayMs; - node->DisposalMethod = PVDM_UNDEFINED; + node->DisposalMethod = frame.disposal; node->ImgHandle = frame.hbitmap; node->TransparentHandle = nullptr; *tail = node.release(); diff --git a/src/plugins/pictview/wic/WicBackend.h b/src/plugins/pictview/wic/WicBackend.h index 13877b020..44f5e1aa6 100644 --- a/src/plugins/pictview/wic/WicBackend.h +++ b/src/plugins/pictview/wic/WicBackend.h @@ -38,6 +38,8 @@ struct FrameData BITMAPINFOHEADER bmi{}; HBITMAP hbitmap = nullptr; DWORD delayMs = 0; + RECT rect{}; + DWORD disposal = PVDM_UNDEFINED; bool decoded = false; }; From a5777f58561d90c6d26ca23436383723925aef8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kotas?= Date: Tue, 7 Oct 2025 02:26:13 +0200 Subject: [PATCH 07/14] Populate GIF format info and clamp frame rectangles --- src/plugins/pictview/wic/WicBackend.cpp | 189 ++++++++++++++++++++++-- src/plugins/pictview/wic/WicBackend.h | 4 + 2 files changed, 183 insertions(+), 10 deletions(-) diff --git a/src/plugins/pictview/wic/WicBackend.cpp b/src/plugins/pictview/wic/WicBackend.cpp index a7fc71525..6a8d14f9e 100644 --- a/src/plugins/pictview/wic/WicBackend.cpp +++ b/src/plugins/pictview/wic/WicBackend.cpp @@ -1307,6 +1307,7 @@ PVCODE PopulateImageInfo(ImageHandle& handle, LPPVImageInfo info, DWORD bufferSi info->NumOfImages = static_cast(handle.frames.size()); info->StretchMode = handle.stretchMode; info->TotalBitDepth = 32; + info->FSI = handle.hasFormatSpecificInfo ? &handle.formatInfo : nullptr; struct FormatLabel { @@ -1430,15 +1431,111 @@ HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& return hr; } handle.frames.resize(frameCount); + handle.hasFormatSpecificInfo = false; + handle.formatInfo = {}; + handle.formatInfo.cbSize = sizeof(PVFormatSpecificInfo); + handle.formatInfo.GIF.DisposalMethod = PVDM_UNDEFINED; + handle.baseInfo.FSI = nullptr; + handle.canvasWidth = 0; + handle.canvasHeight = 0; + + ComPtr decoderQuery; + if (FAILED(decoder->GetMetadataQueryReader(&decoderQuery))) + { + decoderQuery = nullptr; + } + bool hasExif = SourceContainsExif(decoder); - if (!hasExif) + if (!hasExif && decoderQuery) + { + hasExif = QueryReaderContainsExif(decoderQuery.Get()); + } + + UINT logicalScreenWidth = 0; + UINT logicalScreenHeight = 0; + bool hasLogicalScreenWidth = false; + bool hasLogicalScreenHeight = false; + UINT backgroundIndex = 0; + bool hasBackgroundIndex = false; + if (decoderQuery) + { + UINT value = 0; + if (TryReadUnsignedMetadata(decoderQuery.Get(), L"/logscrdesc/Width", value)) + { + logicalScreenWidth = value; + hasLogicalScreenWidth = true; + } + if (TryReadUnsignedMetadata(decoderQuery.Get(), L"/logscrdesc/Height", value)) + { + logicalScreenHeight = value; + hasLogicalScreenHeight = true; + } + if (TryReadUnsignedMetadata(decoderQuery.Get(), L"/logscrdesc/BackgroundColorIndex", value)) + { + backgroundIndex = value; + hasBackgroundIndex = true; + } + } + + COLORREF backgroundColor = RGB(0, 0, 0); + if (hasBackgroundIndex) { - Microsoft::WRL::ComPtr decoderQuery; - if (SUCCEEDED(decoder->GetMetadataQueryReader(&decoderQuery)) && decoderQuery) + ComPtr palette; + if (SUCCEEDED(backend.Factory()->CreatePalette(&palette)) && palette) { - hasExif = QueryReaderContainsExif(decoderQuery.Get()); + if (SUCCEEDED(decoder->CopyPalette(palette.Get()))) + { + UINT paletteCount = 0; + if (SUCCEEDED(palette->GetColorCount(&paletteCount)) && paletteCount > backgroundIndex) + { + std::vector colors(paletteCount); + UINT actualCount = paletteCount; + if (SUCCEEDED(palette->GetColors(paletteCount, colors.data(), &actualCount)) && + actualCount > backgroundIndex) + { + const WICColor color = colors[backgroundIndex]; + const BYTE r = static_cast((color >> 16) & 0xFF); + const BYTE g = static_cast((color >> 8) & 0xFF); + const BYTE b = static_cast(color & 0xFF); + backgroundColor = RGB(r, g, b); + } + } + } } } + + if (hasLogicalScreenWidth) + { + handle.canvasWidth = ClampUnsignedToLong(logicalScreenWidth); + } + if (hasLogicalScreenHeight) + { + handle.canvasHeight = ClampUnsignedToLong(logicalScreenHeight); + } + + const auto clampEdge = [](LONG origin, LONG extent, LONG limit) -> LONG { + if (extent <= 0) + { + return origin; + } + LONGLONG sum = static_cast(origin) + static_cast(extent); + if (limit > 0) + { + if (origin >= limit) + { + return limit; + } + if (sum > limit) + { + sum = limit; + } + } + if (sum > static_cast(std::numeric_limits::max())) + { + sum = std::numeric_limits::max(); + } + return static_cast(sum); + }; for (UINT i = 0; i < frameCount; ++i) { FrameData data; @@ -1475,6 +1572,8 @@ HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& ULONGLONG top64 = 0; bool leftSpecified = false; bool topSpecified = false; + ULONGLONG rectWidth64 = static_cast(width); + ULONGLONG rectHeight64 = static_cast(height); ComPtr frameQuery; if (SUCCEEDED(data.frame->GetMetadataQueryReader(&frameQuery)) && frameQuery) { @@ -1483,35 +1582,95 @@ HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& { left64 = value; leftSpecified = true; - data.rect.left = ClampUnsignedToLong(left64); } if (TryReadUnsignedMetadata(frameQuery.Get(), L"/imgdesc/Top", value)) { top64 = value; topSpecified = true; - data.rect.top = ClampUnsignedToLong(top64); + } + if (TryReadUnsignedMetadata(frameQuery.Get(), L"/imgdesc/Width", value) && value > 0) + { + rectWidth64 = value; + } + if (TryReadUnsignedMetadata(frameQuery.Get(), L"/imgdesc/Height", value) && value > 0) + { + rectHeight64 = value; } if (TryReadUnsignedMetadata(frameQuery.Get(), L"/grctlext/Disposal", value)) { data.disposal = MapGifDisposalToPv(value); } } - const ULONGLONG right64 = (leftSpecified ? left64 : 0ull) + static_cast(data.width); - const ULONGLONG bottom64 = (topSpecified ? top64 : 0ull) + static_cast(data.height); - data.rect.right = ClampUnsignedToLong(right64); - data.rect.bottom = ClampUnsignedToLong(bottom64); + LONG rectLeft = ClampUnsignedToLong(leftSpecified ? left64 : 0ull); + LONG rectTop = ClampUnsignedToLong(topSpecified ? top64 : 0ull); + if (handle.canvasWidth > 0) + { + if (rectLeft < 0) + { + rectLeft = 0; + } + else if (rectLeft > handle.canvasWidth) + { + rectLeft = handle.canvasWidth; + } + } + if (handle.canvasHeight > 0) + { + if (rectTop < 0) + { + rectTop = 0; + } + else if (rectTop > handle.canvasHeight) + { + rectTop = handle.canvasHeight; + } + } + data.rect.left = rectLeft; + data.rect.top = rectTop; + const LONG rectWidthLong = ClampUnsignedToLong(rectWidth64); + const LONG rectHeightLong = ClampUnsignedToLong(rectHeight64); + data.rect.right = clampEdge(rectLeft, rectWidthLong, handle.canvasWidth); + data.rect.bottom = clampEdge(rectTop, rectHeightLong, handle.canvasHeight); if (!hasExif && FrameContainsExif(data.frame.Get())) { hasExif = true; } handle.frames[i] = std::move(data); } + if (handle.canvasWidth <= 0 && !handle.frames.empty()) + { + handle.canvasWidth = ClampUnsignedToLong(static_cast(handle.frames[0].width)); + } + if (handle.canvasHeight <= 0 && !handle.frames.empty()) + { + handle.canvasHeight = ClampUnsignedToLong(static_cast(handle.frames[0].height)); + } GUID container = {}; decoder->GetContainerFormat(&container); handle.baseInfo.Format = MapFormatToPvFormat(container); handle.baseInfo.NumOfImages = frameCount; handle.baseInfo.FileSize = QueryFileSize(handle.fileName); + if (handle.baseInfo.Format == PVF_GIF) + { + handle.hasFormatSpecificInfo = true; + const LONG screenWidth = std::max(0, handle.canvasWidth); + const LONG screenHeight = std::max(0, handle.canvasHeight); + handle.formatInfo.GIF.ScreenWidth = static_cast(screenWidth); + handle.formatInfo.GIF.ScreenHeight = static_cast(screenHeight); + handle.formatInfo.GIF.XPosition = 0; + handle.formatInfo.GIF.YPosition = 0; + handle.formatInfo.GIF.Delay = 0; + handle.formatInfo.GIF.TranspIndex = 0; + handle.formatInfo.GIF.BgColor = backgroundColor; + handle.baseInfo.FSI = &handle.formatInfo; + } + else + { + handle.hasFormatSpecificInfo = false; + handle.baseInfo.FSI = nullptr; + } + if (!hasExif && handle.baseInfo.Format == PVF_JPG) { CExifFileBuffer buffer; @@ -2318,6 +2477,11 @@ PVCODE WINAPI Backend::sPVChangeImage(LPPVHandle Img, DWORD flags) { return HResultToPvCode(finalizeHr); } + frame.rect.left = 0; + frame.rect.top = 0; + frame.rect.right = ClampUnsignedToLong(static_cast(frame.width)); + frame.rect.bottom = ClampUnsignedToLong(static_cast(frame.height)); + frame.disposal = PVDM_UNDEFINED; return PVC_OK; } @@ -2390,6 +2554,11 @@ PVCODE WINAPI Backend::sPVCropImage(LPPVHandle Img, int left, int top, int width { return HResultToPvCode(finalizeHr); } + frame.rect.left = 0; + frame.rect.top = 0; + frame.rect.right = ClampUnsignedToLong(static_cast(frame.width)); + frame.rect.bottom = ClampUnsignedToLong(static_cast(frame.height)); + frame.disposal = PVDM_UNDEFINED; return PVC_OK; } diff --git a/src/plugins/pictview/wic/WicBackend.h b/src/plugins/pictview/wic/WicBackend.h index 44f5e1aa6..c72a6f35b 100644 --- a/src/plugins/pictview/wic/WicBackend.h +++ b/src/plugins/pictview/wic/WicBackend.h @@ -55,6 +55,10 @@ struct ImageHandle COLORREF background = RGB(0, 0, 0); PVImageInfo baseInfo{}; PVImageHandles handles{}; + PVFormatSpecificInfo formatInfo{}; + bool hasFormatSpecificInfo = false; + LONG canvasWidth = 0; + LONG canvasHeight = 0; }; /** From 27dbf87a176abce872e77a52723ad9c5e48d0a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kotas?= Date: Tue, 7 Oct 2025 02:37:40 +0200 Subject: [PATCH 08/14] Handle GIF transparency masks in WIC backend --- src/plugins/pictview/wic/WicBackend.cpp | 145 +++++++++++++++++++++++- src/plugins/pictview/wic/WicBackend.h | 2 + 2 files changed, 145 insertions(+), 2 deletions(-) diff --git a/src/plugins/pictview/wic/WicBackend.cpp b/src/plugins/pictview/wic/WicBackend.cpp index 6a8d14f9e..d5b5ef46f 100644 --- a/src/plugins/pictview/wic/WicBackend.cpp +++ b/src/plugins/pictview/wic/WicBackend.cpp @@ -45,6 +45,7 @@ PVCODE PopulateImageInfo(ImageHandle& handle, LPPVImageInfo info, DWORD bufferSi bool TryReadUnsignedMetadata(IWICMetadataQueryReader* reader, LPCWSTR name, UINT& value); DWORD MapGifDisposalToPv(UINT disposal); LONG ClampUnsignedToLong(ULONGLONG value); +HRESULT EnsureTransparencyMask(FrameData& frame); std::mutex g_errorMutex; std::unordered_map g_errorTexts = { @@ -835,6 +836,113 @@ HRESULT CopyBgraFromSource(FrameData& frame, IWICBitmapSource* source) return S_OK; } +HRESULT EnsureTransparencyMask(FrameData& frame) +{ + if (frame.transparencyMask) + { + DeleteObject(frame.transparencyMask); + frame.transparencyMask = nullptr; + } + frame.hasTransparency = false; + + if (frame.pixels.empty() || frame.width == 0 || frame.height == 0) + { + return S_OK; + } + + bool hasTransparentPixel = false; + for (UINT y = 0; y < frame.height; ++y) + { + BYTE* row = frame.pixels.data() + static_cast(y) * frame.stride; + for (UINT x = 0; x < frame.width; ++x) + { + BYTE* pixel = row + static_cast(x) * 4; + if (pixel[3] == 0) + { + hasTransparentPixel = true; + pixel[0] = 0; + pixel[1] = 0; + pixel[2] = 0; + } + else + { + pixel[3] = 255; + } + } + } + + if (!hasTransparentPixel) + { + return S_OK; + } + + const ULONGLONG unalignedStride = (static_cast(frame.width) + 7ull) / 8ull; + const ULONGLONG alignedStride = (unalignedStride + 3ull) & ~3ull; + if (alignedStride > std::numeric_limits::max()) + { + return E_OUTOFMEMORY; + } + const UINT maskStride = static_cast(alignedStride); + const ULONGLONG maskSize64 = alignedStride * static_cast(frame.height); + if (frame.height != 0 && maskSize64 / frame.height != alignedStride) + { + return E_OUTOFMEMORY; + } + if (maskSize64 > static_cast(std::numeric_limits::max())) + { + return E_OUTOFMEMORY; + } + + BITMAPINFO maskInfo{}; + maskInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + maskInfo.bmiHeader.biWidth = static_cast(frame.width); + maskInfo.bmiHeader.biHeight = -static_cast(frame.height); + maskInfo.bmiHeader.biPlanes = 1; + maskInfo.bmiHeader.biBitCount = 1; + maskInfo.bmiHeader.biCompression = BI_RGB; + maskInfo.bmiHeader.biSizeImage = maskSize64 > std::numeric_limits::max() + ? 0 + : static_cast(maskSize64); + maskInfo.bmiColors[0].rgbBlue = 0; + maskInfo.bmiColors[0].rgbGreen = 0; + maskInfo.bmiColors[0].rgbRed = 0; + maskInfo.bmiColors[1].rgbBlue = 255; + maskInfo.bmiColors[1].rgbGreen = 255; + maskInfo.bmiColors[1].rgbRed = 255; + + void* bits = nullptr; + HBITMAP mask = CreateDIBSection(nullptr, &maskInfo, DIB_RGB_COLORS, &bits, nullptr, 0); + if (!mask || !bits) + { + if (mask) + { + DeleteObject(mask); + } + return E_OUTOFMEMORY; + } + + BYTE* maskData = static_cast(bits); + const size_t maskSize = static_cast(maskSize64); + memset(maskData, 0xFF, maskSize); + + for (UINT y = 0; y < frame.height; ++y) + { + const BYTE* srcRow = frame.pixels.data() + static_cast(y) * frame.stride; + BYTE* dstRow = maskData + static_cast(y) * maskStride; + for (UINT x = 0; x < frame.width; ++x) + { + if (srcRow[static_cast(x) * 4 + 3] != 0) + { + dstRow[x / 8] &= static_cast(~(0x80u >> (x % 8))); + } + } + } + + frame.transparencyMask = mask; + frame.hasTransparency = true; + return S_OK; +} + HRESULT FinalizeDecodedFrame(FrameData& frame) { const size_t lineCount = static_cast(frame.height); @@ -842,6 +950,11 @@ HRESULT FinalizeDecodedFrame(FrameData& frame) { return E_OUTOFMEMORY; } + HRESULT maskHr = EnsureTransparencyMask(frame); + if (FAILED(maskHr)) + { + return maskHr; + } try { frame.linePointers.resize(lineCount); @@ -1838,8 +1951,31 @@ PVCODE CreateSequenceNodes(ImageHandle& handle, LPPVImageSequence* seq) node->Rect = frame.rect; node->Delay = frame.delayMs; node->DisposalMethod = frame.disposal; - node->ImgHandle = frame.hbitmap; + node->ImgHandle = nullptr; node->TransparentHandle = nullptr; + if (frame.hbitmap) + { + HBITMAP frameCopy = static_cast(CopyImage(frame.hbitmap, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION)); + if (!frameCopy) + { + return PVC_GDI_ERROR; + } + node->ImgHandle = frameCopy; + } + if (frame.transparencyMask) + { + HBITMAP maskCopy = static_cast(CopyImage(frame.transparencyMask, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION)); + if (!maskCopy) + { + if (node->ImgHandle) + { + DeleteObject(node->ImgHandle); + node->ImgHandle = nullptr; + } + return PVC_GDI_ERROR; + } + node->TransparentHandle = maskCopy; + } *tail = node.release(); tail = &((*tail)->pNext); } @@ -2209,6 +2345,11 @@ PVCODE WINAPI Backend::sPVCloseImage(LPPVHandle Img) DeleteObject(frame.hbitmap); frame.hbitmap = nullptr; } + if (frame.transparencyMask) + { + DeleteObject(frame.transparencyMask); + frame.transparencyMask = nullptr; + } } delete handle; return PVC_OK; @@ -2381,7 +2522,7 @@ PVCODE WINAPI Backend::sPVGetHandles2(LPPVHandle Img, LPPVImageHandles* pHandles auto& frame = handle->frames[0]; PVImageHandles& handles = handle->handles; ZeroMemory(&handles, sizeof(PVImageHandles)); - handles.TransparentHandle = frame.hbitmap; + handles.TransparentHandle = frame.hasTransparency ? frame.transparencyMask : nullptr; handles.TransparentBackgroundHandle = frame.hbitmap; handles.StretchedHandle = frame.hbitmap; handles.StretchedTransparentHandle = frame.hbitmap; diff --git a/src/plugins/pictview/wic/WicBackend.h b/src/plugins/pictview/wic/WicBackend.h index c72a6f35b..8a81ef8a7 100644 --- a/src/plugins/pictview/wic/WicBackend.h +++ b/src/plugins/pictview/wic/WicBackend.h @@ -37,10 +37,12 @@ struct FrameData std::vector palette; BITMAPINFOHEADER bmi{}; HBITMAP hbitmap = nullptr; + HBITMAP transparencyMask = nullptr; DWORD delayMs = 0; RECT rect{}; DWORD disposal = PVDM_UNDEFINED; bool decoded = false; + bool hasTransparency = false; }; struct ImageHandle From cf16f75267ab78cce2a4e5d03d3ef8cef900eb2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kotas?= Date: Tue, 7 Oct 2025 02:47:23 +0200 Subject: [PATCH 09/14] Fix GIF mask orientation --- src/plugins/pictview/wic/WicBackend.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/pictview/wic/WicBackend.cpp b/src/plugins/pictview/wic/WicBackend.cpp index d5b5ef46f..3310efc90 100644 --- a/src/plugins/pictview/wic/WicBackend.cpp +++ b/src/plugins/pictview/wic/WicBackend.cpp @@ -896,13 +896,15 @@ HRESULT EnsureTransparencyMask(FrameData& frame) BITMAPINFO maskInfo{}; maskInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); maskInfo.bmiHeader.biWidth = static_cast(frame.width); - maskInfo.bmiHeader.biHeight = -static_cast(frame.height); + maskInfo.bmiHeader.biHeight = static_cast(frame.height); maskInfo.bmiHeader.biPlanes = 1; maskInfo.bmiHeader.biBitCount = 1; maskInfo.bmiHeader.biCompression = BI_RGB; maskInfo.bmiHeader.biSizeImage = maskSize64 > std::numeric_limits::max() ? 0 : static_cast(maskSize64); + maskInfo.bmiHeader.biXPelsPerMeter = 0; + maskInfo.bmiHeader.biYPelsPerMeter = 0; maskInfo.bmiColors[0].rgbBlue = 0; maskInfo.bmiColors[0].rgbGreen = 0; maskInfo.bmiColors[0].rgbRed = 0; @@ -928,7 +930,8 @@ HRESULT EnsureTransparencyMask(FrameData& frame) for (UINT y = 0; y < frame.height; ++y) { const BYTE* srcRow = frame.pixels.data() + static_cast(y) * frame.stride; - BYTE* dstRow = maskData + static_cast(y) * maskStride; + const size_t invertedRow = static_cast(frame.height - 1 - y); + BYTE* dstRow = maskData + invertedRow * maskStride; for (UINT x = 0; x < frame.width; ++x) { if (srcRow[static_cast(x) * 4 + 3] != 0) From dc51facd6dfe1850d1e240adcd5c972b87b61eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kotas?= Date: Tue, 7 Oct 2025 02:56:01 +0200 Subject: [PATCH 10/14] Fix GIF mask generation --- src/plugins/pictview/wic/WicBackend.cpp | 47 +++++++++++++++---------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/plugins/pictview/wic/WicBackend.cpp b/src/plugins/pictview/wic/WicBackend.cpp index 3310efc90..15dfc9969 100644 --- a/src/plugins/pictview/wic/WicBackend.cpp +++ b/src/plugins/pictview/wic/WicBackend.cpp @@ -857,12 +857,13 @@ HRESULT EnsureTransparencyMask(FrameData& frame) for (UINT x = 0; x < frame.width; ++x) { BYTE* pixel = row + static_cast(x) * 4; - if (pixel[3] == 0) + if (pixel[3] < 128) { hasTransparentPixel = true; pixel[0] = 0; pixel[1] = 0; pixel[2] = 0; + pixel[3] = 0; } else { @@ -893,10 +894,30 @@ HRESULT EnsureTransparencyMask(FrameData& frame) return E_OUTOFMEMORY; } + std::vector maskBuffer; + HRESULT hr = AllocateBuffer(maskBuffer, static_cast(maskSize64)); + if (FAILED(hr)) + { + return hr; + } + + for (UINT y = 0; y < frame.height; ++y) + { + const BYTE* srcRow = frame.pixels.data() + static_cast(y) * frame.stride; + BYTE* dstRow = maskBuffer.data() + static_cast(y) * maskStride; + for (UINT x = 0; x < frame.width; ++x) + { + if (srcRow[static_cast(x) * 4 + 3] == 0) + { + dstRow[x / 8] |= static_cast(0x80u >> (x % 8)); + } + } + } + BITMAPINFO maskInfo{}; maskInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); maskInfo.bmiHeader.biWidth = static_cast(frame.width); - maskInfo.bmiHeader.biHeight = static_cast(frame.height); + maskInfo.bmiHeader.biHeight = -static_cast(frame.height); maskInfo.bmiHeader.biPlanes = 1; maskInfo.bmiHeader.biBitCount = 1; maskInfo.bmiHeader.biCompression = BI_RGB; @@ -905,12 +926,16 @@ HRESULT EnsureTransparencyMask(FrameData& frame) : static_cast(maskSize64); maskInfo.bmiHeader.biXPelsPerMeter = 0; maskInfo.bmiHeader.biYPelsPerMeter = 0; + maskInfo.bmiHeader.biClrUsed = 2; + maskInfo.bmiHeader.biClrImportant = 2; maskInfo.bmiColors[0].rgbBlue = 0; maskInfo.bmiColors[0].rgbGreen = 0; maskInfo.bmiColors[0].rgbRed = 0; + maskInfo.bmiColors[0].rgbReserved = 0; maskInfo.bmiColors[1].rgbBlue = 255; maskInfo.bmiColors[1].rgbGreen = 255; maskInfo.bmiColors[1].rgbRed = 255; + maskInfo.bmiColors[1].rgbReserved = 0; void* bits = nullptr; HBITMAP mask = CreateDIBSection(nullptr, &maskInfo, DIB_RGB_COLORS, &bits, nullptr, 0); @@ -923,23 +948,7 @@ HRESULT EnsureTransparencyMask(FrameData& frame) return E_OUTOFMEMORY; } - BYTE* maskData = static_cast(bits); - const size_t maskSize = static_cast(maskSize64); - memset(maskData, 0xFF, maskSize); - - for (UINT y = 0; y < frame.height; ++y) - { - const BYTE* srcRow = frame.pixels.data() + static_cast(y) * frame.stride; - const size_t invertedRow = static_cast(frame.height - 1 - y); - BYTE* dstRow = maskData + invertedRow * maskStride; - for (UINT x = 0; x < frame.width; ++x) - { - if (srcRow[static_cast(x) * 4 + 3] != 0) - { - dstRow[x / 8] &= static_cast(~(0x80u >> (x % 8))); - } - } - } + memcpy(bits, maskBuffer.data(), maskBuffer.size()); frame.transparencyMask = mask; frame.hasTransparency = true; From 0423880e5daeab2343068a3d5ef78d18066f86ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kotas?= Date: Thu, 9 Oct 2025 03:49:40 +0200 Subject: [PATCH 11/14] Respect PVSaveImageInfo encoder settings (#195) * Respect PVSaveImageInfo encoder options * Handle TIFF JPEG compression constant availability * Respect Save As transformations * Fix crop dimension min selection * Respect Save As encoder configuration * Fix WIC encoder palette and mapping declarations * Fix GIF transparent index metadata type * Use indexed format converter as GIF frame source * Set GIF encoder palette before writing * Fix GIF Save As palette setup before encoding * Write indexed GIF frames with explicit pixel buffers * Encode GIF frames from indexed bitmaps * Use WritePixels for indexed encodes * Adapt palette encoding to encoder pixel formats * Set GIF palette before initializing frames * Register GIF palette before creating frames * Write indexed frames through WIC WriteSource * Write indexed frames via WritePixels * Use WriteSource for indexed encodes * Refresh GIF palette after pixel format alignment * Log WIC save failures with detailed stages * Fix GIF property bag failure and write version metadata * Set GIF interlace through metadata to avoid property bag failure * Write GIF version metadata via encoder writer * Set GIF logical screen metadata before version tag * Write GIF version metadata as a byte vector * Tolerate unsupported GIF version metadata * Preserve JPEG palette quantization * Handle encoders without DPI support --- src/plugins/pictview/wic/WicBackend.cpp | 1503 ++++++++++++++++++++++- 1 file changed, 1437 insertions(+), 66 deletions(-) diff --git a/src/plugins/pictview/wic/WicBackend.cpp b/src/plugins/pictview/wic/WicBackend.cpp index 15dfc9969..b465666ae 100644 --- a/src/plugins/pictview/wic/WicBackend.cpp +++ b/src/plugins/pictview/wic/WicBackend.cpp @@ -7,11 +7,15 @@ #include #include #include +#include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -38,6 +42,13 @@ constexpr DWORD kBackendVersion = PV_VERSION_156; constexpr UINT kBytesPerPixel = 4; constexpr UINT kMaxGdiDimension = static_cast(std::numeric_limits::max()); +struct GuidMapping +{ + DWORD format; + GUID container; + GUID pixelFormat; +}; + HRESULT AllocatePixelStorage(FrameData& frame, UINT width, UINT height); HRESULT FinalizeDecodedFrame(FrameData& frame); PVCODE PopulateImageInfo(ImageHandle& handle, LPPVImageInfo info, DWORD bufferSize, bool hasPreviousImage, @@ -46,6 +57,469 @@ bool TryReadUnsignedMetadata(IWICMetadataQueryReader* reader, LPCWSTR name, UINT DWORD MapGifDisposalToPv(UINT disposal); LONG ClampUnsignedToLong(ULONGLONG value); HRESULT EnsureTransparencyMask(FrameData& frame); +DWORD MapPixelFormatToColors(const GUID& guid); + +struct PixelFormatSelection +{ + GUID pixelFormat; + UINT paletteEntries; + bool isIndexed; + bool isGray; +}; + +std::wstring ExtractComment(LPPVSaveImageInfo info) +{ + if (!info || !info->Comment || info->CommentSize == 0) + { + return std::wstring(); + } + + size_t length = static_cast(info->CommentSize); + if (length == 0) + { + return std::wstring(); + } + if (info->Comment[length - 1] == '\0') + { + --length; + } + if (length == 0) + { + return std::wstring(); + } + + int required = MultiByteToWideChar(CP_ACP, 0, info->Comment, static_cast(length), nullptr, 0); + if (required <= 0) + { + return std::wstring(); + } + std::wstring result(static_cast(required), L'\0'); + MultiByteToWideChar(CP_ACP, 0, info->Comment, static_cast(length), result.data(), required); + return result; +} + +HRESULT TrySetMetadataString(IWICMetadataQueryWriter* writer, LPCWSTR name, const std::wstring& value) +{ + if (!writer || value.empty()) + { + return S_OK; + } + PROPVARIANT prop; + PropVariantInit(&prop); + prop.vt = VT_BSTR; + prop.bstrVal = SysAllocStringLen(value.data(), static_cast(value.size())); + if (!prop.bstrVal) + { + return E_OUTOFMEMORY; + } + HRESULT hr = writer->SetMetadataByName(name, &prop); + PropVariantClear(&prop); + if (hr == WINCODEC_ERR_PROPERTYNOTSUPPORTED || hr == WINCODEC_ERR_PROPERTYNOTFOUND) + { + return S_OK; + } + return hr; +} + +HRESULT ApplyCommentMetadata(const GUID& container, IWICMetadataQueryWriter* writer, const std::wstring& comment) +{ + if (!writer || comment.empty()) + { + return S_OK; + } + + if (container == GUID_ContainerFormatGif) + { + HRESULT hr = TrySetMetadataString(writer, L"/commentext/{str=Comment}", comment); + if (FAILED(hr)) + { + return hr; + } + } + else if (container == GUID_ContainerFormatPng) + { + HRESULT hr = TrySetMetadataString(writer, L"/tEXt/{str=Comment}", comment); + if (FAILED(hr)) + { + return hr; + } + hr = TrySetMetadataString(writer, L"/tEXt/{str=Description}", comment); + if (FAILED(hr)) + { + return hr; + } + } + else if (container == GUID_ContainerFormatJpeg) + { + HRESULT hr = TrySetMetadataString(writer, L"/comment", comment); + if (FAILED(hr)) + { + return hr; + } + hr = TrySetMetadataString(writer, L"/ifd/{ushort=270}", comment); + if (FAILED(hr)) + { + return hr; + } + hr = TrySetMetadataString(writer, L"/app1/ifd/{ushort=270}", comment); + if (FAILED(hr)) + { + return hr; + } + } + else if (container == GUID_ContainerFormatTiff) + { + HRESULT hr = TrySetMetadataString(writer, L"/ifd/{ushort=270}", comment); + if (FAILED(hr)) + { + return hr; + } + } + else if (container == GUID_ContainerFormatBmp) + { + HRESULT hr = TrySetMetadataString(writer, L"/ifd/{ushort=270}", comment); + if (FAILED(hr)) + { + return hr; + } + } + return S_OK; +} + + +class PropertyBagWriter +{ +public: + void AddFloat(const wchar_t* name, float value) + { + PROPBAG2 option{}; + option.pstrName = const_cast(name); + option.dwType = PROPBAG2_TYPE_DATA; + option.vt = VT_R4; + m_options.push_back(option); + + VARIANT var; + VariantInit(&var); + var.vt = VT_R4; + var.fltVal = value; + m_values.push_back(var); + } + + void AddUInt8(const wchar_t* name, BYTE value) + { + PROPBAG2 option{}; + option.pstrName = const_cast(name); + option.dwType = PROPBAG2_TYPE_DATA; + option.vt = VT_UI1; + m_options.push_back(option); + + VARIANT var; + VariantInit(&var); + var.vt = VT_UI1; + var.bVal = static_cast(value); + m_values.push_back(var); + } + + void AddBool(const wchar_t* name, bool value) + { + PROPBAG2 option{}; + option.pstrName = const_cast(name); + option.dwType = PROPBAG2_TYPE_DATA; + option.vt = VT_BOOL; + m_options.push_back(option); + + VARIANT var; + VariantInit(&var); + var.vt = VT_BOOL; + var.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE; + m_values.push_back(var); + } + + void AddUInt32(const wchar_t* name, UINT value) + { + PROPBAG2 option{}; + option.pstrName = const_cast(name); + option.dwType = PROPBAG2_TYPE_DATA; + option.vt = VT_UI4; + m_options.push_back(option); + + VARIANT var; + VariantInit(&var); + var.vt = VT_UI4; + var.ulVal = value; + m_values.push_back(var); + } + + void AddString(const wchar_t* name, const std::wstring& value) + { + if (value.empty()) + { + return; + } + + PROPBAG2 option{}; + option.pstrName = const_cast(name); + option.dwType = PROPBAG2_TYPE_DATA; + option.vt = VT_BSTR; + m_options.push_back(option); + + VARIANT var; + VariantInit(&var); + var.vt = VT_BSTR; + var.bstrVal = SysAllocStringLen(value.data(), static_cast(value.size())); + if (!var.bstrVal) + { + m_options.pop_back(); + return; + } + m_values.push_back(var); + } + + HRESULT Write(IPropertyBag2* bag) + { + if (!bag || m_options.empty()) + { + return S_OK; + } + + HRESULT hr = bag->Write(static_cast(m_options.size()), m_options.data(), m_values.data()); + if (hr == WINCODEC_ERR_PROPERTYNOTSUPPORTED) + { + return S_OK; + } + return hr; + } + + ~PropertyBagWriter() + { + for (auto& value : m_values) + { + VariantClear(&value); + } + } + +private: + std::vector m_options; + std::vector m_values; +}; + +float ClampQualityToFactor(DWORD quality) +{ + if (quality == 0) + { + return 0.0f; + } + const DWORD clamped = std::min(100, std::max(1, quality)); + return static_cast(clamped) / 100.0f; +} + +std::optional MapSubsamplingToWic(DWORD subsampling) +{ + switch (subsampling) + { + case 0: + return static_cast(WICJpegYCrCbSubsampling422); + case 1: + return static_cast(WICJpegYCrCbSubsampling444); + default: + return std::nullopt; + } +} + +std::optional FindTransparentPixel(IWICBitmapSource* source) +{ + if (!source) + { + return std::nullopt; + } + + UINT width = 0; + UINT height = 0; + HRESULT hr = source->GetSize(&width, &height); + if (FAILED(hr) || width == 0 || height == 0) + { + return std::nullopt; + } + + const size_t stride = static_cast(width) * 4u; + if (stride > std::numeric_limits::max()) + { + return std::nullopt; + } + const size_t bufferSize = stride * static_cast(height); + if (bufferSize > std::numeric_limits::max()) + { + return std::nullopt; + } + + std::vector pixels(bufferSize); + hr = source->CopyPixels(nullptr, static_cast(stride), static_cast(pixels.size()), pixels.data()); + if (FAILED(hr)) + { + return std::nullopt; + } + + for (size_t y = 0; y < height; ++y) + { + const BYTE* row = pixels.data() + y * stride; + for (size_t x = 0; x < width; ++x) + { + const BYTE* pixel = row + x * 4u; + if (pixel[3] == 0) + { + RGBQUAD color{}; + color.rgbBlue = pixel[0]; + color.rgbGreen = pixel[1]; + color.rgbRed = pixel[2]; + color.rgbReserved = 0; + return color; + } + } + } + + return std::nullopt; +} + +BYTE FindClosestPaletteIndex(const std::vector& colors, BYTE red, BYTE green, BYTE blue) +{ + if (colors.empty()) + { + return 0; + } + + BYTE bestIndex = 0; + unsigned int bestDistance = std::numeric_limits::max(); + + for (size_t i = 0; i < colors.size(); ++i) + { + const WICColor color = colors[i]; + const BYTE paletteRed = static_cast((color >> 16) & 0xFF); + const BYTE paletteGreen = static_cast((color >> 8) & 0xFF); + const BYTE paletteBlue = static_cast(color & 0xFF); + + const int dr = static_cast(paletteRed) - static_cast(red); + const int dg = static_cast(paletteGreen) - static_cast(green); + const int db = static_cast(paletteBlue) - static_cast(blue); + + const unsigned int distance = static_cast(dr * dr + dg * dg + db * db); + if (distance < bestDistance) + { + bestDistance = distance; + bestIndex = static_cast(i); + } + } + + return bestIndex; +} + +std::optional DetermineGifTransparency(const PVSaveImageInfo* info, std::vector& colors, + IWICBitmapSource* source) +{ + if (!info) + { + for (size_t i = 0; i < colors.size(); ++i) + { + if (((colors[i] >> 24) & 0xFFu) == 0) + { + return static_cast(i); + } + } + return std::nullopt; + } + + switch (info->Transp.Flags) + { + case PVTF_NONE: + return std::nullopt; + case PVTF_INDEX: + if (info->Transp.Value.Index < colors.size()) + { + return info->Transp.Value.Index; + } + return std::nullopt; + case PVTF_RGB: + { + if (colors.empty()) + { + return std::nullopt; + } + const BYTE red = info->Transp.Value.RGB.Red; + const BYTE green = info->Transp.Value.RGB.Green; + const BYTE blue = info->Transp.Value.RGB.Blue; + const BYTE index = FindClosestPaletteIndex(colors, red, green, blue); + colors[index] = (static_cast(red) << 16) | (static_cast(green) << 8) | + static_cast(blue); + return index; + } + case PVTF_ORIGINAL: + { + auto transparentPixel = FindTransparentPixel(source); + if (!transparentPixel) + { + for (size_t i = 0; i < colors.size(); ++i) + { + if (((colors[i] >> 24) & 0xFFu) == 0) + { + return static_cast(i); + } + } + return std::nullopt; + } + const BYTE index = FindClosestPaletteIndex(colors, transparentPixel->rgbRed, transparentPixel->rgbGreen, + transparentPixel->rgbBlue); + colors[index] = (static_cast(transparentPixel->rgbRed) << 16) | + (static_cast(transparentPixel->rgbGreen) << 8) | + static_cast(transparentPixel->rgbBlue); + return index; + } + default: + return std::nullopt; + } +} + +double ResolveDpiValue(DWORD requested, double fallback, double defaultValue) +{ + if (requested > 0) + { + return static_cast(requested); + } + if (std::isfinite(fallback) && fallback > 0.0) + { + return fallback; + } + return defaultValue; +} + +std::optional MapTiffCompression(DWORD compression) +{ + switch (compression) + { + case PVCS_DEFAULT: + return std::nullopt; + case PVCS_NO_COMPRESSION: + return static_cast(WICTiffCompressionNone); + case PVCS_CCITT_3: + return static_cast(WICTiffCompressionCCITT3); + case PVCS_CCITT_4: + return static_cast(WICTiffCompressionCCITT4); + case PVCS_LZW: + return static_cast(WICTiffCompressionLZW); + case PVCS_RLE: + return static_cast(WICTiffCompressionRLE); + case PVCS_DEFLATE: + return static_cast(WICTiffCompressionZIP); + case PVCS_JPEG_HUFFMAN: +#if defined(WICTiffCompressionJPEG) + return static_cast(WICTiffCompressionJPEG); +#elif defined(WICTiffCompressionJPEGYCBCR) + return static_cast(WICTiffCompressionJPEGYCBCR); +#else + return std::nullopt; +#endif + default: + return std::nullopt; + } +} std::mutex g_errorMutex; std::unordered_map g_errorTexts = { @@ -62,6 +536,39 @@ std::unordered_map g_errorTexts = { {PVC_UNEXPECTED_EOF, "The image data ended unexpectedly."}, }; +std::unordered_map g_customErrorTexts; + +void ClearCustomErrorText(DWORD code) +{ + std::lock_guard lock(g_errorMutex); + g_customErrorTexts.erase(code); +} + +void RecordDetailedError(DWORD code, HRESULT hr, const char* stage) +{ + std::lock_guard lock(g_errorMutex); + std::string baseText = "Unknown WIC error."; + const auto baseIt = g_errorTexts.find(code); + if (baseIt != g_errorTexts.end()) + { + baseText = baseIt->second; + } + + std::ostringstream stream; + stream << baseText; + if (stage && stage[0] != '\0') + { + stream << " (stage: " << stage; + } + else + { + stream << " (stage: unknown"; + } + stream << ", hr=0x" << std::uppercase << std::setfill('0') << std::setw(8) + << static_cast(static_cast(hr)) << ')'; + g_customErrorTexts[code] = stream.str(); +} + bool PathLooksLikeExif(LPCWSTR path) { if (!path) @@ -443,6 +950,11 @@ DWORD MapGifDisposalToPv(UINT disposal) const char* LookupError(DWORD code) { std::lock_guard lock(g_errorMutex); + const auto customIt = g_customErrorTexts.find(code); + if (customIt != g_customErrorTexts.end()) + { + return customIt->second.c_str(); + } const auto it = g_errorTexts.find(code); if (it != g_errorTexts.end()) { @@ -669,13 +1181,6 @@ HRESULT AllocatePixelStorage(FrameData& frame, UINT width, UINT height) 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}, @@ -685,6 +1190,120 @@ const GuidMapping kEncoderMappings[] = { {PVF_ICO, GUID_ContainerFormatIco, GUID_WICPixelFormat32bppBGRA}, }; +std::optional DeterminePixelFormat(const GuidMapping& mapping, LPPVSaveImageInfo info) +{ + PixelFormatSelection selection{}; + selection.pixelFormat = mapping.pixelFormat; + selection.paletteEntries = MapPixelFormatToColors(mapping.pixelFormat); + selection.isIndexed = selection.paletteEntries > 0; + selection.isGray = false; + + if (!info) + { + return selection; + } + + auto chooseIndexed = [&](UINT colorCount) { + UINT clamped = std::max(colorCount, 2u); + UINT bits = 0; + while (((1u << bits) < clamped) && bits < 8) + { + ++bits; + } + if (bits == 0) + { + bits = 1; + } + if (bits <= 1) + { + selection.pixelFormat = GUID_WICPixelFormat1bppIndexed; + selection.paletteEntries = 2; + } + else if (bits <= 4) + { + selection.pixelFormat = GUID_WICPixelFormat4bppIndexed; + selection.paletteEntries = 1u << 4; + } + else + { + selection.pixelFormat = GUID_WICPixelFormat8bppIndexed; + selection.paletteEntries = 1u << bits; + if (selection.paletteEntries > 256) + { + selection.paletteEntries = 256; + } + } + selection.isIndexed = true; + }; + + const DWORD colors = info->Colors; + if (info->ColorModel == PVCM_GRAYS) + { + if (colors == 2) + { + chooseIndexed(2); + } + else + { + selection.pixelFormat = GUID_WICPixelFormat8bppGray; + selection.paletteEntries = 0; + selection.isIndexed = false; + } + selection.isGray = true; + return selection; + } + + if (colors != 0 && colors <= 256) + { + chooseIndexed(colors); + return selection; + } + + switch (colors) + { + case PV_COLOR_HC15: + selection.pixelFormat = GUID_WICPixelFormat16bppBGR555; + selection.paletteEntries = 0; + selection.isIndexed = false; + return selection; + case PV_COLOR_HC16: + selection.pixelFormat = GUID_WICPixelFormat16bppBGR565; + selection.paletteEntries = 0; + selection.isIndexed = false; + return selection; + case PV_COLOR_TC24: + selection.pixelFormat = GUID_WICPixelFormat24bppBGR; + selection.paletteEntries = 0; + selection.isIndexed = false; + return selection; + case PV_COLOR_TC32: + if (mapping.container == GUID_ContainerFormatJpeg) + { + selection.pixelFormat = GUID_WICPixelFormat24bppBGR; + } + else + { + selection.pixelFormat = GUID_WICPixelFormat32bppBGRA; + } + selection.paletteEntries = 0; + selection.isIndexed = false; + return selection; + default: + break; + } + + if (mapping.container == GUID_ContainerFormatJpeg) + { + selection.pixelFormat = info->ColorModel == PVCM_GRAYS ? GUID_WICPixelFormat8bppGray : GUID_WICPixelFormat24bppBGR; + selection.isGray = info->ColorModel == PVCM_GRAYS; + selection.paletteEntries = 0; + selection.isIndexed = false; + return selection; + } + + return selection; +} + HRESULT CreateDecoder(Backend& backend, const std::wstring& path, IWICBitmapDecoder** decoder) { auto factory = backend.Factory(); @@ -2002,38 +2621,470 @@ PVCODE SaveFrame(ImageHandle& handle, int imageIndex, const wchar_t* path, const { return PVC_INVALID_HANDLE; } + + ClearCustomErrorText(PVC_READING_ERROR); + ClearCustomErrorText(PVC_WRITING_ERROR); + ClearCustomErrorText(PVC_EXCEPTION); + const size_t normalizedIndex = NormalizeFrameIndex(handle, imageIndex, 0); FrameData& frame = handle.frames[normalizedIndex]; + + auto recordFailure = [&](HRESULT failureHr, const char* stage) -> PVCODE { + const PVCODE code = HResultToPvCode(failureHr); + RecordDetailedError(code, failureHr, stage); + return code; + }; + HRESULT hr = DecodeFrame(handle, normalizedIndex); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "DecodeFrame"); } Microsoft::WRL::ComPtr encoder; hr = handle.backend->Factory()->CreateEncoder(mapping.container, nullptr, &encoder); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "CreateEncoder"); } Microsoft::WRL::ComPtr stream; hr = handle.backend->Factory()->CreateStream(&stream); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "CreateStream"); } hr = stream->InitializeFromFilename(path, GENERIC_WRITE); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "InitializeFromFilename"); } hr = encoder->Initialize(stream.Get(), WICBitmapEncoderNoCache); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "Encoder::Initialize"); + } + + UINT processedWidth = frame.width; + UINT processedHeight = frame.height; + UINT processedStride = frame.stride; + const BYTE* pixelData = frame.pixels.data(); + std::vector workingPixels; + + const DWORD flags = info ? info->Flags : 0; + + if (info && info->CropWidth != 0 && info->CropHeight != 0) + { + if (info->CropLeft >= processedWidth || info->CropTop >= processedHeight) + { + return PVC_UNSUP_OUT_PARAMS; + } + + const UINT maxCropWidth = processedWidth - info->CropLeft; + const UINT maxCropHeight = processedHeight - info->CropTop; + const UINT cropWidth = std::min(static_cast(info->CropWidth), maxCropWidth); + const UINT cropHeight = std::min(static_cast(info->CropHeight), maxCropHeight); + if (cropWidth == 0 || cropHeight == 0) + { + return PVC_UNSUP_OUT_PARAMS; + } + + std::vector cropped; + const size_t rowBytes = static_cast(cropWidth) * kBytesPerPixel; + const size_t totalBytes = rowBytes * cropHeight; + try + { + cropped.resize(totalBytes); + } + catch (const std::bad_alloc&) + { + return PVC_OUT_OF_MEMORY; + } + + for (UINT y = 0; y < cropHeight; ++y) + { + const BYTE* src = pixelData + (static_cast(info->CropTop + y) * processedStride) + + static_cast(info->CropLeft) * kBytesPerPixel; + BYTE* dst = cropped.data() + static_cast(y) * rowBytes; + memcpy(dst, src, rowBytes); + } + + workingPixels.swap(cropped); + pixelData = workingPixels.data(); + processedWidth = cropWidth; + processedHeight = cropHeight; + processedStride = cropWidth * kBytesPerPixel; + } + + auto ensureMutablePixels = [&]() -> BYTE* { + if (workingPixels.empty()) + { + const size_t totalBytes = static_cast(processedStride) * processedHeight; + try + { + workingPixels.assign(pixelData, pixelData + totalBytes); + } + catch (const std::bad_alloc&) + { + return nullptr; + } + pixelData = workingPixels.data(); + } + return workingPixels.data(); + }; + + if (flags & PVSF_ROTATE90) + { + const UINT newWidth = processedHeight; + const UINT newHeight = processedWidth; + std::vector rotated; + const size_t totalBytes = static_cast(newWidth) * newHeight * kBytesPerPixel; + try + { + rotated.resize(totalBytes); + } + catch (const std::bad_alloc&) + { + return PVC_OUT_OF_MEMORY; + } + + for (UINT y = 0; y < processedHeight; ++y) + { + for (UINT x = 0; x < processedWidth; ++x) + { + const BYTE* src = pixelData + static_cast(y) * processedStride + + static_cast(x) * kBytesPerPixel; + const UINT dstX = newWidth - 1 - y; + const UINT dstY = x; + BYTE* dst = rotated.data() + + (static_cast(dstY) * newWidth + dstX) * kBytesPerPixel; + memcpy(dst, src, kBytesPerPixel); + } + } + + workingPixels.swap(rotated); + pixelData = workingPixels.data(); + processedWidth = newWidth; + processedHeight = newHeight; + processedStride = newWidth * kBytesPerPixel; + } + + if (flags & PVSF_FLIP_VERT) + { + BYTE* mutablePixels = ensureMutablePixels(); + if (!mutablePixels) + { + return PVC_OUT_OF_MEMORY; + } + + const size_t rowBytes = static_cast(processedWidth) * kBytesPerPixel; + for (UINT y = 0; y < processedHeight / 2; ++y) + { + BYTE* top = mutablePixels + static_cast(y) * processedStride; + BYTE* bottom = mutablePixels + static_cast(processedHeight - 1 - y) * processedStride; + for (size_t i = 0; i < rowBytes; ++i) + { + std::swap(top[i], bottom[i]); + } + } + } + + if (flags & PVSF_FLIP_HOR) + { + BYTE* mutablePixels = ensureMutablePixels(); + if (!mutablePixels) + { + return PVC_OUT_OF_MEMORY; + } + + for (UINT y = 0; y < processedHeight; ++y) + { + BYTE* row = mutablePixels + static_cast(y) * processedStride; + for (UINT x = 0; x < processedWidth / 2; ++x) + { + BYTE* left = row + static_cast(x) * kBytesPerPixel; + BYTE* right = row + static_cast(processedWidth - 1 - x) * kBytesPerPixel; + for (UINT c = 0; c < kBytesPerPixel; ++c) + { + std::swap(left[c], right[c]); + } + } + } + } + + if (flags & PVSF_INVERT) + { + BYTE* mutablePixels = ensureMutablePixels(); + if (!mutablePixels) + { + return PVC_OUT_OF_MEMORY; + } + + const size_t pixelCount = static_cast(processedWidth) * processedHeight; + for (size_t i = 0; i < pixelCount; ++i) + { + BYTE* pixel = mutablePixels + i * kBytesPerPixel; + pixel[0] = static_cast(0xFFu - pixel[0]); + pixel[1] = static_cast(0xFFu - pixel[1]); + pixel[2] = static_cast(0xFFu - pixel[2]); + } + } + + UINT targetWidth = processedWidth; + UINT targetHeight = processedHeight; + if (info && info->Width != 0 && info->Height != 0) + { + targetWidth = info->Width; + targetHeight = info->Height; + } + + const auto selectionOpt = DeterminePixelFormat(mapping, info); + if (!selectionOpt) + { + return PVC_UNSUP_OUT_PARAMS; + } + const PixelFormatSelection& selection = *selectionOpt; + const std::wstring comment = ExtractComment(info); + const bool useUniformPalette = info && (info->Flags & PVSF_UNIFORM_PALETTE) != 0; + + std::vector paletteColors; + Microsoft::WRL::ComPtr palette; + Microsoft::WRL::ComPtr quantizedPaletteSource; + std::optional gifTransparencyIndex; + bool gifTransparencyEnabled = false; + const WICBitmapDitherType paletteDither = + useUniformPalette ? WICBitmapDitherTypeNone : WICBitmapDitherTypeErrorDiffusion; + + PropertyBagWriter bagWriter; + bool hasGifInterlaceFlag = false; + bool gifInterlaceFlag = false; + if (info) + { + if (mapping.container == GUID_ContainerFormatJpeg) + { + const float quality = ClampQualityToFactor(info->Misc.JPEG.Quality); + if (quality > 0.0f) + { + bagWriter.AddFloat(L"ImageQuality", quality); + } + if (const auto subsampling = MapSubsamplingToWic(info->Misc.JPEG.SubSampling)) + { + bagWriter.AddUInt8(L"JpegYCrCbSubsampling", *subsampling); + } + } + else if (mapping.container == GUID_ContainerFormatGif) + { + hasGifInterlaceFlag = true; + gifInterlaceFlag = (info->Flags & PVSF_INTERLACE) != 0; + } + else if (mapping.container == GUID_ContainerFormatTiff) + { + if (info->Compression == PVCS_JPEG_HUFFMAN) + { + const float quality = ClampQualityToFactor(info->Misc.TIFF.JPEGQuality); + if (quality > 0.0f) + { + bagWriter.AddFloat(L"ImageQuality", quality); + } + if (const auto subsampling = MapSubsamplingToWic(info->Misc.TIFF.JPEGSubSampling)) + { + bagWriter.AddUInt8(L"JpegYCrCbSubsampling", *subsampling); + } + } + + const std::optional compressionOption = MapTiffCompression(info->Compression); + if (!compressionOption.has_value() && info->Compression != PVCS_DEFAULT) + { + return PVC_UNSUP_OUT_PARAMS; + } + if (compressionOption.has_value()) + { + bagWriter.AddUInt8(L"TiffCompressionMethod", compressionOption.value()); + } + } + + } + + const size_t processedBufferSize = static_cast(processedStride) * processedHeight; + if (processedBufferSize > std::numeric_limits::max()) + { + return PVC_OUT_OF_MEMORY; + } + + Microsoft::WRL::ComPtr bitmap; + hr = handle.backend->Factory()->CreateBitmapFromMemory( + processedWidth, processedHeight, GUID_WICPixelFormat32bppBGRA, processedStride, + static_cast(processedBufferSize), const_cast(pixelData), &bitmap); + if (FAILED(hr)) + { + return recordFailure(hr, "CreateBitmapFromMemory"); + } + + Microsoft::WRL::ComPtr source; + hr = bitmap.As(&source); + if (FAILED(hr)) + { + return recordFailure(hr, "Bitmap::AsBitmapSource"); + } + + if ((targetWidth != processedWidth || targetHeight != processedHeight) && source) + { + Microsoft::WRL::ComPtr scaler; + hr = handle.backend->Factory()->CreateBitmapScaler(&scaler); + if (FAILED(hr)) + { + return recordFailure(hr, "CreateBitmapScaler"); + } + + hr = scaler->Initialize(source.Get(), targetWidth, targetHeight, WICBitmapInterpolationModeFant); + if (FAILED(hr)) + { + return recordFailure(hr, "Scaler::Initialize"); + } + + Microsoft::WRL::ComPtr scaledSource; + hr = scaler.As(&scaledSource); + if (FAILED(hr)) + { + return recordFailure(hr, "Scaler::AsBitmapSource"); + } + source = scaledSource; + } + + if (selection.isIndexed) + { + hr = handle.backend->Factory()->CreatePalette(&palette); + if (FAILED(hr)) + { + return recordFailure(hr, "CreatePalette"); + } + + UINT desiredEntries = selection.paletteEntries > 0 ? selection.paletteEntries : 256; + const char* paletteStage = nullptr; + if (useUniformPalette) + { + hr = palette->InitializePredefined(WICBitmapPaletteTypeFixedWebPalette, FALSE); + paletteStage = "Palette::InitializePredefined"; + } + else + { + hr = palette->InitializeFromBitmap(source.Get(), desiredEntries, FALSE); + paletteStage = "Palette::InitializeFromBitmap"; + } + if (FAILED(hr)) + { + return recordFailure(hr, paletteStage ? paletteStage : "Palette::Initialize"); + } + + UINT paletteCount = 0; + hr = palette->GetColorCount(&paletteCount); + if (FAILED(hr)) + { + return recordFailure(hr, "Palette::GetColorCount"); + } + + std::vector colors(paletteCount); + if (paletteCount > 0) + { + UINT actual = paletteCount; + hr = palette->GetColors(paletteCount, colors.data(), &actual); + if (FAILED(hr)) + { + return recordFailure(hr, "Palette::GetColors"); + } + colors.resize(actual); + } + + const UINT requiredEntries = selection.paletteEntries > 0 ? selection.paletteEntries : static_cast(colors.size()); + if (requiredEntries > 0) + { + if (colors.empty()) + { + colors.resize(requiredEntries, 0); + } + if (colors.size() < requiredEntries) + { + const WICColor fill = colors.empty() ? 0 : colors.back(); + colors.resize(requiredEntries, fill); + } + else if (colors.size() > requiredEntries) + { + colors.resize(requiredEntries); + } + } + + if (mapping.container == GUID_ContainerFormatGif) + { + gifTransparencyIndex = DetermineGifTransparency(info, colors, source.Get()); + gifTransparencyEnabled = gifTransparencyIndex.has_value(); + for (size_t i = 0; i < colors.size(); ++i) + { + const bool isTransparent = gifTransparencyEnabled && i == gifTransparencyIndex.value(); + const WICColor rgb = colors[i] & 0x00FFFFFFu; + colors[i] = rgb | (isTransparent ? 0x00000000u : 0xFF000000u); + } + } + else if (selection.isGray && requiredEntries == 2 && colors.size() >= 2) + { + colors[0] = 0xFF000000u; + colors[1] = 0xFFFFFFFFu; + } + + if (!colors.empty()) + { + hr = palette->InitializeCustom(colors.data(), static_cast(colors.size())); + if (FAILED(hr)) + { + return recordFailure(hr, "Palette::InitializeCustom"); + } + } + paletteColors = std::move(colors); + + Microsoft::WRL::ComPtr paletteQuantizer; + hr = handle.backend->Factory()->CreateFormatConverter(&paletteQuantizer); + if (FAILED(hr)) + { + return recordFailure(hr, "CreateFormatConverter (palette quantize)"); + } + + const WICBitmapPaletteType quantizePaletteType = + useUniformPalette ? WICBitmapPaletteTypeFixedWebPalette : WICBitmapPaletteTypeCustom; + hr = paletteQuantizer->Initialize(source.Get(), selection.pixelFormat, paletteDither, palette.Get(), 0.0, + quantizePaletteType); + if (FAILED(hr)) + { + return recordFailure(hr, "FormatConverter::Initialize (palette quantize)"); + } + + hr = paletteQuantizer.As(&quantizedPaletteSource); + if (FAILED(hr)) + { + return recordFailure(hr, "FormatConverter::As (palette quantize)"); + } + } + + Microsoft::WRL::ComPtr encoderMetadataWriter; + if (mapping.container == GUID_ContainerFormatGif) + { + if (FAILED(encoder->GetMetadataQueryWriter(&encoderMetadataWriter))) + { + encoderMetadataWriter.Reset(); + } + } + + if (mapping.container == GUID_ContainerFormatGif && palette) + { + // The GIF encoder expects its global palette to be registered before any frames are + // negotiated so that the encoder advertises an indexed pixel format compatible with the + // chosen palette. Register it now, before creating the frame, to keep subsequent + // WritePixels calls from failing with WRONGSTATE. + hr = encoder->SetPalette(palette.Get()); + if (FAILED(hr)) + { + return recordFailure(hr, "Encoder::SetPalette"); + } } Microsoft::WRL::ComPtr frameEncode; @@ -2041,121 +3092,441 @@ PVCODE SaveFrame(ImageHandle& handle, int imageIndex, const wchar_t* path, const hr = encoder->CreateNewFrame(&frameEncode, &bag); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "Encoder::CreateNewFrame"); + } + + hr = bagWriter.Write(bag.Get()); + if (FAILED(hr)) + { + return recordFailure(hr, "PropertyBagWriter::Write"); } hr = frameEncode->Initialize(bag.Get()); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "FrameEncode::Initialize"); } - hr = frameEncode->SetSize(frame.width, frame.height); + + hr = frameEncode->SetSize(targetWidth, targetHeight); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "FrameEncode::SetSize"); } - GUID pixelFormat = mapping.pixelFormat; + + GUID pixelFormat = selection.pixelFormat; hr = frameEncode->SetPixelFormat(&pixelFormat); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "FrameEncode::SetPixelFormat"); } - if (pixelFormat != mapping.pixelFormat) + const bool encoderIsIndexed = MapPixelFormatToColors(pixelFormat) > 0; + + UINT bitsPerPixel = 0; { - return PVC_UNSUP_FILE_TYPE; + Microsoft::WRL::ComPtr componentInfo; + hr = handle.backend->Factory()->CreateComponentInfo(pixelFormat, &componentInfo); + if (FAILED(hr)) + { + return recordFailure(hr, "CreateComponentInfo"); + } + Microsoft::WRL::ComPtr pixelInfo; + hr = componentInfo.As(&pixelInfo); + if (FAILED(hr)) + { + return recordFailure(hr, "ComponentInfo::AsPixelFormatInfo"); + } + hr = pixelInfo->GetBitsPerPixel(&bitsPerPixel); + if (FAILED(hr)) + { + return recordFailure(hr, "PixelFormatInfo::GetBitsPerPixel"); + } } - if (mapping.pixelFormat == GUID_WICPixelFormat24bppBGR) + if (bitsPerPixel == 0) { - const UINT stride = frame.width * 3; - std::vector rgb(stride * frame.height); - for (UINT y = 0; y < frame.height; ++y) + return PVC_UNSUP_OUT_PARAMS; + } + const ULONGLONG bitsPerRow = static_cast(targetWidth) * bitsPerPixel; + const ULONGLONG stride64 = (bitsPerRow + 7ull) / 8ull; + if (stride64 > static_cast(std::numeric_limits::max())) + { + return PVC_OUT_OF_MEMORY; + } + const UINT encodedStride = static_cast(stride64); + + Microsoft::WRL::ComPtr baseSource = + (selection.isIndexed && quantizedPaletteSource) ? quantizedPaletteSource : source; + Microsoft::WRL::ComPtr frameSource = baseSource; + Microsoft::WRL::ComPtr framePalette = palette; + + if (encoderIsIndexed) + { + UINT encoderPaletteEntries = MapPixelFormatToColors(pixelFormat); + if (!framePalette) { - const BYTE* src = frame.pixels.data() + y * frame.stride; - BYTE* dst = rgb.data() + y * stride; - for (UINT x = 0; x < frame.width; ++x) + hr = handle.backend->Factory()->CreatePalette(&framePalette); + if (FAILED(hr)) { - dst[x * 3 + 0] = src[x * 4 + 0]; - dst[x * 3 + 1] = src[x * 4 + 1]; - dst[x * 3 + 2] = src[x * 4 + 2]; + return recordFailure(hr, "CreatePaletteForFrame"); } } - 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)) + + if (encoderPaletteEntries > 0) { - return HResultToPvCode(hr); + if (paletteColors.empty()) + { + UINT paletteCount = 0; + hr = framePalette->GetColorCount(&paletteCount); + if (FAILED(hr)) + { + return recordFailure(hr, "FramePalette::GetColorCount"); + } + if (paletteCount > 0) + { + paletteColors.resize(paletteCount); + UINT actual = paletteCount; + hr = framePalette->GetColors(paletteCount, paletteColors.data(), &actual); + if (FAILED(hr)) + { + return recordFailure(hr, "FramePalette::GetColors"); + } + paletteColors.resize(actual); + } + } + + if (paletteColors.empty()) + { + paletteColors.resize(encoderPaletteEntries, 0); + } + if (paletteColors.size() < encoderPaletteEntries) + { + const WICColor fill = paletteColors.empty() ? 0 : paletteColors.back(); + paletteColors.resize(encoderPaletteEntries, fill); + } + else if (paletteColors.size() > encoderPaletteEntries) + { + paletteColors.resize(encoderPaletteEntries); + } + + if (gifTransparencyIndex.has_value()) + { + if (paletteColors.empty()) + { + gifTransparencyIndex.reset(); + } + else if (gifTransparencyIndex.value() >= paletteColors.size()) + { + const BYTE newIndex = static_cast(paletteColors.size() - 1); + gifTransparencyIndex = newIndex; + paletteColors[newIndex] &= 0x00FFFFFFu; + } + } + + if (!paletteColors.empty()) + { + hr = framePalette->InitializeCustom(paletteColors.data(), static_cast(paletteColors.size())); + if (FAILED(hr)) + { + return recordFailure(hr, "FramePalette::InitializeCustom"); + } + } } - Microsoft::WRL::ComPtr palette; - hr = handle.backend->Factory()->CreatePalette(&palette); - if (FAILED(hr)) + if (framePalette && mapping.container == GUID_ContainerFormatGif) { - return HResultToPvCode(hr); + // The GIF encoder caches the palette when SetPalette is called. Re-register the palette + // after aligning it with the negotiated pixel format so the encoder sees the final colors. + hr = encoder->SetPalette(framePalette.Get()); + if (FAILED(hr)) + { + return recordFailure(hr, "Encoder::SetFramePalette"); + } } - hr = palette->InitializeFromBitmap(bitmap.Get(), 256, FALSE); + Microsoft::WRL::ComPtr converter; + hr = handle.backend->Factory()->CreateFormatConverter(&converter); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "CreateFormatConverter (indexed)"); } - hr = frameEncode->SetPalette(palette.Get()); + const WICBitmapPaletteType paletteType = + useUniformPalette ? WICBitmapPaletteTypeFixedWebPalette : WICBitmapPaletteTypeCustom; + hr = converter->Initialize(baseSource.Get(), pixelFormat, paletteDither, framePalette.Get(), 0.0, paletteType); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "FormatConverter::Initialize (indexed)"); } - Microsoft::WRL::ComPtr gifConverter; - hr = handle.backend->Factory()->CreateFormatConverter(&gifConverter); + hr = converter.As(&frameSource); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "FormatConverter::As (indexed)"); } + } + else + { + framePalette.Reset(); - hr = gifConverter->Initialize(bitmap.Get(), GUID_WICPixelFormat8bppIndexed, WICBitmapDitherTypeErrorDiffusion, - palette.Get(), 0.0, WICBitmapPaletteTypeCustom); + WICPixelFormatGUID baseFormat{}; + hr = baseSource->GetPixelFormat(&baseFormat); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "BaseSource::GetPixelFormat"); + } + + if (IsEqualGUID(baseFormat, pixelFormat)) + { + frameSource = baseSource; + } + else + { + Microsoft::WRL::ComPtr converter; + hr = handle.backend->Factory()->CreateFormatConverter(&converter); + if (FAILED(hr)) + { + return recordFailure(hr, "CreateFormatConverter (non-indexed)"); + } + + const bool encoderIsGray = (pixelFormat == GUID_WICPixelFormat8bppGray); + const WICBitmapPaletteType paletteType = + encoderIsGray ? WICBitmapPaletteTypeFixedGray256 : WICBitmapPaletteTypeCustom; + IWICPalette* conversionPalette = nullptr; + if (selection.isIndexed && palette) + { + conversionPalette = palette.Get(); + } + hr = converter->Initialize(baseSource.Get(), pixelFormat, WICBitmapDitherTypeNone, conversionPalette, 0.0, + paletteType); + if (FAILED(hr)) + { + return recordFailure(hr, "FormatConverter::Initialize (non-indexed)"); + } + + hr = converter.As(&frameSource); + if (FAILED(hr)) + { + return recordFailure(hr, "FormatConverter::As (non-indexed)"); + } } + } + + gifTransparencyEnabled = gifTransparencyIndex.has_value(); - 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 (encoderIsIndexed && framePalette) + { + hr = frameEncode->SetPalette(framePalette.Get()); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "FrameEncode::SetPalette"); } + } - hr = frameEncode->WritePixels(frame.height, stride, static_cast(indexed.size()), indexed.data()); + double sourceDpiX = 0.0; + double sourceDpiY = 0.0; + if (frame.frame) + { + frame.frame->GetResolution(&sourceDpiX, &sourceDpiY); } - else + + const DWORD requestedDpiX = info ? info->HorDPI : 0; + const DWORD requestedDpiY = info ? info->VerDPI : 0; + const double dpiX = ResolveDpiValue(requestedDpiX, sourceDpiX, 96.0); + const double dpiY = ResolveDpiValue(requestedDpiY, sourceDpiY, 96.0); + + hr = frameEncode->SetResolution(dpiX, dpiY); + if (FAILED(hr)) { - hr = frameEncode->WritePixels(frame.height, frame.stride, static_cast(frame.pixels.size()), frame.pixels.data()); +#if defined(WINCODEC_ERR_UNSUPPORTEDOPERATION) + if (hr != WINCODEC_ERR_UNSUPPORTEDOPERATION) + { + return recordFailure(hr, "FrameEncode::SetResolution"); + } + // Some encoders (e.g., GIF, ICO) do not support DPI metadata. In that + // case, leave the encoder's default resolution untouched. +#else + return recordFailure(hr, "FrameEncode::SetResolution"); +#endif } + + Microsoft::WRL::ComPtr metadataWriter; + if (FAILED(frameEncode->GetMetadataQueryWriter(&metadataWriter))) + { + metadataWriter.Reset(); + } + if (metadataWriter) + { + HRESULT metaHr = ApplyCommentMetadata(mapping.container, metadataWriter.Get(), comment); + if (FAILED(metaHr)) + { + return recordFailure(metaHr, "ApplyCommentMetadata"); + } + + if (mapping.container == GUID_ContainerFormatGif) + { + Microsoft::WRL::ComPtr gifMetadataWriter = encoderMetadataWriter; + if (!gifMetadataWriter) + { + gifMetadataWriter = metadataWriter; + } + + if (info) + { + if (gifMetadataWriter) + { + if (gifMetadataWriter.Get() == encoderMetadataWriter.Get()) + { + const UINT gifMaxDimension = static_cast(std::numeric_limits::max()); + if (targetWidth > gifMaxDimension || targetHeight > gifMaxDimension) + { + return recordFailure(WINCODEC_ERR_INVALIDPARAMETER, "GIF Logical Screen too large"); + } + + PROPVARIANT prop; + PropVariantInit(&prop); + prop.vt = VT_UI2; + prop.uiVal = static_cast(targetWidth); + metaHr = gifMetadataWriter->SetMetadataByName(L"/logscrdesc/Width", &prop); + PropVariantClear(&prop); + if (FAILED(metaHr) && metaHr != WINCODEC_ERR_PROPERTYNOTSUPPORTED && + metaHr != WINCODEC_ERR_PROPERTYNOTFOUND) + { + return recordFailure(metaHr, "Set GIF LogicalScreenWidth"); + } + + PropVariantInit(&prop); + prop.vt = VT_UI2; + prop.uiVal = static_cast(targetHeight); + metaHr = gifMetadataWriter->SetMetadataByName(L"/logscrdesc/Height", &prop); + PropVariantClear(&prop); + if (FAILED(metaHr) && metaHr != WINCODEC_ERR_PROPERTYNOTSUPPORTED && + metaHr != WINCODEC_ERR_PROPERTYNOTFOUND) + { + return recordFailure(metaHr, "Set GIF LogicalScreenHeight"); + } + } + + PROPVARIANT prop; + PropVariantInit(&prop); + prop.vt = VT_UI1 | VT_VECTOR; + prop.caub.cElems = 3; + prop.caub.pElems = + static_cast(CoTaskMemAlloc(prop.caub.cElems * sizeof(BYTE))); + if (prop.caub.pElems) + { + const char* version = (info->Flags & PVSF_GIF89) != 0 ? "89a" : "87a"; + memcpy(prop.caub.pElems, version, prop.caub.cElems); + metaHr = gifMetadataWriter->SetMetadataByName(L"/logscrdesc/Version", &prop); + } + else + { + metaHr = E_OUTOFMEMORY; + } + PropVariantClear(&prop); + if (FAILED(metaHr) && metaHr != WINCODEC_ERR_PROPERTYNOTSUPPORTED && + metaHr != WINCODEC_ERR_PROPERTYNOTFOUND && metaHr != E_INVALIDARG && + metaHr != HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)) + { + return recordFailure(metaHr, "Set GIF Version"); + } + } + } + + if (hasGifInterlaceFlag) + { + PROPVARIANT prop; + PropVariantInit(&prop); + prop.vt = VT_BOOL; + prop.boolVal = gifInterlaceFlag ? VARIANT_TRUE : VARIANT_FALSE; + metaHr = metadataWriter->SetMetadataByName(L"/imgdesc/InterlaceFlag", &prop); + PropVariantClear(&prop); + if (FAILED(metaHr) && metaHr != WINCODEC_ERR_PROPERTYNOTSUPPORTED && + metaHr != WINCODEC_ERR_PROPERTYNOTFOUND) + { + return recordFailure(metaHr, "Set GIF InterlaceFlag"); + } + } + + PROPVARIANT prop; + PropVariantInit(&prop); + prop.vt = VT_BOOL; + prop.boolVal = gifTransparencyEnabled ? VARIANT_TRUE : VARIANT_FALSE; + metaHr = metadataWriter->SetMetadataByName(L"/grctlext/TransparencyFlag", &prop); + PropVariantClear(&prop); + if (FAILED(metaHr) && metaHr != WINCODEC_ERR_PROPERTYNOTSUPPORTED && metaHr != WINCODEC_ERR_PROPERTYNOTFOUND) + { + return recordFailure(metaHr, "Set GIF TransparencyFlag"); + } + + if (gifTransparencyIndex.has_value()) + { + PropVariantInit(&prop); + prop.vt = VT_UI1; + prop.bVal = static_cast(gifTransparencyIndex.value()); + metaHr = metadataWriter->SetMetadataByName(L"/grctlext/TransparentColorIndex", &prop); + PropVariantClear(&prop); + if (FAILED(metaHr) && metaHr != WINCODEC_ERR_PROPERTYNOTSUPPORTED && metaHr != WINCODEC_ERR_PROPERTYNOTFOUND) + { + return recordFailure(metaHr, "Set GIF TransparentColorIndex"); + } + } + } + + if (mapping.container == GUID_ContainerFormatTiff && info) + { + UINT rowsPerStrip = 0; + if ((info->Flags & PVSF_DO_NOT_STRIP) != 0) + { + rowsPerStrip = targetHeight; + } + else if (info->Misc.TIFF.StripSize != 0 && encodedStride != 0) + { + const ULONGLONG stripBytes = static_cast(info->Misc.TIFF.StripSize) * 1024ull; + if (stripBytes > 0) + { + ULONGLONG rows = stripBytes / encodedStride; + if (rows == 0) + { + rows = 1; + } + if (rows > targetHeight) + { + rows = targetHeight; + } + rowsPerStrip = static_cast(rows); + } + } + if (rowsPerStrip > 0) + { + PROPVARIANT prop; + PropVariantInit(&prop); + prop.vt = VT_UI4; + prop.ulVal = rowsPerStrip; + metaHr = metadataWriter->SetMetadataByName(L"/ifd/{ushort=278}", &prop); + PropVariantClear(&prop); + if (FAILED(metaHr) && metaHr != WINCODEC_ERR_PROPERTYNOTSUPPORTED && metaHr != WINCODEC_ERR_PROPERTYNOTFOUND) + { + return recordFailure(metaHr, "Set TIFF RowsPerStrip"); + } + } + } + } + + hr = frameEncode->WriteSource(frameSource.Get(), nullptr); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "FrameEncode::WriteSource"); } hr = frameEncode->Commit(); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "FrameEncode::Commit"); } hr = encoder->Commit(); if (FAILED(hr)) { - return HResultToPvCode(hr); + return recordFailure(hr, "Encoder::Commit"); } return PVC_OK; } From f2b555b469e01c1f88892b959dee34390cbe5e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kotas?= Date: Fri, 10 Oct 2025 00:46:38 +0200 Subject: [PATCH 12/14] Fix context menu handling in PictView (#201) --- src/plugins/pictview/render1.cpp | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/plugins/pictview/render1.cpp b/src/plugins/pictview/render1.cpp index bb8a4f932..580d1c61a 100644 --- a/src/plugins/pictview/render1.cpp +++ b/src/plugins/pictview/render1.cpp @@ -2266,14 +2266,6 @@ LRESULT CRendererWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) return 0; } - case WM_RBUTTONDOWN: - { - POINT p; - GetCursorPos(&p); - OnContextMenu(&p); - break; - } - case WM_SYSKEYDOWN: case WM_KEYDOWN: { @@ -2338,6 +2330,28 @@ LRESULT CRendererWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) break; } + case WM_CONTEXTMENU: + { + if ((HWND)wParam == HWindow) + { + POINT p; + if ((int)lParam == -1) + { + DWORD pos = GetMessagePos(); + p.x = GET_X_LPARAM(pos); + p.y = GET_Y_LPARAM(pos); + } + else + { + p.x = GET_X_LPARAM(lParam); + p.y = GET_Y_LPARAM(lParam); + } + OnContextMenu(&p); + return 0; + } + break; + } + case WM_SYSKEYUP: case WM_KEYUP: { From eb72902a28eda747b7754d1c2b482aca7b0880ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kotas?= Date: Fri, 10 Oct 2025 00:50:05 +0200 Subject: [PATCH 13/14] revert translation files unwanted Codex changes --- translations/chinesesimplified/pictview.slt | 66 ++++++++++----------- translations/czech/pictview.slt | 64 ++++++++++---------- translations/dutch/pictview.slt | 62 +++++++++---------- 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 ++++++++++---------- 10 files changed, 325 insertions(+), 325 deletions(-) diff --git a/translations/chinesesimplified/pictview.slt b/translations/chinesesimplified/pictview.slt index 82343bf1f..3bf9c8402 100644 --- a/translations/chinesesimplified/pictview.slt +++ b/translations/chinesesimplified/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\chinesesimplified\pictview.atp" -TEXTVERSION,"2.20 (x64)" -VERSION,"2,2,0,0" +TEXTVERSION,"2.13 (x86)" +VERSION,"2,1,3,180" [TRANSLATION] LANGID,2052 @@ -15,13 +15,13 @@ COMMENT,"简体中文版" 2013,11,9,20,20,1,"" 2007,46,9,288,8,1,"PictView %s - 用于 Open Salamander 的图像查看器" 2011,46,19,288,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" -2008,46,51,277,8,1,"%hs" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" -3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@pictview.com" -3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.pictview.com/salamander" +3001,46,40,124,8,1,"此产品基于库" +2008,46,51,277,8,1,"%hs - 图像处理引擎" +3002,46,62,198,8,1,"版权所有 © 1994-2023 Jan Patera" +3005,46,81,23,8,1,"Email:" +2009,71,81,85,8,1,"support@pictview.com" +3006,46,92,22,8,1,"主页:" +2010,71,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,339,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"简体中文版" 2021,14,17,151,12,1,"和 Open Salamander 的一样(&M)" 2022,14,29,148,12,1,"如果需要就更大,但是不会更小(&L)" 2023,14,41,148,12,1,"跟随需要(&N)" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,60,256,67,1,"默认缩放" 2024,14,72,151,12,1,"适应完整(无滚动条)(&F)" 2025,14,84,148,12,1,"适应宽度(无水平滚动条)(&W)" 2026,14,96,148,12,1,"原始大小(&O)" @@ -39,7 +39,7 @@ COMMENT,"简体中文版" [DIALOG 2040] 268,150,1,"高级" -3006,46,92,50,8,1,"Project site:" +3006,6,5,256,69,1,"文件面板中的缩略图" 3007,14,18,240,16,1,"一些图像可能包含不正确的缩略图,这些缩略图没有正确旋转或没有反应最新的变更。" 2050,14,35,200,12,1,"忽略存储在图像文件中的原始缩略图(更慢)(&I)" 3008,14,51,241,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"简体中文版" [DIALOG 2070] 268,150,1,"工具" 3000,6,5,256,94,1,"选择工具" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,18,150,8,1,"当按住 Shift 键时选择的区域:" 2071,24,27,203,12,1,"正方形(&S)" 2072,24,40,218,12,1,"&4:3 长方形" 2073,24,53,218,12,1,"&3:2 长方形" 2074,24,66,218,12,1,"&16:9 长方形" 2075,24,79,45,12,1,"其他(&O):" 2076,70,79,17,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,89,81,4,8,1,":" 2077,93,79,17,12,1,"" 2078,7,106,167,12,1,"颜色吸管以十六进制挑选颜色(&X)" [DIALOG 2080] 268,150,1,"颜色" 3000,6,5,256,50,1,"窗口模式" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,19,103,8,1,"背景(&B)" 2082,128,16,27,14,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,17,36,91,8,1,"透明(&T)" 2081,128,33,27,14,1,"" 3003,6,60,256,50,1,"全屏模式" 3004,17,75,103,8,1,"背景(&A)" 2084,128,72,27,14,1,"" -3005,46,81,30,8,1,"Email:" +3005,17,92,44,8,1,"透明(&R)" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"简体中文版" 2103,67,8,100,12,1,"" 3000,7,24,26,8,1,"宽(&W):" 2104,67,22,100,12,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,7,38,24,8,1,"高(&H):" 2105,67,36,100,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,7,52,33,8,1,"颜色(&C):" 2106,67,50,100,12,1,"" 3003,7,66,58,8,1,"内存中大小(&S):" 2107,67,64,100,12,1,"" 3004,7,80,49,8,1,"文件大小(&F):" 2108,67,78,100,12,1,"" -3005,46,81,30,8,1,"Email:" +3005,7,94,17,8,1,"&DPI:" 2109,67,92,100,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,7,108,33,8,1,"格式(&R):" 2110,67,106,100,12,1,"" 3007,7,122,33,8,1,"压缩(&O):" 2111,67,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"简体中文版" 121,58,1,"缩放到" 3000,8,13,49,8,1,"放大率(&M):" 2131,57,11,44,156,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,105,13,9,8,1,"%" 1,7,37,50,14,1,"确定" 2,63,37,50,14,1,"取消" @@ -143,12 +143,12 @@ COMMENT,"简体中文版" 2157,16,43,61,12,1,"前台窗口(&W)" 2156,16,55,104,12,1,"前台窗口的客户区域(&A) " 2159,16,67,107,12,1,"虚拟屏幕(为多显示器)(&V)" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,92,167,49,1,"捕捉触发器" 2155,16,106,41,12,1,"热键(&H)" 2153,16,121,50,12,1,"计时器(&T)" 2154,67,106,77,12,1,"" 2151,67,121,21,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,92,123,11,8,1,"秒" 2152,8,147,73,12,1,"包括鼠标光标(&I)" 3003,2,167,176,1,1,"" 1,8,172,50,14,1,"捕捉(&C)" @@ -167,7 +167,7 @@ COMMENT,"简体中文版" 3000,5,14,39,8,1,"覆盖文件:" 2181,48,14,286,8,1,"" 2182,48,24,286,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,5,37,39,8,1,"用文件:" 2183,48,37,286,8,1,"" 2184,48,47,286,8,1,"" 6,86,66,50,14,1,"是(&Y)" @@ -179,9 +179,9 @@ COMMENT,"简体中文版" 2300,5,2,33,8,1,"压缩(&C):" 2301,58,0,170,80,1,"" 2302,234,0,50,14,1,"选项(&O)" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,5,26,49,8,1,"颜色深度(&D):" 2303,58,24,70,80,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,5,46,49,8,1,"旋转(&R):" 2304,58,44,70,80,1,"" 3003,5,66,52,8,1,"翻转/镜像(&F):" 2305,58,64,70,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"简体中文版" 2514,232,15,118,8,1,"" 2515,354,12,50,14,1,"设置(&S)..." 3000,225,35,186,62,1," 位置 " -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,238,49,25,8,1,"左(&L):" 2503,267,47,45,12,1,"" 2504,318,47,66,75,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,238,68,25,8,1,"顶(&T):" 2501,267,66,45,12,1,"" 2502,318,66,66,75,1,"" 2516,267,82,60,12,1,"居中图像(&C)" 3004,225,102,186,100,1," 缩放打印大小 " -3005,46,81,30,8,1,"Email:" +3005,231,118,32,8,1,"缩放(&E):" 2505,267,116,35,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,305,118,10,8,1,"%" 2506,318,111,84,12,1,"缩放以适应媒体(&F)" 2507,318,123,70,12,1,"保持宽高比(&K)" 3007,231,139,32,8,1,"宽(&W):" @@ -362,7 +362,7 @@ COMMENT,"简体中文版" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Error calling Windows Imaging Component (WIC)" +1009,1,"调用 PVW32Cnv.DLL 错误" 1010,1,"红: %i, 绿: %i, 蓝: %i" 1011,1,"红: %#02x, 绿: %#02x, 蓝: %#02x" 1012,1,"(%i, %i): 红: %i, 绿: %i, 蓝: %i" @@ -463,8 +463,8 @@ COMMENT,"简体中文版" 1102,1,"显示其他通道(O)" [STRINGTABLE 15] -1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." -1110,1,"An incompatible Windows Imaging Component (WIC) version was found." +1109,1,"无法加载图像处理库 PVW32Cnv.dll." +1110,1,"发现错误的 PVW32Cnv.dll 版本。" 1111,1,"请先选择一个区域。" 1112,1,"默认" 1113,1,"文件 "%s" 已经存在。\n你要替换它吗?" diff --git a/translations/czech/pictview.slt b/translations/czech/pictview.slt index 17d321108..b1c17f50d 100644 --- a/translations/czech/pictview.slt +++ b/translations/czech/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\czech\pictview.atp" -TEXTVERSION,"2.20 (x64)" -VERSION,"2,2,0,0" +TEXTVERSION,"2.13 (x86)" +VERSION,"2,1,3,180" [TRANSLATION] LANGID,1029 @@ -15,13 +15,13 @@ COMMENT,"Česká verze" 2013,11,9,20,20,1,"" 2007,46,9,340,8,1,"Prohlížeč PictView %s - Prohlížeč obrázků pro Open Salamander" 2011,46,19,340,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" -2008,46,51,277,8,1,"%hs" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3001,46,40,123,8,1,"Tento produkt využívá knihovnu" +2008,46,51,338,8,1,"%hs - Image Processing Engine" +3002,46,62,262,8,1,"Copyright © 1994-2023 Jan Patera" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@pictview.com" -3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.pictview.com/salamander" +2009,115,81,85,8,1,"support@pictview.com" +3006,46,92,66,8,1,"Domovská stránka:" +2010,115,92,122,8,1,"www.pictview.com/cz/salamander" 3004,2,108,391,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Česká verze" 2021,14,17,151,12,1,"&Stejná jako u Open Salamandera" 2022,14,29,148,12,1,"Vě&tší, je-li potřeba, ale ne menší" 2023,14,41,148,12,1,"D&le potřeby" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,60,273,67,1,"Výchozí způsob zvětšení" 2024,14,72,151,12,1,"&Umístit do okna (žádné posuvníky)" 2025,14,84,165,12,1,"U&místit na šířku (bez vodorovného posuvníku)" 2026,14,96,148,12,1,"&Původní velikost" @@ -39,7 +39,7 @@ COMMENT,"Česká verze" [DIALOG 2040] 285,150,1,"Rozšířené" -3006,46,92,50,8,1,"Project site:" +3006,6,5,273,69,1,"Miniatury v souborových panelech" 3007,14,18,225,16,1,"Některé soubory s obrázky mohou obsahovat neaktuální miniaturu, která je špatně otočená nebo neodráží poslední úpravy obrázku." 2050,14,35,204,12,1,"&Ignorovat miniatury uložené přímo v souborech (pomalé)" 3008,14,51,257,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Česká verze" [DIALOG 2070] 285,150,1,"Nástroje" 3000,6,5,273,94,1,"Výběr" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,18,150,8,1,"Oblast vybíraná při stisku klávesy Shift:" 2071,24,27,203,12,1,"Čtvere&c" 2072,24,40,218,12,1,"Obdélník &4:3" 2073,24,53,218,12,1,"Obdélník &3:2" 2074,24,66,218,12,1,"Obdélník &16:9" 2075,24,79,35,12,1,"&Jiná:" 2076,60,79,17,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,79,81,4,8,1,":" 2077,83,79,17,12,1,"" 2078,7,106,167,12,1,"Ukazovat barvy pipetou he&xadecimálně" [DIALOG 2080] 285,150,1,"Barvy" 3000,6,5,273,50,1,"Zobrazení v okně" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,19,103,8,1,"&Pozadí" 2082,128,16,27,14,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,17,36,91,8,1,"Průhledná &barva" 2081,128,33,27,14,1,"" 3003,6,60,273,50,1,"Zobrazení přes celou obrazovku" 3004,17,75,103,8,1,"P&ozadí" 2084,128,72,27,14,1,"" -3005,46,81,30,8,1,"Email:" +3005,17,92,58,8,1,"Průhledná b&arva" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Česká verze" 2103,70,8,100,12,1,"" 3000,7,24,49,8,1,"Šíř&ka:" 2104,70,22,100,12,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,7,38,49,8,1,"&Výška:" 2105,70,36,100,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,7,52,44,8,1,"Počet &barev:" 2106,70,50,100,12,1,"" 3003,7,66,61,8,1,"Velikost (&paměť):" 2107,70,64,100,12,1,"" 3004,7,80,52,8,1,"Velikost (d&isk):" 2108,70,78,100,12,1,"" -3005,46,81,30,8,1,"Email:" +3005,7,94,50,8,1,"&DPI:" 2109,70,92,100,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,7,108,50,8,1,"&Formát:" 2110,70,106,100,12,1,"" 3007,7,122,50,8,1,"K&omprese:" 2111,70,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Česká verze" 121,58,1,"Zvětšení obrázku" 3000,8,13,49,8,1,"&Poměr:" 2131,57,11,44,156,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,105,13,9,8,1,"%" 1,7,37,50,14,1,"OK" 2,63,37,50,14,1,"Storno" @@ -143,12 +143,12 @@ COMMENT,"Česká verze" 2157,16,43,120,12,1,"&Aktivní okno" 2156,16,55,135,12,1,"&Klientskou oblast aktivního okna " 2159,16,67,148,12,1,"&Virtuální obrazovku (při více monitorech)" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,92,167,49,1,"Spoušť" 2155,16,106,58,12,1,"&Horká klávesa" 2153,16,121,40,12,1,"Čas&ovač" 2154,75,106,77,12,1,"" 2151,75,121,21,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,100,123,33,8,1,"vteřin" 2152,8,147,85,12,1,"Včetně kurzoru &myši" 3003,2,167,166,1,1,"" 1,8,172,50,14,1,"&Sejmout" @@ -167,7 +167,7 @@ COMMENT,"Česká verze" 3000,5,14,78,8,1,"Má se přepsat soubor:" 2181,85,14,286,8,1,"" 2182,85,24,286,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,5,37,78,8,1,"tímto souborem:" 2183,85,37,286,8,1,"" 2184,85,47,286,8,1,"" 6,104,66,50,14,1,"&Ano" @@ -179,9 +179,9 @@ COMMENT,"Česká verze" 2300,5,2,49,8,1,"&Komprese:" 2301,54,0,170,80,1,"" 2302,230,0,50,14,1,"&Možnosti" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,5,26,49,8,1,"Počet &barev:" 2303,54,24,70,80,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,5,46,49,8,1,"&Otočení:" 2304,54,44,70,80,1,"" 3003,5,66,49,8,1,"&Zrcadlení:" 2305,54,64,70,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Česká verze" 2514,232,15,118,8,1,"" 2515,354,12,50,14,1,"&Nastavení..." 3000,225,35,186,62,1," Umístění na papíře " -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,234,49,24,8,1,"V&levo:" 2503,262,47,45,12,1,"" 2504,313,47,66,75,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,234,68,24,8,1,"&Shora:" 2501,262,66,45,12,1,"" 2502,313,66,66,75,1,"" 2516,262,82,60,12,1,"&Centrovat" 3004,225,102,186,100,1," Přizpůsobit velikost " -3005,46,81,30,8,1,"Email:" +3005,233,118,25,8,1,"&Poměr:" 2505,262,116,35,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,300,118,10,8,1,"%" 2506,313,111,72,12,1,"Přizpůsobit p&apíru" 2507,313,123,86,12,1,"&Zachovat poměr stran" 3007,233,139,25,8,1,"Šíř&ka:" @@ -362,7 +362,7 @@ COMMENT,"Česká verze" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Error calling Windows Imaging Component (WIC)" +1009,1,"Chyba při volání PVW32Cnv.DLL" 1010,1,"Červená: %i, Zelená: %i, Modrá: %i" 1011,1,"Červená: %#02x, Zelená: %#02x, Modrá: %#02x" 1012,1,"(%i, %i): Červená: %i, Zelená: %i, Modrá: %i" @@ -463,8 +463,8 @@ COMMENT,"Česká verze" 1102,1,"Ukázat ostatní kanály (O)" [STRINGTABLE 15] -1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." -1110,1,"An incompatible Windows Imaging Component (WIC) version was found." +1109,1,"Nelze najít knihovnu PVW32Cnv.dll pro zpracování obrázků." +1110,1,"Byla nalezena špatná verze knihovny PVW32Cnv.dll." 1111,1,"Není vybrána žádná oblast." 1112,1,"Výchozí" 1113,1,"Soubor "%s" již existuje.\nMá být přepsán?" diff --git a/translations/dutch/pictview.slt b/translations/dutch/pictview.slt index 334894a8d..930d7b368 100644 --- a/translations/dutch/pictview.slt +++ b/translations/dutch/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\dutch\pictview.atp" -TEXTVERSION,"2.20 (x64)" -VERSION,"2,2,0,0" +TEXTVERSION,"2.13 (x86)" +VERSION,"2,1,3,180" [TRANSLATION] LANGID,1043 @@ -15,12 +15,12 @@ COMMENT,"Nederlandse versie" 2013,11,9,20,20,1,"" 2007,46,9,309,8,1,"PictView %s - Afbeeldingsviewer voor Open Salamander" 2011,46,19,309,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" -2008,46,51,277,8,1,"%hs" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3001,46,40,138,8,1,"Dit product is gebaseerd op bibliotheken" +2008,46,51,298,8,1,"%hs - Image Processing Engine" +3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@pictview.com" -3006,46,92,50,8,1,"Project site:" +2009,99,81,85,8,1,"support@pictview.com" +3006,46,92,50,8,1,"Start pagina:" 2010,99,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,360,1,1,"" @@ -30,7 +30,7 @@ COMMENT,"Nederlandse versie" 2021,14,17,151,12,1,"Gelijk aan Altap Sala&mander" 2022,14,29,148,12,1,"&Groter indien nodig maar niet kleiner" 2023,14,41,148,12,1,"Indien &nodig" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,60,276,67,1,"Standaard zoom" 2024,14,72,151,12,1,"&Passend (geen schuifbalken)" 2025,14,84,173,12,1,"Aanpassen aan &breedte (geen horiz. schuifbalk)" 2026,14,96,148,12,1,"&Werkelijke grootte" @@ -39,7 +39,7 @@ COMMENT,"Nederlandse versie" [DIALOG 2040] 288,150,1,"Geavanceerd" -3006,46,92,50,8,1,"Project site:" +3006,6,5,276,69,1,"Miniatuurweergave in bestandspanelen" 3007,14,18,260,16,1,"Sommige afbeeldingen kunnen een onjuiste miniatuurweergave hebben, die niet juist gedraaid is of niet de laatste wijzigingen weergeeft." 2050,14,35,225,12,1,"&Negeer oorspronkelijke miniaturen in afbeeldingen (langzamer)" 3008,14,51,260,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Nederlandse versie" [DIALOG 2070] 288,150,1,"Gereedschappen" 3000,6,5,276,94,1,"Selectie gereedschap" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,18,150,8,1,"Gebied selectie bij vasthouden Shift toets:" 2071,24,27,203,12,1,"&Vierkant" 2072,24,40,218,12,1,"&4:3 rechthoek" 2073,24,53,218,12,1,"&3:2 rechthoek" 2074,24,66,218,12,1,"&16:9 rechthoek" 2075,24,79,40,12,1,"&Anders:" 2076,67,79,17,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,86,81,4,8,1,":" 2077,90,79,17,12,1,"" 2078,7,106,167,12,1,"Pipet kiest kleuren in &hexadecimalen" [DIALOG 2080] 288,150,1,"Kleuren" 3000,6,5,276,50,1,"Venstermodus" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,19,103,8,1,"&Achtergrond" 2082,128,16,27,14,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,17,36,91,8,1,"&Doorzichtig" 2081,128,33,27,14,1,"" 3003,6,60,276,50,1,"Volledig schermmodus" 3004,17,75,103,8,1,"A&chtergrond" 2084,128,72,27,14,1,"" -3005,46,81,30,8,1,"Email:" +3005,17,92,44,8,1,"D&oorzichtig" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Nederlandse versie" 2103,75,8,90,12,1,"" 3000,7,24,49,8,1,"&Breedte:" 2104,75,22,90,12,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,7,38,49,8,1,"&Hoogte:" 2105,75,36,90,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,7,52,42,8,1,"&Kleuren:" 2106,75,50,90,12,1,"" 3003,7,66,64,8,1,"&Geheugengrootte:" 2107,75,64,90,12,1,"" 3004,7,80,59,8,1,"B&estandsgrootte:" 2108,75,78,90,12,1,"" -3005,46,81,30,8,1,"Email:" +3005,7,94,50,8,1,"&DPI:" 2109,75,92,90,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,7,108,50,8,1,"Fo&rmaat:" 2110,75,106,90,12,1,"" 3007,7,122,50,8,1,"C&ompressie:" 2111,75,120,90,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Nederlandse versie" 121,58,1,"Zoomen naar" 3000,8,13,49,8,1,"&Vergroting:" 2131,57,11,44,156,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,105,13,9,8,1,"%" 1,7,37,50,14,1,"OK" 2,63,37,50,14,1,"Annuleren" @@ -143,12 +143,12 @@ COMMENT,"Nederlandse versie" 2157,16,43,120,12,1,"Voo&rgrondvenster" 2156,16,55,135,12,1,"Client&gebied van voorgrondvenster " 2159,16,67,154,12,1,"&Virtueelscherm (voor meerdere schermen)" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,92,172,49,1,"Trigger" 2155,16,106,44,12,1,"&Sneltoets" 2153,16,121,44,12,1,"&Timer" 2154,65,106,84,12,1,"" 2151,65,121,21,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,90,123,33,8,1,"seconden" 2152,8,147,85,12,1,"&Inclusief muiscursor" 3003,2,167,180,1,1,"" 1,11,172,50,14,1,"Vast&leggen" @@ -167,7 +167,7 @@ COMMENT,"Nederlandse versie" 3000,4,14,78,8,1,"Bestand overschrijven:" 2181,86,14,286,8,1,"" 2182,86,24,286,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,4,37,78,8,1,"Met bestand:" 2183,86,37,286,8,1,"" 2184,86,47,286,8,1,"" 6,105,66,50,14,1,"&Ja" @@ -179,9 +179,9 @@ COMMENT,"Nederlandse versie" 2300,5,2,49,8,1,"&Compressie:" 2301,76,0,170,80,1,"" 2302,252,0,50,14,1,"&Opties" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,5,26,49,8,1,"Kleu&rdiepte:" 2303,76,24,70,80,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,5,46,49,8,1,"&Draaien:" 2304,76,44,70,80,1,"" 3003,5,66,70,8,1,"O&mslaan/Spiegelen:" 2305,76,64,70,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Nederlandse versie" 2514,232,15,131,8,1,"" 2515,367,12,50,14,1,"&Setup..." 3000,225,35,195,62,1," Positie " -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,240,49,22,8,1,"&Links:" 2503,266,47,45,12,1,"" 2504,317,47,66,75,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,244,68,18,8,1,"&Top:" 2501,266,66,45,12,1,"" 2502,317,66,66,75,1,"" 2516,266,82,83,12,1,"A&fbeelding centreren" 3004,225,102,195,100,1," Geschaalde afdruk grootte " -3005,46,81,30,8,1,"Email:" +3005,235,118,27,8,1,"S&chaal:" 2505,266,116,35,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,304,118,10,8,1,"%" 2506,317,111,98,12,1,"Maak &passend aan media" 2507,317,123,78,12,1,"Behoud &verhouding" 3007,231,139,31,8,1,"Br&eedte:" @@ -362,7 +362,7 @@ COMMENT,"Nederlandse versie" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Error calling Windows Imaging Component (WIC)" +1009,1,"Fout bij aanroep PVW32Cnv.DLL" 1010,1,"Rood: %i, Groen: %i, Blauw: %i" 1011,1,"Rood: %#02x, Groen: %#02x, Blauw: %#02x" 1012,1,"(%i, %i): Rood: %i, Groen: %i, Blauw: %i" @@ -463,8 +463,8 @@ COMMENT,"Nederlandse versie" 1102,1,"Andere kanalen weergeven (O)" [STRINGTABLE 15] -1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." -1110,1,"An incompatible Windows Imaging Component (WIC) version was found." +1109,1,"Kan de afbeelding verwerkingsbibiliotheek PVW32Cnv.dll niet laden." +1110,1,"De verkeerde versie van PVW32Cnv.dll is gevonden." 1111,1,"Selecteer eerst een regio alstublieft." 1112,1,"Standaard" 1113,1,"Bestand "%s" bestaat reeds.\nWilt u deze vervangen?" diff --git a/translations/french/pictview.slt b/translations/french/pictview.slt index a261cc266..6466a39df 100644 --- a/translations/french/pictview.slt +++ b/translations/french/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\french\pictview.atp" -TEXTVERSION,"2.20 (x64)" -VERSION,"2,2,0,0" +TEXTVERSION,"2.13 (x86)" +VERSION,"2,1,3,180" [TRANSLATION] LANGID,1036 @@ -15,13 +15,13 @@ COMMENT,"Version française" 2013,11,9,20,20,1,"" 2007,46,9,313,8,1,"PictView %s - Visualiseur d'image pour Open Salamander" 2011,46,19,313,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" -2008,46,51,277,8,1,"%hs" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" -3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@pictview.com" -3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.pictview.com/salamander" +3001,46,40,132,8,1,"Ce produit est basé sur la bibliothèque" +2008,46,51,302,8,1,"%hs - Image Processing Engine" +3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" +3005,46,81,23,8,1,"Email:" +2009,83,81,85,8,1,"support@pictview.com" +3006,46,92,34,8,1,"Site web:" +2010,83,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,364,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Version française" 2021,14,17,117,12,1,"Co&mme pour Open Salamander" 2022,14,29,157,12,1,"Plus &grand si nécessaire mais pas plus petit" 2023,14,41,70,12,1,"Selon les &besoins" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,60,306,67,1,"Zoom par défaut" 2024,14,72,156,12,1,"&Tout remplir (pas de barres de défilement)" 2025,14,84,166,12,1,"Adapté à la &largeur (pas de barre horizontale)" 2026,14,96,64,12,1,"Taille &originelle" @@ -39,7 +39,7 @@ COMMENT,"Version française" [DIALOG 2040] 318,150,1,"Avancé" -3006,46,92,50,8,1,"Project site:" +3006,6,5,306,69,1,"Miniatures dans les panneaux de fichiers" 3007,14,18,240,16,1,"Certaines images peuvent présenter une miniature erronée, mal positionnée ou qui ne présente pas les derniers changements." 2050,14,35,280,12,1,"&Ignorer les miniatures originelles enregistrées dans les fichiers image (plus lent)" 3008,14,51,241,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Version française" [DIALOG 2070] 318,150,1,"Outils" 3000,6,5,306,94,1,"Sélection de l'outil" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,18,164,8,1,"Zone sélectionnée avec la touche Maj enfoncée:" 2071,24,27,203,12,1,"&Carré" 2072,24,40,218,12,1,"rectangle &4:3" 2073,24,53,218,12,1,"rectangle &3:2" 2074,24,66,218,12,1,"rectangle &16:9" 2075,24,79,35,12,1,"&Autre:" 2076,60,79,17,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,79,81,4,8,1,":" 2077,83,79,17,12,1,"" 2078,7,106,167,12,1,"La pipette pointe les couleurs en he&xadécimal" [DIALOG 2080] 318,150,1,"Couleurs" 3000,6,5,306,50,1,"Mode fenêtré" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,19,103,8,1,"&Fond" 2082,128,16,27,14,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,17,36,91,8,1,"&Transparent" 2081,128,33,27,14,1,"" 3003,6,60,306,50,1,"Mode plein écran" 3004,17,75,103,8,1,"F&ond" 2084,128,72,27,14,1,"" -3005,46,81,30,8,1,"Email:" +3005,17,92,44,8,1,"T&ransparent" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Version française" 2103,73,8,100,12,1,"" 3000,7,24,49,8,1,"&Largeur:" 2104,73,22,100,12,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,7,38,49,8,1,"&Hauteur:" 2105,73,36,100,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,7,52,42,8,1,"&Couleurs:" 2106,73,50,100,12,1,"" 3003,7,66,65,8,1,"&Taille en mémoire:" 2107,73,64,100,12,1,"" 3004,7,80,56,8,1,"Taille du &fichier:" 2108,73,78,100,12,1,"" -3005,46,81,30,8,1,"Email:" +3005,7,94,50,8,1,"&DPI:" 2109,73,92,100,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,7,108,50,8,1,"Fo&rmat:" 2110,73,106,100,12,1,"" 3007,7,122,50,8,1,"C&ompression:" 2111,73,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Version française" 132,58,1,"Zoomer jusque" 3000,8,13,59,8,1,"&Agrandissement:" 2131,68,11,44,156,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,113,13,9,8,1,"%" 1,13,37,50,14,1,"OK" 2,69,37,50,14,1,"Annuler" @@ -143,12 +143,12 @@ COMMENT,"Version française" 2157,16,43,120,12,1,"&Fenêtre à l'avant-plan" 2156,16,55,141,12,1,"&Zone client de la fenêtre d'avant-plan " 2159,16,67,146,12,1,"Ecran &virtuel (pour moniteurs multiples)" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,92,167,49,1,"Déclencheur de capture" 2155,16,106,46,12,1,"&Raccourci" 2153,16,121,39,12,1,"&Horloge" 2154,63,106,77,12,1,"" 2151,63,121,21,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,88,123,33,8,1,"secondes" 2152,8,147,104,12,1,"&Inclure le curseur de souris" 3003,2,167,176,1,1,"" 1,8,172,50,14,1,"&Capturer" @@ -167,7 +167,7 @@ COMMENT,"Version française" 3000,4,14,61,8,1,"Ecraser le fichier:" 2181,69,14,286,8,1,"" 2182,69,24,286,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,4,37,61,8,1,"Par le fichier:" 2183,69,37,286,8,1,"" 2184,69,47,286,8,1,"" 6,96,66,50,14,1,"&Oui" @@ -179,9 +179,9 @@ COMMENT,"Version française" 2300,5,2,49,8,1,"&Compression:" 2301,54,0,199,80,1,"" 2302,259,0,50,14,1,"&Options" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,5,26,80,8,1,"&Profondeur de couleur:" 2303,86,24,81,80,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,5,46,33,8,1,"&Rotation:" 2304,86,44,81,80,1,"" 3003,5,66,78,8,1,"Réfle&xion/Mirroir:" 2305,86,64,81,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Version française" 2514,232,15,140,8,1,"" 2515,376,12,50,14,1,"&Réglage..." 3000,225,35,208,62,1," Position " -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,231,49,31,8,1,"&Gauche:" 2503,266,47,45,12,1,"" 2504,317,47,66,75,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,231,68,31,8,1,"&Haut:" 2501,266,66,45,12,1,"" 2502,317,66,66,75,1,"" 2516,266,82,64,12,1,"&Centrer l'image" 3004,225,102,208,100,1," Mise à l'échelle de l'impression " -3005,46,81,30,8,1,"Email:" +3005,231,118,31,8,1,"&Echelle:" 2505,266,116,35,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,304,118,10,8,1,"%" 2506,317,111,71,12,1,"Adapter au &média" 2507,317,123,111,12,1,"C&onserver le rapport d'aspect" 3007,231,139,31,8,1,"&Largeur:" @@ -362,7 +362,7 @@ COMMENT,"Version française" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Error calling Windows Imaging Component (WIC)" +1009,1,"Erreur d'appel de PVW32Cnv.DLL" 1010,1,"Rouge: %i, Vert: %i, Bleu: %i" 1011,1,"Rouge: %#02x, Vert: %#02x, Bleu: %#02x" 1012,1,"(%i, %i): Rouge: %i, Vert: %i, Bleu: %i" @@ -463,8 +463,8 @@ COMMENT,"Version française" 1102,1,"Voir d'autres canaux (O)" [STRINGTABLE 15] -1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." -1110,1,"An incompatible Windows Imaging Component (WIC) version was found." +1109,1,"Impossible de charger la bibliothèque de traitement d'image PVW32Cnv.dll." +1110,1,"Une mauvaise version de PVW32Cnv.dll a été trouvée." 1111,1,"Veuillez d'abord choisir une région." 1112,1,"Par défaut" 1113,1,"Le fichier "%s" existe déjà.\nVoulez-vous le remplacer?" diff --git a/translations/german/pictview.slt b/translations/german/pictview.slt index 4cfc56187..95e77790c 100644 --- a/translations/german/pictview.slt +++ b/translations/german/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\german\pictview.atp" -TEXTVERSION,"2.20 (x64)" -VERSION,"2,2,0,0" +TEXTVERSION,"2.13 (x86)" +VERSION,"2,1,3,180" [TRANSLATION] LANGID,1031 @@ -15,13 +15,13 @@ COMMENT,"Deutsche Version" 2013,11,9,20,20,1,"" 2007,46,9,334,8,1,"PictView %s - Grafikbetrachter-Plugin für Open Salamander" 2011,46,19,334,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" -2008,46,51,277,8,1,"%hs" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" -3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@pictview.com" -3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.pictview.com/salamander" +3001,46,40,150,8,1,"PictView basiert auf der Programmbibliothek" +2008,46,51,323,8,1,"%hs - Bildverarbeitungseinheit" +3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" +3005,46,81,26,8,1,"E-Mail:" +2009,84,81,85,8,1,"support@pictview.com" +3006,46,92,36,8,1,"Webseite:" +2010,84,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,385,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Deutsche Version" 2021,14,17,175,12,1,"&Fenstergröße des Open Salamanders verwenden" 2022,14,29,194,12,1,"&Automatische Anpassung - keine Fensterverkleinerung" 2023,14,41,97,12,1,"A&utomatische Anpassung" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,60,293,67,1,"Zoomeinstellungen" 2024,14,72,224,12,1,"Automatische Anpassung an Fenster&göße (keine Bildlaufleisten)" 2025,14,84,270,12,1,"Automatische Anpassung an Fenster&breite (keine horizontalen Bildlaufleisten)" 2026,14,96,58,12,1,"&Originalgröße" @@ -39,7 +39,7 @@ COMMENT,"Deutsche Version" [DIALOG 2040] 305,150,1,"Erweitert" -3006,46,92,50,8,1,"Project site:" +3006,6,5,293,69,1,"Miniaturansichten in Dateilisten" 3007,14,18,277,16,1,"Einige Grafikdateien können eine fehlerhafte Miniaturansicht enthalten, welche nicht richtig gedreht wurden oder nicht die letzten Änderungen wiederspiegeln." 2050,14,35,233,12,1,"&Interne Miniaturansichten in Grafikdateien ignorieren (langsamer)" 3008,14,51,257,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Deutsche Version" [DIALOG 2070] 305,150,1,"Werkzeuge" 3000,6,5,293,94,1,"Auswahl" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,18,173,8,1,"Umschalt-Taste aktiviert folgenden Auswahlmodus:" 2071,24,27,203,12,1,"&Quadratisch" 2072,24,40,218,12,1,"Rechteckig - Verhältnis &4:3" 2073,24,53,218,12,1,"Rechteckig - Verhältnis &3:2" 2074,24,66,218,12,1,"Rechteckig - Verhältnis &16:9" 2075,24,79,41,12,1,"&Weitere:" 2076,66,79,17,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,85,81,4,8,1,":" 2077,89,79,17,12,1,"" 2078,7,106,167,12,1,"He&xadezimale Farbwerte an Pipette anzeigen" [DIALOG 2080] 305,150,1,"Farben" 3000,6,5,293,50,1,"Fenstereinstellungen" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,19,103,8,1,"&Hintergrund" 2082,128,16,27,14,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,17,36,91,8,1,"&Transparent" 2081,128,33,27,14,1,"" 3003,6,60,293,50,1,"Vollbildeinstellungen" 3004,17,75,103,8,1,"H&intergrund" 2084,128,72,27,14,1,"" -3005,46,81,30,8,1,"Email:" +3005,17,92,44,8,1,"T&ransparent" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Deutsche Version" 2103,67,8,100,12,1,"" 3000,7,24,44,8,1,"Grafik&breite:" 2104,67,22,100,12,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,7,38,42,8,1,"Grafik&höhe:" 2105,67,36,100,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,7,52,50,8,1,"&Farbenanzahl:" 2106,67,50,100,12,1,"" 3003,7,66,44,8,1,"&Grafikgröße:" 2107,67,64,100,12,1,"" 3004,7,80,42,8,1,"&Dateigröße:" 2108,67,78,100,12,1,"" -3005,46,81,30,8,1,"Email:" +3005,7,94,58,8,1,"&Auflösung (DPI):" 2109,67,92,100,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,7,108,29,8,1,"Forma&t:" 2110,67,106,100,12,1,"" 3007,7,122,57,8,1,"&Komprimierung:" 2111,67,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Deutsche Version" 125,58,1,"Zoomfaktor" 3000,8,13,52,8,1,"&Vergrößerung:" 2131,61,11,44,156,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,109,13,9,8,1,"%" 1,9,37,50,14,1,"OK" 2,65,37,50,14,1,"Abbrechen" @@ -143,12 +143,12 @@ COMMENT,"Deutsche Version" 2157,16,43,120,12,1,"F&enster im Vordergrund" 2156,16,55,148,12,1,"&Arbeitsbereich des Vordergrundfensters " 2159,16,67,162,12,1,"Virtueller &Bildschirm (bei Mehrschirmbetrieb)" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,92,182,49,1,"Methode" 2155,16,106,56,12,1,"Tasten&kürzel" 2153,16,121,73,12,1,"A&utomatisch nach" 2154,90,106,92,12,1,"" 2151,90,121,21,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,115,123,36,8,1,"Sekunden" 2152,8,147,96,12,1,"&Mauszeiger fotografieren" 3003,2,167,190,1,1,"" 1,12,172,57,14,1,"&Fotografieren" @@ -167,7 +167,7 @@ COMMENT,"Deutsche Version" 3000,6,14,48,8,1,"Überschreibe:" 2181,58,14,286,8,1,"" 2182,58,24,286,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,9,37,45,8,1,"mit:" 2183,58,37,286,8,1,"" 2184,58,47,286,8,1,"" 6,91,66,50,14,1,"&Ja" @@ -179,9 +179,9 @@ COMMENT,"Deutsche Version" 2300,5,2,57,8,1,"&Komprimierung:" 2301,63,0,186,80,1,"" 2302,255,0,50,14,1,"&Optionen" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,5,26,49,8,1,"Farb&tiefe:" 2303,63,24,86,80,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,5,46,49,8,1,"&Drehung:" 2304,63,44,86,80,1,"" 3003,5,66,55,8,1,"&Spiegeln:" 2305,63,64,86,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Deutsche Version" 2514,232,15,121,8,1,"" 2515,357,12,70,14,1,"&Seite einrichten..." 3000,225,35,209,62,1," Position " -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,247,49,23,8,1,"&Links:" 2503,274,47,45,12,1,"" 2504,325,47,66,75,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,247,68,23,8,1,"&Oben:" 2501,274,66,45,12,1,"" 2502,325,66,66,75,1,"" 2516,274,82,60,12,1,"Bild &zentrieren" 3004,225,102,209,100,1," Skalierte Ausgabegröße " -3005,46,81,30,8,1,"Email:" +3005,229,118,41,8,1,"S&kalierung:" 2505,274,116,35,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,312,118,10,8,1,"%" 2506,325,111,101,12,1,"Auf &Mediengröße skalieren" 2507,325,123,97,12,1,"&Proportionen beibehalten" 3007,229,139,41,8,1,"&Breite:" @@ -362,7 +362,7 @@ COMMENT,"Deutsche Version" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Error calling Windows Imaging Component (WIC)" +1009,1,"Fehler beim Aufruf der Datei PVW32Cnv.DLL" 1010,1,"Rot: %i, Grün: %i, Blau: %i" 1011,1,"Rot: %#02x, Grün: %#02x, Blau: %#02x" 1012,1,"(%i, %i): Rot: %i, Grün: %i, Blau: %i" @@ -463,8 +463,8 @@ COMMENT,"Deutsche Version" 1102,1,"Weitere Kanäle anzeigen (O)" [STRINGTABLE 15] -1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." -1110,1,"An incompatible Windows Imaging Component (WIC) version was found." +1109,1,"Die Datei PVW32Cnv.DLL konnte nicht geladen werden." +1110,1,"Datei PVW32Cnv.DLL liegt in einer falschen Version vor." 1111,1,"Bitte wählen Sie zuerst einen Bereich aus." 1112,1,"Standard" 1113,1,"Die Datei "%s" existiert bereits.\nWollen Sie die Datei ersetzen?" diff --git a/translations/hungarian/pictview.slt b/translations/hungarian/pictview.slt index c3791d953..7c97c5266 100644 --- a/translations/hungarian/pictview.slt +++ b/translations/hungarian/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\hungarian\pictview.atp" -TEXTVERSION,"2.20 (x64)" -VERSION,"2,2,0,0" +TEXTVERSION,"2.13 (x86)" +VERSION,"2,1,3,180" [TRANSLATION] LANGID,1038 @@ -15,13 +15,13 @@ COMMENT,"Magyar változat" 2013,11,9,20,20,1,"" 2007,46,9,290,8,1,"PictView %s - Képnéző Open Salamander-hez" 2011,46,19,290,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" -2008,46,51,277,8,1,"%hs" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" -3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@pictview.com" -3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.pictview.com/salamander" +3001,46,40,123,8,1,"A termék alapjául szolgál" +2008,46,51,279,8,1,"%hs - Képfeldolgozó modul" +3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" +3005,46,81,23,8,1,"Email:" +2009,76,81,85,8,1,"support@pictview.com" +3006,46,92,29,8,1,"Honlap:" +2010,76,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,341,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Magyar változat" 2021,14,17,151,12,1,"&Mint az Open Salamander-é" 2022,14,29,148,12,1,"Nagyobb, ha ke&ll, de kisebb nem" 2023,14,41,148,12,1,"&Amekkora szükséges" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,60,253,67,1,"Alapértelmezett nagyítás" 2024,14,72,151,12,1,"&Teljes igazítás (nincs görgetősáv)" 2025,14,84,183,12,1,"S&zélességhez igazítás (nincs vízszintes görgetősáv)" 2026,14,96,148,12,1,"&Eredeti méret" @@ -39,7 +39,7 @@ COMMENT,"Magyar változat" [DIALOG 2040] 265,150,1,"Haladó" -3006,46,92,50,8,1,"Project site:" +3006,6,5,253,69,1,"Bélyegképek a fájl panelekben" 3007,14,18,225,16,1,"Néhány kép hibás bélyegképet tartalmaz, melyek nem forgathatók helyesen vagy nem állíthatók vissza az utolsó módosítások." 2050,14,35,214,12,1,"Eredet&i bélyegképek, képbe tárolásának kihagyása (lassabb)" 3008,14,51,237,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Magyar változat" [DIALOG 2070] 265,150,1,"Eszközök" 3000,6,5,253,94,1,"Kijelölő eszköz" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,18,180,8,1,"A terület a Shift gomb nyomva tartásával jelölhető ki:" 2071,24,27,203,12,1,"&Négyzet" 2072,24,40,218,12,1,"&4:3-as téglalap" 2073,24,53,218,12,1,"&3:2-es téglalap" 2074,24,66,218,12,1,"&16:9-es téglalap" 2075,24,79,35,12,1,"&Egyéb:" 2076,60,79,17,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,79,81,4,8,1,":" 2077,83,79,17,12,1,"" 2078,7,106,167,12,1,"He&xadecimális színfelvevő eszköz" [DIALOG 2080] 265,150,1,"Színek" 3000,6,5,253,50,1,"Ablak mód" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,19,103,8,1,"&Háttér" 2082,128,16,27,14,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,17,36,91,8,1,"Át&látszó" 2081,128,33,27,14,1,"" 3003,6,60,253,50,1,"Teljes képernyős mód" 3004,17,75,103,8,1,"Hátté&r" 2084,128,72,27,14,1,"" -3005,46,81,30,8,1,"Email:" +3005,17,92,44,8,1,"Á&tlátszó" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Magyar változat" 2103,65,8,100,12,1,"" 3000,7,24,49,8,1,"S&zéles:" 2104,65,22,100,12,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,7,38,49,8,1,"M&agas:" 2105,65,36,100,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,7,52,42,8,1,"&Szín:" 2106,65,50,100,12,1,"" 3003,7,66,51,8,1,"M&emóriában:" 2107,65,64,100,12,1,"" 3004,7,80,42,8,1,"&Fájlméret:" 2108,65,78,100,12,1,"" -3005,46,81,30,8,1,"Email:" +3005,7,94,50,8,1,"&DPI:" 2109,65,92,100,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,7,108,50,8,1,"Fo&rmátum:" 2110,65,106,100,12,1,"" 3007,7,122,50,8,1,"&Tömörítés:" 2111,65,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Magyar változat" 121,58,1,"Nagyítás" 3000,8,13,49,8,1,"&Nagyítás:" 2131,57,11,44,156,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,105,13,9,8,1,"%" 1,7,37,50,14,1,"OK" 2,63,37,50,14,1,"Mégse" @@ -143,12 +143,12 @@ COMMENT,"Magyar változat" 2157,16,43,120,12,1,"A&blak előtere" 2156,16,55,135,12,1,"&Ablak előterének tartalma " 2159,16,67,142,12,1,"&Virtuális képernyő (több monitornál)" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,92,167,49,1,"Mentési jel" 2155,16,106,42,12,1,"&Gyorsbill" 2153,16,121,35,12,1,"I&dőzítő" 2154,59,106,77,12,1,"" 2151,59,121,21,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,84,123,39,8,1,"másodperc" 2152,8,147,85,12,1,"Egérmutató &is" 3003,2,167,166,1,1,"" 1,8,172,50,14,1,"&Mentés" @@ -167,7 +167,7 @@ COMMENT,"Magyar változat" 3000,4,14,54,8,1,"Felülírandó fájl:" 2181,62,14,286,8,1,"" 2182,62,24,286,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,4,37,54,8,1,"Ezzel:" 2183,62,37,286,8,1,"" 2184,62,47,286,8,1,"" 6,93,66,50,14,1,"&Igen" @@ -179,9 +179,9 @@ COMMENT,"Magyar változat" 2300,5,2,49,8,1,"Tömöríté&s:" 2301,54,0,170,80,1,"" 2302,230,0,55,14,1,"Beállítás&ok" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,5,26,49,8,1,"S&zínmélység:" 2303,54,24,70,80,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,5,46,49,8,1,"Fo&rgatás:" 2304,54,44,70,80,1,"" 3003,5,66,49,8,1,"&Tükrözés:" 2305,54,64,70,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Magyar változat" 2514,232,15,118,8,1,"" 2515,354,12,50,14,1,"Beállítá&s..." 3000,225,35,186,62,1," Pozíció " -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,233,49,27,8,1,"Ba&l:" 2503,264,47,45,12,1,"" 2504,315,47,66,75,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,233,68,27,8,1,"&Fent:" 2501,264,66,45,12,1,"" 2502,315,66,66,75,1,"" 2516,264,82,60,12,1,"Ké&p középre" 3004,225,102,186,100,1," Nyomtatási méret " -3005,46,81,30,8,1,"Email:" +3005,233,118,27,8,1,"&Nyújt:" 2505,264,116,35,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,302,118,10,8,1,"%" 2506,315,111,67,12,1,"N&yújtás" 2507,315,123,84,12,1,"&Képarány megtartása" 3007,233,139,27,8,1,"S&zéles:" @@ -362,7 +362,7 @@ COMMENT,"Magyar változat" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Error calling Windows Imaging Component (WIC)" +1009,1,"Hibás PVW32Cnv.DLL meghívás" 1010,1,"Vörös: %i, Zöld: %i, Kék: %i" 1011,1,"Vörös: %#02x, Zöld: %#02x, Kék: %#02x" 1012,1,"(%i, %i): Vörös: %i, Zöld: %i, Kék: %i" @@ -463,8 +463,8 @@ COMMENT,"Magyar változat" 1102,1,"Többi csatorna (O)" [STRINGTABLE 15] -1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." -1110,1,"An incompatible Windows Imaging Component (WIC) version was found." +1109,1,"A képfeldolgozó PVW32Cnv.dll nem tölthető be." +1110,1,"Rossz verziós PVW32Cnv.dll fájl." 1111,1,"Előbb válasszon egy területet." 1112,1,"Alapbeállítás" 1113,1,"A(z) "%s" fájl már létezik.\nLe akarja cserélni?" diff --git a/translations/romanian/pictview.slt b/translations/romanian/pictview.slt index 4aa3e3425..c59f98123 100644 --- a/translations/romanian/pictview.slt +++ b/translations/romanian/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\romanian\pictview.atp" -TEXTVERSION,"2.20 (x64)" -VERSION,"2,2,0,0" +TEXTVERSION,"2.13 (x86)" +VERSION,"2,1,3,180" [TRANSLATION] LANGID,1048 @@ -15,13 +15,13 @@ COMMENT,"Versiunea romana" 2013,11,9,20,20,1,"" 2007,46,9,322,8,1,"PictView %s - Vizualizator imagini pentru Open Salamander" 2011,46,19,322,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" -2008,46,51,277,8,1,"%hs" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" -3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@pictview.com" -3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.pictview.com/salamander" +3001,46,40,123,8,1,"Acest produs este bazat pe libraria" +2008,46,51,311,8,1,"%hs - Motor de Procesare a Imaginii" +3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" +3005,46,81,23,8,1,"Email:" +2009,91,81,85,8,1,"support@pictview.com" +3006,46,92,44,8,1,"Home page:" +2010,91,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,373,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Versiunea romana" 2021,14,17,151,12,1,"La fel ca Open Sala&mander" 2022,14,29,148,12,1,"Mai &larga daca e nevoie dar nu mai mica" 2023,14,41,148,12,1,"Cat e &nevoie" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,60,243,67,1,"Marire implicita" 2024,14,72,151,12,1,"Toata Imaginea (fara bare de de&filare)" 2025,14,84,161,12,1,"Cat latimea (fara &bara orizontala de defilare)" 2026,14,96,148,12,1,"Marime &originala" @@ -39,7 +39,7 @@ COMMENT,"Versiunea romana" [DIALOG 2040] 255,150,1,"Avansat" -3006,46,92,50,8,1,"Project site:" +3006,6,5,243,69,1,"Miniatura in panourile de fisiere" 3007,14,18,225,16,1,"Unele imagini pot contine o miniatura incorecta, care nu este rotita corect sau care nu reflecta ultimele schimbari." 2050,14,35,222,12,1,"&Ignora miniatura originala stocata in fisierul imagine (mai lent)" 3008,14,51,227,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Versiunea romana" [DIALOG 2070] 255,150,1,"Unelte" 3000,6,5,243,94,1,"Unealta de selectare" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,18,150,8,1,"Aria selectata cand tii apasat tasta Shift:" 2071,24,27,203,12,1,"&Patrat" 2072,24,40,218,12,1,"&4:3 dreptunghi" 2073,24,53,218,12,1,"&3:2 dreptunghi" 2074,24,66,218,12,1,"&16:9 dreptunghi" 2075,24,79,35,12,1,"&Alta:" 2076,60,79,17,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,79,81,4,8,1,":" 2077,83,79,17,12,1,"" 2078,7,106,167,12,1,"Pipeta obtine culorile in he&xadecimal" [DIALOG 2080] 255,150,1,"Culori" 3000,6,5,243,50,1,"Mod fereasatra" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,19,103,8,1,"&Fundal" 2082,128,16,27,14,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,17,36,91,8,1,"&Transparent" 2081,128,33,27,14,1,"" 3003,6,60,243,50,1,"Mod pe tot ecranul" 3004,17,75,103,8,1,"Fund&al" 2084,128,72,27,14,1,"" -3005,46,81,30,8,1,"Email:" +3005,17,92,44,8,1,"T&ransparent" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Versiunea romana" 2103,82,8,100,12,1,"" 3000,7,24,49,8,1,"&Latime:" 2104,82,22,100,12,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,7,38,49,8,1,"&Inaltime:" 2105,82,36,100,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,7,52,42,8,1,"&Culori:" 2106,82,50,100,12,1,"" 3003,7,66,74,8,1,"&Marimea in memorie:" 2107,82,64,100,12,1,"" 3004,7,80,48,8,1,"Marime &fisier:" 2108,82,78,100,12,1,"" -3005,46,81,30,8,1,"Email:" +3005,7,94,50,8,1,"&DPI:" 2109,82,92,100,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,7,108,50,8,1,"Fo&rmat:" 2110,82,106,100,12,1,"" 3007,7,122,50,8,1,"C&ompresie:" 2111,82,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Versiunea romana" 121,58,1,"Mareste la" 3000,8,13,49,8,1,"&Marire:" 2131,57,11,44,156,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,105,13,9,8,1,"%" 1,7,37,50,14,1,"OK" 2,63,37,50,14,1,"Anuleaza" @@ -143,12 +143,12 @@ COMMENT,"Versiunea romana" 2157,16,43,120,12,1,"&Fereastra din prim plan" 2156,16,55,146,12,1,"&Aria selectata a fereastrei din prim plan " 2159,16,67,163,12,1,"Ecranul &Virtual (pentru mai multe monitoare)" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,92,177,49,1,"Declansator Captura" 2155,16,106,38,12,1,"Ta&sta" 2153,16,121,53,12,1,"Ti&mp ramas" 2154,70,106,77,12,1,"" 2151,70,121,21,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,95,123,33,8,1,"secunde" 2152,8,147,85,12,1,"I&nclude cursor mouse" 3003,2,167,185,1,1,"" 1,13,172,50,14,1,"&Captura" @@ -167,7 +167,7 @@ COMMENT,"Versiunea romana" 3000,6,14,48,8,1,"Suprascrie:" 2181,58,14,286,8,1,"" 2182,58,24,286,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,9,37,45,8,1,"Cu Fisier:" 2183,58,37,286,8,1,"" 2184,58,47,286,8,1,"" 6,91,66,50,14,1,"&Da" @@ -179,9 +179,9 @@ COMMENT,"Versiunea romana" 2300,5,2,49,8,1,"&Compresie:" 2301,54,0,170,80,1,"" 2302,230,0,50,14,1,"&Optiuni" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,5,26,65,8,1,"A&dancime culoare:" 2303,71,24,75,80,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,5,46,61,8,1,"&Rotire:" 2304,71,44,75,80,1,"" 3003,5,66,61,8,1,"&Flip/Oglinda:" 2305,71,64,75,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Versiunea romana" 2514,232,15,158,8,1,"" 2515,394,12,50,14,1,"Seta&re..." 3000,225,35,226,62,1," Pozitie " -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,233,49,28,8,1,"Stan&ga:" 2503,265,47,45,12,1,"" 2504,316,47,66,75,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,233,68,28,8,1,"&Sus:" 2501,265,66,45,12,1,"" 2502,316,66,66,75,1,"" 2516,265,82,79,12,1,"&Centreaza imaginea" 3004,225,102,226,100,1," Marime Scalata a Imprimarii " -3005,46,81,30,8,1,"Email:" +3005,230,118,31,8,1,"Scalar&e:" 2505,265,116,35,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,303,118,10,8,1,"%" 2506,316,111,132,12,1,"Mareste sa se p&otriveasca pe media" 2507,316,123,86,12,1,"Mentine ra&port aspect" 3007,230,139,31,8,1,"&Latime:" @@ -362,7 +362,7 @@ COMMENT,"Versiunea romana" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Error calling Windows Imaging Component (WIC)" +1009,1,"Eroare apelare PVW32Cnv.DLL" 1010,1,"Rosu: %i, Verde: %i, Albastru: %i" 1011,1,"Rosu: %#02x, Verde: %#02x, Albastru: %#02x" 1012,1,"(%i, %i): Rosu: %i, Verde: %i, Albastru: %i" @@ -463,8 +463,8 @@ COMMENT,"Versiunea romana" 1102,1,"Arata Alte Canale (O)" [STRINGTABLE 15] -1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." -1110,1,"An incompatible Windows Imaging Component (WIC) version was found." +1109,1,"Nu pot incarca libraria de procesare imagine PVW32Cnv.dll." +1110,1,"A fost gasita o versiune gresita a PVW32Cnv.dll." 1111,1,"Mai intai selecteaza o regiune." 1112,1,"Implicit" 1113,1,"Fisierul "%s" exista deja.\nDoriti sa-l inlocuiti?" diff --git a/translations/russian/pictview.slt b/translations/russian/pictview.slt index 174061c28..0060a8750 100644 --- a/translations/russian/pictview.slt +++ b/translations/russian/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\russian\pictview.atp" -TEXTVERSION,"2.20 (x64)" -VERSION,"2,2,0,0" +TEXTVERSION,"2.13 (x86)" +VERSION,"2,1,3,180" [TRANSLATION] LANGID,1049 @@ -15,13 +15,13 @@ COMMENT,"Русская версия" 2013,11,9,20,20,1,"" 2007,46,9,314,8,1,"PictView %s - Просмотр изображений Open Salamander" 2011,46,19,314,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" -2008,46,51,277,8,1,"%hs" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" -3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@pictview.com" -3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.pictview.com/salamander" +3001,46,40,181,8,1,"Используется библиотека обработки изображений" +2008,46,51,303,8,1,"%hs" +3002,46,62,198,8,1,"© 1994-2023 Jan Patera" +3005,46,81,39,8,1,"Эл. почта:" +2009,86,81,85,8,1,"support@pictview.com" +3006,46,92,22,8,1,"Сайт:" +2010,86,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,365,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Русская версия" 2021,14,17,172,12,1,"&Совпадает с главным окном Open Salamander" 2022,14,29,219,12,1,"&Не меньше главного окна или больше при необходимости" 2023,14,41,148,12,1,"&Автоматический" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,60,317,67,1,"Масштаб по умолчанию" 2024,14,72,151,12,1,"&Полностью (без прокрутки)" 2025,14,84,169,12,1,"По &ширине (без горизонтальной прокрутки)" 2026,14,96,148,12,1,"&Реальный размер" @@ -39,7 +39,7 @@ COMMENT,"Русская версия" [DIALOG 2040] 329,150,1,"Дополнительно" -3006,46,92,50,8,1,"Project site:" +3006,6,5,317,69,1,"Эскизы на панелях" 3007,14,18,301,16,1,"Изображения могут содержать некорректные эскизы, которые могут быть неправильно повернуты или не соответствовать последней редакции изображения." 2050,14,35,263,12,1,"&Не использовать встроенные эскизы файлов изображений (медленно)" 3008,14,51,301,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Русская версия" [DIALOG 2070] 329,150,1,"Инструменты" 3000,6,5,317,94,1,"Выделение области" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,18,186,8,1,"При удерживании клавиши Shift выделять область:" 2071,24,27,203,12,1,"&Квадрат" 2072,24,40,218,12,1,"Прямоугольник &4:3" 2073,24,53,218,12,1,"Прямоугольник &3:2" 2074,24,66,218,12,1,"Прямоугольник &16:9" 2075,24,79,40,12,1,"&Другой:" 2076,65,79,17,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,84,81,4,8,1,":" 2077,88,79,17,12,1,"" 2078,7,106,188,12,1,"Выбирать цвета в &шестнадцатеричных значениях" [DIALOG 2080] 329,150,1,"Цвета" 3000,6,5,317,50,1,"Оконный режим" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,19,103,8,1,"&Фон" 2082,128,16,27,14,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,17,36,91,8,1,"&Прозрачный" 2081,128,33,27,14,1,"" 3003,6,60,317,50,1,"Полноэкранный режим" 3004,17,75,103,8,1,"Ф&он" 2084,128,72,27,14,1,"" -3005,46,81,30,8,1,"Email:" +3005,17,92,46,8,1,"П&розрачный" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Русская версия" 2103,76,8,154,12,1,"" 3000,7,24,34,8,1,"&Ширина:" 2104,76,22,154,12,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,7,38,30,8,1,"&Высота:" 2105,76,36,154,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,7,52,56,8,1,"&Глубина цвета:" 2106,76,50,154,12,1,"" 3003,7,66,64,8,1,"&Размер в памяти:" 2107,76,64,154,12,1,"" 3004,7,80,55,8,1,"Ра&змер файла:" 2108,76,78,154,12,1,"" -3005,46,81,30,8,1,"Email:" +3005,7,94,68,8,1,"Разрешен&ие (DPI):" 2109,76,92,154,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,7,108,32,8,1,"&Формат:" 2110,76,106,154,12,1,"" 3007,7,122,31,8,1,"С&жатие:" 2111,76,120,154,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Русская версия" 121,58,1,"Масштаб" 3000,8,13,49,8,1,"&Масштаб:" 2131,57,11,44,156,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,105,13,9,8,1,"%" 1,7,37,50,14,1,"ОК" 2,63,37,50,14,1,"Отмена" @@ -143,12 +143,12 @@ COMMENT,"Русская версия" 2157,16,43,120,12,1,"Активное &окно" 2156,16,55,141,12,1,"&Клиентская область активного окна " 2159,16,67,165,12,1,"В&иртуальный экран (несколько мониторов)" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,92,182,49,1,"Запуск" 2155,16,106,78,12,1,"&Сочетание клавиш" 2153,16,121,38,12,1,"&Таймер" 2154,98,106,77,12,1,"" 2151,98,121,21,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,123,123,33,8,1,"секунд" 2152,8,147,90,12,1,"Включая к&урсор мыши" 3003,2,167,190,1,1,"" 1,16,172,50,14,1,"&Захват" @@ -167,7 +167,7 @@ COMMENT,"Русская версия" 3000,4,14,59,8,1,"Заменить файл:" 2181,67,14,288,8,1,"" 2182,67,24,288,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,4,37,59,8,1,"Файлом:" 2183,67,37,288,8,1,"" 2184,67,47,288,8,1,"" 6,96,66,50,14,1,"&Да" @@ -179,9 +179,9 @@ COMMENT,"Русская версия" 2300,5,2,47,8,1,"С&жатие:" 2301,54,0,190,80,1,"" 2302,250,0,53,14,1,"&Свойства" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,5,26,55,8,1,"Глубина &цвета:" 2303,68,24,90,80,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,5,46,49,8,1,"Пово&рот:" 2304,68,44,90,80,1,"" 3003,5,66,62,8,1,"О&тразить:" 2305,68,64,90,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Русская версия" 2514,232,15,132,8,1,"" 2515,370,12,57,14,1,"&Настройка..." 3000,225,35,208,62,1," Положение " -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,232,49,53,8,1,"&Левое поле:" 2503,288,47,45,12,1,"" 2504,339,47,66,75,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,232,68,53,8,1,"&Верхнее поле:" 2501,288,66,45,12,1,"" 2502,339,66,66,75,1,"" 2516,288,82,63,12,1,"&Центрировать" 3004,225,102,208,100,1," Размер " -3005,46,81,30,8,1,"Email:" +3005,232,118,53,8,1,"&Масштаб:" 2505,288,116,35,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,326,118,10,8,1,"%" 2506,339,111,75,12,1,"По размеру л&иста" 2507,339,123,90,12,1,"&Сохранить пропорции" 3007,232,139,53,8,1,"&Ширина:" @@ -362,7 +362,7 @@ COMMENT,"Русская версия" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Error calling Windows Imaging Component (WIC)" +1009,1,"Ошибка вызова библиотеки "pvw32cnv.dll"" 1010,1,"Красный: %i, Зеленый: %i, Синий: %i" 1011,1,"Красный: %#02x, Зеленый: %#02x, Синий: %#02x" 1012,1,"(%i, %i): Красный: %i, Зеленый: %i, Синий: %i" @@ -463,8 +463,8 @@ COMMENT,"Русская версия" 1102,1,"Другие каналы (O)" [STRINGTABLE 15] -1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." -1110,1,"An incompatible Windows Imaging Component (WIC) version was found." +1109,1,"Не удается загрузить библиотеку обработки изображений "pvw32cnv.dll"." +1110,1,"Несоответствующая версия библиотеки "pvw32cnv.dll"." 1111,1,"Пожалуйста, выберите область." 1112,1,"По умолчанию" 1113,1,"Файл "%s" уже существует.\nЗаменить файл?" diff --git a/translations/slovak/pictview.slt b/translations/slovak/pictview.slt index a2dc2014a..3fbae3887 100644 --- a/translations/slovak/pictview.slt +++ b/translations/slovak/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\slovak\pictview.atp" -TEXTVERSION,"2.20 (x64)" -VERSION,"2,2,0,0" +TEXTVERSION,"2.13 (x86)" +VERSION,"2,1,3,180" [TRANSLATION] LANGID,1051 @@ -15,13 +15,13 @@ COMMENT,"Slovenská verzia" 2013,11,9,20,20,1,"" 2007,46,9,352,8,1,"Prehliadač PictView %s - Prehliadač obrázkov pre Open Salamander" 2011,46,19,352,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" -2008,46,51,277,8,1,"%hs" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3001,46,40,123,8,1,"Tento produkt využíva knižnicu" +2008,46,51,341,8,1,"%hs - Image Processing Engine" +3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@pictview.com" -3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.pictview.com/salamander" +2009,113,81,85,8,1,"support@pictview.com" +3006,46,92,66,8,1,"Domovská stránka:" +2010,113,92,122,8,1,"www.pictview.com/cz/salamander" 3004,2,108,403,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Slovenská verzia" 2021,14,17,151,12,1,"&Rovnaká ako Open Salamandera" 2022,14,29,148,12,1,"Vä&čšia, ak je potrebné, ale nie menšia" 2023,14,41,148,12,1,"Pod&ľa potreby" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,60,248,67,1,"Predvolený spôsob zväčšenia" 2024,14,72,151,12,1,"&Umiestniť do okna (žiadne posuvníky)" 2025,14,84,174,12,1,"U&miestniť na šírku (bez vodorovného posuvníka)" 2026,14,96,148,12,1,"&Pôvodná veľkosť" @@ -39,7 +39,7 @@ COMMENT,"Slovenská verzia" [DIALOG 2040] 260,150,1,"Rozšírené" -3006,46,92,50,8,1,"Project site:" +3006,6,5,248,69,1,"Miniatúry v súborových paneloch" 3007,14,18,231,16,1,"Niektoré súbory s obrázkami môžu obsahovať neaktuálnu miniatúru, ktorá je zle otočená alebo nereflektuje posledné úpravy obrázka." 2050,14,35,205,12,1,"&Ignorovať miniatúry uložené priamo v súboroch (pomalé)" 3008,14,51,232,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Slovenská verzia" [DIALOG 2070] 260,150,1,"Nástroje" 3000,6,5,248,94,1,"Výber" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,18,150,8,1,"Oblasť vyberaná pri stlačení klávesu Shift:" 2071,24,27,203,12,1,"Štvore&c" 2072,24,40,218,12,1,"Obdĺžnik &4:3" 2073,24,53,218,12,1,"Obdĺžnik &3:2" 2074,24,66,218,12,1,"Obdĺžnik &16:9" 2075,24,79,35,12,1,"&Iná:" 2076,60,79,17,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,79,81,4,8,1,":" 2077,83,79,17,12,1,"" 2078,7,106,167,12,1,"Ukazovať farby pipetou he&xadecimálne" [DIALOG 2080] 260,150,1,"Farby" 3000,6,5,248,50,1,"Zobrazenie v okne" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,19,103,8,1,"&Pozadie" 2082,128,16,27,14,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,17,36,91,8,1,"Priehľadná &farba" 2081,128,33,27,14,1,"" 3003,6,60,248,50,1,"Zobrazenie cez celú obrazovku" 3004,17,75,103,8,1,"P&ozadie" 2084,128,72,27,14,1,"" -3005,46,81,30,8,1,"Email:" +3005,17,92,59,8,1,"Priehľadná f&arba" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Slovenská verzia" 2103,69,8,100,12,1,"" 3000,7,24,49,8,1,"Šír&ka:" 2104,69,22,100,12,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,7,38,49,8,1,"&Výška:" 2105,69,36,100,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,7,52,44,8,1,"Počet &farieb:" 2106,69,50,100,12,1,"" 3003,7,66,61,8,1,"Veľkosť (&pamäť):" 2107,69,64,100,12,1,"" 3004,7,80,52,8,1,"Veľkosť (d&isk):" 2108,69,78,100,12,1,"" -3005,46,81,30,8,1,"Email:" +3005,7,94,50,8,1,"&DPI:" 2109,69,92,100,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,7,108,50,8,1,"Fo&rmát:" 2110,69,106,100,12,1,"" 3007,7,122,50,8,1,"K&ompresia:" 2111,69,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Slovenská verzia" 121,58,1,"Zväčšenie obrázku" 3000,8,13,49,8,1,"&Pomer:" 2131,57,11,44,156,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,105,13,9,8,1,"%" 1,7,37,50,14,1,"OK" 2,63,37,50,14,1,"Zrušiť" @@ -143,12 +143,12 @@ COMMENT,"Slovenská verzia" 2157,16,43,120,12,1,"&Aktívne okno" 2156,16,55,135,12,1,"&Klientsku oblasť aktívneho okna " 2159,16,67,151,12,1,"&Virtuálnu obrazovku (pri viac monitoroch)" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,92,167,49,1,"Spúšť" 2155,16,106,57,12,1,"&Horúci kláves" 2153,16,121,40,12,1,"Čas&ovač" 2154,74,106,91,12,1,"" 2151,74,121,21,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,99,123,33,8,1,"sekúnd" 2152,8,147,85,12,1,"Vrátane kurzoru &myši" 3003,2,167,175,1,1,"" 1,8,172,50,14,1,"&Snímať" @@ -167,7 +167,7 @@ COMMENT,"Slovenská verzia" 3000,6,14,76,8,1,"Má sa prepísať súbor:" 2181,84,14,286,8,1,"" 2182,84,24,286,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,37,76,8,1,"týmto súborom:" 2183,84,37,286,8,1,"" 2184,84,47,286,8,1,"" 6,104,66,50,14,1,"&Áno" @@ -179,9 +179,9 @@ COMMENT,"Slovenská verzia" 2300,5,2,40,8,1,"&Kompresia:" 2301,51,0,172,80,1,"" 2302,229,0,50,14,1,"&Možnosti" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,5,26,45,8,1,"Počet &farieb:" 2303,51,24,79,80,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,5,46,34,8,1,"&Otočenie:" 2304,51,44,79,80,1,"" 3003,5,66,43,8,1,"&Zrkadlenie:" 2305,51,64,79,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Slovenská verzia" 2514,232,15,118,8,1,"" 2515,354,12,60,14,1,"&Nastavenie..." 3000,225,35,196,62,1," Umiestnenie na papieri " -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,234,49,24,8,1,"V&ľavo:" 2503,262,47,45,12,1,"" 2504,313,47,66,75,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,234,68,24,8,1,"Z&hora:" 2501,262,66,45,12,1,"" 2502,313,66,66,75,1,"" 2516,262,82,60,12,1,"&Centrovať" 3004,225,102,196,100,1," Prispôsobiť veľkosť " -3005,46,81,30,8,1,"Email:" +3005,233,118,25,8,1,"&Pomer:" 2505,262,116,35,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,300,118,10,8,1,"%" 2506,313,111,77,12,1,"Prispôsobiť p&apieru" 2507,313,123,87,12,1,"&Zachovať pomer strán" 3007,233,139,25,8,1,"Šír&ka:" @@ -362,7 +362,7 @@ COMMENT,"Slovenská verzia" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Error calling Windows Imaging Component (WIC)" +1009,1,"Chyba pri volaní PVW32Cnv.DLL" 1010,1,"Červená: %i, Zelená: %i, Modrá: %i" 1011,1,"Červená: %#02x, Zelená: %#02x, Modrá: %#02x" 1012,1,"(%i, %i): Červená: %i, Zelená: %i, Modrá: %i" @@ -463,8 +463,8 @@ COMMENT,"Slovenská verzia" 1102,1,"Ukázať ostatné kanály (O)" [STRINGTABLE 15] -1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." -1110,1,"An incompatible Windows Imaging Component (WIC) version was found." +1109,1,"Nie je možné nájsť knižnicu PVW32Cnv.dll na spracovanie obrázkov." +1110,1,"Bola nájdená zlá verzia knižnice PVW32Cnv.dll." 1111,1,"Nie je vybraná žiadna oblasť." 1112,1,"Predvolené" 1113,1,"Súbor "%s" úž existuje.\nMá sa prepísať?" diff --git a/translations/spanish/pictview.slt b/translations/spanish/pictview.slt index 254e9f902..52ac47686 100644 --- a/translations/spanish/pictview.slt +++ b/translations/spanish/pictview.slt @@ -1,7 +1,7 @@ [EXPORTINFO] PROJECTNAME,"translator\salamand 4.0\projects\spanish\pictview.atp" -TEXTVERSION,"2.20 (x64)" -VERSION,"2,2,0,0" +TEXTVERSION,"2.13 (x86)" +VERSION,"2,1,3,180" [TRANSLATION] LANGID,3082 @@ -15,13 +15,13 @@ COMMENT,"Versión en español" 2013,11,9,20,20,1,"" 2007,46,9,308,8,1,"PictView %s - Visor de imágenes para Open Salamander" 2011,46,19,308,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" -2008,46,51,277,8,1,"%hs" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3001,46,40,129,8,1,"Este producto se basa en la biblioteca" +2008,46,51,297,8,1,"%hs - Motor de tratamiento de imágenes" +3002,46,62,198,8,1,"Copyright © 1994-2023 Jan Patera" 3005,46,81,30,8,1,"Email:" -2009,99,81,120,8,1,"support@pictview.com" -3006,46,92,50,8,1,"Project site:" -2010,99,92,122,8,1,"www.pictview.com/salamander" +2009,91,81,85,8,1,"support@pictview.com" +3006,46,92,44,8,1,"Página web:" +2010,91,92,122,8,1,"www.pictview.com/salamander" 3004,2,108,359,1,1,"" [DIALOG 2020] @@ -30,7 +30,7 @@ COMMENT,"Versión en español" 2021,14,17,151,12,1,"&Como Open Salamander" 2022,14,29,148,12,1,"&Mayor si es necesario pero no menor" 2023,14,41,148,12,1,"&Según sea necesario" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,60,253,67,1,"Tamaño por defecto" 2024,14,72,151,12,1,"&Ajustar entero (sin barras)" 2025,14,84,148,12,1,"Ajustar al a&ncho (sin barra horizontal)" 2026,14,96,148,12,1,"Tamaño &original" @@ -39,7 +39,7 @@ COMMENT,"Versión en español" [DIALOG 2040] 265,150,1,"Avanzado" -3006,46,92,50,8,1,"Project site:" +3006,6,5,253,69,1,"Miniaturas en paneles de ficheros" 3007,14,18,225,16,1,"Algunas imágenes pueden contener miniatura imprecisa, no rotada adecuadamente o no reflejar los últimos cambios." 2050,14,35,231,12,1,"&Ignorar miniaturas originales incluidas en ficheros imagen (lento)" 3008,14,51,237,1,1,"" @@ -59,28 +59,28 @@ COMMENT,"Versión en español" [DIALOG 2070] 265,150,1,"Herramientas" 3000,6,5,253,94,1,"Herramienta seleccionar" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,18,183,8,1,"Area seleccionada cuando se mantiene pulsada Mays:" 2071,24,27,203,12,1,"&Cuadrado" 2072,24,40,218,12,1,"Rectángulo &4:3" 2073,24,53,218,12,1,"Rectángulo &3:2" 2074,24,66,218,12,1,"Rectángulo &16:9" 2075,24,79,30,12,1,"&Otra:" 2076,56,79,17,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,75,81,4,8,1,":" 2077,79,79,17,12,1,"" 2078,7,106,167,12,1,"La pipeta recoge colores en he&xadecimal" [DIALOG 2080] 265,150,1,"Colores" 3000,6,5,253,50,1,"Modo ventana" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,17,19,103,8,1,"&Fondo" 2082,128,16,27,14,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,17,36,91,8,1,"&Transparente" 2081,128,33,27,14,1,"" 3003,6,60,253,50,1,"Modo pantalla completa" 3004,17,75,103,8,1,"F&ondo" 2084,128,72,27,14,1,"" -3005,46,81,30,8,1,"Email:" +3005,17,92,46,8,1,"T&ransparente" 2083,128,89,27,14,1,"" [DIALOG 2100] @@ -91,17 +91,17 @@ COMMENT,"Versión en español" 2103,72,8,100,12,1,"" 3000,7,24,49,8,1,"&Anchura:" 2104,72,22,100,12,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,7,38,49,8,1,"A<ura:" 2105,72,36,100,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,7,52,42,8,1,"&Colores:" 2106,72,50,100,12,1,"" 3003,7,66,64,8,1,"&Tamaño memoria:" 2107,72,64,100,12,1,"" 3004,7,80,58,8,1,"Tamaño &fichero:" 2108,72,78,100,12,1,"" -3005,46,81,30,8,1,"Email:" +3005,7,94,50,8,1,"&DPI:" 2109,72,92,100,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,7,108,50,8,1,"Fo&rmato:" 2110,72,106,100,12,1,"" 3007,7,122,50,8,1,"C&ompresión:" 2111,72,120,100,12,1,"" @@ -124,7 +124,7 @@ COMMENT,"Versión en español" 121,58,1,"Cambiar visualización" 3000,8,13,49,8,1,"&Aumento:" 2131,57,11,44,156,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,105,13,9,8,1,"%" 1,7,37,50,14,1,"Aceptar" 2,63,37,50,14,1,"Cancelar" @@ -143,12 +143,12 @@ COMMENT,"Versión en español" 2157,16,43,120,12,1,"Ve&ntana en primer plano" 2156,16,55,150,12,1,"&Area cliente de ventana en primer plano " 2159,16,67,142,12,1,"Pantalla &virtual (para varios monitores)" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,6,92,167,49,1,"Disparador de captura" 2155,16,106,54,12,1,"&Tecla rápida" 2153,16,121,60,12,1,"T&emporizador" 2154,77,106,77,12,1,"" 2151,77,121,21,12,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,102,123,33,8,1,"segundos" 2152,8,147,89,12,1,"&Incluir cursor del ratón" 3003,3,167,174,1,1,"" 1,8,172,50,14,1,"&Capturar" @@ -167,7 +167,7 @@ COMMENT,"Versión en español" 3000,6,14,48,8,1,"Reemplazar:" 2181,58,14,286,8,1,"" 2182,58,24,286,8,1,"" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,9,37,45,8,1,"Con fichero:" 2183,58,37,286,8,1,"" 2184,58,47,286,8,1,"" 6,91,66,50,14,1,"&Sí" @@ -179,9 +179,9 @@ COMMENT,"Versión en español" 2300,5,2,49,8,1,"&Compresión:" 2301,54,0,180,80,1,"" 2302,240,0,50,14,1,"&Opciones" -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,5,26,52,8,1,"&Profund. color:" 2303,58,24,80,80,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,5,46,52,8,1,"&Rotación:" 2304,58,44,80,80,1,"" 3003,5,66,52,8,1,"&Trasp./espejo:" 2305,58,64,80,80,1,"" @@ -231,17 +231,17 @@ COMMENT,"Versión en español" 2514,232,15,126,8,1,"" 2515,362,12,50,14,1,"&Disponer..." 3000,225,35,194,62,1," Posición " -3001,46,40,190,8,1,"This plugin uses the following imaging backend:" +3001,232,49,26,8,1,"&Izda:" 2503,262,47,45,12,1,"" 2504,313,47,66,75,1,"" -3002,46,62,220,8,1,"Powered by Microsoft Windows Imaging Component" +3002,232,68,26,8,1,"&Sup:" 2501,262,66,45,12,1,"" 2502,313,66,66,75,1,"" 2516,262,82,65,12,1,"Ce&ntrar imagen" 3004,225,102,194,100,1," Tamaño escalado " -3005,46,81,30,8,1,"Email:" +3005,232,118,26,8,1,"&Escala:" 2505,262,116,35,12,1,"" -3006,46,92,50,8,1,"Project site:" +3006,300,118,10,8,1,"%" 2506,313,111,101,12,1,"Escalar: a&justar al soporte" 2507,313,123,101,12,1,"&Mantener relación aspecto" 3007,232,139,26,8,1,"&Ancho:" @@ -362,7 +362,7 @@ COMMENT,"Versión en español" [STRINGTABLE 9] 1008,1,"LAB" -1009,1,"Error calling Windows Imaging Component (WIC)" +1009,1,"Error llamando a PVW32Cnv.DLL" 1010,1,"Rojo: %i, Verde: %i, Azul: %i" 1011,1,"Rojo: %#02x, Verde: %#02x, Azul: %#02x" 1012,1,"(%i, %i): Rojo: %i, Verde: %i, Azul: %i" @@ -463,8 +463,8 @@ COMMENT,"Versión en español" 1102,1,"Ver otros canales (O)" [STRINGTABLE 15] -1109,1,"Unable to initialize the Windows Imaging Component (WIC) imaging engine." -1110,1,"An incompatible Windows Imaging Component (WIC) version was found." +1109,1,"Imposible cargar la biblioteca de procesado de imágenes PVW32Cnv.dll." +1110,1,"Se encontró una versión errónea de PVW32Cnv.dll." 1111,1,"Seleccione una región primero." 1112,1,"Por defecto" 1113,1,"El fichero "%s" ya existe.\n¿Desea reemplazarlo?" From 80c1a42e3425cb626ed9351ccffda14c8ee51192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kotas?= Date: Fri, 10 Oct 2025 06:03:49 +0200 Subject: [PATCH 14/14] Fix palette decoding for indexed GIF frames (#200) --- doc/manual-tests/animated-gif-transparency.md | 19 + src/plugins/pictview/wic/WicBackend.cpp | 2365 ++++++++++++++++- src/plugins/pictview/wic/WicBackend.h | 37 + 3 files changed, 2290 insertions(+), 131 deletions(-) create mode 100644 doc/manual-tests/animated-gif-transparency.md diff --git a/doc/manual-tests/animated-gif-transparency.md b/doc/manual-tests/animated-gif-transparency.md new file mode 100644 index 000000000..61786e1b6 --- /dev/null +++ b/doc/manual-tests/animated-gif-transparency.md @@ -0,0 +1,19 @@ +# Manual Test: 8-bit Animated GIF Transparency + +This regression check ensures that semi-transparent pixels in paletted GIF animations +render without colour banding or grid artefacts. + +## Required asset +- A dithered 8-bit animated GIF with transparency (for example, a ScreenToGif capture + exported with the gifski encoder). + +## Steps +1. Open the GIF in PictView. +2. Let the animation loop at least once. +3. Inspect regions that fade in/out or show cursor trails. + +## Expected result +- The animation uses the authored colours without blue/green/purple speckles. +- No grid artefacts or haloing appears around semi-transparent content. +- Pixels with fractional alpha look smooth across frames while fully transparent + regions remain transparent. diff --git a/src/plugins/pictview/wic/WicBackend.cpp b/src/plugins/pictview/wic/WicBackend.cpp index b465666ae..93c5da4e2 100644 --- a/src/plugins/pictview/wic/WicBackend.cpp +++ b/src/plugins/pictview/wic/WicBackend.cpp @@ -50,7 +50,7 @@ struct GuidMapping }; HRESULT AllocatePixelStorage(FrameData& frame, UINT width, UINT height); -HRESULT FinalizeDecodedFrame(FrameData& frame); +HRESULT FinalizeDecodedFrame(Backend* backend, FrameData& frame); PVCODE PopulateImageInfo(ImageHandle& handle, LPPVImageInfo info, DWORD bufferSize, bool hasPreviousImage, DWORD previousImageIndex, int currentImage); bool TryReadUnsignedMetadata(IWICMetadataQueryReader* reader, LPCWSTR name, UINT& value); @@ -58,6 +58,29 @@ DWORD MapGifDisposalToPv(UINT disposal); LONG ClampUnsignedToLong(ULONGLONG value); HRESULT EnsureTransparencyMask(FrameData& frame); DWORD MapPixelFormatToColors(const GUID& guid); +HRESULT CompositeGifFrame(ImageHandle& handle, size_t index); +HRESULT RestoreGifCanvasState(ImageHandle& handle, size_t index); +void FillBufferWithColor(std::vector& buffer, UINT width, UINT height, BYTE r, BYTE g, BYTE b, BYTE a); +void ClearBufferRect(std::vector& buffer, UINT width, UINT height, const RECT& rect, BYTE r, BYTE g, BYTE b, + BYTE a); +void ZeroTransparentPixels(std::vector& buffer); +void FillTransparentPixelsWithColor(std::vector& buffer, UINT width, UINT height, UINT stride, BYTE r, BYTE g, + BYTE b, BYTE a); +void PremultiplyGifBuffer(std::vector& buffer, UINT width, UINT height, UINT stride); +void BlendPremultipliedPixel(BYTE* dest, const BYTE* src); +HRESULT CreateSequenceBitmaps(const FrameData& frame, const RECT& rect, HBITMAP& colorBitmap, HBITMAP& maskBitmap); +HRESULT EnsureScaledBitmap(ImageHandle& handle, FrameData& frame, UINT width, UINT height); +DWORD DetermineColorCount(const GUID& pixelFormat, UINT bitsPerPixel, UINT paletteColors, DWORD colorModel); +DWORD DetermineColorModelFromPixelFormat(const GUID& pixelFormat); +HRESULT ConvertBgraSourceToCmyk(IWICImagingFactory* factory, IWICBitmapSource* source, + IWICBitmapSource** convertedSource); +bool IsPaletteUnavailable(HRESULT hr); +void UnpremultiplyBuffer(std::vector& buffer, UINT width, UINT height, UINT stride); +HRESULT CopyBgraFromSource(FrameData& frame, IWICBitmapSource* source); +void ApplyGifTransparentIndices(FrameData& frame); +HRESULT PopulateFramePalette(IWICImagingFactory* factory, FrameData& frame); +HPALETTE CreateGdiPalette(const std::vector& entries); +HRESULT BuildIndexedPixelBuffer(FrameData& frame); struct PixelFormatSelection { @@ -916,7 +939,8 @@ DWORD GetFrameDelayMilliseconds(IWICBitmapFrameDecode* frame) UINT hundredths = 0; static constexpr const wchar_t* kDelayPaths[] = { - L"/grctlext/DelayTime", // GIF frame delay + L"/grctlext/DelayTime", // Some decoders expose DelayTime + L"/grctlext/Delay", // WIC animated GIF sample uses 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 @@ -1035,7 +1059,7 @@ size_t NormalizeFrameIndex(const ImageHandle& handle, int requestedIndex, size_t return index; } -HRESULT PopulateFrameFromBitmapHandle(FrameData& frame, HBITMAP bitmap) +HRESULT PopulateFrameFromBitmapHandle(Backend& backend, FrameData& frame, HBITMAP bitmap) { if (!bitmap) { @@ -1066,6 +1090,10 @@ HRESULT PopulateFrameFromBitmapHandle(FrameData& frame, HBITMAP bitmap) return hr; } + frame.rawWidth = width; + frame.rawHeight = height; + frame.rawStride = frame.stride; + BITMAPINFO bmi{}; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = static_cast(width); @@ -1079,6 +1107,7 @@ HRESULT PopulateFrameFromBitmapHandle(FrameData& frame, HBITMAP bitmap) { const DWORD error = GetLastError(); frame.pixels.clear(); + frame.compositedPixels.clear(); frame.stride = 0; return HRESULT_FROM_WIN32(error != 0 ? error : ERROR_NOT_ENOUGH_MEMORY); } @@ -1095,6 +1124,7 @@ HRESULT PopulateFrameFromBitmapHandle(FrameData& frame, HBITMAP bitmap) { const DWORD error = GetLastError(); frame.pixels.clear(); + frame.compositedPixels.clear(); frame.stride = 0; return HRESULT_FROM_WIN32(error != 0 ? error : ERROR_INVALID_DATA); } @@ -1109,13 +1139,28 @@ HRESULT PopulateFrameFromBitmapHandle(FrameData& frame, HBITMAP bitmap) } } + frame.sourcePixelFormat = GUID_WICPixelFormat32bppBGRA; + const UINT sourceBits = dib.dsBm.bmBitsPixel > 0 ? static_cast(dib.dsBm.bmBitsPixel) : 32u; + frame.bitsPerPixel = sourceBits; + frame.reportedBitDepth = sourceBits; + frame.paletteColorCount = 0; + frame.colorModel = PVCM_RGB; + frame.reportedColors = DetermineColorCount(frame.sourcePixelFormat, frame.bitsPerPixel, frame.paletteColorCount, + frame.colorModel); + frame.realizePalette = false; + frame.hasGifFrameRect = false; + frame.gifFrameRect.left = 0; + frame.gifFrameRect.top = 0; + frame.gifFrameRect.right = ClampUnsignedToLong(static_cast(width)); + frame.gifFrameRect.bottom = ClampUnsignedToLong(static_cast(height)); + frame.rect.left = 0; frame.rect.top = 0; frame.rect.right = ClampUnsignedToLong(static_cast(width)); frame.rect.bottom = ClampUnsignedToLong(static_cast(height)); frame.disposal = PVDM_UNDEFINED; - hr = FinalizeDecodedFrame(frame); + hr = FinalizeDecodedFrame(&backend, frame); if (FAILED(hr)) { return hr; @@ -1171,6 +1216,10 @@ HRESULT AllocatePixelStorage(FrameData& frame, UINT width, UINT height) frame.width = width; frame.height = height; frame.stride = static_cast(stride64); + frame.rawWidth = width; + frame.rawHeight = height; + frame.rawStride = static_cast(stride64); + frame.compositedPixels.clear(); HRESULT hr = AllocateBuffer(frame.pixels, static_cast(buffer64)); if (FAILED(hr)) @@ -1279,7 +1328,14 @@ std::optional DeterminePixelFormat(const GuidMapping& mapp case PV_COLOR_TC32: if (mapping.container == GUID_ContainerFormatJpeg) { - selection.pixelFormat = GUID_WICPixelFormat24bppBGR; + if (info->ColorModel == PVCM_GRAYS) + { + selection.pixelFormat = GUID_WICPixelFormat24bppBGR; + } + else + { + selection.pixelFormat = GUID_WICPixelFormat32bppCMYK; + } } else { @@ -1421,6 +1477,84 @@ HRESULT ApplyEmbeddedColorProfile(ImageHandle& handle, FrameData& frame) return hr; } +void UnpremultiplyBuffer(std::vector& buffer, UINT width, UINT height, UINT stride) +{ + if (buffer.empty() || width == 0 || height == 0) + { + return; + } + + const size_t rowStride = static_cast(stride); + const size_t expectedStride = static_cast(width) * kBytesPerPixel; + if (rowStride < expectedStride) + { + return; + } + + BYTE* data = buffer.data(); + for (UINT y = 0; y < height; ++y) + { + BYTE* row = data + rowStride * static_cast(y); + for (UINT x = 0; x < width; ++x) + { + BYTE* pixel = row + static_cast(x) * kBytesPerPixel; + const BYTE alpha = pixel[3]; + if (alpha == 0) + { + pixel[0] = 0; + pixel[1] = 0; + pixel[2] = 0; + continue; + } + if (alpha == 255) + { + continue; + } + + const unsigned int a = alpha; + pixel[0] = static_cast((static_cast(pixel[0]) * 255u + a / 2u) / a); + pixel[1] = static_cast((static_cast(pixel[1]) * 255u + a / 2u) / a); + pixel[2] = static_cast((static_cast(pixel[2]) * 255u + a / 2u) / a); + } + } +} + +void PremultiplyGifBuffer(std::vector& buffer, UINT width, UINT height, UINT stride) +{ + if (buffer.empty() || width == 0 || height == 0) + { + return; + } + + const size_t rowStride = static_cast(stride); + for (UINT y = 0; y < height; ++y) + { + BYTE* row = buffer.data() + static_cast(y) * rowStride; + for (UINT x = 0; x < width; ++x) + { + BYTE* pixel = row + static_cast(x) * kBytesPerPixel; + const BYTE alpha = pixel[3]; + if (alpha < 128) + { + pixel[0] = 0; + pixel[1] = 0; + pixel[2] = 0; + pixel[3] = 0; + continue; + } + if (pixel[3] != 255) + { + pixel[3] = 255; + } + + const unsigned int a = pixel[3]; + pixel[0] = static_cast((static_cast(pixel[0]) * a + 127u) / 255u); + pixel[1] = static_cast((static_cast(pixel[1]) * a + 127u) / 255u); + pixel[2] = static_cast((static_cast(pixel[2]) * a + 127u) / 255u); + } + } +} + HRESULT CopyBgraFromSource(FrameData& frame, IWICBitmapSource* source) { if (!source) @@ -1436,156 +1570,1765 @@ HRESULT CopyBgraFromSource(FrameData& frame, IWICBitmapSource* source) return hr; } - hr = AllocatePixelStorage(frame, width, height); + UINT targetWidth = width; + UINT targetHeight = height; + if (frame.rawWidth > 0 && frame.rawWidth <= width) + { + targetWidth = frame.rawWidth; + } + if (frame.rawHeight > 0 && frame.rawHeight <= height) + { + targetHeight = frame.rawHeight; + } + + WICRect rect{}; + rect.X = 0; + rect.Y = 0; + rect.Width = static_cast(targetWidth); + rect.Height = static_cast(targetHeight); + + hr = AllocatePixelStorage(frame, targetWidth, targetHeight); 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.compositedPixels.clear(); frame.stride = 0; return hr; } + frame.rawWidth = targetWidth; + frame.rawHeight = targetHeight; + frame.rawStride = frame.stride; + + if (frame.pixelsArePremultiplied) + { + UnpremultiplyBuffer(frame.pixels, targetWidth, targetHeight, frame.stride); + } + + ApplyGifTransparentIndices(frame); + return S_OK; } -HRESULT EnsureTransparencyMask(FrameData& frame) +void ApplyGifTransparentIndices(FrameData& frame) { - if (frame.transparencyMask) + if (!frame.gifHasTransparentColor || !frame.frame) { - DeleteObject(frame.transparencyMask); - frame.transparencyMask = nullptr; + return; } - frame.hasTransparency = false; - if (frame.pixels.empty() || frame.width == 0 || frame.height == 0) + const UINT width = frame.rawWidth > 0 ? frame.rawWidth : frame.width; + const UINT height = frame.rawHeight > 0 ? frame.rawHeight : frame.height; + if (width == 0 || height == 0) { - return S_OK; + return; + } + + GUID sourceFormat{}; + if (FAILED(frame.frame->GetPixelFormat(&sourceFormat))) + { + return; + } + + UINT bitsPerPixel = 0; + if (sourceFormat == GUID_WICPixelFormat8bppIndexed) + { + bitsPerPixel = 8; + } + else if (sourceFormat == GUID_WICPixelFormat4bppIndexed) + { + bitsPerPixel = 4; + } +#if defined(GUID_WICPixelFormat2bppIndexed) + else if (sourceFormat == GUID_WICPixelFormat2bppIndexed) + { + bitsPerPixel = 2; + } +#endif + else if (sourceFormat == GUID_WICPixelFormat1bppIndexed) + { + bitsPerPixel = 1; + } + else + { + return; + } + + const UINT indexStride = static_cast((static_cast(width) * bitsPerPixel + 7ull) / 8ull); + if (indexStride == 0) + { + return; + } + + const size_t bufferSize = static_cast(indexStride) * static_cast(height); + if (bufferSize == 0) + { + return; + } + + std::vector indices; + if (FAILED(AllocateBuffer(indices, bufferSize))) + { + return; + } + + HRESULT hr = frame.frame->CopyPixels(nullptr, indexStride, static_cast(indices.size()), indices.data()); + if (FAILED(hr)) + { + return; + } + + auto extractIndex = [bitsPerPixel](const BYTE* row, UINT x) -> BYTE { + switch (bitsPerPixel) + { + case 8: + return row[x]; + case 4: + { + const BYTE packed = row[x / 2]; + if ((x & 1u) == 0) + { + return static_cast(packed >> 4); + } + return static_cast(packed & 0x0Fu); + } + case 2: + { + const BYTE packed = row[x / 4]; + const UINT shift = 6u - ((x & 3u) * 2u); + return static_cast((packed >> shift) & 0x03u); + } + case 1: + default: + { + const BYTE packed = row[x / 8]; + const UINT shift = 7u - (x & 7u); + return static_cast((packed >> shift) & 0x01u); + } + } + }; + + BYTE* dstBase = frame.pixels.data(); + const size_t dstStride = static_cast(frame.stride); + const BYTE transparentIndex = frame.gifTransparentIndex; + + for (UINT y = 0; y < height; ++y) + { + const BYTE* srcRow = indices.data() + static_cast(y) * indexStride; + BYTE* dstRow = dstBase + static_cast(y) * dstStride; + for (UINT x = 0; x < width; ++x) + { + const BYTE paletteIndex = extractIndex(srcRow, x); + if (paletteIndex != transparentIndex) + { + continue; + } + + BYTE* pixel = dstRow + static_cast(x) * kBytesPerPixel; + pixel[0] = 0; + pixel[1] = 0; + pixel[2] = 0; + pixel[3] = 0; + } + } +} + +HRESULT CompositeGifFrame(ImageHandle& handle, size_t index) +{ + if (index >= handle.frames.size()) + { + return E_INVALIDARG; + } + + FrameData& frame = handle.frames[index]; + const bool multiFrameAnimation = handle.frames.size() > 1; + const LONG canvasWidthLong = handle.canvasWidth > 0 ? handle.canvasWidth : static_cast(frame.width); + const LONG canvasHeightLong = handle.canvasHeight > 0 ? handle.canvasHeight : static_cast(frame.height); + if (canvasWidthLong <= 0 || canvasHeightLong <= 0) + { + return WINCODEC_ERR_INVALIDPARAMETER; + } + + const UINT canvasWidth = static_cast(canvasWidthLong); + const UINT canvasHeight = static_cast(canvasHeightLong); + const size_t canvasStride = static_cast(canvasWidth) * kBytesPerPixel; + const size_t canvasBytes = canvasStride * static_cast(canvasHeight); + if (canvasStride > static_cast(std::numeric_limits::max())) + { + return E_OUTOFMEMORY; + } + + handle.canvasWidth = canvasWidthLong; + handle.canvasHeight = canvasHeightLong; + + if (handle.gifComposeCanvas.size() != canvasBytes) + { + try + { + handle.gifComposeCanvas.resize(canvasBytes); + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + } + + const BYTE backgroundR = GetRValue(handle.formatInfo.GIF.BgColor); + const BYTE backgroundG = GetGValue(handle.formatInfo.GIF.BgColor); + const BYTE backgroundB = GetBValue(handle.formatInfo.GIF.BgColor); + const BYTE backgroundA = handle.gifHasBackgroundColor ? handle.gifBackgroundAlpha : 0; + if (index == 0 || !handle.gifCanvasInitialized) + { + FillBufferWithColor(handle.gifComposeCanvas, canvasWidth, canvasHeight, backgroundR, backgroundG, backgroundB, + backgroundA); + handle.gifCanvasInitialized = true; + handle.gifSavedCanvas.clear(); + } + else + { + FrameData& previous = handle.frames[index - 1]; + const RECT& previousLogicalRect = previous.hasGifFrameRect ? previous.gifFrameRect : previous.rect; + switch (previous.disposal) + { + case PVDM_BACKGROUND: + ClearBufferRect(handle.gifComposeCanvas, canvasWidth, canvasHeight, previousLogicalRect, backgroundR, + backgroundG, backgroundB, backgroundA); + handle.gifSavedCanvas.clear(); + break; + case PVDM_PREVIOUS: + if (handle.gifSavedCanvas.size() == canvasBytes) + { + try + { + handle.gifComposeCanvas = handle.gifSavedCanvas; + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + } + else + { + FillBufferWithColor(handle.gifComposeCanvas, canvasWidth, canvasHeight, backgroundR, backgroundG, + backgroundB, backgroundA); + } + handle.gifSavedCanvas.clear(); + break; + default: + handle.gifSavedCanvas.clear(); + break; + } + } + + if (frame.disposal == PVDM_PREVIOUS) + { + try + { + handle.gifSavedCanvas = handle.gifComposeCanvas; + frame.disposalBuffer = handle.gifSavedCanvas; + } + catch (const std::bad_alloc&) + { + handle.gifSavedCanvas.clear(); + frame.disposalBuffer.clear(); + return E_OUTOFMEMORY; + } + } + else + { + handle.gifSavedCanvas.clear(); + frame.disposalBuffer.clear(); + } + + const UINT sourceWidth = frame.rawWidth > 0 ? frame.rawWidth : frame.width; + const UINT sourceHeight = frame.rawHeight > 0 ? frame.rawHeight : frame.height; + const UINT sourceStride = frame.rawStride > 0 ? frame.rawStride : frame.stride; + std::vector raw; + raw.swap(frame.pixels); + if (!raw.empty()) + { + PremultiplyGifBuffer(raw, sourceWidth, sourceHeight, sourceStride); + } + + const RECT& destinationRect = frame.hasGifFrameRect ? frame.gifFrameRect : frame.rect; + const RECT logicalRect = destinationRect; + + const LONGLONG destLeft64 = static_cast(destinationRect.left); + const LONGLONG destTop64 = static_cast(destinationRect.top); + const LONGLONG destRight64 = destLeft64 + static_cast(sourceWidth); + const LONGLONG destBottom64 = destTop64 + static_cast(sourceHeight); + + const LONGLONG canvasWidth64 = static_cast(canvasWidth); + const LONGLONG canvasHeight64 = static_cast(canvasHeight); + + const LONGLONG startX64 = std::max(0, destLeft64); + const LONGLONG startY64 = std::max(0, destTop64); + const LONGLONG endX64 = std::min(canvasWidth64, destRight64); + const LONGLONG endY64 = std::min(canvasHeight64, destBottom64); + + RECT compositedRect{}; + compositedRect.left = static_cast(std::clamp(destLeft64, 0ll, canvasWidth64)); + compositedRect.top = static_cast(std::clamp(destTop64, 0ll, canvasHeight64)); + compositedRect.right = static_cast(std::clamp(destRight64, 0ll, canvasWidth64)); + compositedRect.bottom = static_cast(std::clamp(destBottom64, 0ll, canvasHeight64)); + + if (sourceWidth > 0 && sourceHeight > 0 && startX64 < endX64 && startY64 < endY64) + { + const LONG startX = static_cast(startX64); + const LONG startY = static_cast(startY64); + const LONG endX = static_cast(endX64); + const LONG endY = static_cast(endY64); + + compositedRect.left = startX; + compositedRect.top = startY; + compositedRect.right = endX; + compositedRect.bottom = endY; + + for (LONG y = startY; y < endY; ++y) + { + const size_t destYOffset = static_cast(y) * canvasStride; + const size_t srcY = static_cast(static_cast(y) - destTop64); + const BYTE* srcRow = raw.data() + srcY * sourceStride; + BYTE* destRow = handle.gifComposeCanvas.data() + destYOffset; + + for (LONG x = startX; x < endX; ++x) + { + const size_t srcX = static_cast(static_cast(x) - destLeft64); + const BYTE* srcPixel = srcRow + srcX * kBytesPerPixel; + if (srcPixel[3] == 0) + { + continue; + } + + BYTE* destPixel = destRow + static_cast(x) * kBytesPerPixel; + BlendPremultipliedPixel(destPixel, srcPixel); + } + } + } + + if (compositedRect.right < compositedRect.left) + { + compositedRect.right = compositedRect.left; + } + if (compositedRect.bottom < compositedRect.top) + { + compositedRect.bottom = compositedRect.top; + } + + frame.rect = compositedRect; + if (!frame.hasGifFrameRect) + { + frame.gifFrameRect = logicalRect; + frame.hasGifFrameRect = true; + } + + frame.width = canvasWidth; + frame.height = canvasHeight; + frame.stride = static_cast(canvasStride); + + std::vector compositedPixels; + try + { + compositedPixels = handle.gifComposeCanvas; + } + catch (const std::bad_alloc&) + { + compositedPixels.clear(); + return E_OUTOFMEMORY; + } + + std::vector displayPixels; + try + { + displayPixels = compositedPixels; + } + catch (const std::bad_alloc&) + { + compositedPixels.clear(); + displayPixels.clear(); + return E_OUTOFMEMORY; + } + + UnpremultiplyBuffer(displayPixels, canvasWidth, canvasHeight, static_cast(canvasStride)); + + const bool fillTransparentWithBackground = multiFrameAnimation && handle.gifHasBackgroundColor; + if (fillTransparentWithBackground) + { + FillTransparentPixelsWithColor(displayPixels, canvasWidth, canvasHeight, static_cast(canvasStride), + backgroundR, backgroundG, backgroundB, backgroundA); + } + else + { + ZeroTransparentPixels(displayPixels); + } + + frame.pixels.swap(displayPixels); + frame.compositedPixels.swap(compositedPixels); + if (multiFrameAnimation) + { + frame.useIndexedPixels = false; + frame.allowIndexedDisplay = false; + frame.realizePalette = false; + frame.indexedPixels.clear(); + frame.indexedStride = 0; + frame.indexedBmi = BITMAPINFOHEADER{}; + frame.displayBmi = BITMAPINFOHEADER{}; + frame.displayStride = 0; + + if (frame.paletteHandle) + { + DeleteObject(frame.paletteHandle); + frame.paletteHandle = nullptr; + } + } + return S_OK; +} + +HRESULT RestoreGifCanvasState(ImageHandle& handle, size_t index) +{ + if (index >= handle.frames.size()) + { + return E_INVALIDARG; + } + + FrameData& frame = handle.frames[index]; + if (frame.compositedPixels.empty()) + { + handle.gifComposeCanvas.clear(); + handle.gifSavedCanvas.clear(); + handle.gifCanvasInitialized = false; + return S_FALSE; + } + + try + { + handle.gifComposeCanvas = frame.compositedPixels; + } + catch (const std::bad_alloc&) + { + handle.gifComposeCanvas.clear(); + handle.gifSavedCanvas.clear(); + handle.gifCanvasInitialized = false; + return E_OUTOFMEMORY; + } + + handle.canvasWidth = static_cast(frame.width); + handle.canvasHeight = static_cast(frame.height); + handle.gifCanvasInitialized = true; + + if (frame.disposal == PVDM_PREVIOUS && !frame.disposalBuffer.empty()) + { + try + { + handle.gifSavedCanvas = frame.disposalBuffer; + } + catch (const std::bad_alloc&) + { + handle.gifSavedCanvas.clear(); + return E_OUTOFMEMORY; + } + } + else + { + handle.gifSavedCanvas.clear(); + } + + return S_OK; +} + +HRESULT EnsureTransparencyMask(FrameData& frame) +{ + if (frame.transparencyMask) + { + DeleteObject(frame.transparencyMask); + frame.transparencyMask = nullptr; + } + frame.hasTransparency = false; + + if (frame.pixels.empty() || frame.width == 0 || frame.height == 0) + { + return S_OK; + } + + bool hasTransparentPixel = false; + for (UINT y = 0; y < frame.height && !hasTransparentPixel; ++y) + { + const BYTE* row = frame.pixels.data() + static_cast(y) * frame.stride; + for (UINT x = 0; x < frame.width; ++x) + { + const BYTE alpha = row[static_cast(x) * 4 + 3]; + if (alpha < 128) + { + hasTransparentPixel = true; + break; + } + } + } + + if (!hasTransparentPixel) + { + return S_OK; + } + + const ULONGLONG unalignedStride = (static_cast(frame.width) + 7ull) / 8ull; + const ULONGLONG alignedStride = (unalignedStride + 3ull) & ~3ull; + if (alignedStride > std::numeric_limits::max()) + { + return E_OUTOFMEMORY; + } + const UINT maskStride = static_cast(alignedStride); + const ULONGLONG maskSize64 = alignedStride * static_cast(frame.height); + if (frame.height != 0 && maskSize64 / frame.height != alignedStride) + { + return E_OUTOFMEMORY; + } + if (maskSize64 > static_cast(std::numeric_limits::max())) + { + return E_OUTOFMEMORY; + } + + std::vector maskBuffer; + HRESULT hr = AllocateBuffer(maskBuffer, static_cast(maskSize64)); + if (FAILED(hr)) + { + return hr; + } + + std::fill(maskBuffer.begin(), maskBuffer.end(), 0); + + for (UINT y = 0; y < frame.height; ++y) + { + const BYTE* srcRow = frame.pixels.data() + static_cast(y) * frame.stride; + BYTE* dstRow = maskBuffer.data() + static_cast(y) * maskStride; + for (UINT x = 0; x < frame.width; ++x) + { + const BYTE alpha = srcRow[static_cast(x) * 4 + 3]; + if (alpha < 128) + { + dstRow[x / 8] |= static_cast(0x80u >> (x % 8)); + } + } + } + + BITMAPINFO maskInfo{}; + maskInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + maskInfo.bmiHeader.biWidth = static_cast(frame.width); + maskInfo.bmiHeader.biHeight = -static_cast(frame.height); + maskInfo.bmiHeader.biPlanes = 1; + maskInfo.bmiHeader.biBitCount = 1; + maskInfo.bmiHeader.biCompression = BI_RGB; + maskInfo.bmiHeader.biSizeImage = maskSize64 > std::numeric_limits::max() + ? 0 + : static_cast(maskSize64); + maskInfo.bmiHeader.biXPelsPerMeter = 0; + maskInfo.bmiHeader.biYPelsPerMeter = 0; + maskInfo.bmiHeader.biClrUsed = 2; + maskInfo.bmiHeader.biClrImportant = 2; + maskInfo.bmiColors[0].rgbBlue = 0; + maskInfo.bmiColors[0].rgbGreen = 0; + maskInfo.bmiColors[0].rgbRed = 0; + maskInfo.bmiColors[0].rgbReserved = 0; + maskInfo.bmiColors[1].rgbBlue = 255; + maskInfo.bmiColors[1].rgbGreen = 255; + maskInfo.bmiColors[1].rgbRed = 255; + maskInfo.bmiColors[1].rgbReserved = 0; + + void* bits = nullptr; + HBITMAP mask = CreateDIBSection(nullptr, &maskInfo, DIB_RGB_COLORS, &bits, nullptr, 0); + if (!mask || !bits) + { + if (mask) + { + DeleteObject(mask); + } + return E_OUTOFMEMORY; + } + + memcpy(bits, maskBuffer.data(), maskBuffer.size()); + + frame.transparencyMask = mask; + frame.hasTransparency = true; + return S_OK; +} + +void FillBufferWithColor(std::vector& buffer, UINT width, UINT height, BYTE r, BYTE g, BYTE b, BYTE a) +{ + if (buffer.empty() || width == 0 || height == 0) + { + return; + } + const unsigned int alpha = a; + const BYTE fillR = static_cast((static_cast(r) * alpha + 127u) / 255u); + const BYTE fillG = static_cast((static_cast(g) * alpha + 127u) / 255u); + const BYTE fillB = static_cast((static_cast(b) * alpha + 127u) / 255u); + const size_t stride = static_cast(width) * kBytesPerPixel; + for (UINT y = 0; y < height; ++y) + { + BYTE* row = buffer.data() + static_cast(y) * stride; + for (UINT x = 0; x < width; ++x) + { + BYTE* pixel = row + static_cast(x) * 4; + pixel[0] = fillB; + pixel[1] = fillG; + pixel[2] = fillR; + pixel[3] = a; + } + } +} + +void ClearBufferRect(std::vector& buffer, UINT width, UINT height, const RECT& rect, BYTE r, BYTE g, BYTE b, + BYTE a) +{ + if (buffer.empty() || width == 0 || height == 0) + { + return; + } + + const LONG maxX = static_cast(width); + const LONG maxY = static_cast(height); + const LONG left = std::clamp(rect.left, 0L, maxX); + const LONG top = std::clamp(rect.top, 0L, maxY); + const LONG right = std::clamp(rect.right, left, maxX); + const LONG bottom = std::clamp(rect.bottom, top, maxY); + if (right <= left || bottom <= top) + { + return; + } + + const unsigned int alpha = a; + const BYTE fillR = static_cast((static_cast(r) * alpha + 127u) / 255u); + const BYTE fillG = static_cast((static_cast(g) * alpha + 127u) / 255u); + const BYTE fillB = static_cast((static_cast(b) * alpha + 127u) / 255u); + const size_t stride = static_cast(width) * kBytesPerPixel; + for (LONG y = top; y < bottom; ++y) + { + BYTE* row = buffer.data() + static_cast(y) * stride + static_cast(left) * 4; + for (LONG x = left; x < right; ++x) + { + row[0] = fillB; + row[1] = fillG; + row[2] = fillR; + row[3] = a; + row += 4; + } + } +} + +void ZeroTransparentPixels(std::vector& buffer) +{ + if (buffer.empty()) + { + return; + } + + const size_t totalPixels = buffer.size() / kBytesPerPixel; + BYTE* data = buffer.data(); + for (size_t i = 0; i < totalPixels; ++i) + { + BYTE* pixel = data + i * kBytesPerPixel; + if (pixel[3] == 0) + { + pixel[0] = 0; + pixel[1] = 0; + pixel[2] = 0; + } + } +} + +void FillTransparentPixelsWithColor(std::vector& buffer, UINT width, UINT height, UINT stride, BYTE r, BYTE g, + BYTE b, BYTE a) +{ + if (buffer.empty() || width == 0 || height == 0) + { + return; + } + + (void)a; + + const size_t rowStride = static_cast(stride); + const size_t expectedStride = static_cast(width) * kBytesPerPixel; + if (rowStride < expectedStride) + { + return; + } + + for (UINT y = 0; y < height; ++y) + { + BYTE* row = buffer.data() + rowStride * static_cast(y); + for (UINT x = 0; x < width; ++x) + { + BYTE* pixel = row + static_cast(x) * kBytesPerPixel; + if (pixel[3] == 0) + { + pixel[0] = b; + pixel[1] = g; + pixel[2] = r; + pixel[3] = 0; + } + } + } +} + +void BlendPremultipliedPixel(BYTE* dest, const BYTE* src) +{ + const unsigned int srcAlpha = src[3]; + if (srcAlpha == 0) + { + return; + } + + if (srcAlpha == 255) + { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = 255; + return; + } + + const unsigned int destAlpha = dest[3]; + const unsigned int invSrcAlpha = 255u - srcAlpha; + + unsigned int outAlpha = srcAlpha + (destAlpha * invSrcAlpha + 127u) / 255u; + if (outAlpha > 255u) + { + outAlpha = 255u; + } + + const unsigned int destBlueScaled = (static_cast(dest[0]) * invSrcAlpha + 127u) / 255u; + const unsigned int destGreenScaled = (static_cast(dest[1]) * invSrcAlpha + 127u) / 255u; + const unsigned int destRedScaled = (static_cast(dest[2]) * invSrcAlpha + 127u) / 255u; + + unsigned int outBlue = static_cast(src[0]) + destBlueScaled; + unsigned int outGreen = static_cast(src[1]) + destGreenScaled; + unsigned int outRed = static_cast(src[2]) + destRedScaled; + + if (outBlue > outAlpha) + { + outBlue = outAlpha; + } + if (outGreen > outAlpha) + { + outGreen = outAlpha; + } + if (outRed > outAlpha) + { + outRed = outAlpha; + } + + dest[0] = static_cast(outBlue); + dest[1] = static_cast(outGreen); + dest[2] = static_cast(outRed); + dest[3] = static_cast(outAlpha); +} + +bool IsPaletteUnavailable(HRESULT hr) +{ +#if defined(WINCODEC_ERR_PALETTEUNAVAILABLE) + if (hr == WINCODEC_ERR_PALETTEUNAVAILABLE) + { + return true; + } +#endif +#if defined(WINCODEC_ERR_NOTINITIALIZED) + if (hr == WINCODEC_ERR_NOTINITIALIZED) + { + return true; + } +#endif +#if defined(WINCODEC_ERR_PROPERTYNOTFOUND) + if (hr == WINCODEC_ERR_PROPERTYNOTFOUND) + { + return true; + } +#endif +#if defined(WINCODEC_ERR_UNSUPPORTEDOPERATION) + if (hr == WINCODEC_ERR_UNSUPPORTEDOPERATION) + { + return true; + } +#endif + return false; +} + +HPALETTE CreateGdiPalette(const std::vector& entries) +{ + if (entries.empty()) + { + return nullptr; + } + if (entries.size() > 256) + { + return nullptr; + } + + const size_t paletteSize = sizeof(LOGPALETTE) + (entries.size() - 1) * sizeof(PALETTEENTRY); + std::vector buffer; + try + { + buffer.resize(paletteSize); + } + catch (const std::bad_alloc&) + { + return nullptr; + } + + auto* logPalette = reinterpret_cast(buffer.data()); + logPalette->palVersion = 0x300; + logPalette->palNumEntries = static_cast(entries.size()); + for (size_t i = 0; i < entries.size(); ++i) + { + const RGBQUAD& quad = entries[i]; + logPalette->palPalEntry[i].peRed = quad.rgbRed; + logPalette->palPalEntry[i].peGreen = quad.rgbGreen; + logPalette->palPalEntry[i].peBlue = quad.rgbBlue; + logPalette->palPalEntry[i].peFlags = PC_NOCOLLAPSE; + } + + return CreatePalette(logPalette); +} + +class PaletteSelector +{ +public: + PaletteSelector(HDC dc, HPALETTE palette, bool enable) + : m_dc(dc) + , m_previous(nullptr) + , m_active(false) + { + if (!enable || !m_dc || !palette) + { + return; + } + + if ((GetDeviceCaps(m_dc, RASTERCAPS) & RC_PALETTE) == 0) + { + return; + } + + m_previous = SelectPalette(m_dc, palette, FALSE); + RealizePalette(m_dc); + m_active = true; + } + + PaletteSelector(const PaletteSelector&) = delete; + PaletteSelector& operator=(const PaletteSelector&) = delete; + + ~PaletteSelector() + { + if (!m_active || !m_dc) + { + return; + } + + HPALETTE restore = m_previous ? m_previous : static_cast(GetStockObject(DEFAULT_PALETTE)); + if (restore) + { + SelectPalette(m_dc, restore, TRUE); + RealizePalette(m_dc); + } + } + +private: + HDC m_dc; + HPALETTE m_previous; + bool m_active; +}; + +HRESULT PopulateFramePalette(IWICImagingFactory* factory, FrameData& frame) +{ + if (frame.paletteHandle) + { + DeleteObject(frame.paletteHandle); + frame.paletteHandle = nullptr; + } + frame.palette.clear(); + + frame.useIndexedPixels = false; + frame.indexedPixels.clear(); + frame.indexedStride = 0; + frame.indexedBmi = BITMAPINFOHEADER{}; + frame.displayBmi = BITMAPINFOHEADER{}; + frame.displayStride = 0; + + const bool needPalette = frame.allowIndexedDisplay || frame.realizePalette; + if (!needPalette) + { + frame.paletteColorCount = 0; + return S_OK; + } + + if (!factory || !frame.frame) + { + frame.paletteColorCount = 0; + frame.realizePalette = false; + return S_OK; + } + + if (frame.paletteColorCount == 0 && frame.bitsPerPixel > 8) + { + frame.realizePalette = false; + return S_OK; + } + + Microsoft::WRL::ComPtr palette; + HRESULT hr = factory->CreatePalette(&palette); + if (FAILED(hr)) + { + return hr; + } + + hr = frame.frame->CopyPalette(palette.Get()); + if (FAILED(hr)) + { + if (IsPaletteUnavailable(hr)) + { + frame.paletteColorCount = 0; + frame.realizePalette = false; + return S_OK; + } + return hr; + } + + UINT colorCount = 0; + hr = palette->GetColorCount(&colorCount); + if (FAILED(hr)) + { + return hr; + } + if (colorCount == 0) + { + frame.paletteColorCount = 0; + frame.realizePalette = false; + return S_OK; + } + + std::vector colors(colorCount); + UINT actual = colorCount; + hr = palette->GetColors(colorCount, colors.data(), &actual); + if (FAILED(hr)) + { + return hr; + } + colors.resize(actual); + + if (frame.gifHasTransparentColor) + { + const size_t transparent = static_cast(frame.gifTransparentIndex); + if (transparent < colors.size()) + { + colors[transparent] = 0u; + } + } + + try + { + frame.palette.resize(colors.size()); + } + catch (const std::bad_alloc&) + { + frame.palette.clear(); + return E_OUTOFMEMORY; + } + + for (size_t i = 0; i < colors.size(); ++i) + { + const WICColor color = colors[i]; + RGBQUAD quad{}; + quad.rgbRed = static_cast((color >> 16) & 0xFF); + quad.rgbGreen = static_cast((color >> 8) & 0xFF); + quad.rgbBlue = static_cast(color & 0xFF); + quad.rgbReserved = static_cast((color >> 24) & 0xFF); + frame.palette[i] = quad; + } + + frame.paletteColorCount = static_cast(colors.size()); + if (!frame.palette.empty()) + { + HPALETTE paletteHandle = CreateGdiPalette(frame.palette); + if (paletteHandle) + { + frame.paletteHandle = paletteHandle; + } + else if (frame.palette.size() <= 256) + { + const DWORD error = GetLastError(); + return HRESULT_FROM_WIN32(error != 0 ? error : ERROR_NOT_ENOUGH_MEMORY); + } + } + + if (frame.bitsPerPixel > 0) + { + frame.reportedColors = + DetermineColorCount(frame.sourcePixelFormat, frame.bitsPerPixel, frame.paletteColorCount, frame.colorModel); + } + + frame.realizePalette = frame.realizePalette && frame.paletteColorCount > 0; + return S_OK; +} + +HRESULT BuildIndexedPixelBuffer(FrameData& frame) +{ + frame.useIndexedPixels = false; + frame.indexedPixels.clear(); + frame.indexedStride = 0; + frame.indexedBmi = BITMAPINFOHEADER{}; + + if (!frame.allowIndexedDisplay) + { + return S_FALSE; + } + + if (frame.hasTransparency) + { + return S_FALSE; + } + + if (frame.bitsPerPixel > 8) + { + return S_FALSE; + } + if (frame.width == 0 || frame.height == 0) + { + return S_FALSE; + } + + if (frame.paletteColorCount == 0) + { + return S_FALSE; + } + + const size_t alignedStride = (static_cast(frame.width) + 3u) & ~static_cast(3u); + if (alignedStride > static_cast(std::numeric_limits::max())) + { + return E_OUTOFMEMORY; + } + const size_t totalSize = alignedStride * static_cast(frame.height); + try + { + frame.indexedPixels.resize(totalSize); + } + catch (const std::bad_alloc&) + { + frame.indexedPixels.clear(); + return E_OUTOFMEMORY; + } + + std::unordered_map colorToIndex; + colorToIndex.reserve(std::min(frame.paletteColorCount != 0 ? frame.paletteColorCount : 256u, 256u)); + std::vector paletteFromPixels; + paletteFromPixels.reserve(256); + + const BYTE* srcBase = frame.pixels.data(); + for (UINT y = 0; y < frame.height; ++y) + { + const BYTE* srcRow = srcBase + static_cast(y) * frame.stride; + BYTE* dstRow = frame.indexedPixels.data() + static_cast(y) * alignedStride; + for (UINT x = 0; x < frame.width; ++x) + { + const BYTE* srcPixel = srcRow + static_cast(x) * kBytesPerPixel; + const BYTE alpha = srcPixel[3]; + const DWORD key = (static_cast(alpha) << 24) | (static_cast(srcPixel[2]) << 16) | + (static_cast(srcPixel[1]) << 8) | static_cast(srcPixel[0]); + + BYTE paletteIndex = 0; + auto it = colorToIndex.find(key); + if (it == colorToIndex.end()) + { + if (colorToIndex.size() >= 256) + { + frame.indexedPixels.clear(); + frame.indexedStride = 0; + return S_FALSE; + } + + paletteIndex = static_cast(colorToIndex.size()); + colorToIndex.emplace(key, paletteIndex); + + RGBQUAD quad{}; + quad.rgbBlue = srcPixel[0]; + quad.rgbGreen = srcPixel[1]; + quad.rgbRed = srcPixel[2]; + quad.rgbReserved = alpha; + paletteFromPixels.push_back(quad); + } + else + { + paletteIndex = it->second; + } + + dstRow[x] = paletteIndex; + } + + if (alignedStride > static_cast(frame.width)) + { + memset(dstRow + frame.width, 0, alignedStride - static_cast(frame.width)); + } + } + + if (!paletteFromPixels.empty()) + { + if (frame.paletteHandle) + { + DeleteObject(frame.paletteHandle); + frame.paletteHandle = nullptr; + } + + frame.palette = paletteFromPixels; + frame.paletteColorCount = static_cast(frame.palette.size()); + + HPALETTE newPaletteHandle = CreateGdiPalette(frame.palette); + if (!frame.palette.empty() && !newPaletteHandle) + { + const DWORD error = GetLastError(); + return HRESULT_FROM_WIN32(error != 0 ? error : ERROR_NOT_ENOUGH_MEMORY); + } + frame.paletteHandle = newPaletteHandle; + } + else + { + frame.palette.clear(); + frame.paletteColorCount = 0; + if (frame.paletteHandle) + { + DeleteObject(frame.paletteHandle); + frame.paletteHandle = nullptr; + } + } + + if (frame.bitsPerPixel > 0) + { + frame.reportedColors = + DetermineColorCount(frame.sourcePixelFormat, frame.bitsPerPixel, frame.paletteColorCount, frame.colorModel); + } + + frame.useIndexedPixels = true; + frame.indexedStride = static_cast(alignedStride); + frame.indexedBmi.biSize = sizeof(BITMAPINFOHEADER); + frame.indexedBmi.biWidth = static_cast(frame.width); + frame.indexedBmi.biHeight = -static_cast(frame.height); + frame.indexedBmi.biPlanes = 1; + frame.indexedBmi.biBitCount = 8; + frame.indexedBmi.biCompression = BI_RGB; + frame.indexedBmi.biSizeImage = totalSize > std::numeric_limits::max() + ? 0 + : static_cast(totalSize); + frame.indexedBmi.biXPelsPerMeter = frame.bmi.biXPelsPerMeter; + frame.indexedBmi.biYPelsPerMeter = frame.bmi.biYPelsPerMeter; + const UINT paletteUsed = std::min(frame.paletteColorCount, 256u); + frame.indexedBmi.biClrUsed = paletteUsed; + frame.indexedBmi.biClrImportant = paletteUsed; + return S_OK; +} + + +HRESULT CreateSequenceBitmaps(const FrameData& frame, const RECT& rect, HBITMAP& colorBitmap, HBITMAP& maskBitmap) +{ + colorBitmap = nullptr; + maskBitmap = nullptr; + + if (frame.pixels.empty() || frame.stride == 0 || frame.width == 0 || frame.height == 0) + { + return E_INVALIDARG; + } + + const LONG maxWidth = static_cast(frame.width); + const LONG maxHeight = static_cast(frame.height); + + const LONG left = std::clamp(rect.left, 0L, maxWidth); + const LONG top = std::clamp(rect.top, 0L, maxHeight); + const LONG right = std::clamp(rect.right, left, maxWidth); + const LONG bottom = std::clamp(rect.bottom, top, maxHeight); + + const LONG visibleWidth = right - left; + const LONG visibleHeight = bottom - top; + const LONG bitmapWidth = std::max(visibleWidth, 1); + const LONG bitmapHeight = std::max(visibleHeight, 1); + + const size_t dstStride = static_cast(bitmapWidth) * kBytesPerPixel; + const size_t bitmapSize = dstStride * static_cast(bitmapHeight); + + BITMAPINFO colorInfo{}; + colorInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + colorInfo.bmiHeader.biWidth = bitmapWidth; + colorInfo.bmiHeader.biHeight = -bitmapHeight; + colorInfo.bmiHeader.biPlanes = 1; + colorInfo.bmiHeader.biBitCount = 32; + colorInfo.bmiHeader.biCompression = BI_RGB; + colorInfo.bmiHeader.biSizeImage = bitmapSize > std::numeric_limits::max() ? 0 + : static_cast(bitmapSize); + + void* colorBits = nullptr; + HBITMAP color = CreateDIBSection(nullptr, &colorInfo, DIB_RGB_COLORS, &colorBits, nullptr, 0); + if (!color || !colorBits) + { + if (color) + { + DeleteObject(color); + } + return E_OUTOFMEMORY; + } + + BYTE* dstBase = reinterpret_cast(colorBits); + memset(dstBase, 0, bitmapSize); + + if (visibleWidth > 0 && visibleHeight > 0) + { + for (LONG y = 0; y < visibleHeight; ++y) + { + const BYTE* srcRow = frame.pixels.data() + + (static_cast(top + y) * frame.stride) + + static_cast(left) * kBytesPerPixel; + BYTE* dstRow = dstBase + static_cast(y) * dstStride; + memcpy(dstRow, srcRow, static_cast(visibleWidth) * kBytesPerPixel); + } + } + + const ULONGLONG maskStride64 = ((static_cast(bitmapWidth) + 7ull) / 8ull + 3ull) & ~3ull; + if (maskStride64 > static_cast(std::numeric_limits::max())) + { + DeleteObject(color); + return E_OUTOFMEMORY; + } + const UINT maskStride = static_cast(maskStride64); + const ULONGLONG maskSize64 = maskStride64 * static_cast(bitmapHeight); + if (bitmapHeight != 0 && maskSize64 / bitmapHeight != maskStride64) + { + DeleteObject(color); + return E_OUTOFMEMORY; + } + if (maskSize64 > static_cast(std::numeric_limits::max())) + { + DeleteObject(color); + return E_OUTOFMEMORY; + } + + std::vector maskBuffer; + HRESULT hr = AllocateBuffer(maskBuffer, static_cast(maskSize64)); + if (FAILED(hr)) + { + DeleteObject(color); + return hr; + } + memset(maskBuffer.data(), 0, maskBuffer.size()); + + bool hasTransparency = false; + if (visibleWidth > 0 && visibleHeight > 0) + { + for (LONG y = 0; y < visibleHeight; ++y) + { + const BYTE* srcRow = frame.pixels.data() + + (static_cast(top + y) * frame.stride) + + static_cast(left) * kBytesPerPixel; + BYTE* maskRow = maskBuffer.data() + static_cast(y) * maskStride; + for (LONG x = 0; x < visibleWidth; ++x) + { + if (srcRow[static_cast(x) * 4 + 3] == 0) + { + maskRow[x / 8] |= static_cast(0x80u >> (x % 8)); + hasTransparency = true; + } + } + } + } + else + { + if (bitmapWidth > 0 && bitmapHeight > 0) + { + for (LONG y = 0; y < bitmapHeight; ++y) + { + BYTE* maskRow = maskBuffer.data() + static_cast(y) * maskStride; + maskRow[0] = 0xFF; + } + hasTransparency = true; + } + } + + HBITMAP mask = nullptr; + if (hasTransparency) + { + BITMAPINFO maskInfo{}; + maskInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + maskInfo.bmiHeader.biWidth = bitmapWidth; + maskInfo.bmiHeader.biHeight = -bitmapHeight; + maskInfo.bmiHeader.biPlanes = 1; + maskInfo.bmiHeader.biBitCount = 1; + maskInfo.bmiHeader.biCompression = BI_RGB; + maskInfo.bmiHeader.biSizeImage = maskSize64 > std::numeric_limits::max() + ? 0 + : static_cast(maskSize64); + maskInfo.bmiColors[0].rgbBlue = 0; + maskInfo.bmiColors[0].rgbGreen = 0; + maskInfo.bmiColors[0].rgbRed = 0; + maskInfo.bmiColors[0].rgbReserved = 0; + maskInfo.bmiColors[1].rgbBlue = 255; + maskInfo.bmiColors[1].rgbGreen = 255; + maskInfo.bmiColors[1].rgbRed = 255; + maskInfo.bmiColors[1].rgbReserved = 0; + + void* maskBits = nullptr; + mask = CreateDIBSection(nullptr, &maskInfo, DIB_RGB_COLORS, &maskBits, nullptr, 0); + if (!mask || !maskBits) + { + if (mask) + { + DeleteObject(mask); + } + DeleteObject(color); + return E_OUTOFMEMORY; + } + memcpy(maskBits, maskBuffer.data(), maskBuffer.size()); + } + + colorBitmap = color; + maskBitmap = mask; + return S_OK; +} + +HRESULT EnsureScaledBitmap(ImageHandle& handle, FrameData& frame, UINT width, UINT height) +{ + if (width == 0 || height == 0) + { + return E_INVALIDARG; + } + + if (frame.scaledBitmap && frame.scaledWidth == width && frame.scaledHeight == height) + { + return S_OK; + } + + if (frame.scaledBitmap) + { + DeleteObject(frame.scaledBitmap); + frame.scaledBitmap = nullptr; + } + frame.scaledPixels.clear(); + frame.scaledStride = 0; + frame.scaledWidth = 0; + frame.scaledHeight = 0; + + IWICImagingFactory* factory = handle.backend ? handle.backend->Factory() : nullptr; + if (!factory) + { + return E_FAIL; + } + + if (frame.width > std::numeric_limits::max() || frame.height > std::numeric_limits::max()) + { + return E_OUTOFMEMORY; + } + const bool usePremultipliedSource = frame.hasTransparency && !frame.compositedPixels.empty(); + const std::vector& sourceBuffer = usePremultipliedSource ? frame.compositedPixels : frame.pixels; + if (sourceBuffer.empty() || frame.width == 0 || frame.height == 0) + { + return E_FAIL; + } + const size_t pixelBytes = sourceBuffer.size(); + if (pixelBytes > static_cast(std::numeric_limits::max())) + { + return E_OUTOFMEMORY; + } + + std::vector premultipliedBuffer; + GUID bitmapPixelFormat = usePremultipliedSource ? GUID_WICPixelFormat32bppPBGRA : GUID_WICPixelFormat32bppBGRA; + BYTE* bitmapMemory = const_cast(sourceBuffer.data()); + if (!usePremultipliedSource && frame.hasTransparency) + { + try + { + premultipliedBuffer.assign(sourceBuffer.begin(), sourceBuffer.end()); + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + + const size_t pixelCount = premultipliedBuffer.size() / kBytesPerPixel; + for (size_t i = 0; i < pixelCount; ++i) + { + BYTE* pixel = premultipliedBuffer.data() + i * kBytesPerPixel; + const BYTE alpha = pixel[3]; + if (alpha == 0) + { + pixel[0] = 0; + pixel[1] = 0; + pixel[2] = 0; + continue; + } + if (alpha == 255) + { + continue; + } + + const unsigned int a = alpha; + pixel[0] = static_cast((static_cast(pixel[0]) * a + 127u) / 255u); + pixel[1] = static_cast((static_cast(pixel[1]) * a + 127u) / 255u); + pixel[2] = static_cast((static_cast(pixel[2]) * a + 127u) / 255u); + } + + bitmapPixelFormat = GUID_WICPixelFormat32bppPBGRA; + bitmapMemory = premultipliedBuffer.data(); + } + + Microsoft::WRL::ComPtr memoryBitmap; + HRESULT hr = factory->CreateBitmapFromMemory(frame.width, frame.height, bitmapPixelFormat, frame.stride, + static_cast(pixelBytes), bitmapMemory, &memoryBitmap); + if (FAILED(hr)) + { + return hr; + } + + Microsoft::WRL::ComPtr scaler; + hr = factory->CreateBitmapScaler(&scaler); + if (FAILED(hr)) + { + return hr; + } + + hr = scaler->Initialize(memoryBitmap.Get(), width, height, WICBitmapInterpolationModeHighQualityCubic); + if (FAILED(hr)) + { + return hr; + } + + const ULONGLONG stride64 = static_cast(width) * kBytesPerPixel; + if (stride64 > static_cast(std::numeric_limits::max())) + { + return E_OUTOFMEMORY; + } + const UINT stride = static_cast(stride64); + const ULONGLONG bufferSize64 = stride64 * static_cast(height); + if (height != 0 && bufferSize64 / height != stride64) + { + return E_OUTOFMEMORY; + } + if (bufferSize64 > static_cast(std::numeric_limits::max())) + { + return E_OUTOFMEMORY; + } + + try + { + frame.scaledPixels.resize(static_cast(bufferSize64)); + } + catch (const std::bad_alloc&) + { + frame.scaledPixels.clear(); + return E_OUTOFMEMORY; + } + + hr = scaler->CopyPixels(nullptr, stride, static_cast(frame.scaledPixels.size()), frame.scaledPixels.data()); + if (FAILED(hr)) + { + frame.scaledPixels.clear(); + return hr; + } + + BITMAPINFO scaledInfo{}; + scaledInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + scaledInfo.bmiHeader.biWidth = static_cast(width); + scaledInfo.bmiHeader.biHeight = -static_cast(height); + scaledInfo.bmiHeader.biPlanes = 1; + scaledInfo.bmiHeader.biBitCount = 32; + scaledInfo.bmiHeader.biCompression = BI_RGB; + scaledInfo.bmiHeader.biSizeImage = frame.scaledPixels.size() > std::numeric_limits::max() + ? 0 + : static_cast(frame.scaledPixels.size()); + scaledInfo.bmiHeader.biXPelsPerMeter = frame.bmi.biXPelsPerMeter; + scaledInfo.bmiHeader.biYPelsPerMeter = frame.bmi.biYPelsPerMeter; + + void* bits = nullptr; + HBITMAP scaledBitmap = CreateDIBSection(nullptr, &scaledInfo, DIB_RGB_COLORS, &bits, nullptr, 0); + if (!scaledBitmap || !bits) + { + if (scaledBitmap) + { + DeleteObject(scaledBitmap); + } + frame.scaledPixels.clear(); + return E_OUTOFMEMORY; + } + + memcpy(bits, frame.scaledPixels.data(), frame.scaledPixels.size()); + if (frame.hasTransparency) + { + BYTE* target = static_cast(bits); + const size_t pixelCount = frame.scaledPixels.size() / kBytesPerPixel; + for (size_t i = 0; i < pixelCount; ++i) + { + BYTE* pixel = target + i * kBytesPerPixel; + if (pixel[3] == 0) + { + pixel[0] = 0; + pixel[1] = 0; + pixel[2] = 0; + } + } + + UnpremultiplyBuffer(frame.scaledPixels, width, height, stride); + ZeroTransparentPixels(frame.scaledPixels); + } + + frame.scaledBitmap = scaledBitmap; + frame.scaledStride = stride; + frame.scaledWidth = width; + frame.scaledHeight = height; + return S_OK; +} + +DWORD DetermineColorModelFromPixelFormat(const GUID& pixelFormat) +{ + if (IsEqualGUID(pixelFormat, GUID_WICPixelFormatBlackWhite) || + IsEqualGUID(pixelFormat, GUID_WICPixelFormat2bppGray) || + IsEqualGUID(pixelFormat, GUID_WICPixelFormat4bppGray) || + IsEqualGUID(pixelFormat, GUID_WICPixelFormat8bppGray) || + IsEqualGUID(pixelFormat, GUID_WICPixelFormat16bppGray) || +#ifdef GUID_WICPixelFormat16bppGrayFixedPoint + IsEqualGUID(pixelFormat, GUID_WICPixelFormat16bppGrayFixedPoint) || +#endif +#ifdef GUID_WICPixelFormat16bppGrayHalf + IsEqualGUID(pixelFormat, GUID_WICPixelFormat16bppGrayHalf) || +#endif +#ifdef GUID_WICPixelFormat32bppGrayFloat + IsEqualGUID(pixelFormat, GUID_WICPixelFormat32bppGrayFloat) || +#endif + IsEqualGUID(pixelFormat, GUID_WICPixelFormat8bppY)) + { + return PVCM_GRAYS; + } + + if (IsEqualGUID(pixelFormat, GUID_WICPixelFormat32bppCMYK) || + IsEqualGUID(pixelFormat, GUID_WICPixelFormat40bppCMYKAlpha) +#ifdef GUID_WICPixelFormat64bppCMYK + || IsEqualGUID(pixelFormat, GUID_WICPixelFormat64bppCMYK) +#endif +#ifdef GUID_WICPixelFormat80bppCMYKAlpha + || IsEqualGUID(pixelFormat, GUID_WICPixelFormat80bppCMYKAlpha) +#endif +#ifdef GUID_WICPixelFormat64bppCMYKFixedPoint + || IsEqualGUID(pixelFormat, GUID_WICPixelFormat64bppCMYKFixedPoint) +#endif +#ifdef GUID_WICPixelFormat128bppCMYKFixedPoint + || IsEqualGUID(pixelFormat, GUID_WICPixelFormat128bppCMYKFixedPoint) +#endif + ) + { + return PVCM_CMYK; + } + + return PVCM_RGB; +} + +DWORD DetermineColorCount(const GUID& pixelFormat, UINT bitsPerPixel, UINT paletteColors, DWORD colorModel) +{ + if (paletteColors > 0) + { + return paletteColors; + } + + if (bitsPerPixel == 0) + { + return PV_COLOR_TC32; + } + + if (bitsPerPixel <= 8) + { + const UINT rawColors = 1u << bitsPerPixel; + return std::max(2u, rawColors); + } + + if (bitsPerPixel == 15) + { + return PV_COLOR_HC15; + } + + if (bitsPerPixel == 16) + { + if (IsEqualGUID(pixelFormat, GUID_WICPixelFormat16bppBGR555) +#ifdef GUID_WICPixelFormat16bppBGRA5551 + || IsEqualGUID(pixelFormat, GUID_WICPixelFormat16bppBGRA5551) +#endif + ) + { + return PV_COLOR_HC15; + } + return PV_COLOR_HC16; + } + + if (bitsPerPixel == 24) + { + return PV_COLOR_TC24; + } + + if (bitsPerPixel == 32) + { + return PV_COLOR_TC32; + } + + if (colorModel == PVCM_GRAYS && bitsPerPixel <= 16) + { + if (bitsPerPixel >= 31) + { + return PV_COLOR_TC32; + } + const UINT rawColors = 1u << bitsPerPixel; + return std::max(2u, rawColors); + } + + if (bitsPerPixel > 32) + { + return PV_COLOR_TC32; + } + + return PV_COLOR_TC32; +} + +HRESULT ConvertBgraSourceToCmyk(IWICImagingFactory* factory, IWICBitmapSource* source, + IWICBitmapSource** convertedSource) +{ + if (!factory || !source || !convertedSource) + { + return E_INVALIDARG; } - bool hasTransparentPixel = false; - for (UINT y = 0; y < frame.height; ++y) + *convertedSource = nullptr; + + UINT width = 0; + UINT height = 0; + HRESULT hr = source->GetSize(&width, &height); + if (FAILED(hr)) { - BYTE* row = frame.pixels.data() + static_cast(y) * frame.stride; - for (UINT x = 0; x < frame.width; ++x) - { - BYTE* pixel = row + static_cast(x) * 4; - if (pixel[3] < 128) - { - hasTransparentPixel = true; - pixel[0] = 0; - pixel[1] = 0; - pixel[2] = 0; - pixel[3] = 0; - } - else - { - pixel[3] = 255; - } - } + return hr; } - - if (!hasTransparentPixel) + if (width == 0 || height == 0) { - return S_OK; + return WINCODEC_ERR_INVALIDPARAMETER; } - const ULONGLONG unalignedStride = (static_cast(frame.width) + 7ull) / 8ull; - const ULONGLONG alignedStride = (unalignedStride + 3ull) & ~3ull; - if (alignedStride > std::numeric_limits::max()) + const ULONGLONG stride64 = static_cast(width) * 4ull; + if (stride64 > static_cast(std::numeric_limits::max())) { return E_OUTOFMEMORY; } - const UINT maskStride = static_cast(alignedStride); - const ULONGLONG maskSize64 = alignedStride * static_cast(frame.height); - if (frame.height != 0 && maskSize64 / frame.height != alignedStride) + const UINT stride = static_cast(stride64); + const ULONGLONG buffer64 = stride64 * static_cast(height); + if (height != 0 && buffer64 / height != stride64) { return E_OUTOFMEMORY; } - if (maskSize64 > static_cast(std::numeric_limits::max())) + if (buffer64 > static_cast(std::numeric_limits::max())) { return E_OUTOFMEMORY; } - std::vector maskBuffer; - HRESULT hr = AllocateBuffer(maskBuffer, static_cast(maskSize64)); + std::vector bgra(static_cast(buffer64)); + WICRect rect{0, 0, static_cast(width), static_cast(height)}; + hr = source->CopyPixels(&rect, stride, static_cast(bgra.size()), bgra.data()); if (FAILED(hr)) { return hr; } - for (UINT y = 0; y < frame.height; ++y) + std::vector cmyk(bgra.size()); + for (UINT y = 0; y < height; ++y) { - const BYTE* srcRow = frame.pixels.data() + static_cast(y) * frame.stride; - BYTE* dstRow = maskBuffer.data() + static_cast(y) * maskStride; - for (UINT x = 0; x < frame.width; ++x) + const BYTE* srcRow = bgra.data() + static_cast(y) * stride; + BYTE* dstRow = cmyk.data() + static_cast(y) * stride; + for (UINT x = 0; x < width; ++x) { - if (srcRow[static_cast(x) * 4 + 3] == 0) + const BYTE* pixel = srcRow + static_cast(x) * 4; + BYTE* out = dstRow + static_cast(x) * 4; + + const BYTE alpha = pixel[3]; + BYTE r = pixel[2]; + BYTE g = pixel[1]; + BYTE b = pixel[0]; + if (alpha < 255) { - dstRow[x / 8] |= static_cast(0x80u >> (x % 8)); + const unsigned int invAlpha = 255u - static_cast(alpha); + r = static_cast((static_cast(r) * alpha + 255u * invAlpha + 127u) / 255u); + g = static_cast((static_cast(g) * alpha + 255u * invAlpha + 127u) / 255u); + b = static_cast((static_cast(b) * alpha + 255u * invAlpha + 127u) / 255u); } - } - } - BITMAPINFO maskInfo{}; - maskInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - maskInfo.bmiHeader.biWidth = static_cast(frame.width); - maskInfo.bmiHeader.biHeight = -static_cast(frame.height); - maskInfo.bmiHeader.biPlanes = 1; - maskInfo.bmiHeader.biBitCount = 1; - maskInfo.bmiHeader.biCompression = BI_RGB; - maskInfo.bmiHeader.biSizeImage = maskSize64 > std::numeric_limits::max() - ? 0 - : static_cast(maskSize64); - maskInfo.bmiHeader.biXPelsPerMeter = 0; - maskInfo.bmiHeader.biYPelsPerMeter = 0; - maskInfo.bmiHeader.biClrUsed = 2; - maskInfo.bmiHeader.biClrImportant = 2; - maskInfo.bmiColors[0].rgbBlue = 0; - maskInfo.bmiColors[0].rgbGreen = 0; - maskInfo.bmiColors[0].rgbRed = 0; - maskInfo.bmiColors[0].rgbReserved = 0; - maskInfo.bmiColors[1].rgbBlue = 255; - maskInfo.bmiColors[1].rgbGreen = 255; - maskInfo.bmiColors[1].rgbRed = 255; - maskInfo.bmiColors[1].rgbReserved = 0; + const double rf = static_cast(r) / 255.0; + const double gf = static_cast(g) / 255.0; + const double bf = static_cast(b) / 255.0; + const double maxRgb = std::max({rf, gf, bf}); + const double k = 1.0 - maxRgb; - void* bits = nullptr; - HBITMAP mask = CreateDIBSection(nullptr, &maskInfo, DIB_RGB_COLORS, &bits, nullptr, 0); - if (!mask || !bits) - { - if (mask) - { - DeleteObject(mask); + double c = 0.0; + double m = 0.0; + double yVal = 0.0; + if (k < 0.999999) + { + const double inv = 1.0 - k; + c = (1.0 - rf - k) / inv; + m = (1.0 - gf - k) / inv; + yVal = (1.0 - bf - k) / inv; + } + + const auto clampByte = [](double value) -> BYTE { + if (value <= 0.0) + { + return 0; + } + if (value >= 1.0) + { + return 255; + } + return static_cast(std::floor(value * 255.0 + 0.5)); + }; + + out[0] = clampByte(c); + out[1] = clampByte(m); + out[2] = clampByte(yVal); + out[3] = clampByte(k); } - return E_OUTOFMEMORY; } - memcpy(bits, maskBuffer.data(), maskBuffer.size()); + Microsoft::WRL::ComPtr bitmap; + hr = factory->CreateBitmapFromMemory(width, height, GUID_WICPixelFormat32bppCMYK, stride, + static_cast(cmyk.size()), cmyk.data(), &bitmap); + if (FAILED(hr)) + { + return hr; + } - frame.transparencyMask = mask; - frame.hasTransparency = true; - return S_OK; + return bitmap.CopyTo(convertedSource); } -HRESULT FinalizeDecodedFrame(FrameData& frame) +HRESULT FinalizeDecodedFrame(Backend* backend, FrameData& frame) { const size_t lineCount = static_cast(frame.height); if (lineCount > frame.linePointers.max_size()) { return E_OUTOFMEMORY; } + IWICImagingFactory* factory = backend ? backend->Factory() : nullptr; + HRESULT paletteHr = PopulateFramePalette(factory, frame); + if (FAILED(paletteHr)) + { + return paletteHr; + } HRESULT maskHr = EnsureTransparencyMask(frame); if (FAILED(maskHr)) { return maskHr; } + HRESULT indexedHr = BuildIndexedPixelBuffer(frame); + if (FAILED(indexedHr) && indexedHr != S_FALSE) + { + return indexedHr; + } try { frame.linePointers.resize(lineCount); @@ -1596,11 +3339,13 @@ HRESULT FinalizeDecodedFrame(FrameData& frame) return E_OUTOFMEMORY; } + const bool useIndexed = frame.useIndexedPixels && !frame.indexedPixels.empty(); + const size_t lineStride = useIndexed ? static_cast(frame.indexedStride) : frame.stride; + BYTE* baseLine = useIndexed ? frame.indexedPixels.data() : frame.pixels.data(); for (UINT y = 0; y < frame.height; ++y) { - frame.linePointers[y] = frame.pixels.data() + static_cast(y) * frame.stride; + frame.linePointers[y] = baseLine + static_cast(y) * lineStride; } - frame.palette.clear(); frame.bmi.biSize = sizeof(BITMAPINFOHEADER); frame.bmi.biWidth = static_cast(frame.width); @@ -1613,12 +3358,31 @@ HRESULT FinalizeDecodedFrame(FrameData& frame) : static_cast(pixelBytes); frame.bmi.biXPelsPerMeter = 0; frame.bmi.biYPelsPerMeter = 0; + if (useIndexed) + { + frame.displayStride = frame.indexedStride; + frame.displayBmi = frame.indexedBmi; + } + else + { + frame.displayStride = frame.stride; + frame.displayBmi = frame.bmi; + } if (frame.hbitmap) { DeleteObject(frame.hbitmap); frame.hbitmap = nullptr; } + if (frame.scaledBitmap) + { + DeleteObject(frame.scaledBitmap); + frame.scaledBitmap = nullptr; + } + frame.scaledPixels.clear(); + frame.scaledStride = 0; + frame.scaledWidth = 0; + frame.scaledHeight = 0; void* bits = nullptr; BITMAPINFO bmi{}; @@ -1631,6 +3395,31 @@ HRESULT FinalizeDecodedFrame(FrameData& frame) if (bits && !frame.pixels.empty()) { memcpy(bits, frame.pixels.data(), frame.pixels.size()); + if (frame.hasTransparency) + { + BYTE* target = static_cast(bits); + const size_t pixelCount = frame.pixels.size() / kBytesPerPixel; + for (size_t i = 0; i < pixelCount; ++i) + { + BYTE* pixel = target + i * kBytesPerPixel; + const BYTE alpha = pixel[3]; + if (alpha == 0) + { + pixel[0] = 0; + pixel[1] = 0; + pixel[2] = 0; + continue; + } + if (alpha == 255) + { + continue; + } + const unsigned int a = alpha; + pixel[0] = static_cast((static_cast(pixel[0]) * a + 127u) / 255u); + pixel[1] = static_cast((static_cast(pixel[1]) * a + 127u) / 255u); + pixel[2] = static_cast((static_cast(pixel[2]) * a + 127u) / 255u); + } + } } frame.decoded = true; @@ -1838,15 +3627,18 @@ HRESULT EnsureConverter(ImageHandle& handle, size_t index) { return hr; } - hr = converter->Initialize(frame.frame.Get(), GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone, nullptr, 0.0, + const bool isGif = handle.baseInfo.Format == PVF_GIF; + const GUID targetFormat = isGif ? GUID_WICPixelFormat32bppBGRA : GUID_WICPixelFormat32bppPBGRA; + frame.pixelsArePremultiplied = !isGif; + hr = converter->Initialize(frame.frame.Get(), targetFormat, 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); + hr = converter->Initialize(frame.colorConvertedSource.Get(), targetFormat, WICBitmapDitherTypeNone, nullptr, + 0.0, WICBitmapPaletteTypeCustom); } else if (FAILED(profileHr) && !IsIgnorableColorProfileError(profileHr)) { @@ -1871,9 +3663,26 @@ HRESULT DecodeFrame(ImageHandle& handle, size_t index) FrameData& frame = handle.frames[index]; if (frame.decoded) { + if (handle.baseInfo.Format == PVF_GIF) + { + HRESULT restoreHr = RestoreGifCanvasState(handle, index); + if (FAILED(restoreHr) && restoreHr != S_FALSE) + { + return restoreHr; + } + } return S_OK; } + if (handle.baseInfo.Format == PVF_GIF && index > 0) + { + HRESULT previousHr = DecodeFrame(handle, index - 1); + if (FAILED(previousHr)) + { + return previousHr; + } + } + HRESULT hr = EnsureConverter(handle, index); if (FAILED(hr)) { @@ -1884,7 +3693,7 @@ HRESULT DecodeFrame(ImageHandle& handle, size_t index) { return hr; } - hr = FinalizeDecodedFrame(frame); + hr = FinalizeDecodedFrame(handle.backend, frame); return hr; } return hr; @@ -1896,7 +3705,16 @@ HRESULT DecodeFrame(ImageHandle& handle, size_t index) return hr; } - return FinalizeDecodedFrame(frame); + if (handle.baseInfo.Format == PVF_GIF) + { + hr = CompositeGifFrame(handle, index); + if (FAILED(hr)) + { + return hr; + } + } + + return FinalizeDecodedFrame(handle.backend, frame); } PVCODE HResultToPvCode(HRESULT hr) @@ -2053,6 +3871,13 @@ PVCODE PopulateImageInfo(ImageHandle& handle, LPPVImageInfo info, DWORD bufferSi info->TotalBitDepth = 32; info->FSI = handle.hasFormatSpecificInfo ? &handle.formatInfo : nullptr; + if (!handle.frames.empty()) + { + info->Colors = handle.frames[0].reportedColors; + info->ColorModel = handle.frames[0].colorModel; + info->TotalBitDepth = handle.frames[0].reportedBitDepth; + } + struct FormatLabel { DWORD format; @@ -2100,9 +3925,12 @@ PVCODE PopulateImageInfo(ImageHandle& handle, LPPVImageInfo info, DWORD bufferSi const FrameData& frame = handle.frames[normalized]; info->CurrentImage = static_cast(normalized); + info->Colors = frame.reportedColors; + info->ColorModel = frame.colorModel; + info->TotalBitDepth = frame.reportedBitDepth; info->Width = frame.width; info->Height = frame.height; - info->BytesPerLine = frame.stride; + info->BytesPerLine = frame.displayStride != 0 ? frame.displayStride : frame.stride; double dpiX = 0.0; double dpiY = 0.0; @@ -2182,6 +4010,18 @@ HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& handle.baseInfo.FSI = nullptr; handle.canvasWidth = 0; handle.canvasHeight = 0; + handle.gifComposeCanvas.clear(); + handle.gifSavedCanvas.clear(); + handle.gifCanvasInitialized = false; + + GUID container = {}; + if (FAILED(decoder->GetContainerFormat(&container))) + { + container = GUID_ContainerFormatBmp; + } + const DWORD mappedFormat = MapFormatToPvFormat(container); + handle.baseInfo.Format = mappedFormat; + const bool isGifContainer = mappedFormat == PVF_GIF; ComPtr decoderQuery; if (FAILED(decoder->GetMetadataQueryReader(&decoderQuery))) @@ -2222,8 +4062,14 @@ HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& } COLORREF backgroundColor = RGB(0, 0, 0); + handle.gifHasBackgroundColor = false; + handle.gifBackgroundAlpha = 0; + handle.gifHasBackgroundIndex = false; + handle.gifBackgroundIndex = 0; if (hasBackgroundIndex) { + handle.gifHasBackgroundIndex = true; + handle.gifBackgroundIndex = static_cast(backgroundIndex & 0xFFu); ComPtr palette; if (SUCCEEDED(backend.Factory()->CreatePalette(&palette)) && palette) { @@ -2238,10 +4084,13 @@ HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& actualCount > backgroundIndex) { const WICColor color = colors[backgroundIndex]; + const BYTE a = static_cast((color >> 24) & 0xFF); const BYTE r = static_cast((color >> 16) & 0xFF); const BYTE g = static_cast((color >> 8) & 0xFF); const BYTE b = static_cast(color & 0xFF); backgroundColor = RGB(r, g, b); + handle.gifHasBackgroundColor = true; + handle.gifBackgroundAlpha = a; } } } @@ -2301,6 +4150,69 @@ HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& } data.width = width; data.height = height; + data.rawWidth = width; + data.rawHeight = height; + data.rawStride = 0; + + data.sourcePixelFormat = GUID_WICPixelFormat32bppBGRA; + data.bitsPerPixel = 0; + data.paletteColorCount = 0; + data.colorModel = PVCM_RGB; + data.reportedBitDepth = 32; + + GUID pixelFormat{}; + if (SUCCEEDED(data.frame->GetPixelFormat(&pixelFormat))) + { + data.sourcePixelFormat = pixelFormat; + IWICImagingFactory* factory = backend.Factory(); + if (factory) + { + Microsoft::WRL::ComPtr componentInfo; + if (SUCCEEDED(factory->CreateComponentInfo(pixelFormat, &componentInfo))) + { + Microsoft::WRL::ComPtr pixelInfo; + if (SUCCEEDED(componentInfo.As(&pixelInfo))) + { + UINT bitsPerPixel = 0; + if (SUCCEEDED(pixelInfo->GetBitsPerPixel(&bitsPerPixel))) + { + data.bitsPerPixel = bitsPerPixel; + data.reportedBitDepth = bitsPerPixel; + } + } + } + + Microsoft::WRL::ComPtr framePalette; + if (SUCCEEDED(factory->CreatePalette(&framePalette)) && framePalette) + { + if (SUCCEEDED(data.frame->CopyPalette(framePalette.Get()))) + { + UINT paletteCount = 0; + if (SUCCEEDED(framePalette->GetColorCount(&paletteCount))) + { + data.paletteColorCount = paletteCount; + } + } + } + } + + data.colorModel = DetermineColorModelFromPixelFormat(pixelFormat); + } + + if (data.bitsPerPixel == 0) + { + data.bitsPerPixel = 32; + } + if (data.reportedBitDepth == 0) + { + data.reportedBitDepth = data.bitsPerPixel; + } + data.reportedColors = DetermineColorCount(data.sourcePixelFormat, data.bitsPerPixel, data.paletteColorCount, + data.colorModel); + const bool palettedSource = (data.bitsPerPixel > 0 && data.bitsPerPixel <= 8 && data.paletteColorCount > 0); + data.allowIndexedDisplay = palettedSource; + data.realizePalette = palettedSource; + data.delayMs = GetFrameDelayMilliseconds(data.frame.Get()); if (frameCount > 1 && data.delayMs == 0) { @@ -2318,6 +4230,8 @@ HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& bool topSpecified = false; ULONGLONG rectWidth64 = static_cast(width); ULONGLONG rectHeight64 = static_cast(height); + data.gifHasTransparentColor = false; + data.gifTransparentIndex = 0; ComPtr frameQuery; if (SUCCEEDED(data.frame->GetMetadataQueryReader(&frameQuery)) && frameQuery) { @@ -2344,6 +4258,32 @@ HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& { data.disposal = MapGifDisposalToPv(value); } + bool transparencySpecified = false; + bool transparencyFlag = false; + if (TryReadUnsignedMetadata(frameQuery.Get(), L"/grctlext/TransparentColorFlag", value) || + TryReadUnsignedMetadata(frameQuery.Get(), L"/grctlext/TransparencyFlag", value)) + { + transparencySpecified = true; + transparencyFlag = (value != 0); + } + UINT transparentIndex = 0; + bool hasTransparentIndex = false; + if (TryReadUnsignedMetadata(frameQuery.Get(), L"/grctlext/TransparentColorIndex", value)) + { + transparentIndex = value; + hasTransparentIndex = true; + } + if (hasTransparentIndex && (!transparencySpecified || transparencyFlag)) + { + data.gifHasTransparentColor = true; + data.gifTransparentIndex = static_cast(transparentIndex & 0xFFu); + if (handle.gifHasBackgroundColor && handle.gifBackgroundAlpha != 0 && + handle.gifHasBackgroundIndex && + data.gifTransparentIndex == handle.gifBackgroundIndex) + { + handle.gifBackgroundAlpha = 0; + } + } } LONG rectLeft = ClampUnsignedToLong(leftSpecified ? left64 : 0ull); LONG rectTop = ClampUnsignedToLong(topSpecified ? top64 : 0ull); @@ -2373,8 +4313,21 @@ HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& data.rect.top = rectTop; const LONG rectWidthLong = ClampUnsignedToLong(rectWidth64); const LONG rectHeightLong = ClampUnsignedToLong(rectHeight64); + if (rectWidthLong > 0) + { + data.rawWidth = static_cast(std::min(rectWidthLong, static_cast(width))); + } + if (rectHeightLong > 0) + { + data.rawHeight = static_cast(std::min(rectHeightLong, static_cast(height))); + } data.rect.right = clampEdge(rectLeft, rectWidthLong, handle.canvasWidth); data.rect.bottom = clampEdge(rectTop, rectHeightLong, handle.canvasHeight); + if (isGifContainer) + { + data.gifFrameRect = data.rect; + data.hasGifFrameRect = true; + } if (!hasExif && FrameContainsExif(data.frame.Get())) { hasExif = true; @@ -2389,13 +4342,10 @@ HRESULT CollectFrames(Backend& backend, IWICBitmapDecoder* decoder, ImageHandle& { handle.canvasHeight = ClampUnsignedToLong(static_cast(handle.frames[0].height)); } - GUID container = {}; - decoder->GetContainerFormat(&container); - handle.baseInfo.Format = MapFormatToPvFormat(container); handle.baseInfo.NumOfImages = frameCount; handle.baseInfo.FileSize = QueryFileSize(handle.fileName); - if (handle.baseInfo.Format == PVF_GIF) + if (isGifContainer) { handle.hasFormatSpecificInfo = true; const LONG screenWidth = std::max(0, handle.canvasWidth); @@ -2527,9 +4477,53 @@ PVCODE DrawFrame(ImageHandle& handle, FrameData& frame, HDC dc, int x, int y, LP } } + if (frame.hasTransparency) + { + RECT fillRect = clipRect; + HBRUSH brush = CreateSolidBrush(handle.background); + if (brush) + { + FillRect(dc, &fillRect, brush); + DeleteObject(brush); + } + } + int previousMode = SetStretchBltMode(dc, handle.stretchMode ? static_cast(handle.stretchMode) : COLORONCOLOR); + std::vector bmiBuffer; BITMAPINFO bmi{}; - bmi.bmiHeader = frame.bmi; + BITMAPINFO* bmiPtr = nullptr; + const bool useIndexed = frame.useIndexedPixels && !frame.indexedPixels.empty(); + if (useIndexed) + { + const size_t paletteCount = std::min(frame.palette.size(), std::min(frame.paletteColorCount, 256)); + const size_t bmiSize = sizeof(BITMAPINFOHEADER) + paletteCount * sizeof(RGBQUAD); + try + { + bmiBuffer.resize(bmiSize); + } + catch (const std::bad_alloc&) + { + return PVC_EXCEPTION; + } + bmiPtr = reinterpret_cast(bmiBuffer.data()); + bmiPtr->bmiHeader = frame.displayBmi; + bmiPtr->bmiHeader.biClrUsed = static_cast(paletteCount); + bmiPtr->bmiHeader.biClrImportant = static_cast(paletteCount); + for (size_t i = 0; i < paletteCount; ++i) + { + RGBQUAD entry = frame.palette[i]; + entry.rgbReserved = 0; + bmiPtr->bmiColors[i] = entry; + } + } + else + { + bmi.bmiHeader = frame.bmi; + bmiPtr = &bmi; + } + + const bool realizePalette = useIndexed && frame.paletteHandle; + PaletteSelector paletteScope(dc, frame.paletteHandle, realizePalette); const int destX = stretchWidthSigned >= 0 ? imageRect.left : imageRect.right - 1; const int destY = stretchHeightSigned >= 0 ? imageRect.top : imageRect.bottom - 1; @@ -2538,8 +4532,86 @@ PVCODE DrawFrame(ImageHandle& handle, FrameData& frame, HDC dc, int x, int y, LP 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); + const UINT targetWidth = static_cast(stretchWidthAbs); + const UINT targetHeight = static_cast(stretchHeightAbs); + const bool requiresScaling = (!useIndexed && (frame.width != targetWidth || frame.height != targetHeight)); + + BITMAPINFO scaledBmi{}; + const BYTE* stretchSource = useIndexed ? frame.indexedPixels.data() : frame.pixels.data(); + UINT sourceWidthForStretch = frame.width; + UINT sourceHeightForStretch = frame.height; + + HBITMAP blendBitmap = frame.hbitmap; + UINT sourceWidthForBlend = frame.width; + UINT sourceHeightForBlend = frame.height; + + if (!useIndexed && requiresScaling) + { + if (SUCCEEDED(EnsureScaledBitmap(handle, frame, targetWidth, targetHeight))) + { + if (frame.scaledBitmap) + { + blendBitmap = frame.scaledBitmap; + sourceWidthForBlend = frame.scaledWidth; + sourceHeightForBlend = frame.scaledHeight; + } + if (!frame.scaledPixels.empty()) + { + stretchSource = frame.scaledPixels.data(); + scaledBmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + scaledBmi.bmiHeader.biWidth = static_cast(frame.scaledWidth); + scaledBmi.bmiHeader.biHeight = -static_cast(frame.scaledHeight); + scaledBmi.bmiHeader.biPlanes = 1; + scaledBmi.bmiHeader.biBitCount = 32; + scaledBmi.bmiHeader.biCompression = BI_RGB; + scaledBmi.bmiHeader.biSizeImage = frame.scaledPixels.size() > std::numeric_limits::max() + ? 0 + : static_cast(frame.scaledPixels.size()); + scaledBmi.bmiHeader.biXPelsPerMeter = frame.bmi.biXPelsPerMeter; + scaledBmi.bmiHeader.biYPelsPerMeter = frame.bmi.biYPelsPerMeter; + bmiPtr = &scaledBmi; + sourceWidthForStretch = frame.scaledWidth; + sourceHeightForStretch = frame.scaledHeight; + } + } + } + + int result = GDI_ERROR; + const bool canAlphaBlend = frame.hasTransparency && blendBitmap && !useIndexed && destWidth >= 0 && destHeight >= 0; + if (canAlphaBlend) + { + HDC sourceDc = CreateCompatibleDC(dc); + if (!sourceDc) + { + result = GDI_ERROR; + } + else + { + HGDIOBJ oldBitmap = SelectObject(sourceDc, blendBitmap); + BLENDFUNCTION blend{}; + blend.BlendOp = AC_SRC_OVER; + blend.BlendFlags = 0; + blend.SourceConstantAlpha = 255; + blend.AlphaFormat = AC_SRC_ALPHA; + const BOOL blendResult = GdiAlphaBlend(dc, destX, destY, destWidth, destHeight, sourceDc, 0, 0, + static_cast(sourceWidthForBlend), + static_cast(sourceHeightForBlend), blend); + SelectObject(sourceDc, oldBitmap); + DeleteDC(sourceDc); + result = blendResult ? 0 : GDI_ERROR; + } + } + + if (!canAlphaBlend || result == GDI_ERROR) + { + if (!stretchSource) + { + return PVC_INVALID_HANDLE; + } + + result = StretchDIBits(dc, destX, destY, destWidth, destHeight, 0, 0, sourceWidthForStretch, + sourceHeightForStretch, stretchSource, bmiPtr, DIB_RGB_COLORS, SRCCOPY); + } if (previousMode > 0) { @@ -2584,29 +4656,23 @@ PVCODE CreateSequenceNodes(ImageHandle& handle, LPPVImageSequence* seq) node->DisposalMethod = frame.disposal; node->ImgHandle = nullptr; node->TransparentHandle = nullptr; - if (frame.hbitmap) + HBITMAP subFrame = nullptr; + HBITMAP subMask = nullptr; + hr = CreateSequenceBitmaps(frame, frame.rect, subFrame, subMask); + if (FAILED(hr)) { - HBITMAP frameCopy = static_cast(CopyImage(frame.hbitmap, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION)); - if (!frameCopy) + if (subFrame) { - return PVC_GDI_ERROR; + DeleteObject(subFrame); } - node->ImgHandle = frameCopy; - } - if (frame.transparencyMask) - { - HBITMAP maskCopy = static_cast(CopyImage(frame.transparencyMask, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION)); - if (!maskCopy) + if (subMask) { - if (node->ImgHandle) - { - DeleteObject(node->ImgHandle); - node->ImgHandle = nullptr; - } - return PVC_GDI_ERROR; + DeleteObject(subMask); } - node->TransparentHandle = maskCopy; + return HResultToPvCode(hr); } + node->ImgHandle = subFrame; + node->TransparentHandle = subMask; *tail = node.release(); tail = &((*tail)->pNext); } @@ -3296,17 +5362,30 @@ PVCODE SaveFrame(ImageHandle& handle, int imageIndex, const wchar_t* path, const { conversionPalette = palette.Get(); } - hr = converter->Initialize(baseSource.Get(), pixelFormat, WICBitmapDitherTypeNone, conversionPalette, 0.0, - paletteType); - if (FAILED(hr)) + if (IsEqualGUID(pixelFormat, GUID_WICPixelFormat32bppCMYK)) { - return recordFailure(hr, "FormatConverter::Initialize (non-indexed)"); + frameSource.Reset(); + hr = ConvertBgraSourceToCmyk(handle.backend->Factory(), baseSource.Get(), + frameSource.ReleaseAndGetAddressOf()); + if (FAILED(hr)) + { + return recordFailure(hr, "ConvertBgraSourceToCmyk"); + } } - - hr = converter.As(&frameSource); - if (FAILED(hr)) + else { - return recordFailure(hr, "FormatConverter::As (non-indexed)"); + hr = converter->Initialize(baseSource.Get(), pixelFormat, WICBitmapDitherTypeNone, conversionPalette, 0.0, + paletteType); + if (FAILED(hr)) + { + return recordFailure(hr, "FormatConverter::Initialize (non-indexed)"); + } + + hr = converter.As(&frameSource); + if (FAILED(hr)) + { + return recordFailure(hr, "FormatConverter::As (non-indexed)"); + } } } } @@ -3655,7 +5734,7 @@ PVCODE WINAPI Backend::sPVOpenImageEx(LPPVHandle* Img, LPPVOpenImageExInfo pOpen } FrameData frame; - HRESULT hr = PopulateFrameFromBitmapHandle(frame, bitmap); + HRESULT hr = PopulateFrameFromBitmapHandle(backend, frame, bitmap); if (FAILED(hr)) { return HResultToPvCode(hr); @@ -3728,11 +5807,21 @@ PVCODE WINAPI Backend::sPVCloseImage(LPPVHandle Img) DeleteObject(frame.hbitmap); frame.hbitmap = nullptr; } + if (frame.scaledBitmap) + { + DeleteObject(frame.scaledBitmap); + frame.scaledBitmap = nullptr; + } if (frame.transparencyMask) { DeleteObject(frame.transparencyMask); frame.transparencyMask = nullptr; } + if (frame.paletteHandle) + { + DeleteObject(frame.paletteHandle); + frame.paletteHandle = nullptr; + } } delete handle; return PVC_OK; @@ -3905,10 +5994,14 @@ PVCODE WINAPI Backend::sPVGetHandles2(LPPVHandle Img, LPPVImageHandles* pHandles auto& frame = handle->frames[0]; PVImageHandles& handles = handle->handles; ZeroMemory(&handles, sizeof(PVImageHandles)); + const bool hasIndexedPixels = frame.useIndexedPixels && !frame.indexedPixels.empty(); + const bool providePaletteHandle = hasIndexedPixels || (frame.realizePalette && frame.paletteHandle); + handles.TransparentHandle = frame.hasTransparency ? frame.transparencyMask : nullptr; handles.TransparentBackgroundHandle = frame.hbitmap; handles.StretchedHandle = frame.hbitmap; handles.StretchedTransparentHandle = frame.hbitmap; + handles.HPal = providePaletteHandle ? frame.paletteHandle : nullptr; handles.Palette = frame.palette.empty() ? nullptr : frame.palette.data(); handles.pLines = frame.linePointers.empty() ? nullptr : frame.linePointers.data(); *pHandles = &handles; @@ -3996,7 +6089,12 @@ PVCODE WINAPI Backend::sPVChangeImage(LPPVHandle Img, DWORD flags) frame.height = newHeight; frame.stride = frame.width * 4; frame.pixels.swap(rotated); - HRESULT finalizeHr = FinalizeDecodedFrame(frame); + frame.disposalBuffer.clear(); + frame.compositedPixels.clear(); + handle->gifComposeCanvas.clear(); + handle->gifSavedCanvas.clear(); + handle->gifCanvasInitialized = false; + HRESULT finalizeHr = FinalizeDecodedFrame(handle->backend, frame); if (FAILED(finalizeHr)) { return HResultToPvCode(finalizeHr); @@ -4073,7 +6171,12 @@ PVCODE WINAPI Backend::sPVCropImage(LPPVHandle Img, int left, int top, int width frame.height = static_cast(height); frame.stride = newStride; frame.pixels.swap(cropped); - HRESULT finalizeHr = FinalizeDecodedFrame(frame); + frame.disposalBuffer.clear(); + frame.compositedPixels.clear(); + handle->gifComposeCanvas.clear(); + handle->gifSavedCanvas.clear(); + handle->gifCanvasInitialized = false; + HRESULT finalizeHr = FinalizeDecodedFrame(handle->backend, frame); if (FAILED(finalizeHr)) { return HResultToPvCode(finalizeHr); diff --git a/src/plugins/pictview/wic/WicBackend.h b/src/plugins/pictview/wic/WicBackend.h index 8a81ef8a7..14e3022f9 100644 --- a/src/plugins/pictview/wic/WicBackend.h +++ b/src/plugins/pictview/wic/WicBackend.h @@ -32,17 +32,47 @@ struct FrameData UINT width = 0; UINT height = 0; UINT stride = 0; + UINT rawWidth = 0; + UINT rawHeight = 0; + UINT rawStride = 0; std::vector pixels; + std::vector compositedPixels; + std::vector indexedPixels; std::vector linePointers; std::vector palette; + std::vector disposalBuffer; + std::vector scaledPixels; BITMAPINFOHEADER bmi{}; + BITMAPINFOHEADER indexedBmi{}; + BITMAPINFOHEADER displayBmi{}; HBITMAP hbitmap = nullptr; HBITMAP transparencyMask = nullptr; + HBITMAP scaledBitmap = nullptr; + HPALETTE paletteHandle = nullptr; DWORD delayMs = 0; RECT rect{}; + RECT gifFrameRect{}; DWORD disposal = PVDM_UNDEFINED; + GUID sourcePixelFormat{}; + DWORD reportedColors = PV_COLOR_TC32; + DWORD reportedBitDepth = 32; + DWORD colorModel = PVCM_RGB; + UINT paletteColorCount = 0; + UINT bitsPerPixel = 0; + UINT indexedStride = 0; + UINT displayStride = 0; + UINT scaledStride = 0; + UINT scaledWidth = 0; + UINT scaledHeight = 0; + bool hasGifFrameRect = false; bool decoded = false; bool hasTransparency = false; + bool useIndexedPixels = false; + bool allowIndexedDisplay = true; + bool realizePalette = false; + bool gifHasTransparentColor = false; + BYTE gifTransparentIndex = 0; + bool pixelsArePremultiplied = true; }; struct ImageHandle @@ -61,6 +91,13 @@ struct ImageHandle bool hasFormatSpecificInfo = false; LONG canvasWidth = 0; LONG canvasHeight = 0; + bool gifHasBackgroundColor = false; + BYTE gifBackgroundAlpha = 0; + bool gifHasBackgroundIndex = false; + BYTE gifBackgroundIndex = 0; + std::vector gifComposeCanvas; + std::vector gifSavedCanvas; + bool gifCanvasInitialized = false; }; /**