[PATCH v4 4/4] shell32: Partially implement IShellItemImageFactory (icon only, no thumbnail).
Jinoh Kang
jinoh.kang.kr at gmail.com
Sat Apr 23 12:53:58 CDT 2022
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52673
Signed-off-by: Jinoh Kang <jinoh.kang.kr at 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
dlls/shell32/shellitem.c | 209 ++++++++++++++++++++++++++++++++-
dlls/shell32/tests/shlfolder.c | 2 +-
2 files changed, 207 insertions(+), 4 deletions(-)
diff --git a/dlls/shell32/shellitem.c b/dlls/shell32/shellitem.c
index 1a0bed55c1e..47a1eeb5701 100644
--- a/dlls/shell32/shellitem.c
+++ b/dlls/shell32/shellitem.c
@@ -31,6 +31,7 @@
#include "pidl.h"
#include "shell32_main.h"
#include "debughlp.h"
+#include "gdiplus.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell);
@@ -565,17 +566,219 @@ 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 gpstatus_to_hresult(GpStatus status)
+{
+ switch (status)
+ {
+ case Ok:
+ return S_OK;
+ case InvalidParameter:
+ return E_INVALIDARG;
+ case OutOfMemory:
+ return E_OUTOFMEMORY;
+ case NotImplemented:
+ return E_NOTIMPL;
+ default:
+ return E_FAIL;
+ }
+}
+
+static HRESULT ShellItem_get_icon_bitmap(ShellItem *This, SIZE size, SIIGBF flags, GpBitmap **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 = gpstatus_to_hresult(GdipCreateBitmapFromHICON(best_icon, bitmap));
+ DeleteObject(best_icon);
+ return hr;
+}
+
+/*************************************************************************
+ * create_argb_hbitmap_from_gpbitmap
+ *
+ * This is similar to GdipCreateHBITMAPFromBitmap, except that it uses
+ * PixelFormat32bppARGB instead of PixelFormat32bppPARGB and lacks the
+ * background parameter.
+ */
+static HRESULT create_argb_hbitmap_from_gpbitmap(GpBitmap *bitmap, HBITMAP *gdibitmap)
+{
+ BITMAPINFOHEADER bmi;
+ HRESULT hr;
+ UINT width, height;
+ BitmapData bitmapdata;
+ HDC dc;
+ HBITMAP bm;
+ void *bits;
+
+ *gdibitmap = NULL;
+
+ hr = GdipGetImageWidth((GpImage *)bitmap, &width);
+ if (FAILED(hr)) return hr;
+
+ hr = GdipGetImageHeight((GpImage *)bitmap, &height);
+ if (FAILED(hr)) return hr;
+
+ dc = CreateCompatibleDC(NULL);
+ if (!dc) return E_FAIL;
+
+ 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");
+ return E_FAIL;
+ }
+
+ memset(&bitmapdata, 0, sizeof(bitmapdata));
+ bitmapdata.Width = width;
+ bitmapdata.Height = height;
+ bitmapdata.Stride = width * 4;
+ bitmapdata.PixelFormat = PixelFormat32bppARGB;
+ bitmapdata.Scan0 = bits;
+ hr = gpstatus_to_hresult(GdipBitmapLockBits(bitmap, NULL,
+ ImageLockModeRead | ImageLockModeUserInputBuf,
+ bitmapdata.PixelFormat, &bitmapdata));
+ if (FAILED(hr))
+ {
+ DeleteObject(bm);
+ return hr;
+ }
+
+ GdipBitmapUnlockBits(bitmap, &bitmapdata);
+
+ *gdibitmap = bm;
+ return S_OK;
+}
+
static HRESULT WINAPI ShellItem_IShellItemImageFactory_GetImage(IShellItemImageFactory *iface,
SIZE size, SIIGBF flags, HBITMAP *phbm)
{
ShellItem *This = impl_from_IShellItemImageFactory(iface);
+ HRESULT hr;
+ GpBitmap *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 = ShellItem_get_icon_bitmap(This, size, flags, &bitmap);
+ if (SUCCEEDED(hr))
+ {
+ hr = create_argb_hbitmap_from_gpbitmap(bitmap, phbm);
+ GdipDisposeImage((GpImage *)bitmap);
+ }
+
+ return hr;
}
static const IShellItemImageFactoryVtbl ShellItem_IShellItemImageFactory_Vtbl = {
diff --git a/dlls/shell32/tests/shlfolder.c b/dlls/shell32/tests/shlfolder.c
index 900c484d0b9..a5f498f8c6d 100644
--- a/dlls/shell32/tests/shlfolder.c
+++ b/dlls/shell32/tests/shlfolder.c
@@ -4510,7 +4510,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
More information about the wine-devel
mailing list