文字列でリージョン

初めに

ビットマップからリージョンを作るってのはよくありますけど、ただの文字列のリージョンを作りたいときに、わざわざビットマップを作るのはめんどうですよね。(そもそもそんなことする人いないか?)

なので文字列のリージョンを作るものを作ってみました。

ソース

#include <windows.h>
#include <stdio.h>

#define PROGRAM_NAME    "RgnTest"

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmd, int nCmdShow);
HWND InitWindow (void);
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

HINSTANCE g_hInstance;

////////////////////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmd, int nCmdShow) {
    MSG msg;
    BOOL bRet;
    g_hInstance = hInstance;
    InitWindow();
    while ( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0 ) {
        if (bRet == -1) {
            MessageBox(0, "handle the error and possibly exit", "error", MB_ICONSTOP);
            break;
        } else {
            TranslateMessage (&msg);
            DispatchMessage (&msg);
        }
    }
    return msg.wParam;
}

HWND InitWindow (void) {
    HWND hWnd;
    WNDCLASSEX wcex;

    ZeroMemory((LPVOID)&wcex, sizeof(WNDCLASSEX));

    wcex.cbSize             = sizeof(WNDCLASSEX);
    wcex.style              = 0;
    wcex.lpfnWndProc        = WndProc;
    wcex.cbClsExtra         = 0;
    wcex.cbWndExtra         = 0;
    wcex.hInstance          = g_hInstance;
    wcex.hIcon              = NULL;
    wcex.hCursor            = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground      = CreateSolidBrush(RGB(0,0,0));
    wcex.lpszMenuName       = NULL;
    wcex.lpszClassName      = PROGRAM_NAME;
    wcex.hIconSm            = NULL;
    RegisterClassEx(&wcex);
    //ウィンドウ作成
    hWnd=CreateWindowEx(
        0,
        PROGRAM_NAME,
        PROGRAM_NAME,
        WS_POPUP,
        200,            //位置とか大きさとかはもちろん適当
        200,
        700,
        50,
        NULL,
        NULL,
        g_hInstance,
        NULL
    );
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
    return hWnd;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    PAINTSTRUCT ps;
    char *text = "それっぽいリージョン";  //表示する文字
    
    //リージョン用
    COLORREF tcolor = RGB(0,0,0);   //透過色(なぜか背景色を黒にしていたので黒)
    static HRGN hRgn;
    HRGN hRgnTemp;
    int width, height, x, y;

    switch (message) {
        case WM_CREATE:
            return 0;
        case WM_COMMAND:
            return 0;
        case WM_LBUTTONDOWN:
            ReleaseCapture();
            SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
            return 0;
        //普通はこんなことやるのだろうか…
        case WM_RBUTTONDOWN:
            hdc = GetDC(hwnd);
            
            RECT r;
            GetWindowRect(hwnd, &r);
            width = r.right - r.left;
            height = r.bottom - r.top;

            hRgn = CreateRectRgn(0,0,0,0);
            hRgnTemp = CreateRectRgn(0,0,0,0);
            for (y = 0;y < height;y++) {
                for (x = 0;x < width;x++) {
                    if (GetPixel(hdc, x, y) != tcolor) {
                        HRGN hRgnPixel;
                        hRgnPixel = CreateRectRgn(x,y,x+1,y+1);
                        hRgnTemp = hRgn;
                        CombineRgn(hRgn, hRgnTemp, hRgnPixel, RGN_OR);
                        DeleteObject(hRgnPixel);
                    }
                }
            }
            SetWindowRgn(hwnd, hRgn, TRUE);
            DeleteObject(hRgnTemp);

            ReleaseDC(hwnd, hdc);
            return 0;
        case WM_PAINT:
            hdc = BeginPaint(hwnd, &ps);
            HFONT hFont, hOldFont;
            hFont = CreateFont(
                30,
                30,
                0,
                0,
                FW_HEAVY,
                FALSE,
                FALSE,
                FALSE,
                SHIFTJIS_CHARSET,
                OUT_DEFAULT_PRECIS,
                CLIP_DEFAULT_PRECIS,
                PROOF_QUALITY,
                FIXED_PITCH | FF_MODERN,
                "MS ゴシック"
            );
            hOldFont = (HFONT)SelectObject(hdc, hFont);

            //もちろん色も表示位置も適当
            SetBkMode(hdc, TRANSPARENT);
            SetTextColor(hdc, RGB(255, 0, 0));
            TextOut(hdc, 1, 1, text, strlen(text));

            SelectObject(hdc, hOldFont);
            DeleteObject(hFont);
            EndPaint(hwnd, &ps);
            return 0;
        case WM_CLOSE:
            DestroyWindow(hwnd);
            return 0;
        case WM_DESTROY:
            if (hRgn != NULL) DeleteObject(hRgn);
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hwnd,message,wParam,lParam));
    }
    return 0L;
}

処理内容

自Windowのデバイスコンテキストに書かれてるものを、GetPixelで一ピクセルごとに色を見ていって、CombineRgnで合成しまくってるだけです。

処理が結構重いです。やる気がある人はCreateRectRgnのところをExtCreateRegionなどにかえてみたりして、最適化してみてください。