Есть ли способ обновить объект Graphics после изменения размера растрового изображения, связанного с HDC? - PullRequest
1 голос
/ 20 апреля 2019

1.Проблема

У меня есть два буфера.Первичный буфер, который отображается на экране, и вторичный буфер, где все отрисовывается, а затем передается первичному.

Объект Graphics создается из вторичного буфера, который связан с растровым изображением размером 800x600.Естественно, когда вы изменяете размер окна, размер растрового изображения должен меняться, чтобы избежать проблем с отсечкой.Вторичный HDC обновляется, и растровое изображение копируется в первичное.

Проблема заключается в том, что в объекте Graphics осталось что-то, связанное со вторичным HDC, которое генерирует отсечение.Область рисования по-прежнему остается 800x600, несмотря на то, что она уже обновлена ​​до чего-то большего (1000x1000).

Мой вопрос заключается в том, что я должен обновить внутри объекта Graphics (без необходимости явно воссоздавать его из существующего HDC), чтобы сделатьего область рисования соответствует размеру растрового изображения.

2.Что я попробовал

Первое, что я попробовал, это воссоздание объекта Graphics из обновленного HDC.Этот метод работает, и область рисования соответствует размеру растрового изображения.Это не соответствует стандарту дизайна как бы то ни было.Графика должна быть многоразовой.

Я также попытался обновить область отсечения графического объекта с помощью метода SetClip.Хотя, похоже, проблема не в этом.

Так я создаю буферы.

HDC CoreWindowFrame::InitPaint(HWND hWnd)
{
    windowHdc = GetDC(hWnd);
    HDC secondaryBuffer = CreateCompatibleDC(windowHdc);
    HBITMAP map = CreateCompatibleBitmap(windowHdc, width, height);
    SelectObject(secondaryBuffer, map);
    return secondaryBuffer;
}

Эта функция вызывается при изменении размера

void CoreWindowFrame::UpdateBitmap(int width, int height)
{
    HBITMAP map = CreateCompatibleBitmap(windowHdc, width, height);
    SelectObject(secondaryBuffer, map);

}

И этоэто обработка сообщений:

case WM_SIZE:
    ConsoleWrite("WM_SIZE RECIEVED");
    width = *((unsigned short*)&lParam);
    height = ((unsigned short*)&lParam)[1];
    UpdateBitmap(width, height);

break;
case WM_PAINT:
    ConsoleWrite("WM_PAINT RECIEVE");

    windowGraphics->Clear(Color(255, 255, 255));

    Pen* testPen = new Pen(Color(255, 0, 0), 1.0F);

    windowGraphics->DrawLine(testPen, 0, 0, calls*3, 100);
    BitBlt(windowHdc, 0, 0, updatedWidth, updatedHeight, secondaryBuffer, 0, 0, MERGECOPY);

3.Ожидаемый результат

Графический объект должен быть многоразовым, его нельзя выбрасывать и заменять новым каждый раз, когда что-то обновляется.Поэтому он должен быть обновлен соответствующим образом в случае, если что-либо изменяется или изменяется.Я ожидаю, что регион будет соответствовать размеру обновленного растрового изображения во вторичном HDC.Растровое изображение, как видно из кода, обновляется должным образом.Это графический объект, который не делает.Он действует так, как будто все еще запоминает некоторые значения из предыдущего BitMap, что приводит к такому поведению.

1 Ответ

1 голос
/ 21 апреля 2019
windowHdc = GetDC(hWnd);
HDC secondaryBuffer = CreateCompatibleDC(windowHdc);
HBITMAP map = CreateCompatibleBitmap(windowHdc, width, height);
SelectObject(secondaryBuffer, map);
return secondaryBuffer;

HDC, полученное из GetDC или BeginPaint, не может быть повторно использовано, как отмечено в комментариях.

Однако вы можете повторно использовать растровое изображение памяти (из CreateCompatibleBitmap) и повторно использовать память постоянного тока (полученную из CreateCompatibleDC), хотя обычно нет смысла повторно использовать память постоянного тока.

Кроме того, очисткатребуется, чтобы избежать утечки ресурсов.Позвоните ReleaseDC, когда вы закончите с GetDCСм. Документацию для соответствующих функций деблокирования / удаления.

Сделайте это для простой процедуры рисования:

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

    Gdiplus::Graphics gr(hdc);
    Gdiplus::Pen testPen(Gdiplus::Color(255, 0, 0), 1.0F);
    gr.Clear(Gdiplus::Color::White);
    gr.DrawLine(&testPen, 0, 0, 100, 100);

    EndPaint(hwnd, &ps);
    return 0;
}

Обычно вам больше ничего не нужно делать.Просто позвоните InvalidateRect в ответ на WM_SIZE для обновления краски.

Для двойной буферизации или рисования на холсте вы можете создать растровое изображение памяти и использовать его повторно.Если размер окна изменяется, вам нужно вызвать DeleteObject для старого растрового изображения и создать новое растровое изображение на основе нового размера.Или вы можете создать растровое изображение, которое соответствует наибольшему размеру окна, а затем использовать это растровое изображение для всех размеров окна.

См. Приведенный ниже пример для рисования с двойным буфером (однако повторное использование hbitmap в этом случае не требуется)

HBITMAP hbitmap = NULL;

void InitPaint(HWND hwnd)
{
    HDC hdc = GetDC(hwnd);
    //create a bitmap with max size:
    int w = GetSystemMetrics(SM_CXFULLSCREEN); 
    int h = GetSystemMetrics(SM_CYFULLSCREEN); 
    hbitmap = CreateCompatibleBitmap(hdc, w, h);
    ReleaseDC(hwnd, hdc);
}

void cleanup()
{
    if (hbitmap)
        DeleteObject(hbitmap);
}

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

    RECT rc; GetClientRect(hwnd, &rc);
    int w = rc.right;
    int h = rc.bottom;
    HDC memdc = CreateCompatibleDC(hdc);
    HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);

    Gdiplus::Graphics gr(memdc);
    Gdiplus::Pen testPen(Gdiplus::Color(255, 0, 0), 1.0F);
    gr.Clear(Gdiplus::Color(255, 255, 255));
    gr.DrawLine(&testPen, 0, 0, w, h);

    BitBlt(hdc, 0, 0, w, h, memdc, 0, 0, SRCCOPY);

    //cleanup:
    SelectObject(memdc, oldbmp);
    DeleteDC(memdc);

    EndPaint(hwnd, &ps);
    return 0;
}


Резюме:

GetDC ничего не создает.Он возвращает только ссылку на существующий дескриптор, который используется системой.Когда вы закончите с этой ручкой, отпустите ее.Дальнейшая оптимизация невозможна.

Другие объекты GDI, такие как перо или растровое изображение, могут быть созданы для вашей программы, и их можно использовать повторно.Создание / уничтожение пера занимает всего несколько наносекунд, поэтому обычно не стоит добавлять сложность для хранения этих объектов и отслеживания.

Основная проблема - когда вы рисуете на экране,например, рисование линии.Вы должны поговорить с видеокартой и поговорить с монитором, что занимает некоторое время.Вы можете использовать двойную буферизацию для рисования на растровом изображении, а затем BitBlt на экране.BitBlt может быть медленным, в зависимости от системы.

Для анимации используйте двойную буферизацию, если вы видите мерцание.

Для повышения производительности вы можете использовать более новые технологии, такие как Direct2D

Если анимация все еще слишком медленная, рассмотрите возможность использования второго потока для выполнения любых вычислений математического типа (второй поток не должен ссылаться на какие-либодескриптор окна, например HDC от GetDC или BeginPaint)

...