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

Jin-oh Kang jinoh.kang.kr at gmail.com
Sun Apr 24 12:25:06 CDT 2022


On Mon, Apr 25, 2022, 1:44 AM Jinoh Kang <jinoh.kang.kr at gmail.com> wrote:

> 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
>
>     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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20220425/5b2cf93c/attachment.htm>


More information about the wine-devel mailing list