WIC(Windowsイメージングコンポーネント)を使って画像ファイルからDIBセクションを作成する方法です。WICはGDIでは扱いにくいアイコンやGIF、PNGを統一された方法でビットマップに変換できます。
以下のコードはC++20で動作するウィンドウアプリケーションで、ドロップした画像ファイルをグレースケールに変換してウィンドウ中央に表示します。MicrosoftのWILを使用しています。
WIC自体の使い方に不慣れなので間違えていたらご指摘ください。
#include <bit> #include <string> #include <vector> #define STRICT #define NOMINMAX #include <Windows.h> #include <wincodec.h> #include "wil/com.h" const WCHAR g_MainWindowClassName[] = L"MainWindow"; const WCHAR g_MainWindowTitle[] = L"Project1"; // COMの初期化・解放 auto coinit = wil::CoInitializeEx(); HINSTANCE g_hinstApp{}; wil::com_ptr<IWICImagingFactory> g_pwicImgFactory; wil::unique_hbitmap g_hbmp{}; UINT g_bmpWidth{}; UINT g_bmpHeight{}; LPVOID g_bmpPixels{}; static inline constexpr UINT GetBitmapStride(UINT width, UINT bits) noexcept { return ((width * bits / 8 + 3) / 4) * 4; } static HBITMAP CreateDIBitmapFromFileByWIC(IWICImagingFactory* pfactory, LPCWSTR filename, UINT frame, REFWICPixelFormatGUID formatGuid, UINT& w, UINT& h, LPVOID* pppixels); static inline constexpr LONG GetWidth(const RECT& rc) noexcept { return rc.right - rc.left; } static inline constexpr LONG GetHeight(const RECT& rc) noexcept { return rc.bottom - rc.top; } static inline constexpr SIZE GetSize(const RECT& rc) noexcept { return { GetWidth(rc), GetHeight(rc) }; } static inline constexpr RECT SetSize(RECT rc, const SIZE& size) { rc.right = rc.left + size.cx; rc.bottom = rc.top + size.cy; return rc; } static inline constexpr SIZE FitSize(SIZE size, UINT maxWidth, UINT maxHeight) noexcept { if (maxWidth == 0 || maxHeight == 0) return {}; if (size.cx != maxWidth) { size.cy = (UINT)(size.cy / (double)size.cx * maxWidth); size.cx = maxWidth; } if (size.cy != maxHeight) { size.cx = (UINT)(size.cx / (double)size.cy * maxHeight); size.cy = maxHeight; } return size; } static inline constexpr SIZE FitSize(SIZE size, const SIZE& maxSize) noexcept { return FitSize(size, maxSize.cx, maxSize.cy); } static inline constexpr RECT CenterRect(SIZE size, const RECT& whole) noexcept { RECT rc; rc.left = (GetWidth(whole) - size.cx) / 2; rc.top = (GetHeight(whole) - size.cy) / 2; rc.right = rc.left + size.cx; rc.bottom = rc.top + size.cy; return rc; } LRESULT CALLBACK MainWindow_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_CREATE: DragAcceptFiles(hwnd, TRUE); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; case WM_DROPFILES: { auto hdrop = std::bit_cast<HDROP>(wParam); auto required = DragQueryFileW(hdrop, 0, nullptr, 0); std::wstring s(required, L'\0'); DragQueryFileW(hdrop, 0, s.data(), required + 1); try { g_hbmp.reset(CreateDIBitmapFromFileByWIC(g_pwicImgFactory.get(), s.data(), 0, GUID_WICPixelFormat24bppBGR, g_bmpWidth, g_bmpHeight, &g_bmpPixels)); // TODO:以下をコメントアウトするとグレースケール処理を無効化できます。 auto stride = GetBitmapStride(g_bmpWidth, 24); UINT yx = 0; auto ppixels = std::bit_cast<BYTE*>(g_bmpPixels); for (UINT y = 0; y < g_bmpHeight; y++) { UINT z = yx; for (UINT x = 0; x < g_bmpWidth; x++) { auto r = ppixels[z + 2]; auto g = ppixels[z + 1]; auto b = ppixels[z + 0]; auto gray = (r + g + b) / 3; ppixels[z + 2] = gray; ppixels[z + 1] = gray; ppixels[z + 0] = gray; z += 3; } yx += stride; } } catch (const std::exception&) { g_hbmp.reset(); g_bmpPixels = nullptr; MessageBoxW(hwnd, L"JPEGファイルの読み込みに失敗しました。", L"エラー", MB_ICONINFORMATION); } DragFinish(hdrop); InvalidateRect(hwnd, nullptr, TRUE); } return 0; case WM_PAINT: { PAINTSTRUCT ps; BeginPaint(hwnd, &ps); // ウィンドウの大きさに合わせて描画する。 wil::unique_hdc hdcSrc(CreateCompatibleDC(ps.hdc)); SelectObject(hdcSrc.get(), g_hbmp.get()); RECT rcClient; GetClientRect(hwnd, &rcClient); SIZE bmpSize = FitSize(SIZE(g_bmpWidth, g_bmpHeight), GetSize(rcClient)); auto center = CenterRect(bmpSize, rcClient); SetStretchBltMode(ps.hdc, HALFTONE); // 画質をきれいにする。 StretchBlt(ps.hdc, center.left, center.top, GetWidth(center), GetHeight(center), hdcSrc.get(), 0, 0, g_bmpWidth, g_bmpHeight, SRCCOPY); EndPaint(hwnd, &ps); } return 0; } return DefWindowProcW(hwnd, msg, wParam, lParam); } int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int cmdShow) { g_hinstApp = hInstance; // WICの準備 g_pwicImgFactory = wil::CoCreateInstance<IWICImagingFactory>(CLSID_WICImagingFactory); // メインウィンドウの登録と作成 WNDCLASSEXW wcex{}; wcex.cbSize = sizeof(WNDCLASSEXW); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = MainWindow_WndProc; wcex.hInstance = g_hinstApp; wcex.hIcon = LoadIconW(nullptr, IDI_APPLICATION); wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW); wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1); wcex.lpszClassName = g_MainWindowClassName; if (RegisterClassExW(&wcex) == 0) return -1; auto hwndMain = CreateWindowExW( WS_EX_OVERLAPPEDWINDOW, g_MainWindowClassName, g_MainWindowTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, g_hinstApp, nullptr); ShowWindow(hwndMain, cmdShow); UpdateWindow(hwndMain); MSG msg; int iret = 0; while ((iret = GetMessage(&msg, nullptr, 0, 0) != 0 && iret != -1)) { TranslateMessage(&msg); DispatchMessageW(&msg); } return iret; } static HBITMAP CreateDIBitmapFromFileByWIC(IWICImagingFactory* pfactory, LPCWSTR filename, UINT frame, REFWICPixelFormatGUID formatGuid, UINT& w, UINT& h, LPVOID* pppixels) { THROW_IF_NULL_ALLOC(pfactory); // ビットマップの読み込み wil::com_ptr<IWICBitmapDecoder> pwicBmpDecoder; wil::com_ptr<IWICBitmapFrameDecode> pframe; THROW_IF_FAILED(pfactory->CreateDecoderFromFilename( filename, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &pwicBmpDecoder)); THROW_IF_FAILED(pwicBmpDecoder->GetFrame(frame, &pframe)); // formatGuid形式への変換 wil::com_ptr<IWICFormatConverter> pconverter; wil::com_ptr<IWICBitmap> pbmp2; THROW_IF_FAILED(pfactory->CreateFormatConverter(&pconverter)); THROW_IF_FAILED(pconverter->Initialize( pframe.get(), formatGuid, WICBitmapDitherTypeNone, nullptr, 0, WICBitmapPaletteTypeCustom)); THROW_IF_FAILED(pfactory->CreateBitmapFromSource(pconverter.get(), WICBitmapCacheOnDemand, &pbmp2)); // 上下反転 wil::com_ptr<IWICBitmapFlipRotator> protator; THROW_IF_FAILED(pfactory->CreateBitmapFlipRotator(&protator)); THROW_IF_FAILED(protator->Initialize(pbmp2.get(), WICBitmapTransformFlipVertical)); // DIBセクションの作成 THROW_IF_FAILED(protator->GetSize(&w, &h)); auto stride = GetBitmapStride(w, 24); BITMAPINFO bmi = { {sizeof(BITMAPINFOHEADER), w, h, 1, 24, BI_RGB} }; auto hbmp = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, pppixels, nullptr, 0); WICRect rc{ 0, 0, w, h }; auto hr = protator->CopyPixels(&rc, stride, stride * h, std::bit_cast<BYTE*>(*pppixels)); if (SUCCEEDED(hr)) { return hbmp; } else { DeleteObject(hbmp); THROW_HR(hr); } }
参考