2012-06-18 1 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

깜박임은 느린 그림과 다른 것들로 인해 발생할 수 있습니다. 일반적으로, 다음을 확인/시도 : 가능하면

  • 시도가 WNDCLASS::hbrBackgroundNULL을 즉, WM_ERASEBKGND 메시지에 의존이 아닌 0을 반환하거나 지정하지. 종종 페인트 방법은 더러운 영역의 모든 배경을 칠합니다. 그런 다음 지우기를 수행 할 필요가 없습니다. 당신은 지우기가 필요한 경우

  • , 그것은 종종 최적화 할 수 있도록 그 WM_ERASEBKGND 반환 0이 아닌 및 PAINTSTRUCT::fErase가 설정되어있는 경우 페인트 방법은 다음 정규 그린 내용에 포함되지도 그림 영역으로 "지우기"를 보장 .

  • 합리적으로 가능한 경우 한 번의 호출로 동일한 픽셀을 다시 채우지 않도록 paint 메서드를 작성합니다. 예 : 빨간색 테두리가있는 파란색 직사각형을 만들려면 FillRect(red)을 사용하지 말고 FillRect(blue)으로 안쪽 부분을 다시 그립니다. 합리적으로 가능한 한 각 픽셀을 한 번 페인트합니다.

  • 복잡한 컨트롤/윈도우의 경우 컨트롤 데이터를 적절하게 구성하여 더러운 사각형 (PAINTSTRUCT::rcPaint) 외부의 많은 그림을 쉽게 건너 뛰도록 페인트 메서드를 최적화 할 수 있습니다.

  • 컨트롤 상태를 변경하는 경우 컨트롤의 최소 필수 영역 만 무효화하십시오.

  • 최상위 창이 아닌 경우 CS_PARENTDC을 사용해보십시오. 페인트 메서드가 컨트롤의 클라이언트 rect에 대한 시스템 클리핑 사각형 설정에 의존하지 않는 경우이 클래스 스타일은 다소 우수한 성능을 제공합니다.

  • 제어/창 크기 조정시 깜박 거림이 표시되는 경우 CS_HREDRAWCS_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

예, 알 수 있습니다. 지금 나는 더 상세한 대답을 줄 수는 없다. 하지만 몇 년 전에 나는 대략 10000 포인트 이상의 라인 그래픽을 가지고 아주 잘 동작하는 어플리케이션 (C++, MFC, GDI +)을 작성했다. (사용자가 조금 더 큰 시간을 선택한다면) – gahcep

-1

문제는 내가 처리하지 않은 것입니다 WM_ERASEBKGND 메시지 나 자신에 의해.

0

라인이 DC (그래픽) 경계 밖으로 확장되는 경우 클리핑시 Win32/GDI +가 너무 느립니다. 마찬가지로, 자신의 클리핑 기능을 롤링하는 것보다 최대 2 배까지 느립니다. Liang/Barsky를 구현 한 몇 가지 C# 코드가 있습니다. 20 년 전 원래 C++에 있던 이전 라이브러리에서이 라이브러리를 찾아 냈습니다. 포트를 다시 열 수있을만큼 쉬워야합니다.

선이 클라이언트 직사각형 너머까지 확장 될 수 있으면 GraphicsLineLine에 전달하기 전에 점에서 ClipLine (rect, ...)을 호출하십시오.

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; 
} 
관련 문제