[PATCH 5/5] shell32: Partially implement IShellItemImageFactory (icon only, no thumbnail).

Jinoh Kang jinoh.kang.kr at gmail.com
Mon Mar 14 13:09:09 CDT 2022


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52673
Signed-off-by: Jinoh Kang <jinoh.kang.kr at gmail.com>
---
 dlls/shell32/Makefile.in       |   4 +-
 dlls/shell32/shellitem.c       | 179 ++++++++++++++++++++++++++++++++-
 dlls/shell32/tests/shlfolder.c |   4 +-
 3 files changed, 180 insertions(+), 7 deletions(-)

diff --git a/dlls/shell32/Makefile.in b/dlls/shell32/Makefile.in
index eeb6cd63d60..1ed28a761fa 100644
--- a/dlls/shell32/Makefile.in
+++ b/dlls/shell32/Makefile.in
@@ -1,8 +1,8 @@
 EXTRADEFS = -D_SHELL32_
 MODULE    = shell32.dll
 IMPORTLIB = shell32
-IMPORTS   = uuid shlwapi user32 gdi32 advapi32
-DELAYIMPORTS = ole32 oleaut32 shdocvw version comctl32 gdiplus
+IMPORTS   = uuid shlwapi user32 gdi32 advapi32 win32u
+DELAYIMPORTS = ole32 oleaut32 shdocvw version comctl32 gdiplus windowscodecs
 
 C_SRCS = \
 	appbar.c \
diff --git a/dlls/shell32/shellitem.c b/dlls/shell32/shellitem.c
index a3bcacbb531..3bf5aa0e693 100644
--- a/dlls/shell32/shellitem.c
+++ b/dlls/shell32/shellitem.c
@@ -31,9 +31,13 @@
 #include "pidl.h"
 #include "shell32_main.h"
 #include "debughlp.h"
+#include "ntuser.h"
+#include "wincodec.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
+HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
+
 typedef struct _ShellItem {
     IShellItem2             IShellItem2_iface;
     LONG                    ref;
@@ -574,17 +578,186 @@ static ULONG WINAPI ShellItem_IShellItemImageFactory_Release(IShellItemImageFact
     return IShellItem2_Release(&This->IShellItem2_iface);
 }
 
+static HRESULT ShellItem_get_icons(ShellItem *This, SIZE size, HICON *big_icon, HICON *small_icon)
+{
+    HRESULT hr;
+    IExtractIconW *ei;
+    WCHAR icon_file[MAX_PATH];
+    INT source_index;
+    UINT gil_in_flags = 0, gil_out_flags;
+    INT iconsize;
+
+    iconsize = min(size.cx, size.cy);
+    if (iconsize <= 0 || iconsize > 0x7fff)
+        iconsize = 0x7fff;
+
+    hr = ShellItem_get_uiobject(This, &IID_IExtractIconW, (void **)&ei);
+    if (FAILED(hr)) goto done;
+
+    hr = IExtractIconW_GetIconLocation(ei, gil_in_flags, icon_file, MAX_PATH, &source_index, &gil_out_flags);
+    if (FAILED(hr)) goto free_ei;
+
+    if (!(gil_out_flags & GIL_NOTFILENAME))
+    {
+        UINT ei_res;
+
+        if (source_index == -1)
+            source_index = 0;  /* special case for some control panel applications */
+
+        FIXME("%s %d\n", debugstr_w(icon_file), source_index);
+        ei_res = ExtractIconExW(icon_file, source_index, big_icon, small_icon, 1);
+        if (!ei_res || ei_res == (UINT)-1)
+        {
+            WARN("Failed to extract icon.\n");
+            hr = E_FAIL;
+        }
+    }
+    else
+    {
+        hr = IExtractIconW_Extract(ei, icon_file, source_index, big_icon, small_icon, MAKELONG(iconsize, iconsize));
+    }
+
+free_ei:
+    IExtractIconW_Release(ei);
+done:
+    return hr;
+}
+
+static HICON choose_best_icon(HICON *icons, UINT count, SIZE size_limit, SIZE *out_size)
+{
+    HICON best_icon = NULL;
+    SIZE best_size = {0, 0};
+    UINT i;
+
+    for (i = 0; i < count; i++)
+    {
+        SIZE size;
+
+        if (!icons[i] || !NtUserGetIconSize(icons[i], 0, &size.cx, &size.cy)) continue;
+        size.cy /= 2;
+
+        if (!best_icon || (best_size.cx < size.cx && size.cx <= size_limit.cx &&
+                           best_size.cy < size.cy && size.cy <= size_limit.cy))
+        {
+            best_icon = icons[i];
+            best_size = size;
+        }
+    }
+
+    *out_size = best_size;
+    return best_icon;
+}
+
+static HRESULT ShellItem_get_icon_bitmap(ShellItem *This, IWICImagingFactory *imgfactory,
+                                         SIZE size, SIIGBF flags, IWICBitmap **bitmap)
+{
+    HRESULT hr;
+    HICON icons[2] = { NULL, NULL }, best_icon;
+    SIZE best_icon_size;
+    UINT i;
+
+    *bitmap = NULL;
+
+    hr = ShellItem_get_icons(This, size, &icons[0], &icons[1]);
+    if (FAILED(hr)) return hr;
+
+    best_icon = choose_best_icon(icons, ARRAY_SIZE(icons), size, &best_icon_size);
+    for (i = 0; i < ARRAY_SIZE(icons); i++)
+        if (icons[i] && icons[i] != best_icon) DeleteObject(icons[i]);
+
+    if (!best_icon) return E_FAIL;
+
+    hr = IWICImagingFactory_CreateBitmapFromHICON(imgfactory, best_icon, bitmap);
+    DeleteObject(best_icon);
+    return hr;
+}
+
+static HRESULT convert_wicbitmapsource_to_gdi(IWICImagingFactory *imgfactory, IWICBitmapSource *bitmapsource, HBITMAP *gdibitmap)
+{
+    BITMAPINFOHEADER bmi;
+    HRESULT hr;
+    UINT width, height;
+    IWICBitmapSource *newsrc;
+    HDC dc;
+    HBITMAP bm;
+    void *bits;
+
+    *gdibitmap = NULL;
+
+    hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, bitmapsource, &newsrc);
+    if (FAILED(hr)) goto done;
+
+    hr = IWICBitmapSource_GetSize(newsrc, &width, &height);
+    if (FAILED(hr)) goto free_newsrc;
+
+    dc = GetDC(NULL);
+    if (!dc)
+    {
+        hr = E_FAIL;
+        goto free_newsrc;
+    }
+
+    memset(&bmi, 0, sizeof(bmi));
+    bmi.biSize = sizeof(bmi);
+    bmi.biWidth = width;
+    bmi.biHeight = -height;
+    bmi.biPlanes = 1;
+    bmi.biBitCount = 32;
+    bmi.biCompression = BI_RGB;
+    bmi.biSizeImage = width * height * 4;
+
+    bm = CreateDIBSection(dc, (const BITMAPINFO *)&bmi, DIB_RGB_COLORS, &bits, NULL, 0);
+    ReleaseDC(NULL, dc);
+    if (!bm)
+    {
+        WARN("Cannot create bitmap.\n");
+        hr = E_FAIL;
+        goto free_newsrc;
+    }
+
+    hr = IWICBitmapSource_CopyPixels(newsrc, NULL, bmi.biWidth * 4, bmi.biSizeImage, bits);
+    if (FAILED(hr))
+    {
+        DeleteObject(bm);
+        goto free_newsrc;
+    }
+
+    hr = S_OK;
+    *gdibitmap = bm;
+
+free_newsrc:
+    IWICBitmapSource_Release(newsrc);
+done:
+    return hr;
+}
+
 static HRESULT WINAPI ShellItem_IShellItemImageFactory_GetImage(IShellItemImageFactory *iface,
     SIZE size, SIIGBF flags, HBITMAP *phbm)
 {
     ShellItem *This = impl_from_IShellItemImageFactory(iface);
+    HRESULT hr;
+    IWICImagingFactory *imgfactory;
+    IWICBitmap *bitmap = NULL;
     static int once;
 
     if (!once++)
-        FIXME("%p ({%lu, %lu} %lu %p): stub\n", This, size.cx, size.cy, flags, phbm);
+        FIXME("%p ({%lu, %lu} %lu %p): partial stub\n", This, size.cx, size.cy, flags, phbm);
 
-    *phbm = NULL;
-    return E_NOTIMPL;
+    if (flags != SIIGBF_BIGGERSIZEOK) return E_NOTIMPL;
+
+    hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &imgfactory);
+    if (SUCCEEDED(hr))
+    {
+        hr = ShellItem_get_icon_bitmap(This, imgfactory, size, flags, &bitmap);
+        if (SUCCEEDED(hr))
+        {
+            hr = convert_wicbitmapsource_to_gdi(imgfactory, (IWICBitmapSource *)bitmap, phbm);
+            IWICBitmap_Release(bitmap);
+        }
+        IWICImagingFactory_Release(imgfactory);
+    }
+
+    return hr;
 }
 
 static const IShellItemImageFactoryVtbl ShellItem_IShellItemImageFactory_Vtbl = {
diff --git a/dlls/shell32/tests/shlfolder.c b/dlls/shell32/tests/shlfolder.c
index 43f115af1f8..f3e35a7d201 100644
--- a/dlls/shell32/tests/shlfolder.c
+++ b/dlls/shell32/tests/shlfolder.c
@@ -4511,8 +4511,8 @@ static void test_IShellItemImageFactory(void)
                 SIZE size = {32, 32};
 
                 ret = IShellItemImageFactory_GetImage(siif, size, SIIGBF_BIGGERSIZEOK, &hbm);
-                todo_wine ok(ret == S_OK, "GetImage returned %lx\n", ret);
-                todo_wine ok(!!hbm, "bitmap should not be NULL\n");
+                ok(ret == S_OK, "GetImage returned %lx\n", ret);
+                ok(!!hbm, "bitmap should not be NULL\n");
                 if (SUCCEEDED(ret) && hbm)
                 {
                     DWORD objtype = GetObjectType(hbm);
-- 
2.34.1




More information about the wine-devel mailing list