Масштабирование растровых изображений WinAPI

Есть окно с областью где можно рисовать мышкой.

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

То есть я рисую например букву А в первой области размером 300x300, потом например после нажатия кнопки "Сжать" во второй области размером 64x64 появляется сжатый рисунок А.

Вот код окна:

#include <Windows.h>
#include <vector>

#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 800
#define WINDOW_XPOS (GetSystemMetrics(SM_CXSCREEN) >> 1) - (WINDOW_WIDTH >> 1)
#define WINDOW_YPOS (GetSystemMetrics(SM_CYSCREEN) >> 1) - (WINDOW_HEIGHT >> 1)
#define CHILD_WIDTH 300
#define CHILD_HEIGHT 300
#define PADDING_L 50
#define PADDING_B 50

HINSTANCE hInst;
RECT clientRect;

HBRUSH WINDOW_BACKGROUND = CreateSolidBrush(RGB(41, 49, 51));
HBRUSH CHILD_BACKGROUND = CreateSolidBrush(RGB(76, 81, 74));
HBRUSH BRUSH_COLOR = CreateSolidBrush(RGB(213, 213, 213));
HPEN hPen = CreatePen(PS_NULL, 0, NULL);

HDC child_hdc;
HDC hdc;

std::vector<std::vector<bool>> pixels = {};

LRESULT CALLBACK ChildProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    static int x = 0, y = 0, x_prev = 0, y_prev = 0, N = 10;
    static BOOL fDrawEllipse;
    static HBITMAP hBMP;
    static HDC hMemDC;

    switch (msg) {
    case WM_CREATE: {
        child_hdc = GetDC(hwnd);

        hMemDC = CreateCompatibleDC(child_hdc);
        hBMP = CreateCompatibleBitmap(child_hdc, CHILD_WIDTH, CHILD_HEIGHT);
        SelectObject(hMemDC, hBMP);

        SelectObject(hMemDC, hPen);
        SelectObject(hMemDC, BRUSH_COLOR);
        FillRect(hMemDC, &clientRect, CHILD_BACKGROUND);
        return 0;
    }
    case WM_LBUTTONDOWN: {
        x = LOWORD(lParam);
        y = HIWORD(lParam);
        fDrawEllipse = true;
        Ellipse(hMemDC, x - 5, y - 5, x + 5, y + 5);
        BitBlt(child_hdc, 0, 0, CHILD_WIDTH, CHILD_HEIGHT, hMemDC, 0, 0, SRCCOPY);
        return 0;
    }
    case WM_PAINT: {
        BitBlt(child_hdc, 0, 0, CHILD_WIDTH, CHILD_HEIGHT, hMemDC, 0, 0, SRCCOPY);
        return 0;
    }
    case WM_MOUSEMOVE: {
        if (fDrawEllipse) {
            x_prev = x;
            y_prev = y;
            x = LOWORD(lParam); 
            y = HIWORD(lParam);
            for (int i = 0; i < N; ++i) {
                int px = x_prev + (x - x_prev) * i / N;
                int py = y_prev + (y - y_prev) * i / N;
                Ellipse(hMemDC, px - 5, py - 5, px + 5, py + 5);
                BitBlt(child_hdc, 0, 0, CHILD_WIDTH, CHILD_HEIGHT, hMemDC, 0, 0, SRCCOPY);
            }
        }
        return 0;
    }
    case WM_LBUTTONUP: {
        fDrawEllipse = false;
        x_prev = 0; y_prev = 0; x = 0; y = 0;
        StretchBlt(hdc, 0, 0, 64, 64, child_hdc, 0, 0, CHILD_WIDTH, CHILD_HEIGHT, SRCCOPY);
        return 0;
    }
    case WM_DESTROY: {
        ReleaseDC(hwnd, child_hdc);
        DeleteObject(WINDOW_BACKGROUND);
        DeleteObject(CHILD_BACKGROUND);
        DeleteObject(BRUSH_COLOR);
        DeleteObject(hPen);
        DeleteDC(hMemDC);
        DeleteDC(child_hdc);
        PostQuitMessage(0);
        return 0;
    }
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    static HBITMAP hBMP;
    switch (msg) {
    case WM_CREATE: {

        hdc = GetDC(hwnd);

        GetClientRect(hwnd, &clientRect);

        WNDCLASSEX wc{};
        wc.cbSize = sizeof(WNDCLASSEX);
        wc.lpfnWndProc = ChildProc;
        wc.hInstance = hInst;
        wc.hbrBackground = CHILD_BACKGROUND;
        wc.lpszClassName = "ChildClass";
        wc.hCursor = LoadCursor(nullptr, IDC_CROSS);

        RegisterClassEx(&wc);

        HWND child = CreateWindowEx(0, wc.lpszClassName, (LPCTSTR)nullptr, WS_CHILD | WS_BORDER | WS_VISIBLE,
                                    clientRect.left + PADDING_L, clientRect.bottom - CHILD_HEIGHT - PADDING_B, CHILD_WIDTH, CHILD_HEIGHT, 
                                    hwnd, nullptr, hInst, nullptr);

        ShowWindow(child, SW_NORMAL);
        UpdateWindow(child);

        return 0;
    }
    case WM_DESTROY: {
        ReleaseDC(hwnd, hdc);
        PostQuitMessage(0);
        return 0;
    }
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE,
                   _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {
    HWND hwnd{};
    MSG msg{};
    WNDCLASSEX wc;

    hInst = hInstance;

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = WINDOW_BACKGROUND;
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    wc.hIconSm = nullptr;
    wc.hInstance = hInstance;
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = "WindowClass";
    wc.lpszMenuName = nullptr;
    wc.style = CS_VREDRAW | CS_HREDRAW;

    if (!RegisterClassEx(&wc))
        return EXIT_FAILURE;

    hwnd = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_TOPMOST, wc.lpszClassName, "Окно windows",
                          WS_OVERLAPPEDWINDOW, WINDOW_XPOS, WINDOW_YPOS, WINDOW_WIDTH, WINDOW_HEIGHT,
                          nullptr, nullptr, hInstance, nullptr);

    if (hwnd == INVALID_HANDLE_VALUE)
        return EXIT_FAILURE;

    ShowWindow(hwnd, nShowCmd);
    UpdateWindow(hwnd);

    BOOL bRet;
    while (bRet = GetMessage(&msg, nullptr, 0, 0)) {
        if (bRet == -1)
            return EXIT_FAILURE;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

Я знаю про функцию StretchBlt, но когда пытался разобраться появилось очень много проблем.


Ответы (1 шт):

Автор решения: Павел Ериков

Вот полностью рабочий код, который мне нужен был.

#include <Windows.h>
#include <vector>

#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 800
#define WINDOW_XPOS (GetSystemMetrics(SM_CXSCREEN) >> 1) - (WINDOW_WIDTH >> 1)
#define WINDOW_YPOS (GetSystemMetrics(SM_CYSCREEN) >> 1) - (WINDOW_HEIGHT >> 1)
#define CHILD_WIDTH 300
#define CHILD_HEIGHT 300
#define PADDING_L 50
#define PADDING_B 50

HINSTANCE hInst;
RECT clientRect;

HBRUSH WINDOW_BACKGROUND = CreateSolidBrush(RGB(41, 49, 51));
HBRUSH CHILD_BACKGROUND = CreateSolidBrush(RGB(76, 81, 74));
HBRUSH BRUSH_COLOR = CreateSolidBrush(RGB(213, 213, 213));
HPEN hPen = CreatePen(PS_NULL, 0, NULL);

HDC child_hdc;
HDC hdc;

std::vector<std::vector<bool>> pixels = {};

LRESULT CALLBACK ChildProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    static int x = 0, y = 0, x_prev = 0, y_prev = 0, N = 20;
    static BOOL fDrawEllipse;
    static HBITMAP hBMP;
    static HDC hMemDC;

    switch (msg) {
    case WM_CREATE: {
        child_hdc = GetDC(hwnd);

        hMemDC = CreateCompatibleDC(child_hdc);
        hBMP = CreateCompatibleBitmap(child_hdc, CHILD_WIDTH, CHILD_HEIGHT);
        SelectObject(hMemDC, hBMP);

        SelectObject(hMemDC, hPen);
        SelectObject(hMemDC, BRUSH_COLOR);
        FillRect(hMemDC, &clientRect, CHILD_BACKGROUND);
        return 0;
    }
    case WM_LBUTTONDOWN: {
        x = LOWORD(lParam);
        y = HIWORD(lParam);
        fDrawEllipse = true;
        Ellipse(hMemDC, x - 5, y - 5, x + 5, y + 5);
        BitBlt(child_hdc, 0, 0, CHILD_WIDTH, CHILD_HEIGHT, hMemDC, 0, 0, SRCCOPY);
        return 0;
    }
    case WM_PAINT: {
        BitBlt(child_hdc, 0, 0, CHILD_WIDTH, CHILD_HEIGHT, hMemDC, 0, 0, SRCCOPY);
        return 0;
    }
    case WM_MOUSEMOVE: {
        if (fDrawEllipse) {
            x_prev = x;
            y_prev = y;
            x = LOWORD(lParam); 
            y = HIWORD(lParam);
            for (int i = 0; i < N; ++i) {
                int px = x_prev + (x - x_prev) * i / N;
                int py = y_prev + (y - y_prev) * i / N;
                Ellipse(hMemDC, px - 5, py - 5, px + 5, py + 5);
                BitBlt(child_hdc, 0, 0, CHILD_WIDTH, CHILD_HEIGHT, hMemDC, 0, 0, SRCCOPY);
            }
        }
        return 0;
    }
    case WM_LBUTTONUP: {
        fDrawEllipse = false;
        x_prev = 0; y_prev = 0; x = 0; y = 0;
        return 0;
    }
    case WM_DESTROY: {
        ReleaseDC(hwnd, child_hdc);
        DeleteObject(WINDOW_BACKGROUND);
        DeleteObject(CHILD_BACKGROUND);
        DeleteObject(BRUSH_COLOR);
        DeleteObject(hPen);
        DeleteDC(hMemDC);
        DeleteDC(child_hdc);
        PostQuitMessage(0);
        return 0;
    }
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    static HBITMAP hBMP;
    static HWND child;
    switch (msg) {
    case WM_CREATE: {

        hdc = GetDC(hwnd);

        GetClientRect(hwnd, &clientRect);

        WNDCLASSEX wc{};
        wc.cbSize = sizeof(WNDCLASSEX);
        wc.lpfnWndProc = ChildProc;
        wc.hInstance = hInst;
        wc.hbrBackground = CHILD_BACKGROUND;
        wc.lpszClassName = "ChildClass";
        wc.hCursor = LoadCursor(nullptr, IDC_CROSS);

        RegisterClassEx(&wc);

        child = CreateWindowEx(0, wc.lpszClassName, (LPCTSTR)nullptr, WS_CHILD | WS_BORDER | WS_VISIBLE,
                                    clientRect.left + PADDING_L, clientRect.bottom - CHILD_HEIGHT - PADDING_B, CHILD_WIDTH, CHILD_HEIGHT, 
                                    hwnd, nullptr, hInst, nullptr);

        ShowWindow(child, SW_NORMAL);
        UpdateWindow(child);

        return 0;
    }
    case WM_PAINT: {
        StretchBlt(hdc, 0, 0, 64, 64, child_hdc, 0, 0, CHILD_WIDTH, CHILD_HEIGHT, SRCCOPY);
        UpdateWindow(child);
        return 0;
    }
    case WM_DESTROY: {
        ReleaseDC(hwnd, hdc);
        PostQuitMessage(0);
        return 0;
    }
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE,
                   _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {
    HWND hwnd{};
    MSG msg{};
    WNDCLASSEX wc;

    hInst = hInstance;

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = WINDOW_BACKGROUND;
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    wc.hIconSm = nullptr;
    wc.hInstance = hInstance;
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = "WindowClass";
    wc.lpszMenuName = nullptr;
    wc.style = CS_VREDRAW | CS_HREDRAW;

    if (!RegisterClassEx(&wc))
        return EXIT_FAILURE;

    hwnd = CreateWindowEx(WS_EX_WINDOWEDGE, wc.lpszClassName, "Окно windows",
                          WS_OVERLAPPEDWINDOW, WINDOW_XPOS, WINDOW_YPOS, WINDOW_WIDTH, WINDOW_HEIGHT,
                          nullptr, nullptr, hInstance, nullptr);

    if (hwnd == INVALID_HANDLE_VALUE)
        return EXIT_FAILURE;

    ShowWindow(hwnd, nShowCmd);
    UpdateWindow(hwnd);

    BOOL bRet;
    while (bRet = GetMessage(&msg, nullptr, 0, 0)) {
        if (bRet == -1)
            return EXIT_FAILURE;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

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

Возможно выглядит костыльно, если так, то приведите в виде ответа пример хорошего кода. Буду очень рад!) И если будет так же работать, то выберу ваш ответ как правильный.

→ Ссылка