On Mon, Apr 25, 2022, 1:44 AM Jinoh Kang <jinoh.kang.kr@gmail.com> wrote:
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52673
Signed-off-by: Jinoh Kang <jinoh.kang.kr@gmail.com>
---

Notes:
������ ������ v1 -> v2: no changes

������ ������ v3 -> v4:
������ ������ - Remove patch "shell32: Factor out ShellItem_get_uiobject."
������ ������ - Use IShellItem2_BindToHandler instead of factoring out
������ ������ ������ ShellItem_get_uiobject
������ ������ - Use CreateCompatibleDC(NULL) instead of GetDC(NULL)
������ ������ - Remove new dependency on win32u, use GetIconInfo instead
������ ������ - Remove new dependency on windowcodecs, use gdiplus instead

������ ������ v4 -> v5: revert to using windowscodecs

������dlls/shell32/Makefile.in������ ������ ������ ������|������ ������2 +-
������dlls/shell32/shellitem.c������ ������ ������ ������| 195 ++++++++++++++++++++++++++++++++-
������dlls/shell32/tests/shlfolder.c |������ ������2 -
������3 files changed, 193 insertions(+), 6 deletions(-)

diff --git a/dlls/shell32/Makefile.in b/dlls/shell32/Makefile.in
index eeb6cd63d60..0cfaac8e9cc 100644
--- a/dlls/shell32/Makefile.in
+++ b/dlls/shell32/Makefile.in
@@ -2,7 +2,7 @@ EXTRADEFS = -D_SHELL32_
������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 0381fa67f0d..a1b3bc57c66 100644
--- a/dlls/shell32/shellitem.c
+++ b/dlls/shell32/shellitem.c
@@ -31,9 +31,12 @@
������#include "pidl.h"
������#include "shell32_main.h"
������#include "debughlp.h"
+#include "wincodec.h"

������WINE_DEFAULT_DEBUG_CHANNEL(shell);

+HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
+
������typedef struct _ShellItem {
������ ������ ������IShellItem2������ ������ ������ ������ ������ ������ ������IShellItem2_iface;
������ ������ ������LONG������ ������ ������ ������ ������ ������ ������ ������ ������ ������ ref;
@@ -565,17 +568,203 @@ 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;
+������ ������ IBindCtx *pbc;
+������ ������ 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 = CreateBindCtx(0, &pbc);
+������ ������ if (FAILED(hr)) goto done;
+
+������ ������ hr = IShellItem2_BindToHandler(&This->IShellItem2_iface, pbc, &BHID_SFUIObject,
+������ ������ ������ ������ ������ ������ ������ ������ ������ ������ ������ ������ ������ ������ ������ ������ ������ ������&IID_IExtractIconW, (void **)&ei);
+������ ������ IBindCtx_Release(pbc);
+������ ������ 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++)
+������ ������ {
+������ ������ ������ ������ ICONINFO iinfo;
+������ ������ ������ ������ BITMAP bm;
+������ ������ ������ ������ SIZE size;
+������ ������ ������ ������ BOOL is_color, ret;
+
+������ ������ ������ ������ if (!icons[i] || !GetIconInfo(icons[i], &iinfo)) continue;
+
+������ ������ ������ ������ is_color = iinfo.hbmColor != NULL;
+������ ������ ������ ������ ret = GetObjectW(is_color ? iinfo.hbmColor : iinfo.hbmMask, sizeof(bm), &bm);
+������ ������ ������ ������ DeleteObject(iinfo.hbmColor);
+������ ������ ������ ������ DeleteObject(iinfo.hbmMask);
+������ ������ ������ ������ if (!ret) continue;
+
+������ ������ ������ ������ size.cx = bm.bmWidth;
+������ ������ ������ ������ size.cy = is_color ? abs(bm.bmHeight) : abs(bm.bmHeight) / 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 = CreateCompatibleDC(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;
+
+������ ������ bm = CreateDIBSection(dc, (const BITMAPINFO *)&bmi, DIB_RGB_COLORS, &bits, NULL, 0);
+������ ������ DeleteDC(dc);
+������ ������ if (!bm)
+������ ������ {
+������ ������ ������ ������ WARN("Cannot create bitmap.\n");
+������ ������ ������ ������ hr = E_FAIL;
+������ ������ ������ ������ goto free_newsrc;
+������ ������ }
+
+������ ������ hr = IWICBitmapSource_CopyPixels(newsrc, NULL, width * 4, width * height * 4, 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} %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;

This line shouldn't be deleted.

-������ ������ 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 78f199eaafc..507b3b1eac5 100644
--- a/dlls/shell32/tests/shlfolder.c
+++ b/dlls/shell32/tests/shlfolder.c
@@ -4464,11 +4464,9 @@ static void test_IShellItemImageFactory(void)
������ ������ ������ ������ ������ ������ ������ ������ ������ok(!GetModuleHandleA("windowscodecs.dll"), "WIC should not have already been loaded\n");

������ ������ ������ ������ ������ ������ ������ ������ ������ret = IShellItemImageFactory_GetImage(siif, size, SIIGBF_BIGGERSIZEOK, &hbm);
-������ ������ ������ ������ ������ ������ ������ ������ todo_wine
������ ������ ������ ������ ������ ������ ������ ������ ������ok(ret == S_OK || broken(ret == E_PENDING /* win7 */), "GetImage returned %lx\n", ret);
������ ������ ������ ������ ������ ������ ������ ������ ������ok(FAILED(ret) == !hbm, "result = %lx but bitmap = %p\n", ret, hbm);

-������ ������ ������ ������ ������ ������ ������ ������ todo_wine
������ ������ ������ ������ ������ ������ ������ ������ ������ok(!!GetModuleHandleA("windowscodecs.dll"), "WIC should have been loaded\n");

������ ������ ������ ������ ������ ������ ������ ������ ������if (SUCCEEDED(ret) && hbm)
--
2.34.1