2022/09/03(土) [n年前の日記]
#2 [prog][windows] MinGWとWindows APIで画像を描画
MinGW と WIndows API (GDI) を使って、bitmap画像を描画できそうかどうか実験中。環境は Windows10 x64 21H2 + gcc 9.2.0。MSYS2 + MinGW-w64 ではなくて、MinGW + MSYS を使ってる。
数日前の作業で、bitmapをベタ描画するだけなら問題無くできることは分かったけれど、透明色というか抜け色を反映させて描画する方法がよく分からなくて調べていた。どうやら、ソース画像とは別にマスク画像を用意して、マスク画像を描画した直後に本来のソース画像を上書き描画するのが一般的っぽいなと。そのあたりの仕組みは、以下のページが参考になった。
_マスク画像で透明色
_透明色とマスク画像
_ビットマップの表示
数日前の作業で、bitmapをベタ描画するだけなら問題無くできることは分かったけれど、透明色というか抜け色を反映させて描画する方法がよく分からなくて調べていた。どうやら、ソース画像とは別にマスク画像を用意して、マスク画像を描画した直後に本来のソース画像を上書き描画するのが一般的っぽいなと。そのあたりの仕組みは、以下のページが参考になった。
_マスク画像で透明色
_透明色とマスク画像
_ビットマップの表示
◎ ソース。 :
ソースは以下のような感じになった。
_01_draw_bitmap.c
リソースファイルは以下。bitmap画像を3ファイルほど指定している。
_bmp_res.rc
使用したbitmap画像は以下。
_images.zip
一応、png画像に変換したものも載せておく。
![bg.png](img/_2022/20220903_gcc_draw_bmp/bg.png)
![test.png](img/_2022/20220903_gcc_draw_bmp/test.png)
![test_mask.png](img/_2022/20220903_gcc_draw_bmp/test_mask.png)
Makefile は以下。
_Makefile
make と打てばコンパイル/ビルドできる。01_draw_bitmap.exe が得られた。
01_draw_bitmap.exe を実行した結果は以下。
![01_draw_bitmap_ss.png](img/_2022/20220903_gcc_draw_bmp/01_draw_bitmap_ss.png)
ということで、赤いボールの周辺がちゃんと抜けた状態で描画できた。
_01_draw_bitmap.c
#include <windows.h> #define SCRW 512 #define SCRH 512 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { HDC hdc, hBmpDC; PAINTSTRUCT ps; static HBITMAP hBitmap, hBitmap_mask, hBitmap_bg; static BITMAP bp; static int imgw, imgh; static int bgw, bgh; static int x, y; switch (msg) { case WM_CREATE: // load bitmaps hBitmap = LoadBitmap(((LPCREATESTRUCT)lp)->hInstance, TEXT("BALL_IMG")); hBitmap_mask = LoadBitmap(((LPCREATESTRUCT)lp)->hInstance, TEXT("BALL_MSK")); hBitmap_bg = LoadBitmap(((LPCREATESTRUCT)lp)->hInstance, TEXT("BG_IMG")); // get source image information GetObject(hBitmap, sizeof(BITMAP), &bp); imgw = bp.bmWidth; imgh = bp.bmHeight; // get bg image information GetObject(hBitmap_bg, sizeof(BITMAP), &bp); bgw = bp.bmWidth; bgh = bp.bmHeight; return 0; case WM_PAINT: x = 32; y = 32; hdc = BeginPaint(hwnd, &ps); // draw bg image hBmpDC = CreateCompatibleDC(hdc); SelectObject(hBmpDC, hBitmap_bg); BitBlt(hdc, 0, 0, bgw, bgh, hBmpDC, 0, 0, SRCCOPY); DeleteDC(hBmpDC); // draw mask hBmpDC = CreateCompatibleDC(hdc); SelectObject(hBmpDC, hBitmap_mask); BitBlt(hdc, x, y, imgw, imgh, hBmpDC, 0, 0, SRCAND); DeleteDC(hBmpDC); // draw source image hBmpDC = CreateCompatibleDC(hdc); SelectObject(hBmpDC, hBitmap); BitBlt(hdc, x, y, imgw, imgh, hBmpDC, 0, 0, SRCPAINT); DeleteDC(hBmpDC); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: DeleteObject(hBitmap); DeleteObject(hBitmap_mask); DeleteObject(hBitmap_bg); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wp, lp); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd; MSG msg; WNDCLASS winc; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("DrawBitmapSample"); if (!RegisterClass(&winc)) return -1; hwnd = CreateWindow( TEXT("DrawBitmapSample"), TEXT("Draw Bitmap Sample"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, SCRW, SCRH, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return -1; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
リソースファイルは以下。bitmap画像を3ファイルほど指定している。
_bmp_res.rc
BALL_IMG BITMAP "test.bmp" BALL_MSK BITMAP "test_mask.bmp" BG_IMG BITMAP "bg.bmp"
使用したbitmap画像は以下。
_images.zip
- bg.bmp。背景にする画像。
- test.bmp。元ソース画像。抜け部分にしたいところは黒(#000000)にしておく。
- test_mask.bmp。マスク画像。不透明部分は黒(#000000)、透明部分は白(#ffffff)にしておく。アルファチャンネルを白黒反転させた状態とも言える。
一応、png画像に変換したものも載せておく。
![bg.png](img/_2022/20220903_gcc_draw_bmp/bg.png)
![test.png](img/_2022/20220903_gcc_draw_bmp/test.png)
![test_mask.png](img/_2022/20220903_gcc_draw_bmp/test_mask.png)
Makefile は以下。
_Makefile
01_draw_bitmap.exe: 01_draw_bitmap.o bmp_res.o gcc 01_draw_bitmap.o bmp_res.o -o 01_draw_bitmap.exe -mwindows 01_draw_bitmap.o: 01_draw_bitmap.c gcc -c 01_draw_bitmap.c bmp_res.o: bmp_res.rc test.bmp test_mask.bmp bg.bmp windres bmp_res.rc bmp_res.o .PHONY: clean clean: rm -f *.exe rm -f *.o
make と打てばコンパイル/ビルドできる。01_draw_bitmap.exe が得られた。
01_draw_bitmap.exe を実行した結果は以下。
![01_draw_bitmap_ss.png](img/_2022/20220903_gcc_draw_bmp/01_draw_bitmap_ss.png)
ということで、赤いボールの周辺がちゃんと抜けた状態で描画できた。
◎ 仕組み。 :
どうして抜け部分を作れるのか、一応図にしてみた。
![about_gdi_draw_bmp.png](img/_2022/20220903_gcc_draw_bmp/about_gdi_draw_bmp.png)
![about_gdi_draw_bmp.png](img/_2022/20220903_gcc_draw_bmp/about_gdi_draw_bmp.png)
- マスク画像を AND (SRCAND) で重ねることで、マスク画像の黒い部分は黒く、白い部分は描画先の元の色が残る状態になる。
- 本来描きたかったソース画像を OR (SRCPAINT) で重ねることで、ぼっかり黒く抜けた部分にソース画像がハメ込まれるような状態になる。
[ ツッコむ ]
以上です。