前回までで、RAW画像データの表示、DirectWriteを使ったテキスト描画までDirect2Dでできたので、
あえてDCを使う必要もなくなってしまいました。
ということで、前回までのコードをベースにしてDXGIを使って画像描画する方法を考えていきます。
DXGI Surfaceをレンダーターゲットにするには
マイクロソフトのサイトからの引用画像です。左下に行くまでめんどっちいですね。
ヘッダーにincudeファイルを追加
DXGI(Direct3D)関連のヘッダーファイルの追加。
Direct2Dは、DeviceContextにDrawTextの機能が追加されたd2d1_3.hを使用してみました。
#include <dxgi1_3.h>
#include <d3d11_2.h>
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d11.lib")
#include <d2d1_3.h>
#pragma comment(lib, "d2d1.lib")
//// DirectWrite
#include <dwrite.h>
#pragma comment(lib, "dwrite.lib")
//// WIC
#include <wincodec.h>
#pragma comment(lib, "windowscodecs.lib")
グローバル変数
DCRenderTarget, HWNDRenderTarget, DeviceContextの3種のうちどれを使用するか
defineで切り分けるようにしています。
//#define USE_DCRT
//#define USE_HWNDRT
#define USE_CONTEXT
HINSTANCE hInst;
char HomeFolderName[MAX_PATH];
HWND hWnd, hDlg;
const int FrameWidth=240, FrameHeight=150;
HDC hFrameDC;
HBITMAP hFrameBMP, hOldFrameBMP;
HBITMAP hImageBMP1;
int ImageWidth1, ImageHeight1;
unsigned char *ImageBuffer1;
ID2D1Factory1 *pD2D_Factory;
ID2D1Device *pD2D_Device;
ID2D1RenderTarget *pRT;
ID2D1DeviceContext4 *pDC;
ID2D1Bitmap1 *pD2D_Bitmap;
ID2D1Bitmap1 *pD2D_BitmapDXGI;
ID3D11Device *pDX_Device;
IDXGIDevice1 *pDXGI_Device;
IDXGIFactory2 *pDXGI_Factory;
IDXGISwapChain1 *pDXGI_SwapChain;
IDXGISurface *pDXGI_Surface;
const D2D1_PIXEL_FORMAT D2D_PixelFormat=D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE);//D2D1_ALPHA_MODE_PREMULTIPLIED);
const D2D1_RENDER_TARGET_PROPERTIES D2D_RenderProps=D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D_PixelFormat);
IWICImagingFactory *pWIC_Factory;
IDWriteFactory *pDWR_Factory;
ID2D1SolidColorBrush *pBrush;
IDWriteTextFormat *pFont;
Initialize()
メインウィンドウ作成後、ざっくりと初期化処理をします。
CoInitialize(NULL);
D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, 0, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
NULL, 0, D3D11_SDK_VERSION, &pDX_Device, NULL, NULL);
pDX_Device->QueryInterface(IID_PPV_ARGS(&pDXGI_Device));
pDXGI_Device->SetMaximumFrameLatency(1);
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2D_Factory);
pD2D_Factory->CreateDevice(pDXGI_Device, &pD2D_Device);
CreateDXGIFactory2(0, IID_PPV_ARGS(&pDXGI_Factory));
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(&pDWR_Factory));
CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pWIC_Factory));
#ifdef USE_DCRT
HDC hdc=GetDC(hDlgImg);
hFrameDC=CreateCompatibleDC(hdc);
hFrameBMP=CreateCompatibleBitmap(hdc, FrameWidth, FrameHeight);
hOldFrameBMP=(HBITMAP)SelectObject(hFrameDC, hFrameBMP);
ReleaseDC(hDlgImg, hdc);
pD2D_Factory->CreateDCRenderTarget(&D2D_RenderProps, (ID2D1DCRenderTarget **)&pRT);
RECT rect={0, 0, FrameWidth, FrameHeight};
reinterpret_cast<ID2D1DCRenderTarget *>(pRT)->BindDC(hFrameDC, &rect);
#endif
#ifdef USE_HWNDRT
D2D1_HWND_RENDER_TARGET_PROPERTIES D2D_HwndRenderProps=D2D1::HwndRenderTargetProperties(hDlgImg, D2D1::SizeU(FrameWidth, FrameHeight));
D2D_HwndRenderProps.presentOptions=D2D1_PRESENT_OPTIONS_IMMEDIATELY; // D2D1_PRESENT_OPTIONS_NONE;
pD2D_Factory->CreateHwndRenderTarget(&D2D_RenderProps, &D2D_HwndRenderProps, (ID2D1HwndRenderTarget **)&pRT);
#endif
#ifdef USE_CONTEXT
pD2D_Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, reinterpret_cast<ID2D1DeviceContext **>(&pDC));
DXGI_SWAP_CHAIN_DESC1 swapChainDesc={};
swapChainDesc.Format=DXGI_FORMAT_B8G8R8A8_UNORM;
swapChainDesc.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.SwapEffect=DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
swapChainDesc.BufferCount=2;
swapChainDesc.SampleDesc.Count=1;
pDXGI_Factory->CreateSwapChainForHwnd(pDXGI_Device, hDlgImg, &swapChainDesc, NULL, NULL, &pDXGI_SwapChain);
pDXGI_SwapChain->GetBuffer(0, IID_PPV_ARGS(&pDXGI_Surface));
D2D1_BITMAP_PROPERTIES1 bitmapPropsDXGI=D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2D_PixelFormat);
bitmapPropsDXGI.bitmapOptions|=(D2D1_BITMAP_OPTIONS_TARGET|D2D1_BITMAP_OPTIONS_CANNOT_DRAW);
pDC->CreateBitmapFromDxgiSurface(pDXGI_Surface, &bitmapPropsDXGI, &pD2D_BitmapDXGI);
pDC->SetTarget(pD2D_BitmapDXGI);
#endif
if(pRT!=NULL) pRT->CreateSolidColorBrush(D2D1::ColorF(0xff0000, 1.f), &pBrush); // ColorF(RGB,A)
if(pDC!=NULL) pDC->CreateSolidColorBrush(D2D1::ColorF(0xff0000, 1.f), &pBrush); // ColorF(RGB,A)
pDWR_Factory->CreateTextFormat(L"Consolas", NULL,
DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
10.f*(96.f/72.f), L"en-US", &pFont);
bitmap生成
前回からのソースの改変なので、CreateDIBSectionの確保したメモリをバッファとして使用していますが、malloc()等で普通に確保したメモリでOK。
型が違うだけで、関数名とその引数は全く同じに書けます。
WICの利用もコード上は一緒。
BITMAPV5HEADER bi={};
bi.bV5Size=sizeof(BITMAPV5HEADER);
bi.bV5Width=FrameWidth;
bi.bV5Height=FrameHeight;
bi.bV5Planes=1;
bi.bV5BitCount=32;
bi.bV5SizeImage=((ImageWidth1*bi.bV5BitCount+31)/32*4)*ImageHeight1;
bi.bV5Compression=BI_BITFIELDS;
bi.bV5RedMask =0x00ff0000;
bi.bV5GreenMask=0x0000ff00;
bi.bV5BlueMask =0x000000ff;
bi.bV5AlphaMask=0xff000000;
if(hImageBMP1!=NULL){ DeleteObject(hImageBMP1); hImageBMP1=NULL; }
ImageWidth1=FrameWidth;
ImageHeight1=FrameHeight;
BITMAPINFO *BitmapInfo=(BITMAPINFO *)calloc(sizeof(BITMAPV5HEADER), 1);
memcpy(&(BitmapInfo->bmiHeader), &bi, sizeof(bi));
hImageBMP1=CreateDIBSection(NULL, BitmapInfo, DIB_RGB_COLORS, (void **)(&ImageBuffer1), NULL, 0);
free(BitmapInfo);
for(int i=0; i<ImageWidth1*ImageHeight1; i++){
*(ImageBuffer1+i*4+0)=0xff; // B
*(ImageBuffer1+i*4+1)=0; // G
*(ImageBuffer1+i*4+2)=0; // R
*(ImageBuffer1+i*4+3)=0xff; // A 0x00: 透明 0xff: 不透明
}
if(pRT!=NULL) pRT->CreateBitmap(D2D1::SizeU(ImageWidth1, ImageHeight1), ImageBuffer1, ImageWidth1*32/8,
D2D1::BitmapProperties(D2D_PixelFormat), (ID2D1Bitmap **)&pD2D_Bitmap);
if(pDC!=NULL) pDC->CreateBitmap(D2D1::SizeU(ImageWidth1, ImageHeight1), ImageBuffer1, ImageWidth1*32/8,
D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2D_PixelFormat), &pD2D_Bitmap);
WM_PAINTでの描画
ここも、型が違うだけで関数名とその引数は全く同じに書けます。
HWNDRenderTargetでは、(コード上は)直接描画するため、最終的なFlush(Commit)的コードが不要です。
case WM_PAINT:
{
#ifndef USE_CONTEXT
pRT->BeginDraw();
pRT->SetTransform(D2D1::Matrix3x2F::Identity());
pRT->Clear(D2D1::ColorF(D2D1::ColorF::Black));
if(pD2D_Bitmap!=NULL){
pRT->DrawBitmap(pD2D_Bitmap, D2D1::RectF(0.f, 0.f, (float)ImageWidth1, (float)ImageHeight1),
1.f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR);
wchar_t wbuf[256]; MultiByteToWideChar(CP_ACP, 0, "test", -1, wbuf, 256);
pRT->DrawText(wbuf, wcslen(wbuf), pFont, D2D1::RectF(0, 0, 100, 20), pBrush);
}
pRT->EndDraw();
#else
pDC->BeginDraw();
pDC->SetTransform(D2D1::Matrix3x2F::Identity());
pDC->Clear(D2D1::ColorF(D2D1::ColorF::Black));
if(pD2D_Bitmap!=NULL){
pDC->DrawBitmap(pD2D_Bitmap, D2D1::RectF(0.f, 0.f, (float)ImageWidth1, (float)ImageHeight1),
1.f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR);
wchar_t wbuf[256]; MultiByteToWideChar(CP_ACP, 0, "test", -1, wbuf, 256);
pDC->DrawText(wbuf, wcslen(wbuf), pFont, D2D1::RectF(0, 0, 100, 20), pBrush);
}
pDC->EndDraw();
#endif
#ifdef USE_DCRT
HDC hdc=GetDC(hDlgImg);
BitBlt(hdc, 0, 0, FrameWidth, FrameHeight, hFrameDC, 0, 0, SRCCOPY);
ReleaseDC(hDlgImg, hdc);
#endif
#ifdef USE_CONTEXT
pDXGI_SwapChain->Present(1, 0);
#endif
ValidateRect(hDlgImg, NULL);
}
break;