Skip to content

Instantly share code, notes, and snippets.

@smourier
Created December 22, 2023 06:48
Show Gist options
  • Save smourier/9b546fe2b8fd17491af93331e72fe691 to your computer and use it in GitHub Desktop.
Save smourier/9b546fe2b8fd17491af93331e72fe691 to your computer and use it in GitHub Desktop.
Some WIC Bitmap tests
#include <windows.h>
#include <atlbase.h>
#include <stdio.h>
#include <wincodec.h>
#define HRASSERT(x) ATLASSERT(SUCCEEDED(x))
HRESULT ConvertBitmapTo256(PCWSTR input, PCWSTR output, REFGUID outputFormat)
{
HRESULT hr = S_OK;
CComPtr<IWICImagingFactory> fac;
HRASSERT(fac.CoCreateInstance(CLSID_WICImagingFactory));
// or use fac->CreateBitmapFromHBITMAP(...) to start from HBITMAP
CComPtr<IWICBitmapDecoder> decoder;
HRASSERT(fac->CreateDecoderFromFilename(input, nullptr, GENERIC_READ, WICDecodeOptions::WICDecodeMetadataCacheOnDemand, &decoder));
// create a converter
CComPtr<IWICFormatConverter> converter;
HRASSERT(fac->CreateFormatConverter(&converter));
// get first frame (bmp only have one)
CComPtr<IWICBitmapFrameDecode> frame;
HRASSERT(decoder->GetFrame(0, &frame));
// you can use your custom palette too (4th parameter)
HRASSERT(converter->Initialize(frame, GUID_WICPixelFormat8bppIndexed, WICBitmapDitherTypeOrdered16x16, nullptr, 0, WICBitmapPaletteTypeFixedHalftone216));
// you can do B&W too, note not all combination of dithering & palette work, these works
//converter->Initialize(frame, GUID_WICPixelFormatBlackWhite, WICBitmapDitherTypeErrorDiffusion, nullptr, 0, WICBitmapPaletteTypeFixedBW);
//converter->Initialize(frame, GUID_WICPixelFormatBlackWhite, WICBitmapDitherTypeOrdered16x16, nullptr, 0, WICBitmapPaletteTypeFixedHalftone216);
// save back, create encoder
CComPtr<IWICBitmapEncoder> encoder;
HRASSERT(fac->CreateEncoder(outputFormat, nullptr, &encoder));
// create output stream, here a file stream, but any stream can do (SHCreateMemStream for mem stream)
CComPtr<IStream> stream;
SHCreateStreamOnFile(output, STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, &stream);
HRASSERT(encoder->Initialize(stream, WICBitmapEncoderNoCache));
// create new frame, write and commit
CComPtr<IWICBitmapFrameEncode> frameEncode;
HRASSERT(encoder->CreateNewFrame(&frameEncode, nullptr));
HRASSERT(frameEncode->Initialize(nullptr));
HRASSERT(frameEncode->WriteSource(converter, nullptr));
HRASSERT(frameEncode->Commit());
HRASSERT(encoder->Commit());
return hr;
}
HRESULT ConvertHBITMAPTo256(HBITMAP input, PCWSTR output, REFGUID outputFormat)
{
HRESULT hr = S_OK;
CComPtr<IWICImagingFactory> fac;
HRASSERT(fac.CoCreateInstance(CLSID_WICImagingFactory));
CComPtr<IWICBitmap> bitmap;
HRASSERT(fac->CreateBitmapFromHBITMAP(input, nullptr, WICBitmapUseAlpha, &bitmap));
// create a converter
CComPtr<IWICFormatConverter> converter;
HRASSERT(fac->CreateFormatConverter(&converter));
// you can use your custom palette too (4th parameter)
HRASSERT(converter->Initialize(bitmap, GUID_WICPixelFormat8bppIndexed, WICBitmapDitherTypeOrdered16x16, nullptr, 0, WICBitmapPaletteTypeFixedHalftone216));
// you can do B&W output too, note not all combination of dithering & palette work, these works
//converter->Initialize(frame, GUID_WICPixelFormatBlackWhite, WICBitmapDitherTypeErrorDiffusion, nullptr, 0, WICBitmapPaletteTypeFixedBW);
//converter->Initialize(frame, GUID_WICPixelFormatBlackWhite, WICBitmapDitherTypeOrdered16x16, nullptr, 0, WICBitmapPaletteTypeFixedHalftone216);
// save back, create encoder
CComPtr<IWICBitmapEncoder> encoder;
HRASSERT(fac->CreateEncoder(outputFormat, nullptr, &encoder));
// create output stream, here a file stream, but any stream can do (SHCreateMemStream for mem stream)
CComPtr<IStream> stream;
SHCreateStreamOnFile(output, STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, &stream);
HRASSERT(encoder->Initialize(stream, WICBitmapEncoderNoCache));
// create new frame, write and commit
CComPtr<IWICBitmapFrameEncode> frameEncode;
HRASSERT(encoder->CreateNewFrame(&frameEncode, nullptr));
HRASSERT(frameEncode->Initialize(nullptr));
HRASSERT(frameEncode->WriteSource(converter, nullptr));
HRASSERT(frameEncode->Commit());
HRASSERT(encoder->Commit());
return hr;
}
// *warning* not sure this one works!
HRESULT ConvertHBITMAPTo256(HBITMAP input, HBITMAP* output, HPALETTE *palette)
{
HRESULT hr = S_OK;
CComPtr<IWICImagingFactory> fac;
HRASSERT(fac.CoCreateInstance(CLSID_WICImagingFactory));
CComPtr<IWICBitmap> bitmap;
HRASSERT(fac->CreateBitmapFromHBITMAP(input, nullptr, WICBitmapUseAlpha, &bitmap));
// create a converter
CComPtr<IWICFormatConverter> converter;
HRASSERT(fac->CreateFormatConverter(&converter));
// here we use 256 color format with predefined palette, but one can use a custom palette too (4th parameter)
HRASSERT(converter->Initialize(bitmap, GUID_WICPixelFormat8bppIndexed, WICBitmapDitherTypeOrdered16x16, nullptr, 0, WICBitmapPaletteTypeFixedHalftone256));
// one can do B&W output too, note not all combination of dithering & palette work, these works
//converter->Initialize(frame, GUID_WICPixelFormatBlackWhite, WICBitmapDitherTypeErrorDiffusion, nullptr, 0, WICBitmapPaletteTypeFixedBW);
//converter->Initialize(frame, GUID_WICPixelFormatBlackWhite, WICBitmapDitherTypeOrdered16x16, nullptr, 0, WICBitmapPaletteTypeFixedHalftone216);
// save back, create encoder
CComPtr<IWICBitmapEncoder> encoder;
HRASSERT(fac->CreateEncoder(GUID_ContainerFormatBmp, nullptr, &encoder));
// create output stream in memory
CComPtr<IStream> stream;
HRASSERT(CreateStreamOnHGlobal(nullptr, TRUE, &stream));
HRASSERT(encoder->Initialize(stream, WICBitmapEncoderNoCache));
// create new frame, write and commit
CComPtr<IWICBitmapFrameEncode> frame;
HRASSERT(encoder->CreateNewFrame(&frame, nullptr));
HRASSERT(frame->Initialize(nullptr));
HRASSERT(frame->WriteSource(converter, nullptr));
HRASSERT(frame->Commit());
HRASSERT(encoder->Commit());
HGLOBAL h;
HRASSERT(GetHGlobalFromStream(stream, &h));
auto file = (BITMAPFILEHEADER*)GlobalLock(h);
auto info = (BITMAPINFOHEADER*)((LPBYTE)file + sizeof(BITMAPFILEHEADER));
auto bits = (LPBYTE)file + file->bfOffBits;
*output = CreateBitmap(info->biWidth, info->biHeight, info->biPlanes, info->biBitCount, bits);
if (*output)
{
// bit of a hack, we write the memory file so we don't have to allocate for the palette
// but we won't need it anymore
auto lp = (LOGPALETTE*)&info->biClrImportant;
lp->palNumEntries = info->biClrUsed;
lp->palVersion = 0x300;
*palette = CreatePalette(lp);
if (!*palette)
{
hr = E_FAIL;
DeleteObject(*output);
}
}
else
{
hr = E_FAIL;
}
GlobalUnlock(h);
return hr;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment