Skip to content

Instantly share code, notes, and snippets.

@smourier
Created April 3, 2023 13:18
Show Gist options
  • Save smourier/5b770d32043121d477a8079ef6be0995 to your computer and use it in GitHub Desktop.
Save smourier/5b770d32043121d477a8079ef6be0995 to your computer and use it in GitHub Desktop.
#define _AFXDLL
#include <WinSock2.h> // MFC...
#include <windows.h>
#include <afxwin.h> // CBitmap
#include <atlbase.h>
#include <atlcom.h> // CComPtr
#include <d2d1.h>
#include <d2d1_3.h> // ID2D1DeviceContext5
#include <wincodec.h> // Wic
#pragma comment(lib, "d2d1")
void WicSave(HBITMAP bitmap)
{
// create a Wic bitmap
CComPtr<IWICImagingFactory> wicFactory;
auto hr = wicFactory.CoCreateInstance(CLSID_WICImagingFactory);
CComPtr<IWICBitmap> wicBitmap;
hr = wicFactory->CreateBitmapFromHBITMAP(bitmap, nullptr, WICBitmapUseAlpha, &wicBitmap);
// create PNG file
CComPtr<IWICStream> wicStream;
hr = wicFactory->CreateStream(&wicStream);
hr = wicStream->InitializeFromFilename(L"drawing2.png", GENERIC_WRITE);
// create PNG encoder
CComPtr<IWICBitmapEncoder> encoder;
hr = wicFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, &encoder);
hr = encoder->Initialize(wicStream, WICBitmapEncoderNoCache);
// create frame
CComPtr<IWICBitmapFrameEncode> frame;
hr = encoder->CreateNewFrame(&frame, nullptr);
hr = frame->Initialize(nullptr);
// write bitmap
hr = frame->WriteSource(wicBitmap, nullptr);
// commit
hr = frame->Commit();
hr = encoder->Commit();
}
void BuildSvgAsCBitmap(int width, int height)
{
// initialize Direct2D
D2D1_FACTORY_OPTIONS options = { D2D1_DEBUG_LEVEL_INFORMATION }; // remove this in release
CComPtr<ID2D1Factory> factory;
auto hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_ID2D1Factory, &options, (void**)&factory);
// create an in-memory bitmap
BITMAPINFO bitmapInfo = {};
bitmapInfo.bmiHeader.biSize = sizeof(bitmapInfo.bmiHeader);
bitmapInfo.bmiHeader.biWidth = width;
bitmapInfo.bmiHeader.biHeight = height;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
CBitmap bitmap;
void* bits = 0;
auto res = bitmap.Attach(CreateDIBSection(nullptr, &bitmapInfo, DIB_RGB_COLORS, &bits, nullptr, 0));
// create a DC render target
CComPtr<ID2D1DCRenderTarget> target;
D2D1_RENDER_TARGET_PROPERTIES props = {};
props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
hr = factory->CreateDCRenderTarget(&props, &target);
// create a DC, select bitmap and bind DC
CDC cdc;
cdc.CreateCompatibleDC(nullptr);
auto old = cdc.SelectObject(bitmap);
RECT rc = { 0, 0, width, height };
hr = target->BindDC(cdc.GetSafeHdc(), &rc);
// requires Windows 10 1703
CComPtr<ID2D1DeviceContext5> dc;
hr = target->QueryInterface(&dc);
// open a stream in the .SVG file
CComPtr<IStream> svgStream;
hr = SHCreateStreamOnFileW(L"d:\\downloads\\drawing.svg", 0, &svgStream);
// open the SVG as a document
CComPtr<ID2D1SvgDocument> svg;
D2D1_SIZE_F size = { (float)width, (float)height };
hr = dc->CreateSvgDocument(svgStream, size, &svg);
// draw it on the render target
target->BeginDraw();
dc->DrawSvgDocument(svg);
hr = target->EndDraw();
// TODO: do something with the bitmap
WicSave(bitmap);
// cleanup, etc.
cdc.SelectObject(old);
}
void SaveSvgAsPng(UINT width, UINT height)
{
// initialize Direct2D
D2D1_FACTORY_OPTIONS options = { D2D1_DEBUG_LEVEL_INFORMATION }; // remove this in release
CComPtr<ID2D1Factory> factory;
auto hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_ID2D1Factory, &options, (void**)&factory);
// create a Wic bitmap
CComPtr<IWICImagingFactory> wicFactory;
hr = wicFactory.CoCreateInstance(CLSID_WICImagingFactory);
CComPtr<IWICBitmap> bitmap;
hr = wicFactory->CreateBitmap(width, height, GUID_WICPixelFormat32bppPBGRA, WICBitmapCacheOnLoad, &bitmap);
// use the Wic bitmap as a Direct2D render target
CComPtr<ID2D1RenderTarget> target;
D2D1_RENDER_TARGET_PROPERTIES props = {};
hr = factory->CreateWicBitmapRenderTarget(bitmap, &props, &target);
// requires Windows 10 1703
CComPtr<ID2D1DeviceContext5> dc;
hr = target->QueryInterface(&dc);
// open a stream in the .SVG file
CComPtr<IStream> svgStream;
hr = SHCreateStreamOnFileW(L"d:\\downloads\\drawing.svg", 0, &svgStream);
// open the SVG as a document
CComPtr<ID2D1SvgDocument> svg;
D2D1_SIZE_F size = { (float)width, (float)height };
hr = dc->CreateSvgDocument(svgStream, size, &svg);
// draw it on the render target
target->BeginDraw();
dc->DrawSvgDocument(svg);
hr = target->EndDraw();
// create PNG file
CComPtr<IWICStream> wicStream;
hr = wicFactory->CreateStream(&wicStream);
hr = wicStream->InitializeFromFilename(L"drawing.png", GENERIC_WRITE);
// create PNG encoder
CComPtr<IWICBitmapEncoder> encoder;
hr = wicFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, &encoder);
hr = encoder->Initialize(wicStream, WICBitmapEncoderNoCache);
// create frame
CComPtr<IWICBitmapFrameEncode> frame;
hr = encoder->CreateNewFrame(&frame, nullptr);
hr = frame->Initialize(nullptr);
// write bitmap
hr = frame->WriteSource(bitmap, nullptr);
// commit
hr = frame->Commit();
hr = encoder->Commit();
}
int main()
{
CoInitialize(nullptr);
SaveSvgAsPng(200, 200);
BuildSvgAsCBitmap(200, 200);
CoUninitialize();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment