[c ++] WinApi - Простая программа рисования линий - PullRequest
0 голосов
/ 13 ноября 2018

Я новичок и пытаюсь написать простую программу рисования линий мышью. У меня проблема с прорисовкой этих линий, потому что она оставляет следы.

Вот изображение моей проблемы:

image

А вот пример моего кода:

LRESULT APIENTRY WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_DESTROY:PostQuitMessage(0);break;

        case WM_LBUTTONDOWN:
            hdc = GetDC(hwnd);
            last_x = LOWORD(lParam);
            last_y = HIWORD(lParam);
            isDown = true;
            break;
        case WM_MOUSEMOVE:
            if (isDown)
            {
                Pen = CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
                Box = (HPEN)SelectObject(hdc, Pen);
                int x = LOWORD(lParam);
                int y = HIWORD(lParam);
                MoveToEx(hdc, last_x, last_y, NULL);
                LineTo(hdc, x, y);
            }
            break;
        case WM_LBUTTONUP:
            isDown = false;
            ReleaseDC;
            break;

        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

EDIT: Сейчас это работает хорошо, но если бы Вы могли объяснить мне одну вещь, как я могу сделать так, чтобы мои старые линии оставались в области Клиента, когда я рисовал новые линии? Потому что теперь я могу нарисовать только одну линию. Должен ли я использовать растровое изображение для сохранения экрана или что-то?

EDIT: EDIT: Хорошо, я использовал Вектор, чтобы сохранить координаты каждой строки. Спасибо, ребята, за помощь!

1 Ответ

0 голосов
/ 13 ноября 2018

Вы получаете остаточные следы, потому что вы не стираете свои старые чертежи перед рисованием новой линии или, по крайней мере, обновляете last_x и last_y при каждом движении, чтобы новая линия соединялась с концом предыдущей линии, как в примере Microsoft .

Но на самом деле вам вообще не следует рисовать в окне непосредственно в обработчиках сообщений мыши. Как только окно по какой-либо причине требует перекраски, весь ваш рисунок будет потерян. Правильный способ справиться с этим - вместо этого выполнить все ваши рисунки в обработчике сообщений WM_PAINT. Используйте сообщения мыши для отслеживания информации о линии по мере необходимости, а затем выполняйте весь фактический рисунок только в WM_PAINT.

Например:

LRESULT APIENTRY WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_CREATE:
            Pen = CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
            break;

        case WM_DESTROY:
            DeleteObject(Pen);
            PostQuitMessage(0);
            break;

        case WM_LBUTTONDOWN:
            x = last_x = LOWORD(lParam);
            y = last_y = HIWORD(lParam);
            isDown = true;
            InvalidateRect(hwnd, NULL, TRUE);
            break;

        case WM_MOUSEMOVE:
            if (isDown)
            {
                x = LOWORD(lParam);
                y = HIWORD(lParam);
                InvalidateRect(hwnd, NULL, TRUE);
            }
            break;

        case WM_LBUTTONUP:
            isDown = false;
            InvalidateRect(hwnd, NULL, TRUE);
            break;

        /* if your WNDCLASS sets hbrBackground=NULL, uncomment this handler...
        case WM_ERASEBKGND:
        {
            HDC hdc = (HDC) wParam; 
            draw a background on the hdc as needed...
            return 1;
        }
        */

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            if (last_x != x) || (last_y != y)
            {
                HPEN OldPen = (HPEN) SelectObject(hdc, Pen);
                MoveToEx(hdc, last_x, last_y, NULL);
                LineTo(hdc, x, y);
                SelectObject(hdc, OldPen);
            }

            EndPaint(hwnd, &ps);
            break;
        }

        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

Это нарисует одну линию, которая начинается в точке, где мышь сначала удерживалась, а затем следует за мышью, когда она перемещается.

Или, если вы хотите нарисовать несколько сквозных линий, которые следуют за мышью, пока она удерживается, попробуйте следующее:

std::vector<POINT> points;

LRESULT APIENTRY WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_CREATE:
            Pen = CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
            points.clear();
            break;

        case WM_DESTROY:
            DeleteObject(Pen);
            PostQuitMessage(0);
            break;

        case WM_LBUTTONDOWN:
        {
            points.clear();
            POINT pt;
            pt.x = LOWORD(lParam);
            pt.y = HIWORD(lParam);
            points.push_back(pt);
            isDown = true;
            InvalidateRect(hwnd, NULL, TRUE);
            break;
        }

        case WM_MOUSEMOVE:
            if (isDown)
            {
                POINT pt;
                pt.x = LOWORD(lParam);
                pt.y = HIWORD(lParam);
                points.push_back(pt);
                InvalidateRect(hwnd, NULL, TRUE);
            }
            break;

        case WM_LBUTTONUP:
            isDown = false;
            InvalidateRect(hwnd, NULL, TRUE);
            break;

        /* if your WNDCLASS sets hbrBackground=NULL, uncomment this handler...
        case WM_ERASEBKGND:
        {
            HDC hdc = (HDC) wParam; 
            draw a background on the hdc as needed...
            return 1;
        }
        */

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            if (points.size() > 1)
            {
                HPEN OldPen = (HPEN) SelectObject(hdc, Pen);
                MoveToEx(hdc, points[0].x, points[0].y, NULL);
                for (size_t i = 1; i < points.size(); ++i) {
                    LineTo(hdc, points[i].x, points[i].y);
                }
                SelectObject(hdc, OldPen);
            }

            EndPaint(hwnd, &ps);
            break;
        }

        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}
...