summaryrefslogtreecommitdiff
path: root/lib/Thumbnail/thumbnail.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Thumbnail/thumbnail.cpp')
-rw-r--r--lib/Thumbnail/thumbnail.cpp167
1 files changed, 167 insertions, 0 deletions
diff --git a/lib/Thumbnail/thumbnail.cpp b/lib/Thumbnail/thumbnail.cpp
new file mode 100644
index 00000000..b8d00c38
--- /dev/null
+++ b/lib/Thumbnail/thumbnail.cpp
@@ -0,0 +1,167 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) *
+// **************************************************************************
+
+#include "thumbnail.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include "windows.h"
+
+#define STRICT_TYPED_ITEMIDS //better type safety for IDLists
+#include <Shlobj.h>
+
+#include <Shellapi.h>
+#include <CommonControls.h>
+#include <zen/com_ptr.h>
+#include <zen/string_tools.h>
+#include <string>
+#include <zen/scope_guard.h>
+
+using namespace zen;
+
+
+thumb::HICON thumb::getThumbnail(const wchar_t* filename, int requestedSize) //return 0 on failure, caller takes ownership!
+{
+ const std::wstring filenameStr(filename);
+
+ ComPtr<IShellFolder> shellFolder;
+ {
+ HRESULT hr = ::SHGetDesktopFolder(shellFolder.init());
+ if (FAILED(hr) || !shellFolder)
+ return NULL;
+ }
+
+ PIDLIST_RELATIVE pidlFolder = NULL;
+ {
+ const std::wstring& pathName = beforeLast(filenameStr, '\\');
+ HRESULT hr = shellFolder->ParseDisplayName(NULL, // [in] HWND hwnd,
+ NULL, // [in] IBindCtx *pbc,
+ const_cast<LPWSTR>(pathName.c_str()), // [in] LPWSTR pszDisplayName,
+ NULL, // [out] ULONG *pchEaten,
+ &pidlFolder, // [out] PIDLIST_RELATIVE* ppidl,
+ NULL); // [in, out] ULONG *pdwAttributes
+ if (FAILED(hr) || !pidlFolder)
+ return NULL;
+ }
+ ZEN_ON_BLOCK_EXIT(::ILFree(pidlFolder)); //older version: ::CoTaskMemFree
+
+ ComPtr<IShellFolder> imageFolder;
+ {
+ HRESULT hr = shellFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl,
+ NULL,
+ IID_PPV_ARGS(imageFolder.init()));
+ if (FAILED(hr) || !imageFolder)
+ return NULL;
+ }
+
+ PIDLIST_RELATIVE pidImage = NULL;
+ {
+ const std::wstring& shortName = afterLast(filenameStr, '\\');
+ HRESULT hr = imageFolder->ParseDisplayName(NULL, // [in] HWND hwnd,
+ NULL, // [in] IBindCtx *pbc,
+ const_cast<LPWSTR>(shortName.c_str()), // [in] LPWSTR pszDisplayName,
+ NULL, // [out] ULONG *pchEaten,
+ &pidImage, // [out] PIDLIST_RELATIVE *ppidl,
+ NULL); // [in, out] ULONG *pdwAttributes
+ if (FAILED(hr) || !pidImage)
+ return NULL;
+ }
+ ZEN_ON_BLOCK_EXIT(::ILFree(pidImage)); //older version: ::CoTaskMemFree
+
+ ComPtr<IExtractImage> extractImage;
+ {
+ PCUITEMID_CHILD_ARRAY pidlIn = reinterpret_cast<PCUITEMID_CHILD_ARRAY>(&pidImage);
+ //this is where STRICT_TYPED_ITEMIDS gets us ;)
+
+ HRESULT hr = imageFolder->GetUIObjectOf(NULL, // [in] HWND hwndOwner,
+ 1, // [in] UINT cidl,
+ pidlIn, // [in] PCUITEMID_CHILD_ARRAY apidl,
+ IID_IExtractImage, // [in] REFIID riid,
+ NULL, // [in, out] UINT *rgfReserved,
+ reinterpret_cast<void**>(extractImage.init())); // [out] void **ppv
+ if (FAILED(hr) || !extractImage)
+ return NULL;
+ }
+
+ {
+ wchar_t pathBuffer[MAX_PATH];
+ DWORD priority = 0;
+ const SIZE prgSize = { requestedSize, requestedSize }; //preferred size only!
+ DWORD clrDepth = 32;
+ DWORD flags = IEIFLAG_SCREEN | IEIFLAG_OFFLINE;
+
+ HRESULT hr = extractImage->GetLocation(pathBuffer, // [out] LPWSTR pszPathBuffer,
+ MAX_PATH, // [in] DWORD cchMax,
+ &priority, // [out] DWORD *pdwPriority,
+ &prgSize, // [in] const SIZE *prgSize,
+ clrDepth, // [in] DWORD dwRecClrDepth,
+ &flags); // [in, out] DWORD *pdwFlags
+ if (FAILED(hr))
+ return NULL;
+ }
+
+ HBITMAP bitmap = NULL;
+ {
+ HRESULT hr = extractImage->Extract(&bitmap);
+ if (FAILED(hr) || !bitmap)
+ return NULL;
+ }
+ ZEN_ON_BLOCK_EXIT(::DeleteObject(bitmap));
+
+
+ BITMAP bmpInfo = {};
+ if (::GetObject(bitmap, //__in HGDIOBJ hgdiobj,
+ sizeof(bmpInfo), //__in int cbBuffer,
+ &bmpInfo) == 0) //__out LPVOID lpvObject
+ return NULL;
+
+ HBITMAP bitmapMask = ::CreateCompatibleBitmap(::GetDC(NULL), bmpInfo.bmWidth, bmpInfo.bmHeight);
+ if (bitmapMask == 0)
+ return NULL;
+ ZEN_ON_BLOCK_EXIT(::DeleteObject(bitmapMask));
+
+ ICONINFO iconInfo = {};
+ iconInfo.fIcon = true;
+ iconInfo.hbmColor = bitmap;
+ iconInfo.hbmMask = bitmapMask;
+
+ return ::CreateIconIndirect(&iconInfo);
+}
+
+
+thumb::HICON thumb::getIconByIndex(int iconIndex, int shilIconType) //return 0 on failure, caller takes ownership!
+{
+ //Note: using IExtractIcon::Extract is *no* alternative, just as ::SHGetFileInfo(), it only supports small (16x16) and large (32x32) icons
+
+ ComPtr<IImageList> imageList; //perf: 0,12 µs only to get the image list
+ {
+ HRESULT hr = ::SHGetImageList(shilIconType, //__in int iImageList,
+ IID_PPV_ARGS(imageList.init()));
+ if (FAILED(hr) || !imageList)
+ return NULL;
+ }
+
+ bool hasAlpha = false; //perf: 0,14 µs
+ {
+ DWORD flags = 0;
+ HRESULT hr = imageList->GetItemFlags(iconIndex, //[in] int i,
+ &flags); //[out] DWORD *dwFlags
+ if (SUCCEEDED(hr))
+ hasAlpha = flags & ILIF_ALPHA;
+ }
+
+ ::HICON hIcon = NULL; //perf: 1,5 ms - the dominant block
+ {
+ HRESULT hr = imageList->GetIcon(iconIndex, // [in] int i,
+ hasAlpha ? ILD_IMAGE : ILD_NORMAL, // [in] UINT flags,
+ //ILD_IMAGE -> do not draw mask - fixes glitch with ILD_NORMAL where both mask *and* alpha channel are applied, e.g. for ffs_batch and folder icon
+ //other flags: http://msdn.microsoft.com/en-us/library/windows/desktop/bb775230(v=vs.85).aspx
+ &hIcon); // [out] HICON *picon
+ if (FAILED(hr) || !hIcon)
+ return NULL;
+ }
+
+ return hIcon;
+} \ No newline at end of file
bgstack15