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

Jinoh Kang wine at gitlab.winehq.org
Wed Jun 15 10:35:49 CDT 2022


From: Jinoh Kang <jinoh.kang.kr at gmail.com>

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       |   2 +-
 dlls/shell32/shellitem.c       | 177 ++++++++++++++++++++++++++++++++-
 dlls/shell32/tests/shlfolder.c |   1 -
 3 files changed, 175 insertions(+), 5 deletions(-)

diff --git a/dlls/shell32/Makefile.in b/dlls/shell32/Makefile.in
index 9e2395126fc..95afa482de5 100644
--- a/dlls/shell32/Makefile.in
+++ b/dlls/shell32/Makefile.in
@@ -1,7 +1,7 @@
 MODULE    = shell32.dll
 IMPORTLIB = shell32
 IMPORTS   = uuid shlwapi user32 gdi32 advapi32
-DELAYIMPORTS = ole32 oleaut32 shdocvw version comctl32 gdiplus
+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 3e8bd24a429..84dd597b02c 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 "wincodec.h"
+#include "commoncontrols.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
+HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
+
 typedef struct _ShellItem {
     IShellItem2             IShellItem2_iface;
     LONG                    ref;
@@ -565,17 +569,184 @@ static ULONG WINAPI ShellItem_IShellItemImageFactory_Release(IShellItemImageFact
     return IShellItem2_Release(&This->IShellItem2_iface);
 }
 
+static IImageList *get_system_imagelist_by_size(SIZE size)
+{
+    int i;
+
+    for (i = 0; i <= SHIL_LAST; i++)
+    {
+        IImageList *cur_list;
+
+        if (SUCCEEDED(SHGetImageList(i, &IID_IImageList, (void **)&cur_list)))
+        {
+            int cx, cy;
+
+            if (SUCCEEDED(IImageList_GetIconSize(cur_list, &cx, &cy)) &&
+                cx == size.cx && cy == size.cy)
+            {
+                return cur_list;
+            }
+
+            IImageList_Release(cur_list);
+        }
+    }
+
+    return NULL;
+}
+
+static HICON get_icon_via_cache(LPCWSTR icon_file, INT source_index, DWORD flags, SIZE size)
+{
+    IImageList *icon_list;
+    int ic_index;
+    HICON icon;
+
+    if (!(icon_list = get_system_imagelist_by_size(size))) return NULL;
+
+    if ((ic_index = SIC_GetIconIndex(icon_file, source_index, flags)) != INVALID_INDEX)
+    {
+        if (FAILED(IImageList_GetIcon(icon_list, ic_index, ILD_NORMAL, &icon)))
+        {
+            icon = NULL;
+        }
+    }
+    IImageList_Release(icon_list);
+
+    return icon;
+}
+
+static HRESULT ShellItem_get_icon(ShellItem *This, SIZE size, HICON *icon)
+{
+    IExtractIconW *extracticon;
+    WCHAR icon_file[MAX_PATH];
+    INT icon_source_index;
+    UINT gil_out_flags;
+    HICON cached_icon;
+    HRESULT hr;
+
+    if (FAILED(hr = IShellItem2_BindToHandler(&This->IShellItem2_iface, NULL, &BHID_SFUIObject,
+                                              &IID_IExtractIconW, (void **)&extracticon)))
+    {
+        goto done;
+    }
+
+    if (FAILED(hr = IExtractIconW_GetIconLocation(extracticon, 0, icon_file, ARRAY_SIZE(icon_file),
+                                                  &icon_source_index, &gil_out_flags)))
+    {
+        goto free_extracticon;
+    }
+
+    if (!(gil_out_flags & (GIL_NOTFILENAME | GIL_DONTCACHE)) &&
+        (cached_icon = get_icon_via_cache(icon_file, icon_source_index, 0, size)))
+    {
+        *icon = cached_icon;
+        hr = S_OK;
+    }
+    else
+    {
+        LONG iconsize = min(size.cx, size.cy);
+        if (iconsize <= 0 || iconsize > 0x7fff)
+            iconsize = 0x7fff;
+        hr = IExtractIconW_Extract(extracticon, icon_file, icon_source_index,
+                                   icon, NULL, MAKELONG(0, iconsize));
+    }
+
+free_extracticon:
+    IExtractIconW_Release(extracticon);
+done:
+    return hr;
+}
+
+static HRESULT convert_wicbitmapsource_to_gdi(IWICImagingFactory *imgfactory,
+                                              IWICBitmapSource *bitmapsource, HBITMAP *gdibitmap)
+{
+    IWICBitmapSource *newsrc;
+    UINT width, height;
+    BITMAPINFO bmi;
+    HBITMAP bm;
+    void *bits;
+    HRESULT hr;
+
+    *gdibitmap = NULL;
+
+    if (FAILED(hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, bitmapsource, &newsrc)))
+    {
+        goto done;
+    }
+
+    if (FAILED(hr = IWICBitmapSource_GetSize(newsrc, &width, &height)))
+    {
+        goto free_newsrc;
+    }
+
+    memset(&bmi, 0, sizeof(bmi));
+    bmi.bmiHeader.biSize = sizeof(bmi);
+    bmi.bmiHeader.biWidth = width;
+    bmi.bmiHeader.biHeight = -height;
+    bmi.bmiHeader.biPlanes = 1;
+    bmi.bmiHeader.biBitCount = 32;
+    bmi.bmiHeader.biCompression = BI_RGB;
+
+    if (!(bm = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &bits, NULL, 0)))
+    {
+        WARN("Cannot create bitmap.\n");
+        hr = E_FAIL;
+        goto free_newsrc;
+    }
+
+    if (FAILED(hr = IWICBitmapSource_CopyPixels(newsrc, NULL, width * 4, width * height * 4, bits)))
+    {
+        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)
+                                                                SIZE size, SIIGBF flags, HBITMAP *phbm)
 {
     ShellItem *This = impl_from_IShellItemImageFactory(iface);
+    IWICImagingFactory *imgfactory;
+    IWICBitmap *bitmap = NULL;
     static int once;
+    HICON hicon;
+    HRESULT hr;
 
     if (!once++)
-        FIXME("%p ({%lu, %lu} %d %p): stub\n", This, size.cx, size.cy, flags, phbm);
+        FIXME("%p ({%lu, %lu} %d %p): partial stub\n", This, size.cx, size.cy, flags, phbm);
 
     *phbm = NULL;
-    return E_NOTIMPL;
+
+    if (flags != SIIGBF_BIGGERSIZEOK) return E_NOTIMPL;
+
+    if (FAILED(hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &imgfactory)))
+    {
+        return hr;
+    }
+
+    if (SUCCEEDED(hr = ShellItem_get_icon(This, size, &hicon)))
+    {
+        if (FAILED(hr = IWICImagingFactory_CreateBitmapFromHICON(imgfactory, hicon, &bitmap)))
+        {
+            bitmap = NULL;
+        }
+        DeleteObject(hicon);
+    }
+
+    if (bitmap)
+    {
+        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 da606a9e707..49614aed3c4 100644
--- a/dlls/shell32/tests/shlfolder.c
+++ b/dlls/shell32/tests/shlfolder.c
@@ -4456,7 +4456,6 @@ static void test_IShellItemImageFactory(void)
 
         ret = IShellItemImageFactory_GetImage(siif, size, SIIGBF_BIGGERSIZEOK, &hbm);
         IShellItemImageFactory_Release(siif);
-        todo_wine
         ok(ret == S_OK, "GetImage returned %lx\n", ret);
         ok(FAILED(ret) == !hbm, "result = %lx but bitmap = %p\n", ret, hbm);
 
-- 
GitLab

https://gitlab.winehq.org/wine/wine/-/merge_requests/190



More information about the wine-devel mailing list