From: Jinoh Kang Subject: [PATCH v3 5/5] shell32: Partially implement IShellItemImageFactory (icon only, no thumbnail). Message-Id: Date: Sat, 19 Mar 2022 02:38:49 +0900 In-Reply-To: <7580c1c6-cfa7-b72d-0b96-257c48b2071f@gmail.com> References: <7580c1c6-cfa7-b72d-0b96-257c48b2071f@gmail.com> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52673 Signed-off-by: Jinoh Kang --- Notes: v1 -> v2: no changes dlls/shell32/Makefile.in | 4 +- dlls/shell32/shellitem.c | 179 ++++++++++++++++++++++++++++++++- dlls/shell32/tests/shlfolder.c | 2 +- 3 files changed, 179 insertions(+), 6 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 6318636cf23..61edad6227c 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 093e0add653..1a36a7b1bd1 100644 --- a/dlls/shell32/tests/shlfolder.c +++ b/dlls/shell32/tests/shlfolder.c @@ -4511,7 +4511,7 @@ static void test_IShellItemImageFactory(void) SIZE size = {32, 32}; 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(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); if (SUCCEEDED(ret) && hbm) { -- 2.34.1