mirror of
https://github.com/reactos/reactos.git
synced 2025-02-28 19:32:59 +00:00
[SHIMGVW] Choose a better icon image and also support .cur files (#7706)
- Choose an icon image based on size and color and place it first so GDI+ will pick it - Change .cur files to .ico so GDI+ can open it CORE-19945
This commit is contained in:
parent
166d83b206
commit
329a414584
4 changed files with 350 additions and 58 deletions
|
@ -3,6 +3,7 @@ spec2def(shimgvw.dll shimgvw.spec)
|
||||||
|
|
||||||
list(APPEND SOURCE
|
list(APPEND SOURCE
|
||||||
anime.c
|
anime.c
|
||||||
|
loader.cpp
|
||||||
shimgvw.c
|
shimgvw.c
|
||||||
comsup.c
|
comsup.c
|
||||||
shimgvw.rc
|
shimgvw.rc
|
||||||
|
|
283
dll/win32/shimgvw/loader.cpp
Normal file
283
dll/win32/shimgvw/loader.cpp
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
/*
|
||||||
|
* PROJECT: ReactOS Picture and Fax Viewer
|
||||||
|
* LICENSE: GPL-2.0 (https://spdx.org/licenses/GPL-2.0)
|
||||||
|
* PURPOSE: Image file browsing and manipulation
|
||||||
|
* COPYRIGHT: Copyright 2025 Whindmar Saksit <whindsaks@proton.me>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <objbase.h>
|
||||||
|
#include <gdiplus.h>
|
||||||
|
using namespace Gdiplus;
|
||||||
|
#include "shimgvw.h"
|
||||||
|
|
||||||
|
#define HResultFromWin32 SHIMGVW_HResultFromWin32
|
||||||
|
|
||||||
|
static HRESULT Read(HANDLE hFile, void* Buffer, DWORD Size)
|
||||||
|
{
|
||||||
|
DWORD Transferred;
|
||||||
|
if (!ReadFile(hFile, Buffer, Size, &Transferred, NULL))
|
||||||
|
return HResultFromWin32(GetLastError());
|
||||||
|
return Size == Transferred ? S_OK : HResultFromWin32(ERROR_HANDLE_EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IMAGEINFO
|
||||||
|
{
|
||||||
|
UINT w, h;
|
||||||
|
BYTE bpp;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BitmapInfoHeader : public BITMAPINFOHEADER
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BitmapInfoHeader() {}
|
||||||
|
BitmapInfoHeader(const void* pbmiHeader) { Initialize(pbmiHeader); }
|
||||||
|
|
||||||
|
void Initialize(const void* pbmiHeader)
|
||||||
|
{
|
||||||
|
BITMAPINFOHEADER& bih = *(BITMAPINFOHEADER*)pbmiHeader;
|
||||||
|
if (bih.biSize >= sizeof(BITMAPINFOHEADER))
|
||||||
|
{
|
||||||
|
CopyMemory(this, &bih, min(bih.biSize, sizeof(*this)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ZeroMemory(this, sizeof(*this));
|
||||||
|
BITMAPCOREHEADER& bch = *(BITMAPCOREHEADER*)pbmiHeader;
|
||||||
|
if (bih.biSize >= sizeof(BITMAPCOREHEADER))
|
||||||
|
{
|
||||||
|
biSize = bch.bcSize;
|
||||||
|
biWidth = bch.bcWidth;
|
||||||
|
biHeight = bch.bcHeight;
|
||||||
|
biPlanes = bch.bcPlanes;
|
||||||
|
biBitCount = bch.bcBitCount;
|
||||||
|
biCompression = BI_RGB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#include <pshpack1.h>
|
||||||
|
union PNGSIGNATURE { UINT64 number; BYTE bytes[8]; };
|
||||||
|
struct PNGCHUNKHEADER { UINT length, type; };
|
||||||
|
struct PNGCHUNKFOOTER { UINT crc; };
|
||||||
|
struct PNGIHDR { UINT w, h; BYTE depth, type, compression, filter, interlace; };
|
||||||
|
struct PNGSIGANDIHDR
|
||||||
|
{
|
||||||
|
PNGSIGNATURE sig;
|
||||||
|
PNGCHUNKHEADER chunkheader;
|
||||||
|
PNGIHDR ihdr;
|
||||||
|
PNGCHUNKFOOTER chunkfooter;
|
||||||
|
};
|
||||||
|
struct PNGFOOTER { PNGCHUNKHEADER chunkheader; PNGCHUNKFOOTER footer; };
|
||||||
|
#include <poppack.h>
|
||||||
|
|
||||||
|
static inline bool IsPngSignature(const void* buffer)
|
||||||
|
{
|
||||||
|
const BYTE* p = (BYTE*)buffer;
|
||||||
|
return p[0] == 0x89 && p[1] == 'P' && p[2] == 'N' && p[3] == 'G' &&
|
||||||
|
p[4] == 0x0D && p[5] == 0x0A && p[6] == 0x1A && p[7] == 0x0A;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool IsPngSignature(const void* buffer, SIZE_T size)
|
||||||
|
{
|
||||||
|
return size >= sizeof(PNGSIGNATURE) && IsPngSignature(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BYTE GetPngBppFromIHDRData(const void* buffer)
|
||||||
|
{
|
||||||
|
static const BYTE channels[] = { 1, 0, 3, 1, 2, 0, 4 };
|
||||||
|
const BYTE* p = (BYTE*)buffer, depth = p[8], type = p[8 + 1];
|
||||||
|
return (depth <= 16 && type <= 6) ? channels[type] * depth : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GetInfoFromPng(const void* file, SIZE_T size, IMAGEINFO& info)
|
||||||
|
{
|
||||||
|
C_ASSERT(sizeof(PNGSIGNATURE) == 8);
|
||||||
|
C_ASSERT(sizeof(PNGSIGANDIHDR) == 8 + (4 + 4 + (4 + 4 + 5) + 4));
|
||||||
|
|
||||||
|
if (size > sizeof(PNGSIGANDIHDR) + sizeof(PNGFOOTER) && IsPngSignature(file))
|
||||||
|
{
|
||||||
|
const UINT PNGIHDRSIG = 0x52444849; // Note: Big endian
|
||||||
|
const UINT* chunkhdr = (UINT*)((char*)file + sizeof(PNGSIGNATURE));
|
||||||
|
if (BigToHost32(chunkhdr[0]) >= sizeof(PNGIHDR) && chunkhdr[1] == PNGIHDRSIG)
|
||||||
|
{
|
||||||
|
info.w = BigToHost32(chunkhdr[2]);
|
||||||
|
info.h = BigToHost32(chunkhdr[3]);
|
||||||
|
info.bpp = GetPngBppFromIHDRData(&chunkhdr[2]);
|
||||||
|
return info.bpp != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GetInfoFromBmp(const void* pBitmapInfo, IMAGEINFO& info)
|
||||||
|
{
|
||||||
|
BitmapInfoHeader bih(pBitmapInfo);
|
||||||
|
info.w = bih.biWidth;
|
||||||
|
info.h = abs((int)bih.biHeight);
|
||||||
|
UINT bpp = bih.biBitCount * bih.biPlanes;
|
||||||
|
info.bpp = LOBYTE(bpp);
|
||||||
|
return info.w && bpp == info.bpp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GetInfoFromIcoBmp(const void* pBitmapInfo, IMAGEINFO& stat)
|
||||||
|
{
|
||||||
|
bool ret = GetInfoFromBmp(pBitmapInfo, stat);
|
||||||
|
stat.h /= 2; // Don't include mask
|
||||||
|
return ret && stat.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXTERN_C PCWSTR GetExtraExtensionsGdipList(VOID)
|
||||||
|
{
|
||||||
|
return L"*.CUR"; // "*.FOO;*.BAR" etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OverrideFileContent(HGLOBAL& hMem, DWORD& Size)
|
||||||
|
{
|
||||||
|
PBYTE buffer = (PBYTE)GlobalLock(hMem);
|
||||||
|
if (!buffer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: We could try to load an ICO/PNG/BMP resource from a PE file here into buffer
|
||||||
|
|
||||||
|
// ICO/CUR
|
||||||
|
struct ICOHDR { WORD Sig, Type, Count; };
|
||||||
|
ICOHDR* pIcoHdr = (ICOHDR*)buffer;
|
||||||
|
if (Size > sizeof(ICOHDR) && !pIcoHdr->Sig && pIcoHdr->Type > 0 && pIcoHdr->Type < 3 && pIcoHdr->Count)
|
||||||
|
{
|
||||||
|
const UINT minbmp = sizeof(BITMAPCOREHEADER) + 1, minpng = sizeof(PNGSIGANDIHDR);
|
||||||
|
const UINT minfile = min(minbmp, minpng), count = pIcoHdr->Count;
|
||||||
|
struct ICOENTRY { BYTE w, h, pal, null; WORD planes, bpp; UINT size, offset; };
|
||||||
|
ICOENTRY* entries = (ICOENTRY*)&pIcoHdr[1];
|
||||||
|
if (Size - sizeof(ICOHDR) > (sizeof(ICOENTRY) + minfile) * count)
|
||||||
|
{
|
||||||
|
UINT64 best = 0;
|
||||||
|
int bestindex = -1;
|
||||||
|
// Inspect all the images and find the "best" image
|
||||||
|
for (UINT i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
BOOL valid = FALSE;
|
||||||
|
IMAGEINFO info;
|
||||||
|
const BYTE* data = buffer + entries[i].offset;
|
||||||
|
if (IsPngSignature(data, entries[i].size))
|
||||||
|
valid = GetInfoFromPng(data, entries[i].size, info);
|
||||||
|
else
|
||||||
|
valid = GetInfoFromIcoBmp(data, info);
|
||||||
|
|
||||||
|
if (valid)
|
||||||
|
{
|
||||||
|
// Note: This treats bpp as more important compared to LookupIconIdFromDirectoryEx
|
||||||
|
UINT64 score = UINT64(info.w) * info.h * info.bpp;
|
||||||
|
if (score > best)
|
||||||
|
{
|
||||||
|
best = score;
|
||||||
|
bestindex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bestindex >= 0)
|
||||||
|
{
|
||||||
|
if (pIcoHdr->Type == 2)
|
||||||
|
{
|
||||||
|
// GDI+ does not support .cur files, convert to .ico
|
||||||
|
pIcoHdr->Type = 1;
|
||||||
|
#if 0 // Because we are already overriding the order, we don't need to correct the ICOENTRY lookup info
|
||||||
|
for (UINT i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
BitmapInfoHeader bih;
|
||||||
|
const BYTE* data = buffer + entries[i].offset;
|
||||||
|
if (IsPngSignature(data, entries[i].size))
|
||||||
|
{
|
||||||
|
IMAGEINFO info;
|
||||||
|
if (!GetInfoFromPng(data, entries[i].size, info))
|
||||||
|
continue;
|
||||||
|
bih.biPlanes = 1;
|
||||||
|
bih.biBitCount = info.bpp;
|
||||||
|
entries[i].pal = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bih.Initialize(data);
|
||||||
|
entries[i].pal = bih.biPlanes * bih.biBitCount <= 8 ? bih.biClrUsed : 0;
|
||||||
|
}
|
||||||
|
entries[i].planes = (WORD)bih.biPlanes;
|
||||||
|
entries[i].bpp = (WORD)bih.biBitCount;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
// Convert to a .ico with a single image
|
||||||
|
pIcoHdr->Count = 1;
|
||||||
|
const BYTE* data = buffer + entries[bestindex].offset;
|
||||||
|
entries[0] = entries[bestindex];
|
||||||
|
entries[0].offset = (UINT)UINT_PTR((PBYTE)&entries[1] - buffer);
|
||||||
|
MoveMemory(buffer + entries[0].offset, data, entries[0].size);
|
||||||
|
Size = entries[0].offset + entries[0].size;
|
||||||
|
#else
|
||||||
|
// Place the best image first, GDI+ will return the first image
|
||||||
|
ICOENTRY temp = entries[0];
|
||||||
|
entries[0] = entries[bestindex];
|
||||||
|
entries[bestindex] = temp;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalUnlock(hMem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT LoadImageFromStream(IStream* pStream, GpImage** ppImage)
|
||||||
|
{
|
||||||
|
Status status = DllExports::GdipLoadImageFromStream(pStream, ppImage);
|
||||||
|
return HResultFromGdiplus(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT LoadImageFromFileHandle(HANDLE hFile, GpImage** ppImage)
|
||||||
|
{
|
||||||
|
DWORD size = GetFileSize(hFile, NULL);
|
||||||
|
if (!size || size == INVALID_FILE_SIZE)
|
||||||
|
return HResultFromWin32(ERROR_NOT_SUPPORTED);
|
||||||
|
|
||||||
|
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, size);
|
||||||
|
if (!hMem)
|
||||||
|
return HResultFromWin32(ERROR_OUTOFMEMORY);
|
||||||
|
HRESULT hr = E_FAIL;
|
||||||
|
void* buffer = GlobalLock(hMem);
|
||||||
|
if (buffer)
|
||||||
|
{
|
||||||
|
hr = Read(hFile, buffer, size);
|
||||||
|
GlobalUnlock(hMem);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
OverrideFileContent(hMem, size);
|
||||||
|
IStream* pStream;
|
||||||
|
if (SUCCEEDED(hr = CreateStreamOnHGlobal(hMem, TRUE, &pStream)))
|
||||||
|
{
|
||||||
|
// CreateStreamOnHGlobal does not know the real size, we do
|
||||||
|
pStream->SetSize(MakeULargeInteger(size));
|
||||||
|
hr = LoadImageFromStream(pStream, ppImage);
|
||||||
|
pStream->Release(); // Calls GlobalFree
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GlobalFree(hMem);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXTERN_C HRESULT LoadImageFromPath(LPCWSTR Path, GpImage** ppImage)
|
||||||
|
{
|
||||||
|
// NOTE: GdipLoadImageFromFile locks the file.
|
||||||
|
// Avoid file locking by using GdipLoadImageFromStream and memory stream.
|
||||||
|
|
||||||
|
HANDLE hFile = CreateFileW(Path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE,
|
||||||
|
NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
||||||
|
if (hFile != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
HRESULT hr = LoadImageFromFileHandle(hFile, ppImage);
|
||||||
|
CloseHandle(hFile);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
return HResultFromWin32(GetLastError());
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
* PURPOSE: Image file browsing and manipulation
|
* PURPOSE: Image file browsing and manipulation
|
||||||
* COPYRIGHT: Copyright Dmitry Chapyshev (dmitry@reactos.org)
|
* COPYRIGHT: Copyright Dmitry Chapyshev (dmitry@reactos.org)
|
||||||
* Copyright 2018-2023 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
|
* Copyright 2018-2023 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
|
||||||
|
* Copyright 2025 Whindmar Saksit <whindsaks@proton.me>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "shimgvw.h"
|
#include "shimgvw.h"
|
||||||
|
@ -13,6 +14,9 @@
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
|
||||||
|
EXTERN_C PCWSTR GetExtraExtensionsGdipList(VOID);
|
||||||
|
EXTERN_C HRESULT LoadImageFromPath(LPCWSTR Path, GpImage** ppImage);
|
||||||
|
|
||||||
/* Toolbar image size */
|
/* Toolbar image size */
|
||||||
#define TB_IMAGE_WIDTH 16
|
#define TB_IMAGE_WIDTH 16
|
||||||
#define TB_IMAGE_HEIGHT 16
|
#define TB_IMAGE_HEIGHT 16
|
||||||
|
@ -114,7 +118,6 @@ typedef struct tagPREVIEW_DATA
|
||||||
UINT m_nTimerInterval;
|
UINT m_nTimerInterval;
|
||||||
BOOL m_bHideCursor;
|
BOOL m_bHideCursor;
|
||||||
POINT m_ptOrigin;
|
POINT m_ptOrigin;
|
||||||
IStream *m_pMemStream;
|
|
||||||
WCHAR m_szFile[MAX_PATH];
|
WCHAR m_szFile[MAX_PATH];
|
||||||
} PREVIEW_DATA, *PPREVIEW_DATA;
|
} PREVIEW_DATA, *PPREVIEW_DATA;
|
||||||
|
|
||||||
|
@ -371,67 +374,20 @@ Preview_pFreeImage(PPREVIEW_DATA pData)
|
||||||
g_pImage = NULL;
|
g_pImage = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pData->m_pMemStream)
|
|
||||||
{
|
|
||||||
pData->m_pMemStream->lpVtbl->Release(pData->m_pMemStream);
|
|
||||||
pData->m_pMemStream = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
pData->m_szFile[0] = UNICODE_NULL;
|
pData->m_szFile[0] = UNICODE_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
IStream* MemStreamFromFile(LPCWSTR pszFileName)
|
|
||||||
{
|
|
||||||
HANDLE hFile;
|
|
||||||
DWORD dwFileSize, dwRead;
|
|
||||||
LPBYTE pbMemFile = NULL;
|
|
||||||
IStream *pStream;
|
|
||||||
|
|
||||||
hFile = CreateFileW(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
||||||
OPEN_EXISTING, 0, NULL);
|
|
||||||
if (hFile == INVALID_HANDLE_VALUE)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
dwFileSize = GetFileSize(hFile, NULL);
|
|
||||||
pbMemFile = QuickAlloc(dwFileSize, FALSE);
|
|
||||||
if (!dwFileSize || (dwFileSize == INVALID_FILE_SIZE) || !pbMemFile)
|
|
||||||
{
|
|
||||||
CloseHandle(hFile);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ReadFile(hFile, pbMemFile, dwFileSize, &dwRead, NULL) || (dwRead != dwFileSize))
|
|
||||||
{
|
|
||||||
QuickFree(pbMemFile);
|
|
||||||
CloseHandle(hFile);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(hFile);
|
|
||||||
pStream = SHCreateMemStream(pbMemFile, dwFileSize);
|
|
||||||
QuickFree(pbMemFile);
|
|
||||||
return pStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
static VOID
|
static VOID
|
||||||
Preview_pLoadImage(PPREVIEW_DATA pData, LPCWSTR szOpenFileName)
|
Preview_pLoadImage(PPREVIEW_DATA pData, LPCWSTR szOpenFileName)
|
||||||
{
|
{
|
||||||
|
HRESULT hr;
|
||||||
Preview_pFreeImage(pData);
|
Preview_pFreeImage(pData);
|
||||||
|
InvalidateRect(pData->m_hwnd, NULL, FALSE); /* Schedule redraw in case we change to "No preview" */
|
||||||
|
|
||||||
pData->m_pMemStream = MemStreamFromFile(szOpenFileName);
|
hr = LoadImageFromPath(szOpenFileName, &g_pImage);
|
||||||
if (!pData->m_pMemStream)
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
DPRINT1("MemStreamFromFile() failed\n");
|
DPRINT1("GdipLoadImageFromStream() failed, %d\n", hr);
|
||||||
Preview_UpdateTitle(pData, NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NOTE: GdipLoadImageFromFile locks the file.
|
|
||||||
Avoid file locking by using GdipLoadImageFromStream and memory stream. */
|
|
||||||
GdipLoadImageFromStream(pData->m_pMemStream, &g_pImage);
|
|
||||||
if (!g_pImage)
|
|
||||||
{
|
|
||||||
DPRINT1("GdipLoadImageFromStream() failed\n");
|
|
||||||
Preview_pFreeImage(pData);
|
Preview_pFreeImage(pData);
|
||||||
Preview_UpdateTitle(pData, NULL);
|
Preview_UpdateTitle(pData, NULL);
|
||||||
return;
|
return;
|
||||||
|
@ -439,8 +395,8 @@ Preview_pLoadImage(PPREVIEW_DATA pData, LPCWSTR szOpenFileName)
|
||||||
|
|
||||||
Anime_LoadInfo(&pData->m_Anime);
|
Anime_LoadInfo(&pData->m_Anime);
|
||||||
|
|
||||||
SHAddToRecentDocs(SHARD_PATHW, szOpenFileName);
|
|
||||||
GetFullPathNameW(szOpenFileName, _countof(pData->m_szFile), pData->m_szFile, NULL);
|
GetFullPathNameW(szOpenFileName, _countof(pData->m_szFile), pData->m_szFile, NULL);
|
||||||
|
SHAddToRecentDocs(SHARD_PATHW, pData->m_szFile);
|
||||||
|
|
||||||
/* Reset zoom and redraw display */
|
/* Reset zoom and redraw display */
|
||||||
Preview_ResetZoom(pData);
|
Preview_ResetZoom(pData);
|
||||||
|
@ -625,7 +581,7 @@ static SHIMGVW_FILENODE*
|
||||||
pBuildFileList(LPCWSTR szFirstFile)
|
pBuildFileList(LPCWSTR szFirstFile)
|
||||||
{
|
{
|
||||||
HANDLE hFindHandle;
|
HANDLE hFindHandle;
|
||||||
WCHAR *extension;
|
WCHAR *extension, *buffer;
|
||||||
WCHAR szSearchPath[MAX_PATH];
|
WCHAR szSearchPath[MAX_PATH];
|
||||||
WCHAR szSearchMask[MAX_PATH];
|
WCHAR szSearchMask[MAX_PATH];
|
||||||
WCHAR szFileTypes[MAX_PATH];
|
WCHAR szFileTypes[MAX_PATH];
|
||||||
|
@ -634,15 +590,19 @@ pBuildFileList(LPCWSTR szFirstFile)
|
||||||
SHIMGVW_FILENODE *root = NULL;
|
SHIMGVW_FILENODE *root = NULL;
|
||||||
SHIMGVW_FILENODE *conductor = NULL;
|
SHIMGVW_FILENODE *conductor = NULL;
|
||||||
ImageCodecInfo *codecInfo;
|
ImageCodecInfo *codecInfo;
|
||||||
UINT num;
|
UINT num = 0, size = 0, ExtraSize = 0;
|
||||||
UINT size;
|
|
||||||
UINT j;
|
UINT j;
|
||||||
|
|
||||||
|
const PCWSTR ExtraExtensions = GetExtraExtensionsGdipList();
|
||||||
|
const UINT ExtraCount = ExtraExtensions[0] ? 1 : 0;
|
||||||
|
if (ExtraCount)
|
||||||
|
ExtraSize += sizeof(*codecInfo) + (wcslen(ExtraExtensions) + 1) * sizeof(WCHAR);
|
||||||
|
|
||||||
StringCbCopyW(szSearchPath, sizeof(szSearchPath), szFirstFile);
|
StringCbCopyW(szSearchPath, sizeof(szSearchPath), szFirstFile);
|
||||||
PathRemoveFileSpecW(szSearchPath);
|
PathRemoveFileSpecW(szSearchPath);
|
||||||
|
|
||||||
GdipGetImageDecodersSize(&num, &size);
|
GdipGetImageDecodersSize(&num, &size);
|
||||||
codecInfo = QuickAlloc(size, FALSE);
|
codecInfo = QuickAlloc(size + ExtraSize, FALSE);
|
||||||
if (!codecInfo)
|
if (!codecInfo)
|
||||||
{
|
{
|
||||||
DPRINT1("QuickAlloc() failed in pLoadFileList()\n");
|
DPRINT1("QuickAlloc() failed in pLoadFileList()\n");
|
||||||
|
@ -650,6 +610,10 @@ pBuildFileList(LPCWSTR szFirstFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
GdipGetImageDecoders(num, size, codecInfo);
|
GdipGetImageDecoders(num, size, codecInfo);
|
||||||
|
buffer = (PWSTR)((UINT_PTR)codecInfo + size + (sizeof(*codecInfo) * ExtraCount));
|
||||||
|
if (ExtraCount)
|
||||||
|
codecInfo[num].FilenameExtension = wcscpy(buffer, ExtraExtensions);
|
||||||
|
num += ExtraCount;
|
||||||
|
|
||||||
root = QuickAlloc(sizeof(SHIMGVW_FILENODE), FALSE);
|
root = QuickAlloc(sizeof(SHIMGVW_FILENODE), FALSE);
|
||||||
if (!root)
|
if (!root)
|
||||||
|
@ -663,6 +627,7 @@ pBuildFileList(LPCWSTR szFirstFile)
|
||||||
|
|
||||||
for (j = 0; j < num; ++j)
|
for (j = 0; j < num; ++j)
|
||||||
{
|
{
|
||||||
|
// FIXME: Parse each FilenameExtension list to bypass szFileTypes limit
|
||||||
StringCbCopyW(szFileTypes, sizeof(szFileTypes), codecInfo[j].FilenameExtension);
|
StringCbCopyW(szFileTypes, sizeof(szFileTypes), codecInfo[j].FilenameExtension);
|
||||||
|
|
||||||
extension = wcstok(szFileTypes, L";");
|
extension = wcstok(szFileTypes, L";");
|
||||||
|
|
|
@ -78,3 +78,46 @@ static inline VOID QuickFree(LPVOID ptr)
|
||||||
{
|
{
|
||||||
HeapFree(GetProcessHeap(), 0, ptr);
|
HeapFree(GetProcessHeap(), 0, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline WORD Swap16(WORD v)
|
||||||
|
{
|
||||||
|
return MAKEWORD(HIBYTE(v), LOBYTE(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline UINT Swap32(UINT v)
|
||||||
|
{
|
||||||
|
return MAKELONG(Swap16(HIWORD(v)), Swap16(LOWORD(v)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define BigToHost32 Swap32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline ULARGE_INTEGER MakeULargeInteger(UINT64 value)
|
||||||
|
{
|
||||||
|
ULARGE_INTEGER ret;
|
||||||
|
ret.QuadPart = value;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline HRESULT SHIMGVW_HResultFromWin32(DWORD hr)
|
||||||
|
{
|
||||||
|
// HRESULT_FROM_WIN32 will evaluate its parameter twice, this function will not.
|
||||||
|
return HRESULT_FROM_WIN32(hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline HRESULT HResultFromGdiplus(Status status)
|
||||||
|
{
|
||||||
|
switch ((UINT)status)
|
||||||
|
{
|
||||||
|
case Ok: return S_OK;
|
||||||
|
case InvalidParameter: return E_INVALIDARG;
|
||||||
|
case OutOfMemory: return E_OUTOFMEMORY;
|
||||||
|
case NotImplemented: return HRESULT_FROM_WIN32(ERROR_CALL_NOT_IMPLEMENTED);
|
||||||
|
case Win32Error: return SHIMGVW_HResultFromWin32(GetLastError());
|
||||||
|
case FileNotFound: return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
||||||
|
case AccessDenied: return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
||||||
|
case UnknownImageFormat: return HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
|
||||||
|
}
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue