/* * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: GPL-2.0-or-later */ // global compilation flag configuring windows sdk headers // preventing inclusion of min and max macros clashing with #define NOMINMAX 1 // override byte to prevent clashes with #define byte win_byte_override #include // gdi plus requires Windows.h // ...includes for other windows header that may use byte... // Define min max macros required by GDI+ headers. #ifndef max #define max(a, b) (((a) > (b)) ? (a) : (b)) #else #error max macro is already defined #endif #ifndef min #define min(a, b) (((a) < (b)) ? (a) : (b)) #else #error min macro is already defined #endif #include // Undefine min max macros so they won't collide with header content. #undef min #undef max // Undefine byte macros so it won't collide with header content. #undef byte #include "thumbnailprovider.h" #include #include #include extern long dllObjectsCount; namespace VfsShellExtensions { std::pair hBitmapAndAlphaTypeFromData(const QByteArray &thumbnailData) { if (thumbnailData.isEmpty()) { return {NULL, WTSAT_UNKNOWN}; } Gdiplus::Bitmap *gdiPlusBitmap = nullptr; ULONG_PTR gdiPlusToken; Gdiplus::GdiplusStartupInput gdiPlusStartupInput; if (Gdiplus::GdiplusStartup(&gdiPlusToken, &gdiPlusStartupInput, nullptr) != Gdiplus::Status::Ok) { return {NULL, WTSAT_UNKNOWN}; } const auto handleFailure = [gdiPlusToken]() -> std::pair { Gdiplus::GdiplusShutdown(gdiPlusToken); return {NULL, WTSAT_UNKNOWN}; }; const std::vector bitmapData(thumbnailData.begin(), thumbnailData.end()); auto const stream{::SHCreateMemStream(&bitmapData[0], static_cast(bitmapData.size()))}; if (!stream) { return handleFailure(); } gdiPlusBitmap = Gdiplus::Bitmap::FromStream(stream); auto hasAlpha = false; HBITMAP hBitmap = NULL; if (gdiPlusBitmap) { hasAlpha = Gdiplus::IsAlphaPixelFormat(gdiPlusBitmap->GetPixelFormat()); if (gdiPlusBitmap->GetHBITMAP(Gdiplus::Color(0, 0, 0), &hBitmap) != Gdiplus::Status::Ok) { return handleFailure(); } } Gdiplus::GdiplusShutdown(gdiPlusToken); return {hBitmap, hasAlpha ? WTSAT_ARGB : WTSAT_RGB}; } ThumbnailProvider::ThumbnailProvider() : _referenceCount(1) { InterlockedIncrement(&dllObjectsCount); } ThumbnailProvider::~ThumbnailProvider() { InterlockedDecrement(&dllObjectsCount); } IFACEMETHODIMP ThumbnailProvider::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(ThumbnailProvider, IInitializeWithItem), QITABENT(ThumbnailProvider, IThumbnailProvider), {0}, }; return QISearch(this, qit, riid, ppv); } IFACEMETHODIMP_(ULONG) ThumbnailProvider::AddRef() { return InterlockedIncrement(&_referenceCount); } IFACEMETHODIMP_(ULONG) ThumbnailProvider::Release() { const auto refCount = InterlockedDecrement(&_referenceCount); if (refCount == 0) { delete this; } return refCount; } IFACEMETHODIMP ThumbnailProvider::Initialize(_In_ IShellItem *item, _In_ DWORD mode) { HRESULT hresult = item->QueryInterface(__uuidof(_shellItem), reinterpret_cast(&_shellItem)); if (FAILED(hresult)) { return hresult; } LPWSTR pszName = NULL; hresult = _shellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszName); if (FAILED(hresult)) { return hresult; } _shellItemPath = QString::fromWCharArray(pszName); return S_OK; } IFACEMETHODIMP ThumbnailProvider::GetThumbnail(_In_ UINT cx, _Out_ HBITMAP *bitmap, _Out_ WTS_ALPHATYPE *alphaType) { *bitmap = nullptr; *alphaType = WTSAT_UNKNOWN; const auto thumbnailDataReceived = _thumbnailProviderIpc.fetchThumbnailForFile(_shellItemPath, QSize(cx, cx)); if (thumbnailDataReceived.isEmpty()) { return E_FAIL; } const auto bitmapAndAlphaType = hBitmapAndAlphaTypeFromData(thumbnailDataReceived); if (!bitmapAndAlphaType.first) { return E_FAIL; } *bitmap = bitmapAndAlphaType.first; *alphaType = bitmapAndAlphaType.second; return S_OK; } }