2012-06-18 3 views
2

Я хочу нарисовать много строк в обработчике сообщений WM_PAINT со следующим кодом.Рисунок (слишком медленный) в WM_PAINT вызывает мерцание?

//DrawLine with double buffering 
LRESULT CALLBACK CMyDoc::OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) 
{ 
    std::vector<Gdiplus::Point> points; 
    std::vector<Gdiplus::Point>::iterator iter1, iter2; 
    HDC hdc, hdcMem; 
    HBITMAP hbmScreen, hbmOldBitmap; 
    PAINTSTRUCT ps; 
    RECT rect; 

    hdc = BeginPaint(hWnd, &ps); 

    //Create memory dc 
    hdcMem = CreateCompatibleDC(hdc); 
    GetClientRect(hWnd, &rect); 
    hbmScreen = CreateCompatibleBitmap(hdc, rect.right, rect.bottom); 
    hbmOldBitmap = (HBITMAP)SelectObject(hdcMem, hbmScreen); 

    //Fill the rect with white 
    FillRect(hdcMem, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); 

    //Draw the lines 
    Gdiplus::Graphics graphics(hdcMem); 
    Gdiplus::Pen blackPen(Gdiplus::Color(255, 0, 0)); 

    points = m_pPolyLine->GetPoints(); 
    for (iter1 = points.begin(); iter1 != points.end(); iter1++) { 
     for (iter2 = iter1 + 1; iter2 != points.end(); iter2++) 
      graphics.DrawLine(&blackPen, *iter1, *iter2); 
    } 

    //Copy the bitmap from memory dc to the real dc 
    BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY); 

    //Clean up 
    SelectObject(hdcMem, hbmOldBitmap); 
    DeleteObject(hbmScreen); 
    DeleteDC(hdcMem); 

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

Однако, если размер точек превышает 20, клиентский адрес просто мерцает. Я думаю, причина в том, что Gdiplus :: DrawLines слишком медленный.

Есть ли способ решить проблему мерцания? Спасибо.

+0

Он не должен быть связан с «скоростью» рисования линий из-за окончательного бита, если только hdcMem не привязан непосредственно к hdc. Возможно, что-то еще происходит? (например, «слезы» в BitBlt) –

ответ

4

Мерцание может быть вызвано медленной покраской, а также другими вещами. В общем, попробуйте/обеспечить следующее:

  • Старайтесь не полагаться на WM_ERASEBKGND сообщения, т.е. возвращают ненулевое, либо указать NULL в WNDCLASS::hbrBackground, если это возможно. Часто метод краски рисует весь фон грязной области, тогда нет необходимости делать стирание.

  • Если вам необходимо удаление, это часто может быть оптимизировано таким образом, что WM_ERASEBKGND возвращает ненулевой, и метод краски затем обеспечивает «стирание» по также малярным районам, не охваченных регулярным окрашенного содержанием, если PAINTSTRUCT::fErase установлен ,

  • Если возможно, напишите метод краски, чтобы он не перекрашивал одинаковые пиксели за один вызов. Например. сделать синий прямоугольник с красной рамкой, не FillRect(red), а затем перекрасить внутреннюю часть его FillRect(blue). Попытайтесь нарисовать каждый пиксель один раз настолько, насколько это возможно.

  • Для сложных элементов управления/окон метод окраски часто может быть оптимизирован, чтобы легко пропускать много рисунков за пределами грязного прямоугольника (PAINTSTRUCT::rcPaint) путем правильной организации управляющих данных.

  • При изменении состояния управления аннулировать только минимально требуемую область элемента управления.

  • Если это не окно верхнего уровня, рассмотрите возможность использования CS_PARENTDC. Если ваш метод рисования не полагается на отсекающий прямоугольник системы клиенту rect элемента управления, этот стиль класса приведет к несколько лучшей производительности.

  • Если вы видите мерцание при изменении размера окна управления/окна, считайте, что не используете CS_HREDRAW и CS_VREDRAW. Вместо этого аннулируйте соответствующие части элемента управления в WM_SIZE вручную. Это часто позволяет аннулировать только более мелкие части элемента управления.

  • Если вы видите мерцание в контрольной прокрутке, не делайте недействительным весь клиент, но используйте ScrollWindow() и аннулируйте только небольшую область, которая предоставляет новый (прокрученный) контент.

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

3

Использовать двойной буфер. Это проблема с приложением Win32 C++ и, в частности, с функциями OnPaint и DC.

Вот несколько ссылок, которые помогут вам проверить, все ли в порядке с вашей реализации двойного буфера: Flicker Free Drawing In MFC и SO вопрос «Reduce flicker with GDI+ and C++»

+0

Но это использует BitBlt в конце ... не так ли эффективно doublebuffer? (То есть, этот ответ требует большего расширения и/или поддержки.) –

+0

Да, я вижу. Сейчас я не могу дать более подробный ответ. Но несколько лет назад я написал приложение (C++, MFC, GDI +), которое отлично работает с линейной графикой с примерно 10000 точками или даже больше (в случае, если пользователь выбирает бит более большой промежуток времени) – gahcep

-1

Проблема в том, я не обрабатывать WM_ERASEBKGND сообщение сам.

0

Если ваши линии проходят вне границ DC (графика), Win32/GDI + болезненно медленно при отсечении. Например, на два порядка медленнее, чем сканирование собственной функции отсечения. Вот какой-то код на C#, который реализует Liang/Barsky - я искривил это из старой библиотеки, которая первоначально была на C++ 20 лет назад. Должно быть достаточно легко, чтобы вернуться обратно.

Если ваши строки могут выходить за пределы клиентского прямоугольника, вызовите ClipLine (rect, ...) в своих точках, прежде чем передавать их в Graphics :: DrawLine.

private static bool clipTest(double dp, double dq, ref double du1, ref double du2) 
{ 
    double dr; 
    if (dp < 0.0) 
    { 
     dr = dq/dp; 

     if (dr > du2) 
     { 
      return false; 
     } 
     else if (dr > du1) 
     { 
      du1 = dr; 
     } 
    } 
    else 
    { 
     if (dp > 0.0) 
     { 
      dr = dq/dp; 
      if (dr < du1) 
      { 
       return false; 
      } 
      else if (dr < du2) 
      { 
       du2 = dr; 
      } 
     } 
     else 
     { 
      if (dq < 0.0) 
      { 
       return false; 
      } 
     } 
    } 

    return true; 
} 

public static bool ClipLine(Rectangle clipRect, ref int x1, ref int y1, ref int x2, ref int y2) 
{ 
    double dx1 = (double)x1; 
    double dx2 = (double)x2; 
    double dy1 = (double)y1; 
    double dy2 = (double)y2; 

    double du1 = 0; 
    double du2 = 1; 
    double deltaX = dx2 - dx1; 
    double deltaY; 

    if (clipTest(-deltaX, dx1 - clipRect.Left, ref du1, ref du2)) 
    { 
     if (clipTest(deltaX, clipRect.Right - dx1, ref du1, ref du2)) 
     { 
      deltaY = dy2 - dy1; 
      if (clipTest(-deltaY, dy1 - clipRect.Top, ref du1, ref du2)) 
      { 
       if (clipTest(deltaY, clipRect.Bottom - dy1, ref du1, ref du2)) 
       { 
        if (du2 < 1.0) 
        { 
         x2 = DoubleRoundToInt(dx1 + du2 * deltaX); 
         y2 = DoubleRoundToInt(dy1 + du2 * deltaY); 
        } 
        if (du1 > 0.0) 
        { 
         x1 = DoubleRoundToInt(dx1 + du1 * deltaX); 
         y1 = DoubleRoundToInt(dy1 + du1 * deltaY); 
        } 

        return x1 != x2 || y1 != y2; 
       } 
      } 
     } 
    } 
    return false; 
} 
Смежные вопросы