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

前回までで、RAW画像データの表示、DirectWriteを使ったテキスト描画までDirect2Dでできたので、
あえてDCを使う必要もなくなってしまいました。
ということで、前回までのコードをベースにしてDXGIを使って画像描画する方法を考えていきます。

## DXGI Surfaceをレンダーターゲットにするには

マイクロソフトのサイトからの引用画像です。左下に行くまでめんどっちいですね。
![](https://docs.microsoft.com/ja-jp/windows/desktop/Direct2D/images/devicecontextdiagram.png)

## ヘッダーに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;

```