Ethan Young-Jae Park Ethan Young-Jae Park - 21 days ago 4
C Question

draw on transparent window using pen

I have a problem in my project using WinAPI.
I want to draw on transparent window. It does well. However, when I've drawing a line, pixels around the line are filled black color.
How to solve this problem?
attach the code and capture file.

enter code here


LRESULT __stdcall WindowProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc, backDC;
PAINTSTRUCT ps;

static Point prevPt;

// Draw or Erase
static bool isDraw = false;
static bool isErase = false;

// Select Pen Color
static int selectColor = 1;

// Color Pen(R, G, B) and Current Pen
static HPEN redPen;
static HPEN greenPen;
static HPEN bluePen;
static HPEN* currentPen = &redPen;

switch (iMessage)
{
case WM_CREATE:
{
redPen = CreatePen(PS_SOLID, 4, RGB(255, 0, 0));
greenPen = CreatePen(PS_SOLID, 4, RGB(0, 255, 0));
bluePen = CreatePen(PS_SOLID, 4, RGB(0, 0, 255));
return 0L;
}
case WM_DESTROY:
cout << "\n" << "destroying window" << endl;
PostQuitMessage(0);
return 0L;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0L;
case WM_LBUTTONDOWN:
prevPt.x = LOWORD(lParam);
prevPt.y = HIWORD(lParam);
isDraw = true;
return 0L;
case WM_LBUTTONUP:
isDraw = false;
return 0L;
case WM_MOUSEMOVE:
{
int x = LOWORD(lParam);
int y = HIWORD(lParam);
if (isDraw)
{
hdc = GetDC(g_hWnd);

HPEN OldPen = (HPEN)SelectObject(hdc, *currentPen);
MoveToEx(hdc, prevPt.x, prevPt.y, NULL);
LineTo(hdc, x, y);

prevPt.x = x;
prevPt.y = y;
DeleteObject(OldPen);
ReleaseDC(g_hWnd, hdc);
}
}
return 0L;
case WM_RBUTTONDOWN:
isErase = true;
return 0L;
case WM_RBUTTONUP:
isErase = false;
return 0L;
case WM_MOUSEWHEEL:
if (selectColor > 3)
selectColor = 1;

if (selectColor == 1) // Red
currentPen = &redPen;
else if (selectColor == 2)
currentPen = &greenPen;
else if (selectColor == 3)
currentPen = &bluePen;

selectColor++;
return 0L;
}

return DefWindowProc(hWnd, iMessage, wParam, lParam);
}

void main()
{
HWND window;
LPCWSTR myclass = L"DrawTest";

WNDCLASSEX wndclass = { sizeof(WNDCLASSEX), CS_VREDRAW | CS_HREDRAW, WindowProc,
0, 0, NULL, LoadIcon(0,IDI_APPLICATION), LoadCursor(0,IDC_ARROW), (HBRUSH)WHITE_BRUSH, 0, myclass, LoadIcon(0,IDI_APPLICATION) };
if (RegisterClassEx(&wndclass))
{
window = CreateWindowEx(WS_EX_TRANSPARENT, myclass, L"title", WS_POPUP, 0, 0, GetSystemMetrics(SM_CXFULLSCREEN), GetSystemMetrics(SM_CYFULLSCREEN), 0, 0, NULL, 0);
}

VideoCapture* pCapture = nullptr;
pCapture = new VideoCapture(0);

if (pCapture)
{
if (!pCapture->isOpened())
{
cout << "Can not open video file." << endl;
return;
}

int fps = (int)(pCapture->get(CAP_PROP_FPS));

int delay = 0;
if (fps == 0)
fps = 24;

delay = 1000 / fps;

Mat colorMat;

while (1)
{
*pCapture >> colorMat;
if (colorMat.empty())
break;

Mat copyColor;
colorMat.copyTo(copyColor);

imshow("colorMat", copyColor);

int ckey = waitKey(delay);
if (ckey == 27)
break;

if (window)
{
ShowWindow(window, SW_SHOW);
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
GetMessage(&msg, 0, 0, 0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}

cv::destroyAllWindows();
}
}


enter image description here

Answer

As I said in my comment, create a layered window:

window = CreateWindowEx(WS_EX_LAYERED, myclass, L"title", WS_POPUP, 0, 0,
                        GetSystemMetrics(SM_CXFULLSCREEN), GetSystemMetrics(SM_CYFULLSCREEN),
                        HWND_DESKTOP, NULL, NULL, NULL);

set color transparency the same as your background brush:

SetLayeredWindowAttributes(window, RGB(255, 255, 255), 0, LWA_COLORKEY);

In WM_PAINT

hdc = BeginPaint(hwnd, &ps);

HPEN OldPen = (HPEN)SelectObject(hdc, *currentPen);

//set random values
MoveToEx(hdc, 50, 50, NULL);
LineTo(hdc, 450, 450);

SelectObject(hdc, OldPen);

EndPaint(hwnd, &ps);

return 0;

This code works, BUT you cant get mouse messages cause the window is transparent. That is the main issue, not the drawing.

EDIT

The problem is how to get mouse messages. The solution is to create a second window on top of your main window with opacity nearly zero so it is not visible but get the mouse messages!

window = CreateWindowEx(WS_EX_LAYERED, myclass, L"title", WS_POPUP, 0, 0,
                        GetSystemMetrics(SM_CXFULLSCREEN), GetSystemMetrics(SM_CYFULLSCREEN),
                        HWND_DESKTOP, NULL, NULL, NULL);

windowClone = CreateWindowEx(WS_EX_LAYERED, myclass, L"title", WS_POPUP, 0, 0,
                        GetSystemMetrics(SM_CXFULLSCREEN), GetSystemMetrics(SM_CYFULLSCREEN),
                        window, NULL, NULL, NULL);

Make your main window complete transparent:

//background color MUST be the same with color Key!
SetLayeredWindowAttributes(window, RGB(255, 255, 255), 0, LWA_COLORKEY);

Make nearly transparent your clone window

//The transparency is set to 1
SetLayeredWindowAttributes(windowClone, 0, 1, LWA_ALPHA);

LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
    PAINTSTRUCT ps;
    HDC hdc;
    static int draw = FALSE, startX, startY, endX, endY, posX, posY;


    switch(message){ //handle the messages 
        case WM_PAINT:
            printf("WM_PAINT \n");

            if( hwnd == window && draw == TRUE ){
                HPEN OldPen, redPen;

                redPen = CreatePen(PS_SOLID, 4, RGB(255, 0, 0));

                hdc = BeginPaint(hwnd, &ps);

                OldPen = (HPEN)SelectObject(hdc, redPen);

                MoveToEx(hdc, startX, startY, NULL);
                LineTo(hdc, endX, endY);

                SelectObject(hdc, OldPen);

                EndPaint(hwnd, &ps);

                DeleteObject(redPen);

                return 0;
            }

            break;

        case WM_MOUSEMOVE:
            //printf("WM_MOUSEMOVE \n");

            if( hwnd == windowClone && draw == TRUE ){
                startX = posX;
                startY = posY;
                endX = GET_X_LPARAM(lParam);
                endY = GET_Y_LPARAM(lParam);

                posX = endX;
                posY = endY;

                InvalidateRect(window, NULL, FALSE);
            }

            break;

        case WM_LBUTTONDOWN:
            printf("WM_LBUTTONDOWN \n");

            if( hwnd == windowClone ){
                posX = GET_X_LPARAM(lParam);
                posY = GET_Y_LPARAM(lParam);
                draw = TRUE;
            }

            break;

       case WM_LBUTTONUP:
           printf("WM_LBUTTONUP \n");

           if( hwnd == windowClone && draw == TRUE ){
               draw = FALSE;
           }

           break;

       case WM_CAPTURECHANGED:
           printf("WM_CAPTURECHANGED \n");

           if( hwnd == windowClone && draw == TRUE ){
               draw = FALSE;
           }

           break;

        default:   //for messages that we don't deal with
            return DefWindowProc(hwnd, message, wParam, lParam);
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}
Comments