DuiLib(四)——控件绘制

duilib的所有控件均绘制在唯一的真实窗口之中,本篇就具体看下这个绘制的过程。所有的绘制过程均在WM_PAINT消息处理过程中完成。由窗口及消息篇可以看到,窗口消息处理最终流到了CPaintManagerUI::MessageHandler中。包括WM_PAINT在内消息均在此函数中处理,我们仅关注WM_PAINT消息

  1     case WM_PAINT:
  2         {
  3             // Should we paint?
  4             RECT rcPaint = { 0 };
  5             if( !::GetUpdateRect(m_hWndPaint, &rcPaint, FALSE) ) return true;
  6             if( m_pRoot == NULL ) {
  7                 PAINTSTRUCT ps = { 0 };
  8                 ::BeginPaint(m_hWndPaint, &ps);
  9                 ::EndPaint(m_hWndPaint, &ps);
 10                 return true;
 11             }            
 12             // Do we need to resize anything?
 13             // This is the time where we layout the controls on the form.
 14             // We delay this even from the WM_SIZE messages since resizing can be
 15             // a very expensize operation.
 16             if( m_bUpdateNeeded ) {
 17                 m_bUpdateNeeded = false;
 18                 RECT rcClient = { 0 };
 19                 ::GetClientRect(m_hWndPaint, &rcClient);
 20                 if( !::IsRectEmpty(&rcClient) ) {
 21                     if( m_pRoot->IsUpdateNeeded() ) {
 22                         m_pRoot->SetPos(rcClient);
 23                         if( m_hDcOffscreen != NULL ) ::DeleteDC(m_hDcOffscreen);
 24                         if( m_hDcBackground != NULL ) ::DeleteDC(m_hDcBackground);
 25                         if( m_hbmpOffscreen != NULL ) ::DeleteObject(m_hbmpOffscreen);
 26                         if( m_hbmpBackground != NULL ) ::DeleteObject(m_hbmpBackground);
 27                         m_hDcOffscreen = NULL;
 28                         m_hDcBackground = NULL;
 29                         m_hbmpOffscreen = NULL;
 30                         m_hbmpBackground = NULL;
 31                     }
 32                     else {
 33                         CControlUI* pControl = NULL;
 34                         while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) ) {
 35                             pControl->SetPos( pControl->GetPos() );
 36                         }
 37                     }
 38                     // We'll want to notify the window when it is first initialized
 39                     // with the correct layout. The window form would take the time
 40                     // to submit swipes/animations.
 41                     if( m_bFirstLayout ) {
 42                         m_bFirstLayout = false;
 43                         SendNotify(m_pRoot, _T("windowinit"),  0, 0, false);
 44                     }
 45                 }
 46             }
 47             // Set focus to first control?
 48             if( m_bFocusNeeded ) {
 49                 SetNextTabControl();
 50             }
 51             //
 52             // Render screen
 53             //
 54             // Prepare offscreen bitmap?
 55             if( m_bOffscreenPaint && m_hbmpOffscreen == NULL )
 56             {
 57                 RECT rcClient = { 0 };
 58                 ::GetClientRect(m_hWndPaint, &rcClient);
 59                 m_hDcOffscreen = ::CreateCompatibleDC(m_hDcPaint);
 60                 m_hbmpOffscreen = ::CreateCompatibleBitmap(m_hDcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); 
 61                 ASSERT(m_hDcOffscreen);
 62                 ASSERT(m_hbmpOffscreen);
 63             }
 64             // Begin Windows paint
 65             PAINTSTRUCT ps = { 0 };
 66             ::BeginPaint(m_hWndPaint, &ps);
 67             if( m_bOffscreenPaint )
 68             {
 69                 HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(m_hDcOffscreen, m_hbmpOffscreen);
 70                 int iSaveDC = ::SaveDC(m_hDcOffscreen);
 71                 if( m_bAlphaBackground ) {
 72                     if( m_hbmpBackground == NULL ) {
 73                         RECT rcClient = { 0 };
 74                         ::GetClientRect(m_hWndPaint, &rcClient);
 75                         m_hDcBackground = ::CreateCompatibleDC(m_hDcPaint);;
 76                         m_hbmpBackground = ::CreateCompatibleBitmap(m_hDcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); 
 77                         ASSERT(m_hDcBackground);
 78                         ASSERT(m_hbmpBackground);
 79                         ::SelectObject(m_hDcBackground, m_hbmpBackground);
 80                         ::BitBlt(m_hDcBackground, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,
 81                             ps.rcPaint.bottom - ps.rcPaint.top, ps.hdc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
 82                     }
 83                     else
 84                         ::SelectObject(m_hDcBackground, m_hbmpBackground);
 85                     ::BitBlt(m_hDcOffscreen, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,
 86                         ps.rcPaint.bottom - ps.rcPaint.top, m_hDcBackground, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
 87                 }
 88                 m_pRoot->DoPaint(m_hDcOffscreen, ps.rcPaint);//绘制控件
 89                 for( int i = 0; i < m_aPostPaintControls.GetSize(); i++ ) {
 90                     CControlUI* pPostPaintControl = static_cast<CControlUI*>(m_aPostPaintControls[i]);
 91                     pPostPaintControl->DoPostPaint(m_hDcOffscreen, ps.rcPaint);
 92                 }
 93                 ::RestoreDC(m_hDcOffscreen, iSaveDC);
 94                 ::BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,
 95                     ps.rcPaint.bottom - ps.rcPaint.top, m_hDcOffscreen, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
 96                 ::SelectObject(m_hDcOffscreen, hOldBitmap);
 97 
 98                 if( m_bShowUpdateRect ) {
 99                     HPEN hOldPen = (HPEN)::SelectObject(ps.hdc, m_hUpdateRectPen);
100                     ::SelectObject(ps.hdc, ::GetStockObject(HOLLOW_BRUSH));
101                     ::Rectangle(ps.hdc, rcPaint.left, rcPaint.top, rcPaint.right, rcPaint.bottom);
102                     ::SelectObject(ps.hdc, hOldPen);
103                 }
104             }
105             else
106             {
107                 // A standard paint job
108                 int iSaveDC = ::SaveDC(ps.hdc);
109                 m_pRoot->DoPaint(ps.hdc, ps.rcPaint);//绘制控件
110                 ::RestoreDC(ps.hdc, iSaveDC);
111             }
112             // All Done!
113             ::EndPaint(m_hWndPaint, &ps);
114         }
115         // If any of the painting requested a resize again, we'll need
116         // to invalidate the entire window once more.
117         if( m_bUpdateNeeded ) {
118             ::InvalidateRect(m_hWndPaint, NULL, FALSE);
119         }
120         return true;

在::BeginPaint(m_hWndPaint, &ps)和::EndPaint(m_hWndPaint, &ps)中间是窗口绘制部分,duilib包含了两种方式:双缓存方式(解决闪烁问题)和标准方式,默认为双缓存方式。两种方式最终都调用了m_pRoot->DoPaint,m_pRoot为控件容器,且DoPaint为虚函数,实际调用了CContainerUI::DoPaint

 1 void CContainerUI::DoPaint(HDC hDC, const RECT& rcPaint)
 2 {
 3     RECT rcTemp = { 0 };
 4     if( !::IntersectRect(&rcTemp, &rcPaint, &m_rcItem) ) return;
 5 
 6     CRenderClip clip;
 7     CRenderClip::GenerateClip(hDC, rcTemp, clip);
 8     CControlUI::DoPaint(hDC, rcPaint);
 9 
10     if( m_items.GetSize() > 0 ) {
11         RECT rc = m_rcItem;
12         rc.left += m_rcInset.left;
13         rc.top += m_rcInset.top;
14         rc.right -= m_rcInset.right;
15         rc.bottom -= m_rcInset.bottom;
16         if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth();
17         if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight();
18 
19         if( !::IntersectRect(&rcTemp, &rcPaint, &rc) ) {
20             for( int it = 0; it < m_items.GetSize(); it++ ) {
21                 CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);
22                 if( !pControl->IsVisible() ) continue;
23                 if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue;
24                 if( pControl ->IsFloat() ) {
25                     if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue;
26                     pControl->DoPaint(hDC, rcPaint);
27                 }
28             }
29         }
30         else {
31             CRenderClip childClip;
32             CRenderClip::GenerateClip(hDC, rcTemp, childClip);
33             for( int it = 0; it < m_items.GetSize(); it++ ) {
34                 CControlUI* pControl = static_cast<CControlUI*>(m_items[it]);
35                 if( !pControl->IsVisible() ) continue;
36                 if( !::IntersectRect(&rcTemp, &rcPaint, &pControl->GetPos()) ) continue;
37                 if( pControl ->IsFloat() ) {
38                     if( !::IntersectRect(&rcTemp, &m_rcItem, &pControl->GetPos()) ) continue;
39                     CRenderClip::UseOldClipBegin(hDC, childClip);
40                     pControl->DoPaint(hDC, rcPaint);
41                     CRenderClip::UseOldClipEnd(hDC, childClip);
42                 }
43                 else {
44                     if( !::IntersectRect(&rcTemp, &rc, &pControl->GetPos()) ) continue;
45                     pControl->DoPaint(hDC, rcPaint);
46                 }
47             }
48         }
49     }
50 
51     if( m_pVerticalScrollBar != NULL && m_pVerticalScrollBar->IsVisible() ) {
52         if( ::IntersectRect(&rcTemp, &rcPaint, &m_pVerticalScrollBar->GetPos()) ) {
53             m_pVerticalScrollBar->DoPaint(hDC, rcPaint);
54         }
55     }
56 
57     if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) {
58         if( ::IntersectRect(&rcTemp, &rcPaint, &m_pHorizontalScrollBar->GetPos()) ) {
59             m_pHorizontalScrollBar->DoPaint(hDC, rcPaint);
60         }
61     }
62 }

控件容器绘制完自己后,遍历子控件(包括子控件容器)调用其DoPaint,完成子控件绘制

 1 void CControlUI::DoPaint(HDC hDC, const RECT& rcPaint)
 2 {
 3     if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return;
 4 
 5     // 绘制循序:背景颜色->背景图->状态图->文本->边框
 6     if( m_cxyBorderRound.cx > 0 || m_cxyBorderRound.cy > 0 ) {
 7         CRenderClip roundClip;
 8         CRenderClip::GenerateRoundClip(hDC, m_rcPaint,  m_rcItem, m_cxyBorderRound.cx, m_cxyBorderRound.cy, roundClip);
 9         PaintBkColor(hDC);
10         PaintBkImage(hDC);
11         PaintStatusImage(hDC);
12         PaintText(hDC);
13         PaintBorder(hDC);
14     }
15     else {
16         PaintBkColor(hDC);
17         PaintBkImage(hDC);
18         PaintStatusImage(hDC);
19         PaintText(hDC);
20         PaintBorder(hDC);
21     }
22 }

最终的绘制都是通过渲染引擎CRenderEngine实现的。

这样看来,整个绘制思路还是很清晰的:CPaintManagerUI::MessageHandler(WM_PAINT)--->CContainerUI::DoPaint--->CControlUI::DoPaint--->CRenderEngine。

 

posted @ 2013-12-06 10:35  iThinking  阅读(3909)  评论(0编辑  收藏  举报