4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

画像表示を含む自分用アプリの雛形(4) Direct2D化-3, DXGI

Last updated at Posted at 2018-11-16

前回までで、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;

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?