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.cpp486
1 files changed, 0 insertions, 486 deletions
diff --git a/lib/Thumbnail/thumbnail.cpp b/lib/Thumbnail/thumbnail.cpp
deleted file mode 100644
index 53c30bc3..00000000
--- a/lib/Thumbnail/thumbnail.cpp
+++ /dev/null
@@ -1,486 +0,0 @@
-// **************************************************************************
-// * 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) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "thumbnail.h"
-#include <algorithm>
-#include <string>
-
-#define WIN32_LEAN_AND_MEAN
-#include <zen/win.h>
-#include <zen/win_ver.h>
-#include <zen/sys_error.h>
-
-#define STRICT_TYPED_ITEMIDS //better type safety for IDLists
-#include <Shlobj.h>
-
-#include <Shellapi.h>
-#include <CommonControls.h>
-
-#include <zen/com_error.h>
-#include <zen/com_ptr.h>
-#include <zen/string_tools.h>
-#include <zen/scope_guard.h>
-#include <zen/basic_math.h>
-//#include <zen/perf.h>
-
-using namespace zen;
-
-
-namespace
-{
-thumb::ImageData* allocImageData(int width, int height) //throw SysError; return value always bound!
-{
- ZEN_COM_ASSERT(width >= 0 && height >= 0); //throw SysError
-
- std::unique_ptr<thumb::ImageData> idata = make_unique<thumb::ImageData>();
-
- idata->width = width;
- idata->height = height;
- idata->rgb = new unsigned char[width * height * 4];
- idata->alpha = idata->rgb + width * height * 3;
-
- return idata.release();
-}
-
-void releaseImageData_impl(const thumb::ImageData* id)
-{
- if (id)
- {
- delete [] id->rgb;
- delete id;
- }
-}
-
-
-//caller takes ownership!
-HICON createIconFromBitmap(HBITMAP bitmap) //throw SysError
-{
- BITMAP bmpInfo = {};
- ZEN_COM_ASSERT(::GetObject(bitmap, //__in HGDIOBJ hgdiobj,
- sizeof(bmpInfo), //__in int cbBuffer,
- &bmpInfo)); //__out LPVOID lpvObject
- //no documented extended error info
-
- HDC hScreenDC = ::GetDC(nullptr);
- ZEN_COM_ASSERT(hScreenDC); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hScreenDC));
-
- HBITMAP bitmapMask = ::CreateCompatibleBitmap(hScreenDC, bmpInfo.bmWidth, bmpInfo.bmHeight);
- ZEN_COM_ASSERT(bitmapMask); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmapMask));
-
- ICONINFO iconInfo = {};
- iconInfo.fIcon = true;
- iconInfo.hbmColor = bitmap;
- iconInfo.hbmMask = bitmapMask;
-
- HICON result = ::CreateIconIndirect(&iconInfo);
- if (!result) throw SysError(formatSystemError(L"CreateIconIndirect", getLastError()));
- return result;
-}
-
-
-//caller takes ownership!
-thumb::ImageData* convertToImageData(HBITMAP bmp) //throw SysError
-{
- //GetDIBits ????
-
- BITMAP bmpInfo = {};
- ZEN_COM_ASSERT(::GetObject(bmp, //__in HGDIOBJ hgdiobj,
- sizeof(BITMAP), //__in int cbBuffer,
- &bmpInfo)); //__out LPVOID lpvObject
-
- HDC hScreenDC = ::GetDC(nullptr);
- ZEN_COM_ASSERT(hScreenDC); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hScreenDC));
-
- //32-bit RGB with alpha channel support
- BITMAPV5HEADER bi = {};
- bi.bV5Size = sizeof(bi);
- bi.bV5Width = bmpInfo.bmWidth;
- bi.bV5Height = -bmpInfo.bmHeight; //negative for top left origin
- bi.bV5Planes = 1;
- bi.bV5BitCount = 32;
- bi.bV5Compression = BI_BITFIELDS;
- bi.bV5AlphaMask = 0xFF000000;
- bi.bV5RedMask = 0x00FF0000;
- bi.bV5GreenMask = 0x0000FF00;
- bi.bV5BlueMask = 0x000000FF;
- unsigned char* bitsRgbBmp = nullptr;
-
- HBITMAP rgbBmp = ::CreateDIBSection(hScreenDC, //_In_ HDC hdc,
- reinterpret_cast<const BITMAPINFO*>(&bi), //_In_ const BITMAPINFO *pbmi,
- DIB_RGB_COLORS, //_In_ UINT iUsage,
- reinterpret_cast<VOID**>(&bitsRgbBmp), //_Out_ VOID **ppvBits,
- nullptr, //_In_ HANDLE hSection,
- 0); //_In_ DWORD dwOffset
- ZEN_COM_ASSERT(rgbBmp); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteObject(rgbBmp););
- ZEN_COM_ASSERT(bitsRgbBmp); //check after rgbBmp is owned by us
-
- HDC memDCSrc = ::CreateCompatibleDC(hScreenDC);
- ZEN_COM_ASSERT(memDCSrc); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteDC(memDCSrc));
-
- HDC memDCTrg = ::CreateCompatibleDC(hScreenDC);
- ZEN_COM_ASSERT(memDCTrg); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteDC(memDCTrg));
-
- HGDIOBJ hgdiSrcOld = ::SelectObject(memDCSrc, bmp);
- ZEN_COM_ASSERT(hgdiSrcOld); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::SelectObject(memDCSrc, hgdiSrcOld));
-
- HGDIOBJ hgdiTrgOld = ::SelectObject(memDCTrg, rgbBmp);
- ZEN_COM_ASSERT(hgdiTrgOld); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::SelectObject(memDCTrg, hgdiTrgOld));
-
- if (!::BitBlt(memDCTrg, //_In_ HDC hdcDest,
- 0, //_In_ int nXDest,
- 0, //_In_ int nYDest,
- bmpInfo.bmWidth, //_In_ int nWidth,
- bmpInfo.bmHeight, //_In_ int nHeight,
- memDCSrc, //_In_ HDC hdcSrc,
- 0, //_In_ int nXSrc,
- 0, //_In_ int nYSrc,
- SRCCOPY)) //_In_ DWORD dwRop
- throw SysError(formatSystemError(L"BitBlt", getLastError()));
-
- //CreateDIBSection: "Access to the bitmap must be synchronized. [...]. This applies to any use of the pointer to the bitmap bit values."
- /*bool rv = */
- ::GdiFlush();
-
- thumb::ImageData* imgOut = allocImageData(bmpInfo.bmWidth, bmpInfo.bmHeight); //throw SysError
- ScopeGuard guardImgData = zen::makeGuard([&] { releaseImageData_impl(imgOut); });
-
- unsigned char* rgbPtr = imgOut->rgb;
- unsigned char* alphaPtr = imgOut->alpha;
-
- for (int i = 0; i < bmpInfo.bmWidth * bmpInfo.bmHeight; ++i)
- {
- unsigned char b = *bitsRgbBmp++;
- unsigned char g = *bitsRgbBmp++;
- unsigned char r = *bitsRgbBmp++;
- unsigned char a = *bitsRgbBmp++;
-
- *rgbPtr++ = r;
- *rgbPtr++ = g;
- *rgbPtr++ = b;
- *alphaPtr++ = a;
- }
-
- guardImgData.dismiss();
- return imgOut;
-}
-
-
-//caller takes ownership!
-const thumb::ImageData* getThumbnail_impl(const wchar_t* filename, int requestedSize) //throw SysError
-{
- const std::wstring filenameStr(filename);
-
- ComPtr<IShellFolder> desktopFolder;
- ZEN_COM_CHECK(::SHGetDesktopFolder(desktopFolder.init())); //throw SysError
- ZEN_COM_ASSERT(desktopFolder); //throw SysError -> better safe than sorry?
-
- PIDLIST_RELATIVE pidlFolder = nullptr;
- {
- std::wstring pathName = beforeLast(filenameStr, L'\\');
- ZEN_COM_CHECK(desktopFolder->ParseDisplayName(nullptr, // [in] HWND hwnd,
- nullptr, // [in] IBindCtx *pbc,
- const_cast<LPWSTR>(pathName.c_str()), // [in] LPWSTR pszDisplayName,
- nullptr, // [out] ULONG *pchEaten,
- &pidlFolder, // [out] PIDLIST_RELATIVE* ppidl,
- nullptr)); // [in, out] ULONG *pdwAttributes
- }
- ZEN_COM_ASSERT(pidlFolder);
- ZEN_ON_SCOPE_EXIT(::ILFree(pidlFolder)); //older version: ::CoTaskMemFree
-
- ComPtr<IShellFolder> imageFolder;
- ZEN_COM_CHECK(desktopFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl,
- nullptr, // [in] IBindCtx *pbc,
- IID_PPV_ARGS(imageFolder.init())));
- ZEN_COM_ASSERT(imageFolder);
-
- PIDLIST_RELATIVE pidImage = nullptr;
- {
- std::wstring shortName = afterLast(filenameStr, L'\\');
- ZEN_COM_CHECK(imageFolder->ParseDisplayName(nullptr, // [in] HWND hwnd,
- nullptr, // [in] IBindCtx *pbc,
- const_cast<LPWSTR>(shortName.c_str()), // [in] LPWSTR pszDisplayName,
- nullptr, // [out] ULONG *pchEaten,
- &pidImage, // [out] PIDLIST_RELATIVE *ppidl,
- nullptr)); // [in, out] ULONG *pdwAttributes
- }
- ZEN_COM_ASSERT(pidImage);
- ZEN_ON_SCOPE_EXIT(::ILFree(pidImage)); //older version: ::CoTaskMemFree
-
- ComPtr<IExtractImage> extractImage;
- ZEN_COM_CHECK(imageFolder->GetUIObjectOf(nullptr, // [in] HWND hwndOwner,
- 1, // [in] UINT cidl,
- reinterpret_cast<PCUITEMID_CHILD_ARRAY>(&pidImage), // [in] PCUITEMID_CHILD_ARRAY apidl,
- //this is where STRICT_TYPED_ITEMIDS gets us ;)
- IID_IExtractImage, // [in] REFIID riid,
- nullptr, // [in, out] UINT *rgfReserved,
- reinterpret_cast<void**>(extractImage.init()))); // [out] void **ppv
- ZEN_COM_ASSERT(extractImage);
-
- {
- wchar_t pathBuffer[MAX_PATH];
- DWORD priority = 0;
- const SIZE prgSize = { requestedSize, requestedSize };
- DWORD clrDepth = 32; //"recommended color depth"
- DWORD flags = IEIFLAG_SCREEN | IEIFLAG_OFFLINE;
-
- ZEN_COM_CHECK(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
- }
-
- HBITMAP bitmap = nullptr;
- ZEN_COM_CHECK(extractImage->Extract(&bitmap));
- ZEN_COM_ASSERT(bitmap);
- ZEN_ON_SCOPE_EXIT(::DeleteObject(bitmap));
-
- return convertToImageData(bitmap); //throw SysError, pass ownership
-}
-
-
-const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup
-
-//caller takes ownership!
-const thumb::ImageData* getIconByIndex_impl(int iconIndex, thumb::IconSizeType st) //throw SysError
-{
- //Note:
- //- using IExtractIcon::Extract is *no* alternative, just as ::SHGetFileInfo(), it only supports small (16x16) and large (32x32) icons
- //- IShellItemImageFactory::GetImage requires Vista or later
-
- using namespace thumb;
- int requestedSize = 16;
- int shilIconType = SHIL_SMALL; //16x16, size can be customized by the user.
- {
- if (!wereVistaOrLater && //XP doesn't have jumbo icons
- (st == ICON_SIZE_128 ||
- st == ICON_SIZE_256))
- st = ICON_SIZE_48;
-
- switch (st)
- {
- case ICON_SIZE_16:
- break;
- case ICON_SIZE_32:
- requestedSize = 32;
- shilIconType = SHIL_LARGE; //32x32, may be 48x48 if "Use large icon" option is set in Display Properties
- break;
- case ICON_SIZE_48:
- requestedSize = 48;
- shilIconType = SHIL_EXTRALARGE; //48x48, size can be customized by the user.
- break;
- case ICON_SIZE_128:
- requestedSize = 128;
- shilIconType = SHIL_JUMBO; //256x256 pixels -> scale down!
- break;
- case ICON_SIZE_256:
- requestedSize = 256;
- shilIconType = SHIL_JUMBO; //256x256 pixels; Vista and later only
- break;
- }
- }
-
- ComPtr<IImageList> imageList; //perf: 0,12 µs only to get the image list
- ZEN_COM_CHECK(::SHGetImageList(shilIconType, //__in int iImageList,
- IID_PPV_ARGS(imageList.init())));
- ZEN_COM_ASSERT(imageList);
-
- int srcWidth = 0;
- int srcHeight = 0;
- ZEN_COM_CHECK(imageList->GetIconSize(&srcWidth, &srcHeight));
-
- int targetWidth = srcWidth;
- int targetHeight = srcHeight;
- bool needDownScale = false; //scale down if required (e.g Vista Jumbo icons/user-customized icon sizes)
-
- ZEN_COM_ASSERT(srcWidth > 0 && srcHeight > 0 && requestedSize > 0);
-
- const int maxExtent = std::max(srcWidth, srcHeight);
- if (requestedSize < maxExtent)
- {
- needDownScale = true;
- targetWidth = srcWidth * requestedSize / maxExtent;
- targetHeight = srcHeight * requestedSize / maxExtent;
- }
-
- HDC hScreenDC = ::GetDC(nullptr);
- ZEN_COM_ASSERT(hScreenDC); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::ReleaseDC(nullptr, hScreenDC));
-
- auto createRGBDib = [&](unsigned char** rawBits) -> HBITMAP
- {
- BITMAPINFO bi = {};
- bi.bmiHeader.biSize = sizeof(bi);
- bi.bmiHeader.biWidth = targetWidth;
- bi.bmiHeader.biHeight = -targetHeight; //negative for top left origin
- bi.bmiHeader.biPlanes = 1;
- bi.bmiHeader.biBitCount = 24; //we don't want an alpha channel
- bi.bmiHeader.biCompression = BI_RGB;
-
- return ::CreateDIBSection(hScreenDC, //_In_ HDC hdc,
- &bi, //_In_ const BITMAPINFO *pbmi,
- DIB_RGB_COLORS, //_In_ UINT iUsage,
- reinterpret_cast<VOID**>(rawBits), //_Out_ VOID **ppvBits,
- nullptr, //_In_ HANDLE hSection,
- 0); //_In_ DWORD dwOffset
- };
-
- unsigned char* bitsBlackBg = nullptr;
- HBITMAP bmpBlackBg = createRGBDib(&bitsBlackBg);
- ZEN_COM_ASSERT(bmpBlackBg); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteObject(bmpBlackBg););
- ZEN_COM_ASSERT(bitsBlackBg); //check after bmpBlackBg is owned by us
-
- HDC memDC = ::CreateCompatibleDC(hScreenDC);
- ZEN_COM_ASSERT(memDC); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteDC(memDC));
-
- HGDIOBJ hgdiOld = ::SelectObject(memDC, bmpBlackBg);
- ZEN_COM_ASSERT(hgdiOld); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::SelectObject(memDC, hgdiOld));
-
- IMAGELISTDRAWPARAMS drawParams = {};
- drawParams.cbSize = sizeof(drawParams);
- drawParams.hdcDst = memDC;
- drawParams.i = iconIndex;
- drawParams.rgbBk = 0x000000; //black
- drawParams.fStyle = ILD_NORMAL;
- //other flags: http://msdn.microsoft.com/en-us/library/windows/desktop/bb775230(v=vs.85).aspx
-
- if (needDownScale)
- {
- drawParams.fStyle |= ILD_SCALE;
- drawParams.cx = targetWidth;
- drawParams.cy = targetHeight;
- }
-
- //IDO_SHGIOI_LINK does not draw properly in some cases:
- //Win7: draws link overlay *twice* if SHIL_JUMBO is requested, but icon does not have this size
- //XP: drawing IDO_SHGIOI_LINK generally draws corrupted icons and links
- //if (addShortcutOverlay)
- //{
- // int linkOverlay = ::SHGetIconOverlayIndex(nullptr, IDO_SHGIOI_LINK); //-1 on error
- // if (linkOverlay != -1)
- // {
- // //int imgIndex = 0;
- // //if (SUCCEEDED(imageList->GetOverlayImage(linkOverlay, &imgIndex)))
- // //{
- // // drawParams.i = imgIndex;
- // // ZEN_COM_CHECK(imageList->Draw(&drawParams));
- // //}
-
- // drawParams.fStyle |= INDEXTOOVERLAYMASK(linkOverlay);
- // }
- //}
-
- ZEN_COM_CHECK(imageList->Draw(&drawParams));
-
- //-----------------------------------------------
- //we draw the icon twice on different backgrounds to extract the alpha channel:
- //- IImageList::GetIcon doesn't properly render SHIL_JUMBO for icons that don't have jumbo sizes, but IImageList::Draw does!
- //- minor: each HICON consumes 3 GDI handles
- //- IImageList::Draw does not reliably support alpha channel on device context (Windows XP)
- //- wxBitmap created from HBITMAP is very unreliable; often drawn incorrectly by wxDC::DrawBitmap => support wxImage instead
-
- unsigned char* bitsWhiteBg = nullptr;
- HBITMAP bmpWhiteBg = createRGBDib(&bitsWhiteBg);
- ZEN_COM_ASSERT(bmpWhiteBg); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::DeleteObject(bmpWhiteBg));
- ZEN_COM_ASSERT(bitsWhiteBg); //check after bmpWhiteBg is owned by us
-
- HGDIOBJ hgdiOld2 = ::SelectObject(memDC, bmpWhiteBg);
- ZEN_COM_ASSERT(hgdiOld2); //no documented extended error info
- ZEN_ON_SCOPE_EXIT(::SelectObject(memDC, hgdiOld2));
-
- drawParams.rgbBk = 0xFFFFFF; //white
-
- ZEN_COM_CHECK(imageList->Draw(&drawParams));
-
- //#####################################################################################
-
- //"Access to the bitmap must be synchronized. [...]. This applies to any use of the pointer to the bitmap bit values."
- /*bool rv = */
- ::GdiFlush();
-
- ImageData* imgOut = allocImageData(targetWidth, targetHeight); //throw SysError
- ScopeGuard guardImgData = zen::makeGuard([&] { releaseImageData_impl(imgOut); });
-
- unsigned char* rgbPtr = imgOut->rgb;
- unsigned char* alphaPtr = imgOut->alpha;
-
- for (int i = 0; i < targetWidth * targetHeight; ++i)
- {
- unsigned char b_black = *bitsBlackBg++;
- unsigned char g_black = *bitsBlackBg++;
- unsigned char r_black = *bitsBlackBg++;
-
- unsigned char b_white = *bitsWhiteBg++;
- unsigned char g_white = *bitsWhiteBg++;
- unsigned char r_white = *bitsWhiteBg++;
-
- const int tmp = 255 + r_black - r_white + //mixed mode arithmetics!
- 255 + g_black - g_white +
- 255 + b_black - b_white;
- unsigned char alpha = static_cast<unsigned char>(numeric::confineCpy(tmp / 3, 0, 255));
-
- auto calcColor = [&](unsigned char c_black, unsigned char c_white)
- {
- return static_cast<unsigned char>(tmp == 0 ? 0 : numeric::confineCpy
- (255 * (3 * (-255 + c_white + c_black) + tmp) / (2 * tmp), //mixed mode arithmetics!
- 0, 255));
- };
-
- *rgbPtr++ = calcColor(r_black, r_white);
- *rgbPtr++ = calcColor(g_black, g_white);
- *rgbPtr++ = calcColor(b_black, b_white);
- *alphaPtr++ = alpha;
- }
-
- guardImgData.dismiss();
- return imgOut;
-}
-}
-
-
-const thumb::ImageData* thumb::getThumbnail(const wchar_t* filename, int requestedSize) //return 0 on failure, caller takes ownership!
-{
- try
- {
- return getThumbnail_impl(filename, requestedSize); //throw SysError
- }
- catch (const SysError&)
- {
- return nullptr;
- }
-}
-
-
-const thumb::ImageData* thumb::getIconByIndex(int iconIndex, thumb::IconSizeType st) //return 0 on failure, caller takes ownership!
-{
- try
- {
- return getIconByIndex_impl(iconIndex, st); //throw SysError
- }
- catch (const SysError&)
- {
- return nullptr;
- }
-}
-
-
-void thumb::releaseImageData(const thumb::ImageData* id)
-{
- releaseImageData_impl(id);
-}
bgstack15