1
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-11-09

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;
}

1
2
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
1
2