diff options
Diffstat (limited to 'lib/Thumbnail/thumbnail.cpp')
-rw-r--r-- | lib/Thumbnail/thumbnail.cpp | 486 |
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); -} |