zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    【算法导论】第11章,散列表
    【算法导论】第10章,基本数据结构
    【推荐系统实践】冷启动问题
    【算法导论】第8、9章,线性时间排序,中位数顺序统计量
    【推荐系统实践】协同过滤
    Java编程思想---第五章 初始化与清理(下)
    Java编程思想---第五章 初始化与清理(上)
    Java编程思想---第四章 控制执行流程
    Java编程思想---第三章 操作符
    去除MyEclipse频繁弹出的Update Progress窗口
  • 原文地址:https://www.cnblogs.com/dahai/p/3460932.html
Copyright © 2011-2022 走看看