mieki256's diary



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をベタ描画するだけなら問題無くできることは分かったけれど、透明色というか抜け色を反映させて描画する方法がよく分からなくて調べていた。どうやら、ソース画像とは別にマスク画像を用意して、マスク画像を描画した直後に本来のソース画像を上書き描画するのが一般的っぽいなと。そのあたりの仕組みは、以下のページが参考になった。

_マスク画像で透明色
_透明色とマスク画像
_ビットマップの表示

ソース。 :

ソースは以下のような感じになった。

_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

test.png

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

ということで、赤いボールの周辺がちゃんと抜けた状態で描画できた。

仕組み。 :

どうして抜け部分を作れるのか、一応図にしてみた。

about_gdi_draw_bmp.png
  • マスク画像を AND (SRCAND) で重ねることで、マスク画像の黒い部分は黒く、白い部分は描画先の元の色が残る状態になる。
  • 本来描きたかったソース画像を OR (SRCPAINT) で重ねることで、ぼっかり黒く抜けた部分にソース画像がハメ込まれるような状態になる。

以上です。

過去ログ表示

Prev - 2022/09 - Next
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project