diff --git a/src/Tools/thumbs/ThumbnailProvider/ThumbnailProvider.cpp b/src/Tools/thumbs/ThumbnailProvider/ThumbnailProvider.cpp index 0263805c04..162263e467 100644 --- a/src/Tools/thumbs/ThumbnailProvider/ThumbnailProvider.cpp +++ b/src/Tools/thumbs/ThumbnailProvider/ThumbnailProvider.cpp @@ -29,8 +29,132 @@ #include #include #include -#include -#include +#include +#include +#pragma comment(lib, "WindowsCodecs.lib") + +// The functions +// * CreateStreamOnResource +// * LoadBitmapFromStream +// * CreateHBITMAP +// are taken from https://faithlife.codes/blog/2008/09/displaying_a_splash_screen_with_c_part_i/ +// The code is released under an MIT-style license + +// Creates a stream object initialized with the data from an executable resource. +IStream* CreateStreamOnResource(void* buffer, size_t length) +{ + // initialize return value + IStream* ipStream = NULL; + + // allocate memory to hold the resource data + HGLOBAL hgblResourceData = GlobalAlloc(GMEM_MOVEABLE, length); + if (hgblResourceData == NULL) + goto Return; + + // get a pointer to the allocated memory + LPVOID pvResourceData = GlobalLock(hgblResourceData); + if (pvResourceData == NULL) + goto FreeData; + + // copy the data from the resource to the new memory block + CopyMemory(pvResourceData, buffer, length); + GlobalUnlock(hgblResourceData); + + // create a stream on the HGLOBAL containing the data + + if (SUCCEEDED(CreateStreamOnHGlobal(hgblResourceData, TRUE, &ipStream))) + goto Return; + +FreeData: + // couldn't create stream; free the memory + + GlobalFree(hgblResourceData); + +Return: + // no need to unlock or free the resource + return ipStream; +} + +IWICBitmapSource* LoadBitmapFromStream(IStream* ipImageStream) +{ + // initialize return value + IWICBitmapSource* ipBitmap = NULL; + + // load WIC's PNG decoder + IWICBitmapDecoder* ipDecoder = NULL; + if (FAILED(CoCreateInstance(CLSID_WICPngDecoder, NULL, CLSCTX_INPROC_SERVER, __uuidof(ipDecoder), reinterpret_cast(&ipDecoder)))) + goto Return; + + // load the PNG + if (FAILED(ipDecoder->Initialize(ipImageStream, WICDecodeMetadataCacheOnLoad))) + goto ReleaseDecoder; + + // check for the presence of the first frame in the bitmap + UINT nFrameCount = 0; + if (FAILED(ipDecoder->GetFrameCount(&nFrameCount)) || nFrameCount != 1) + goto ReleaseDecoder; + + // load the first frame (i.e., the image) + IWICBitmapFrameDecode* ipFrame = NULL; + if (FAILED(ipDecoder->GetFrame(0, &ipFrame))) + goto ReleaseDecoder; + + // convert the image to 32bpp BGRA format with pre-multiplied alpha + // (it may not be stored in that format natively in the PNG resource, + // but we need this format to create the DIB to use on-screen) + WICConvertBitmapSource(GUID_WICPixelFormat32bppPBGRA, ipFrame, &ipBitmap); + ipFrame->Release(); + +ReleaseDecoder: + ipDecoder->Release(); +Return: + return ipBitmap; +} + +HBITMAP CreateHBITMAP(IWICBitmapSource* ipBitmap) +{ + // initialize return value + HBITMAP hbmp = NULL; + + // get image attributes and check for valid image + UINT width = 0; + UINT height = 0; + if (FAILED(ipBitmap->GetSize(&width, &height)) || width == 0 || height == 0) + goto Return; + + // prepare structure giving bitmap information (negative height indicates a top-down DIB) + BITMAPINFO bminfo; + ZeroMemory(&bminfo, sizeof(bminfo)); + bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bminfo.bmiHeader.biWidth = width; + bminfo.bmiHeader.biHeight = -((LONG)height); + bminfo.bmiHeader.biPlanes = 1; + bminfo.bmiHeader.biBitCount = 32; + bminfo.bmiHeader.biCompression = BI_RGB; + + // create a DIB section that can hold the image + void* pvImageBits = NULL; + HDC hdcScreen = GetDC(NULL); + hbmp = CreateDIBSection(hdcScreen, &bminfo, DIB_RGB_COLORS, &pvImageBits, NULL, 0); + ReleaseDC(NULL, hdcScreen); + if (hbmp == NULL) + goto Return; + + // extract the image into the HBITMAP + + const UINT cbStride = width * 4; + const UINT cbImage = cbStride * height; + if (FAILED(ipBitmap->CopyPixels(NULL, cbStride, cbImage, static_cast(pvImageBits)))) + { + // couldn't extract image; delete HBITMAP + + DeleteObject(hbmp); + hbmp = NULL; + } + +Return: + return hbmp; +} CThumbnailProvider::CThumbnailProvider() { @@ -136,11 +260,16 @@ STDMETHODIMP CThumbnailProvider::GetThumbnail(UINT cx, } // pass the memory buffer to CxImage library to create the bitmap handle - CxMemFile mem(&(content[0]),content.size()); - CxImagePNG png; - png.Decode(&mem); - *phbmp = png.MakeBitmap(); - *pdwAlpha = WTSAT_UNKNOWN; + IStream* stream = CreateStreamOnResource(&(content[0]), content.size()); + if (stream) { + IWICBitmapSource* bmpSrc = LoadBitmapFromStream(stream); + stream->Release(); + if (bmpSrc) { + *phbmp = CreateHBITMAP(bmpSrc); + *pdwAlpha = WTSAT_UNKNOWN; + bmpSrc->Release(); + } + } } } catch(...) { diff --git a/src/Tools/thumbs/ThumbnailProvider/ThumbnailProvider.sln b/src/Tools/thumbs/ThumbnailProvider/ThumbnailProvider.sln new file mode 100644 index 0000000000..f95f8943f2 --- /dev/null +++ b/src/Tools/thumbs/ThumbnailProvider/ThumbnailProvider.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31410.357 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FCStdThumbnail", "ThumbnailProvider.vcxproj", "{B4F22D8C-736E-46BF-85FB-259EF73EA0FC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B4F22D8C-736E-46BF-85FB-259EF73EA0FC}.Debug|x64.ActiveCfg = Debug|x64 + {B4F22D8C-736E-46BF-85FB-259EF73EA0FC}.Debug|x64.Build.0 = Debug|x64 + {B4F22D8C-736E-46BF-85FB-259EF73EA0FC}.Debug|x86.ActiveCfg = Debug|Win32 + {B4F22D8C-736E-46BF-85FB-259EF73EA0FC}.Debug|x86.Build.0 = Debug|Win32 + {B4F22D8C-736E-46BF-85FB-259EF73EA0FC}.Release|x64.ActiveCfg = Release|x64 + {B4F22D8C-736E-46BF-85FB-259EF73EA0FC}.Release|x64.Build.0 = Release|x64 + {B4F22D8C-736E-46BF-85FB-259EF73EA0FC}.Release|x86.ActiveCfg = Release|Win32 + {B4F22D8C-736E-46BF-85FB-259EF73EA0FC}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7336F399-F2B2-4F65-A701-52024EB9A7A0} + EndGlobalSection +EndGlobal diff --git a/src/Tools/thumbs/ThumbnailProvider/ThumbnailProvider.vcxproj b/src/Tools/thumbs/ThumbnailProvider/ThumbnailProvider.vcxproj new file mode 100644 index 0000000000..bf1f65f19a --- /dev/null +++ b/src/Tools/thumbs/ThumbnailProvider/ThumbnailProvider.vcxproj @@ -0,0 +1,245 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + FCStdThumbnail + {B4F22D8C-736E-46BF-85FB-259EF73EA0FC} + ThumbnailProvider + Win32Proj + + + + DynamicLibrary + v142 + Unicode + true + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + true + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>16.0.31401.40 + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + true + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + true + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + false + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + false + + + + Disabled + ..\..\..\;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;THUMBNAILPROVIDER_EXPORTS;FreeCADBase_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + + + shlwapi.lib;comctl32.lib;propsys.lib;zlibstatic.lib;%(AdditionalDependencies) + ThumbnailProvider.def + true + Windows + MachineX86 + + + + + X64 + + + Disabled + ..\..\..\;%(AdditionalIncludeDirectories) + WIN64;_DEBUG;_WINDOWS;_USRDLL;THUMBNAILPROVIDER_EXPORTS;FreeCADBase_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + ProgramDatabase + + + shlwapi.lib;comctl32.lib;propsys.lib;zlibstatic.lib;%(AdditionalDependencies) + ThumbnailProvider.def + true + Windows + MachineX64 + + + + + MaxSpeed + true + .;..\..\..\;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;THUMBNAILPROVIDER_EXPORTS;FreeCADBase_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + shlwapi.lib;comctl32.lib;propsys.lib;%(AdditionalDependencies) + ThumbnailProvider.def + true + Windows + true + true + MachineX86 + + + + + X64 + + + MaxSpeed + true + .;..\..\..\;%(AdditionalIncludeDirectories) + WIN64;NDEBUG;_WINDOWS;THUMBNAILPROVIDER_EXPORTS;FreeCADBase_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + shlwapi.lib;comctl32.lib;propsys.lib;zlibstatic.lib;%(AdditionalDependencies) + ThumbnailProvider.def + true + Windows + true + true + MachineX64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file