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を使用して別確保したメモリにコピーする必要があります)
ソースコード
追加:)
なげーよ><
ビルドしたい場合は、適当に空のプロジェクトファイルを作って編集してください。(リソースファイル、マニフェストファイルは省略)
個人的な好みで、マルチバイト仕様です。
# 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];
# 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;
}