#include #include #include // Adapted from https://faithlife.codes/blog/2008/09/displaying_a_splash_screen_with_c_part_i/ // 1. Get resource as an IStream // 2. Use Windows Imaging Component to load the PNG // 3. Convert the WIC bitmap to an HBITMAP // Why does loading a PNG need to be so difficult? typedef HRESULT (WINAPI *_WICConvertBitmapSource)(REFWICPixelFormatGUID dstFormat, IWICBitmapSource *pISrc, IWICBitmapSource **ppIDst); static _WICConvertBitmapSource $WICConvertBitmapSource; static HGLOBAL GetRawResource(HINSTANCE hInstance, LPWSTR name, LPWSTR type) { HRSRC resource = FindResource(hInstance, name, type); if (!resource) { TRACE(L"FindResource failed: %d", GetLastError()); return NULL; } DWORD resourceSize = SizeofResource(hInstance, resource); HGLOBAL imageHandle = LoadResource(hInstance, resource); if (!imageHandle) { TRACE(L"LoadResource failed: %d", GetLastError()); return NULL; } LPVOID sourceResourceData = LockResource(imageHandle); if (!sourceResourceData) { TRACE(L"LockResource failed: %d", GetLastError()); return NULL; } HGLOBAL resourceDataHandle = GlobalAlloc(GMEM_MOVEABLE, resourceSize); if (!resourceDataHandle) { TRACE(L"GlobalAlloc failed: %d", GetLastError()); return NULL; } LPVOID resourceData = GlobalLock(resourceDataHandle); if (!resourceData) { TRACE(L"GlobalLock failed: %d", GetLastError()); GlobalFree(resourceDataHandle); return NULL; } CopyMemory(resourceData, sourceResourceData, resourceSize); GlobalUnlock(resourceDataHandle); return resourceDataHandle; } static IStream *GetResourceStream(HINSTANCE hInstance, LPWSTR name, LPWSTR type) { IStream *stream; HGLOBAL resource = GetRawResource(hInstance, name, type); if (!resource) { TRACE(L"GetResource failed: %d", GetLastError()); return NULL; } if (SUCCEEDED(CreateStreamOnHGlobal(resource, TRUE, &stream))) { return stream; } GlobalFree(resource); return NULL; } static IWICBitmapSource *GetWICBitmap(IStream *imageStream) { IWICBitmapSource *bitmap; IWICBitmapDecoder *decoder; UINT frameCount; IWICBitmapFrameDecode *frame; if (!SUCCEEDED(CoCreateInstance(&CLSID_WICPngDecoder, NULL, CLSCTX_INPROC_SERVER, &IID_IWICBitmapDecoder, (LPVOID *)&decoder))) { return NULL; } if (!SUCCEEDED(IWICBitmapDecoder_Initialize(decoder, imageStream, WICDecodeMetadataCacheOnLoad))) { goto end; } if (!SUCCEEDED(IWICBitmapDecoder_GetFrameCount(decoder, &frameCount)) || frameCount != 1) { goto end; } if (!SUCCEEDED(IWICBitmapDecoder_GetFrame(decoder, 0, &frame))) { goto end; } $WICConvertBitmapSource(&GUID_WICPixelFormat32bppPBGRA, (IWICBitmapSource *)frame, &bitmap); IWICBitmapFrameDecode_Release(frame); end: IWICBitmapDecoder_Release(decoder); return bitmap; } static HBITMAP GetHBitmapForWICBitmap(IWICBitmapSource *bitmap) { HBITMAP hBitmap; UINT width, height; if (!SUCCEEDED(IWICBitmapSource_GetSize(bitmap, &width, &height)) || width == 0 || height == 0) { return NULL; } 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; void *imageBits; HDC screenDC = GetDC(NULL); hBitmap = CreateDIBSection(screenDC, &bminfo, DIB_RGB_COLORS, &imageBits, NULL, 0); ReleaseDC(NULL, screenDC); if (!hBitmap) { return NULL; } UINT stride = width * 4; UINT imageSize = stride * height; if (!SUCCEEDED(IWICBitmapSource_CopyPixels(bitmap, NULL, stride, imageSize, (BYTE*)imageBits))) { DeleteObject(hBitmap); return NULL; } return hBitmap; } HBITMAP LoadPNGResource(HINSTANCE hInstance, LPWSTR resourceName, LPWSTR resourceType) { if (!$WICConvertBitmapSource) { $WICConvertBitmapSource = (_WICConvertBitmapSource)GetProcAddress(LoadLibrary(L"windowscodecs.dll"), "WICConvertBitmapSource"); if (!$WICConvertBitmapSource) { return NULL; } } IStream *imageStream = GetResourceStream(hInstance, resourceName, resourceType); if (!imageStream) { TRACE(L"GetResourceStream failed: %d", GetLastError()); return NULL; } IWICBitmapSource *bitmap = GetWICBitmap(imageStream); if (!bitmap) { TRACE(L"GetWICBitmap failed: %d", GetLastError()); IStream_Release(imageStream); return NULL; } HBITMAP result = GetHBitmapForWICBitmap(bitmap); IWICBitmapSource_Release(bitmap); IStream_Release(imageStream); return result; } BOOL ScaleAndWriteToBMP(HBITMAP hBitmap, DWORD width, DWORD height, LPWSTR outputPath) { BOOL result = FALSE; if (!hBitmap) { TRACE(L"Null bitmap"); return FALSE; } HDC hdc = GetDC(NULL); HDC hdcMem = CreateCompatibleDC(hdc); HBITMAP scaledBitmap = CreateCompatibleBitmap(hdc, width, height); if (!scaledBitmap) { TRACE(L"CreateCompatibleBitmap failed: %d", GetLastError()); goto end; } BITMAP bmp; if (!GetObject(hBitmap, sizeof(BITMAP), &bmp)) { TRACE(L"GetObject failed: %d", GetLastError()); goto end; } HBITMAP hOld = (HBITMAP)SelectObject(hdcMem, hBitmap); HDC hdcMemScaled = CreateCompatibleDC(hdc); HBITMAP hOldScaled = (HBITMAP)SelectObject(hdcMemScaled, scaledBitmap); SetStretchBltMode(hdcMemScaled, HALFTONE); if (!StretchBlt(hdcMemScaled, 0, 0, width, height, hdcMem, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY)) { TRACE(L"StretchBlt failed: %d", GetLastError()); goto end; } BITMAPINFOHEADER bmih = {0}; bmih.biSize = sizeof(BITMAPINFOHEADER); bmih.biWidth = width; bmih.biHeight = height; bmih.biPlanes = 1; bmih.biBitCount = bmp.bmBitsPixel; bmih.biCompression = BI_RGB; bmih.biSizeImage = ((width * bmp.bmBitsPixel + 31) / 32) * 4 * height; BITMAPFILEHEADER bmfh = {0}; bmfh.bfType = 0x4D42; bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); bmfh.bfSize = bmfh.bfOffBits + bmih.biSizeImage; HGLOBAL handle = GlobalAlloc(GMEM_MOVEABLE, bmih.biSizeImage); if (!handle) { TRACE(L"GlobalAlloc failed: %d", GetLastError()); goto end; } BYTE *bitmapData = (BYTE *)GlobalLock(handle); if (!bitmapData) { TRACE(L"GlobalLock failed: %d", GetLastError()); goto end; } if (!GetDIBits(hdcMemScaled, scaledBitmap, 0, height, bitmapData, (BITMAPINFO *)&bmih, DIB_RGB_COLORS)) { TRACE(L"GetDIBits failed: %d", GetLastError()); goto end; } HANDLE file = CreateFile(outputPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file == INVALID_HANDLE_VALUE) { TRACE(L"CreateFile failed: %d", GetLastError()); goto end; } DWORD written; if (!WriteFile(file, &bmfh, sizeof(BITMAPFILEHEADER), &written, NULL) || !WriteFile(file, &bmih, sizeof(BITMAPINFOHEADER), &written, NULL) || !WriteFile(file, bitmapData, bmih.biSizeImage, &written, NULL)) { TRACE(L"WriteFile failed: %d", GetLastError()); goto end; } result = TRUE; end: if (file && file != INVALID_HANDLE_VALUE) { CloseHandle(file); } if (scaledBitmap) { DeleteObject(scaledBitmap); } if (handle) { GlobalUnlock(handle); GlobalFree(handle); } ReleaseDC(NULL, hdc); DeleteDC(hdcMem); DeleteDC(hdcMemScaled); return result; }