From 0a6749407124bd96dac2c6742e95dfdb020b30d6 Mon Sep 17 00:00:00 2001 From: "Carl J. Bialorucki" Date: Wed, 5 Mar 2025 20:51:44 -0700 Subject: [PATCH] [0.4.15][SHIMGVW] Choose a better icon image CORE-19945 - 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 This is a backport of the following commits: a07a35d0f6139e759a59c6fe645a8912472d4516 [SHIMGVW] Update Portuguese (pt-PT) translation (#7688) 329a41458416f633fb6ec016dc434b063cd92b9b [SHIMGVW] Choose a better icon image and also support .cur files (#7706) --- dll/win32/shimgvw/CMakeLists.txt | 1 + dll/win32/shimgvw/lang/pt-PT.rc | 46 ++--- dll/win32/shimgvw/loader.cpp | 283 +++++++++++++++++++++++++++++++ dll/win32/shimgvw/shimgvw.c | 81 +++------ dll/win32/shimgvw/shimgvw.h | 43 +++++ 5 files changed, 374 insertions(+), 80 deletions(-) create mode 100644 dll/win32/shimgvw/loader.cpp diff --git a/dll/win32/shimgvw/CMakeLists.txt b/dll/win32/shimgvw/CMakeLists.txt index 1a4589abd43..15983aff150 100644 --- a/dll/win32/shimgvw/CMakeLists.txt +++ b/dll/win32/shimgvw/CMakeLists.txt @@ -3,6 +3,7 @@ spec2def(shimgvw.dll shimgvw.spec) list(APPEND SOURCE anime.c + loader.cpp shimgvw.c comsup.c shimgvw.rc diff --git a/dll/win32/shimgvw/lang/pt-PT.rc b/dll/win32/shimgvw/lang/pt-PT.rc index d51396c5018..c224cd5f3ba 100644 --- a/dll/win32/shimgvw/lang/pt-PT.rc +++ b/dll/win32/shimgvw/lang/pt-PT.rc @@ -1,6 +1,8 @@ -/* Portuguese Language resource file - * - * Traduzido por: Jose Carlos Jesus 17-01-2020 zecarlos1957@hotmail.com +/* + * PROJECT: ReactOS Picture and Fax Viewer + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Portuguese (Portugal) resource file + * TRANSLATOR: Copyright 2019-2025 Jose Carlos Jesus */ LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL @@ -9,34 +11,34 @@ STRINGTABLE BEGIN IDS_APPTITLE "Visualizador de imagens e faxes do ReactOS" IDS_SETASDESKBG "Definir como plano de fundo da área de trabalho" - IDS_NOPREVIEW "No preview available." - IDS_PREVIEW "Pre-visualizar" + IDS_NOPREVIEW "Nenhuma pré-visualização disponível." + IDS_PREVIEW "Pré-visualizar" /* Tooltips */ IDS_TOOLTIP_NEXT_PIC "Próxima imagem" IDS_TOOLTIP_PREV_PIC "Imagem anterior" - IDS_TOOLTIP_BEST_FIT "Best fit to window (Ctrl+B)" - IDS_TOOLTIP_REAL_SIZE "Actual size (Ctrl+A)" - IDS_TOOLTIP_SLIDE_SHOW "Start slideshow (F11)" + IDS_TOOLTIP_BEST_FIT "Melhor ajuste à janela(Ctrl+B)" + IDS_TOOLTIP_REAL_SIZE "Tamanho actual (Ctrl+A)" + IDS_TOOLTIP_SLIDE_SHOW "Iniciar apresentação de diapositivos (F11)" IDS_TOOLTIP_ZOOM_IN "Zoom (+)" IDS_TOOLTIP_ZOOM_OUT "Zoom (-)" - IDS_TOOLTIP_ROT_CLOCKW "Girar no sentido dos ponteiros do relógio(Ctrl+K)" - IDS_TOOLTIP_ROT_COUNCW "Girar no sentido anti-horário (Ctrl+L)" - IDS_TOOLTIP_ROT_CWSAVE "Rotate Clockwise and Save (Lossy)" - IDS_TOOLTIP_ROT_CCWSAVE "Rotate Counterclockwise and Save (Lossy)" - IDS_TOOLTIP_DELETE "Delete (DEL)" + IDS_TOOLTIP_ROT_CLOCKW "Girar no sentido dos ponteiros do relógio (Ctrl+K)" + IDS_TOOLTIP_ROT_COUNCW "Girar no sentido contrário aos ponteiros do relógio (Ctrl+L)" + IDS_TOOLTIP_ROT_CWSAVE "Girar no sentido dos ponteiros do relógio e guardar (com perdas)" + IDS_TOOLTIP_ROT_CCWSAVE "Girar no sentido contrário aos ponteiros do relógio e guardar (com perdas)" + IDS_TOOLTIP_DELETE "Eliminar (DEL)" IDS_TOOLTIP_PRINT "Imprimir (Ctrl+P)" IDS_TOOLTIP_SAVEAS "Guardar como... (Ctrl+S)" - IDS_TOOLTIP_MODIFY "Modify (Ctrl+E)" - IDS_TOOLTIP_HELP_TOC "Help topics (F1)" + IDS_TOOLTIP_MODIFY "Modificar (Ctrl+E)" + IDS_TOOLTIP_HELP_TOC "Tópicos da ajuda (F1)" END STRINGTABLE BEGIN - IDS_EMF_FILE "EMF Image" - IDS_GIF_FILE "GIF Image" - IDS_JPG_FILE "JPEG Image" - IDS_BMP_FILE "Bitmap Image" - IDS_PNG_FILE "PNG Image" - IDS_TIF_FILE "TIF Image" - IDS_WMF_FILE "WMF Image" + IDS_EMF_FILE "Imagem EMF" + IDS_GIF_FILE "Imagem GIF" + IDS_JPG_FILE "Imagem JPEG" + IDS_BMP_FILE "Imagem Bitmap" + IDS_PNG_FILE "Imagem PNG" + IDS_TIF_FILE "Imagem TIF" + IDS_WMF_FILE "Imagem WMF" END diff --git a/dll/win32/shimgvw/loader.cpp b/dll/win32/shimgvw/loader.cpp new file mode 100644 index 00000000000..50112f6c486 --- /dev/null +++ b/dll/win32/shimgvw/loader.cpp @@ -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 + */ + +#include +#include +#include +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 +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 + +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()); +} diff --git a/dll/win32/shimgvw/shimgvw.c b/dll/win32/shimgvw/shimgvw.c index 693b499761f..bbfe3302da4 100644 --- a/dll/win32/shimgvw/shimgvw.c +++ b/dll/win32/shimgvw/shimgvw.c @@ -4,6 +4,7 @@ * PURPOSE: Image file browsing and manipulation * COPYRIGHT: Copyright Dmitry Chapyshev (dmitry@reactos.org) * Copyright 2018-2023 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + * Copyright 2025 Whindmar Saksit */ #include "shimgvw.h" @@ -13,6 +14,9 @@ #include #include +EXTERN_C PCWSTR GetExtraExtensionsGdipList(VOID); +EXTERN_C HRESULT LoadImageFromPath(LPCWSTR Path, GpImage** ppImage); + /* Toolbar image size */ #define TB_IMAGE_WIDTH 16 #define TB_IMAGE_HEIGHT 16 @@ -110,7 +114,6 @@ typedef struct tagPREVIEW_DATA INT m_yScrollOffset; UINT m_nMouseDownMsg; POINT m_ptOrigin; - IStream *m_pMemStream; WCHAR m_szFile[MAX_PATH]; } PREVIEW_DATA, *PPREVIEW_DATA; @@ -345,67 +348,20 @@ Preview_pFreeImage(PPREVIEW_DATA pData) 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; } -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 Preview_pLoadImage(PPREVIEW_DATA pData, LPCWSTR szOpenFileName) { + HRESULT hr; Preview_pFreeImage(pData); + InvalidateRect(pData->m_hwnd, NULL, FALSE); /* Schedule redraw in case we change to "No preview" */ - pData->m_pMemStream = MemStreamFromFile(szOpenFileName); - if (!pData->m_pMemStream) + hr = LoadImageFromPath(szOpenFileName, &g_pImage); + if (FAILED(hr)) { - DPRINT1("MemStreamFromFile() failed\n"); - 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"); + DPRINT1("GdipLoadImageFromStream() failed, %d\n", hr); Preview_pFreeImage(pData); Preview_UpdateTitle(pData, NULL); return; @@ -413,8 +369,8 @@ Preview_pLoadImage(PPREVIEW_DATA pData, LPCWSTR szOpenFileName) Anime_LoadInfo(&pData->m_Anime); - SHAddToRecentDocs(SHARD_PATHW, szOpenFileName); GetFullPathNameW(szOpenFileName, _countof(pData->m_szFile), pData->m_szFile, NULL); + SHAddToRecentDocs(SHARD_PATHW, pData->m_szFile); /* Reset zoom and redraw display */ Preview_ResetZoom(pData); @@ -599,7 +555,7 @@ static SHIMGVW_FILENODE* pBuildFileList(LPCWSTR szFirstFile) { HANDLE hFindHandle; - WCHAR *extension; + WCHAR *extension, *buffer; WCHAR szSearchPath[MAX_PATH]; WCHAR szSearchMask[MAX_PATH]; WCHAR szFileTypes[MAX_PATH]; @@ -608,15 +564,19 @@ pBuildFileList(LPCWSTR szFirstFile) SHIMGVW_FILENODE *root = NULL; SHIMGVW_FILENODE *conductor = NULL; ImageCodecInfo *codecInfo; - UINT num; - UINT size; + UINT num = 0, size = 0, ExtraSize = 0; 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); PathRemoveFileSpecW(szSearchPath); GdipGetImageDecodersSize(&num, &size); - codecInfo = QuickAlloc(size, FALSE); + codecInfo = QuickAlloc(size + ExtraSize, FALSE); if (!codecInfo) { DPRINT1("QuickAlloc() failed in pLoadFileList()\n"); @@ -624,6 +584,10 @@ pBuildFileList(LPCWSTR szFirstFile) } 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); if (!root) @@ -637,6 +601,7 @@ pBuildFileList(LPCWSTR szFirstFile) for (j = 0; j < num; ++j) { + // FIXME: Parse each FilenameExtension list to bypass szFileTypes limit StringCbCopyW(szFileTypes, sizeof(szFileTypes), codecInfo[j].FilenameExtension); extension = wcstok(szFileTypes, L";"); diff --git a/dll/win32/shimgvw/shimgvw.h b/dll/win32/shimgvw/shimgvw.h index 776da7f849c..c17a15bb8f2 100644 --- a/dll/win32/shimgvw/shimgvw.h +++ b/dll/win32/shimgvw/shimgvw.h @@ -78,3 +78,46 @@ static inline VOID QuickFree(LPVOID 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; +}