画像表示を含む自分用アプリの雛形(1)

GDIによる描画の基本的な構成は、20年以上前から変わってないですね。さすがWin32API(笑
以下、記事用に編集したのでバグがあるかもしれません。

## GDIの基本形

まずはシンプルなウィンドウの作成から。

```
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
{
  Initialize();     // 事前処理
  Show();           // CreateWindow(), ShowWindow()
  MessageLoop();    // ウィンドウプロシージャ
  Uninitialize();   // 事後処理
}
```
```
LRESULT __stdcall MyWin::wndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  static MyWin *_this=NULL;
  switch(msg){
  case WM_CREATE:
    _this=reinterpret_cast<MyWin *>((reinterpret_cast<CREATESTRUCT *>(lparam))->lpCreateParams);
    break;
  case WM_CLOSE:
    {
      DestroyWindow(hwnd);
    }
    break;
  case WM_DESTROY:
    {
      QuitMessage(0);
    }
    break;
  case WM_SIZE:
    {
      ...
    }
    break;
  case WM_MOVE:
    {
      ...
    }
    break;
  case WM_TIMER:
    {
      _this->TimerFunc(wparam, lparam);
    }
    break;
  }
  return DefWindowProc(hwnd, msg, wparam, lparam);
}

```

私の場合、ここで作成したウィンドウ(hwnd)の上に、同じサイズのダイアログウィンドウ(hdlg)をかぶせています。
ウィンドウに配置するボタン(BUTTON)や画像表示用の子ウィンドウ(STATIC)は、`ResEdit.exe`で設計していくのが楽なので＾＾；
また、必要に応じて画像表示用の子ウィンドウ(hdlgImg)等をサブクラス化しておきます。
ちらつき防止処理は、適当に。。。＾＾；

```
  hdlg=CreateDialogParam(hAppInstance, MAKEINTRESOURCE(IDD_DIALOG1), hwnd, (DLGPROC)dlgProc, (LONG_PTR)this);
  HWND hdlgImg=GetDlgItem(hdlg, IDC_STATIC_IMAGE);
  SetWindowSubclass(hdlgImg, dlgProcImg, 0, (DWORD_PTR)this);
```
```
LRESULT __stdcall MyWin::dlgProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
  static MyWin *_this=NULL;
  switch(msg){
  case WM_INITDIALOG:
    {
      _this=reinterpret_cast<MyWin *>(lparam);
      // 初期化処理
      return FALSE;
    }
    break;
  case WM_PAINT:
    {
      HRGN hrgn=NULL;
      HDC hdc=GetDC(hdlg);
      // ExtSelectClipRgn()で、適宜非更新リージョンを切り抜き
      ReleaseDC(hdlg, hdc);
    }
    break;
  case WM_COMMAND:
    {
      _this->CommandFunc(wparam, lparam);
    }
    break;
  }
  return FALSE;
}
```
```
LRESULT __stdcall MyWin::dlgProcImg(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR, DWORD_PTR parent)
{
  static MyWin *_this=NULL;
  switch(msg){
  case WM_INITDIALOG:
    {
      if(_this==NULL) _this=reinterpret_cast<MyWin *>(parent);
      return FALSE;
    }
    break;
  case WM_PAINT:
    {
      HDC hdc=GetDC(hdlg);
      HDC hmemdc=CreateCompatibleDC(hdc);
      HBITMAP holdbmp=(HBITMAP)SelectObject(hmemdc, _this->hbmp);
      BitBlt(hdc, dx, dy, dw, dh, hmemdc, 0, 0, SRCCOPY);
      SelectObject(hmemdc, holdbmp);
      ReleaseDC(hdlg, hdc);
    }
    break;
  case WM_ERASEBKGND:
    return TRUE;
  }
  return DefSubclassProc(hdlg, msg, wparam, lparam);
}
```
(静止)画像の表示ですが
あらかじめ確保しておいたHBITMAP形式のメモリ領域に画像を読み込んでおき、
メモリDCにそれをSelectObjectして、WM_PAINT時にBitBltで転送します。
BitBltにて、コピー元とコピー先の色深度が異なっていても、適当に変換してくれます。
画像データを読み込んで更新したところでInvalidateRectによりWM_PAINTを発生させて
表示に反映させます。

HBITMAPですが、
任意の色深度の空バッファとして作成したい場合、

```
  hbmp=CreateDIBSection(NULL, pbi, DIB_RGB_COLORS, (void **)(&buffer), NULL, 0);
```
とすると、CreateDIBSectionがメモリを確保して、画素データの先頭としてbufferに入れて返してくれます。
上記引数のpbiには、色深度(画像フォーマット)を指定します。例えば、32bitRGBAのbitmapの場合は、以下の通り。

```
  PBITMAPINFO pbi=(PBITMAPINFO)calloc(SIZEOFBITMAPINFO, 1);
  BITMAPV5HEADER bi{};
  bi.bV5Size=sizeof(BITMAPV5HEADER);
  bi.bV5Width=width;
  bi.bV5Height=height;
  bi.bV5Planes=1;
  bi.bV5BitCount=32;
  bi.bV5Compression=BI_BITFIELDS;
  bi.bV5SizeImage=buffersize;
  bi.bV5RedMask  =0x00ff0000;
  bi.bV5GreenMask=0x0000ff00;
  bi.bV5BlueMask =0x000000ff;
  bi.bV5AlphaMask=0xff000000;
  memcpy(&(pbi->bmiHeader), &bi, bi.bV5Size);
```

また、画面表示と同じ色深度のものをワーク用の空バッファとして欲しい場合には、

```
 HBITMAP hbmp=CreateCompatibleBitmap(hdc, width, height);
```
で作成します。
(CreateCompatibleBitmapで作成したhbmpの個々の画素値にアクセスしたい場合には、GetDIBitsを使用して別確保したメモリにコピーする必要があります)

## ソースコード
追加:)
なげーよ＞＜
ビルドしたい場合は、適当に空のプロジェクトファイルを作って編集してください。(リソースファイル、マニフェストファイルは省略)
個人的な好みで、マルチバイト仕様です。

```MyTest.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable: 4100) // disable warning "unreferenced formal parameter"
#pragma warning(disable: 4477) // disable warning "unreferenced formal parameter"

// CRT
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#define _USE_MATH_DEFINES // use M_PI
#include <math.h>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>

// STL
#include <vector>

// WindowsAPI
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN
#define OEMRESOURCE
#include <windows.h>
#include <shlobj.h>
#include <shellapi.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")
#pragma comment(linker, "\"/manifestdependency:type='Win32' "\
    "name='Microsoft.Windows.Common-Controls' version='6.0.0.0' "\
    "processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

extern HINSTANCE hInst;
extern char      HomeFolderName[MAX_PATH];

```

```MyTest.cpp
#include "MyTest.h"
#include "resource.h"

HINSTANCE     hInst;
char          HomeFolderName[MAX_PATH];

HWND          hWnd;

HDC           hFrameDC;
HBITMAP       hFrameBMP, hOldFrameBMP;
const int     FrameWidth=240, FrameHeight=150;

HDC           hImageDC1;
HBITMAP       hImageBMP1, hOldImageBMP1;
unsigned char *ImageBuffer1;
int           ImageWidth1, ImageHeight1;

#define WINDOW_TITLE TEXT("mytestwin")
#define WINDOW_CLASS TEXT("mytestwinclass")

bool LoadImageFile(char filename[])
{
  char *ext=PathFindExtension(filename);
  if(ext!=NULL){
    if(_strcmpi(ext, ".bmp")==0){ // as 24bit-bmp
      char ffilename[MAX_PATH];
      sprintf(ffilename, "%s\\%s", HomeFolderName, filename);

      FILE *fp=fopen(ffilename, "rb");
      if(fp!=NULL){
        BITMAPFILEHEADER bf;
        fread(&bf, sizeof(BITMAPFILEHEADER), 1, fp);
        BITMAPINFOHEADER bi;
        fread(&bi, sizeof(BITMAPINFOHEADER), 1, fp);
        if(bi.biBitCount==24){
          if(hOldImageBMP1!=NULL) SelectObject(hImageDC1, hOldImageBMP1);
          if(hImageBMP1!=NULL){ DeleteObject(hImageBMP1); hImageBMP1=NULL; }

          ImageWidth1=bi.biWidth;
          ImageHeight1=bi.biHeight;
          
          BITMAPINFO *BitmapInfo=(BITMAPINFO *)calloc(sizeof(BITMAPINFOHEADER), 1);
          memcpy(&(BitmapInfo->bmiHeader), &bi, sizeof(bi));
          hImageBMP1=CreateDIBSection(NULL, BitmapInfo, DIB_RGB_COLORS, (void **)(&ImageBuffer1), NULL, 0);
          free(BitmapInfo);
          
          fread(ImageBuffer1, bi.biSizeImage, 1, fp);

          hOldImageBMP1=(HBITMAP)SelectObject(hImageDC1, hImageBMP1);
        }
        fclose(fp);

      }
      return true;
    }
  }
  
  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(hOldImageBMP1!=NULL) SelectObject(hImageDC1, hOldImageBMP1);
  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: 不透明
  }
  hOldImageBMP1=(HBITMAP)SelectObject(hImageDC1, hImageBMP1);

  return false;
}

LRESULT CALLBACK DlgProcImg(HWND hDlgImg, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR parent)
{
  switch(message){
  case WM_INITDIALOG:
    {
    }
    break;

  case WM_PAINT:
    {
      BitBlt(hFrameDC, 0, 0, ImageWidth1, ImageHeight1, hImageDC1, 0, 0, SRCCOPY);
      HDC hdc=GetDC(hDlgImg);
      BitBlt(hdc, 0, 0, FrameWidth, FrameHeight, hFrameDC, 0, 0, SRCCOPY);
      ReleaseDC(hDlgImg, hdc);
      ValidateRect(hDlgImg, NULL);
    }
    break;

  case WM_ERASEBKGND:
    SetWindowLongPtr(hDlgImg, DWLP_MSGRESULT, TRUE);
    break;
  }
  return FALSE;
}

LRESULT CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
  static HWND hDlgImg=NULL;
  switch(message){
  case WM_INITDIALOG:
    {
      hDlgImg=GetDlgItem(hDlg, IDC_STATIC_IMAGE);
    }
    break;

  case WM_COMMAND:
    {
      switch(LOWORD(wParam)){
      case IDC_BUTTON_LOADIMAGE:
        {
          char filename[MAX_PATH];
          strcpy(filename, "TestImage.bmp");
          LoadImageFile(filename);
          InvalidateRect(hDlgImg, NULL, FALSE);
        }
        break;
      }
    }
    break;
    
  case WM_PAINT:
    {
      HDC hDC=GetDC(hDlg);
      HRGN hRgn=NULL;
      if(GetClipRgn(hDC, hRgn)==0){
        RECT ClipRect={};
        GetClientRect(hDlgImg, &ClipRect);
        hRgn=CreateRectRgnIndirect(&ClipRect);
        ExtSelectClipRgn(hDC, hRgn, RGN_DIFF);
        DeleteObject(hRgn);
      }
      ReleaseDC(hDlg, hDC);
    }
    break;
  }
  return FALSE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message){
  case WM_COMMAND:
    {
      switch(LOWORD(wParam)){
      case IDM_EXIT:
        DestroyWindow(hWnd);
        break;
      default:
        return DefWindowProc(hWnd, message, wParam, lParam);
      }
    }
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  default:
    return DefWindowProc(hWnd, message, wParam, lParam);
  }
  return 0;
}

void Initialize()
{
  WNDCLASSEX wcex={};
  wcex.cbSize         = sizeof(WNDCLASSEX);
  wcex.style          = CS_HREDRAW | CS_VREDRAW;
  wcex.lpfnWndProc    = WndProc;
  wcex.hInstance      = hInst;
  wcex.hCursor        =(HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(OCR_NORMAL), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE|LR_SHARED);
  wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
  wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_MYTEST);
  wcex.lpszClassName  = WINDOW_CLASS;
  wcex.hIcon          = LoadIcon(hInst, MAKEINTRESOURCE(IDI_MYTEST));
  wcex.hIconSm        = LoadIcon(hInst, MAKEINTRESOURCE(IDI_SMALL));
  RegisterClassEx(&wcex);

  hFrameDC=CreateCompatibleDC(NULL);
  HDC hdc=GetDC(NULL);
  hFrameBMP=CreateCompatibleBitmap(hdc, FrameWidth, FrameHeight);
  ReleaseDC(NULL, hdc);
  hOldFrameBMP=(HBITMAP)SelectObject(hFrameDC, hFrameBMP);

  hImageDC1=CreateCompatibleDC(NULL);
  LoadImageFile(NULL);
  hOldImageBMP1=(HBITMAP)SelectObject(hImageDC1, hImageBMP1);
}

void Uninitialize()
{
  SelectObject(hImageDC1, hOldImageBMP1);
  if(hImageBMP1!=NULL) DeleteObject(hImageBMP1);
  if(hImageDC1!=NULL)  DeleteDC(hImageDC1);

  SelectObject(hFrameDC, hOldFrameBMP);
  if(hFrameBMP!=NULL) DeleteObject(hFrameBMP);
  if(hFrameDC!=NULL)  DeleteDC(hFrameDC);

  UnregisterClass(WINDOW_CLASS, hInst);
}

void Show()
{
  hWnd=CreateWindow(WINDOW_CLASS, WINDOW_TITLE, WS_OVERLAPPEDWINDOW,
                    CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL);
  HWND hDlg=CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_TEST), hWnd, (DLGPROC)DlgProc, 0);

  RECT wrect, drect;
  GetWindowRect(hWnd, &wrect);
  GetWindowRect(hDlg, &drect);
  AdjustWindowRect(&drect, GetWindowLongPtr(hWnd, GWL_STYLE), GetMenu(hWnd)? TRUE : FALSE);
  SetWindowPos(hWnd, 0, wrect.left, wrect.top, drect.right-drect.left, drect.bottom-drect.top, SWP_NOZORDER);

  HWND hDlgImg=GetDlgItem(hDlg, IDC_STATIC_IMAGE);
  SetWindowSubclass(hDlgImg, DlgProcImg, 0, 0);
  SetWindowPos(hDlgImg, 0, 0, 0, FrameWidth, FrameHeight, SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE);
  
  ShowWindow(hWnd, SW_SHOW);
  ShowWindow(hDlg, SW_SHOW);
  UpdateWindow(hWnd);
}

void MessageLoop()
{
  timeBeginPeriod(1);
  MSG msg={};
  for(;;){
    if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){
      if(msg.message==WM_QUIT) break;
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }
  timeEndPeriod(1);
}


#ifdef __cplusplus_cli
[System::STAThreadAttribute]
#endif
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
{
  hInst=hInstance;
  GetModuleFileName(NULL, HomeFolderName, MAX_PATH); PathRemoveFileSpec(HomeFolderName);

  Initialize();
  Show();
  MessageLoop();
  Uninitialize();
  return 0;
}

```