浅析μC/GUI-v3.90a之WM_ITERATE_START剪切域计算宏
文章来源:http://gliethttp.cublog.cn
接续《浅析μC/GUI-v3.90a之GUI_DispString函数》讨论WM_ITERATE_START剪切域计算宏 //剪切域宗旨:本hWin内部被控件占用的部分不用绘制;窗体Z序中比本hWin窗体高的窗体遮盖住本hWin的部分不用绘制; //本hWin超出parent窗体边界的部分不用绘制;窗体的窗体Z序比本hWin的parent窗体高的窗体遮盖住本hWin需要绘制的部分不需要绘制; //这就是剪切域绘制的宗旨,让绘制部分最小,通过复杂的cpu计算,避免窗体互相遮盖部分的重复绘制,提高显式速度,增加lcd使用寿命.[gliethttp] //计算完剪切域之后,通过WM__ActivateClipRect()将会使_ClipContext.CurRect传递给GUI_Context.ClipRect,最终影响绘图的有效空间 //拿对当前hWin进行GUI_Clear()来说,剪切域的绘制顺序是上到下窄条依次绘制,在上到下的过程中又会由左到右依次计算绘制 ------------------------------------------------------------------------------------- 1. ... { r = *pr; #if GUI_WINSUPPORT WM_ADDORG(r.x0, r.y0); WM_ADDORG(r.x1, r.y1); //平移矩形r WM_ITERATE_START(&r) { //计算r矩形区域内的剪切域 #endif GUI_Context.DispPosX = r.x0; GUI_Context.DispPosY = r.y0; _DispLine(s, MaxNumChars, &r); #if GUI_WINSUPPORT } WM_ITERATE_END(); #endif } ... ------------------------------------------------------------------------------------- 2.WM_ITERATE_START宏 gui/wm/WM_GUI.h #define WM_ITERATE_START(pRect) \ { \ if (WM__InitIVRSearch(pRect)) \ //构造循环体 do { ------------------------------------------------------------------------------------- 3.WM__InitIVRSearch()函数 gui/wm/WM.c int WM__InitIVRSearch(const GUI_RECT* pMaxRect) { GUI_RECT r; WM_Obj* pAWin; GUI_ASSERT_LOCK(); if (WM_IsActive==0) { WM__ActivateClipRect(); return 1; } if (++_ClipContext.EntranceCnt > 1) //如果重入,返回,保持计算操作的唯一性 return 1; pAWin = WM_H2P(GUI_Context.hAWin); //当前句柄,详情请参考《浅析μC/GUI-v3.98之WM_H2P()句柄hWin转内存函数》 _ClipContext.Cnt = -1; if (WM__PaintCallbackCnt) { //从回调函数进入的剪切计算 WM__GetInvalidRectAbs(pAWin, &r); //r=pWin->InvalidRect;r存放pWin失效矩形区 } else { if (pAWin->Status & WM_SF_ISVIS) { r = pAWin->Rect; //不是回调函数引起的剪切计算,那么整个pWin窗体作为失效区域 } else { --_ClipContext.EntranceCnt; //窗体属性为不可见 return 0; } } if (pMaxRect) { GUI__IntersectRect(&r, pMaxRect); //r依据pMaxRect,缩小自己的矩形区域 } /* If user has reduced the cliprect size, reduce the rectangle */ if (GUI_Context.WM__pUserClipRect) { WM_Obj* pWin = pAWin; //用户指定了失效重绘区域,那么继续缩小r的矩形区域 GUI_RECT rUser = *(GUI_Context.WM__pUserClipRect); #if WM_SUPPORT_TRANSPARENCY if (WM__hATransWindow) { //具有半透明属性 pWin = WM_H2P(WM__hATransWindow); } #endif WM__Client2Screen(pWin, &rUser); //以pWin(x0,y0)位坐标原点,平移rUser矩形 GUI__IntersectRect(&r, &rUser); //继续缩小r矩形区 } #if WM_SUPPORT_TRANSPARENCY if (WM__hATransWindow) { //如果有半透明窗体,那么需要继续缩小剪切域r if (WM__ClipAtParentBorders(&r, WM__hATransWindow) == 0) { --_ClipContext.EntranceCnt; return 0; } } #endif //主要进行如下五步操作: //一.以上所有工作主要是是计算当前激活窗体hWin自身与pMaxRect的平面剪切区域r, //二.接下来WM__ClipAtParentBorders()函数完成和hWin所有父窗体边框剪切,如果有一个父窗体属性为不可见,直接0退出 if (WM__ClipAtParentBorders(&r, GUI_Context.hAWin) == 0) { --_ClipContext.EntranceCnt; return 0; } _ClipContext.ClientRect = r; //保存r重绘区域到IVR专用单元_ClipContext.ClientRect return WM__GetNextIVR(); //hWin自己的、所有父窗体的同级兄弟窗体与该r进行平面、三维立体z序混合剪切 } //pWin->hNext其实从窗口Z序来讲,是在pWin上面的控件,而不把pWin->hNext叫作hWin的兄弟 //pWin->hNext以及hNext->hNext都有权力覆盖hWin窗体,因为他们在窗口Z序的比hWin更靠近顶层处 ------------------------------------------------------------------------------------- 4.WM__GetInvalidRectAbs()函数 gui/wm/WM.c static void WM__GetInvalidRectAbs(WM_Obj* pWin, GUI_RECT* pRect) { *pRect = pWin->InvalidRect; } ------------------------------------------------------------------------------------- 5.WM__GetNextIVR()函数 gui/wm/WM.c int WM__GetNextIVR(void) { #if GUI_SUPPORT_CURSOR static char _CursorHidden; #endif if (WM_IsActive==0) { return 0; } if (_ClipContext.EntranceCnt > 1) { _ClipContext.EntranceCnt--; return 0; } //非法性检查 #if GUI_SUPPORT_CURSOR if (_CursorHidden) { _CursorHidden = 0; (*GUI_CURSOR_pfTempUnhide) (); //如果当前鼠标,即上一次进入时鼠标不可见,那么显示鼠标 } #endif ++_ClipContext.Cnt; /* Find next rectangle and use it as ClipRect */ //三.与窗口Z序比hWin->parent大的窗体进行剪切计算,缩小本次hWin绘制的区域 //四.与窗口Z序比hWin大的窗体进行剪切计算,继续缩小本次hWin绘制的区域 //五.与hWin窗体内部所有Child控件进行剪切计算,继继续缩小本次hWin绘制的区域 if (!_FindNext_IVR()) { //见函数6的注解 _ClipContext.EntranceCnt--; return 0; } WM__ActivateClipRect(); /* Hide cursor if necessary */ #if GUI_SUPPORT_CURSOR if (GUI_CURSOR_pfTempHide) { _CursorHidden = (*GUI_CURSOR_pfTempHide) ( &_ClipContext.CurRect); } #endif return 1; } ------------------------------------------------------------------------------------- 6._FindNext_IVR()函数 //2007-07-11 gliethttp宗旨就是:本hWin内部被控件占用的部分不用绘制,窗体Z序中比本hWin窗体高的窗体遮盖住本hWin的部分不用绘制 //本hWin超出parent窗体边界的部分不用绘制,窗体的窗体Z序比本hWin的parent窗体高的窗体遮盖住本hWin需要绘制的部分不需要绘制 //这样就是剪切域绘制的宗旨了,让绘制部分最小,通过复杂的cpu计算,避免窗体遮盖部分的重复绘制,提高显式速度,增加lcd使用寿命. gui/wm/WM.c static int _FindNext_IVR(void) { WM_HMEM hParent; GUI_RECT r; WM_Obj* pAWin; WM_Obj* pParent; r = _ClipContext.CurRect; //1.设置r的初始化值 if (_ClipContext.Cnt == 0) { //如果从WM__InitIVRSearch()->WM__GetNextIVR()->_FindNext_IVR()进入 r.x0 = _ClipContext.ClientRect.x0; //那么_ClipContext.Cnt==0成立,将WM__InitIVRSearch()计算的存储到_ClipContext.ClientRect的剪切域 r.y0 = _ClipContext.ClientRect.y0; //读取到r中 } else { r.x0 = _ClipContext.CurRect.x1+1; //接续上一已经绘制的窄条剪切域,继续进行其它窄条剪切域的计算 r.y0 = _ClipContext.CurRect.y0; //绘制顺序是上到下窄条依次绘制,在上到下的过程中又会由左到右依次计算绘制 if (r.x0 > _ClipContext.ClientRect.x1) { NextStripe: /* go down to next stripe */ r.x0 = _ClipContext.ClientRect.x0; r.y0 = _ClipContext.CurRect.y1+1; } } //2.检测剪切计算完成否 if (r.y0 >_ClipContext.ClientRect.y1) { return 0; } //3.寻找pRect->y1这个下边沿在所有和pRect存在剪切关系控件窗体中的最小位置,即:"r.y1靠上计算" pAWin = WM_H2P(GUI_Context.hAWin); if (r.x0 == _ClipContext.ClientRect.x0) { r.y1 = _ClipContext.ClientRect.y1; r.x1 = _ClipContext.ClientRect.x1; /* Iterate over all windows which are above */ /* Check all siblings above (Iterate over Parents and top siblings (hNext) */ for (hParent = GUI_Context.hAWin; hParent; hParent = pParent->hParent) { //_Findy1就是寻找pRect->y1这个下边沿的最小位置 pParent = WM_H2P(hParent); //计算GUI_Context.hAWin自己的哥们窗体[Z序比hWin高的窗口], _Findy1(pParent->hNext, &r, NULL); //和父窗体的哥们窗体分别和r剪切域[Z序比老爹高的窗口] //2007-07-11 gliethttp _Findy1()计算pParent->hNext和hNext->hNext等窗口Z序比pParent窗口Z序大的r剪切域 //举例:如A,B两个窗口,A窗口Z序比B窗口Z序大,那么A就在B的上面,A可以覆盖B,但B不能覆盖A,因为A窗口Z序比B窗口Z序大 } /* Check all children */ _Findy1(pAWin->hFirstChild, &r, NULL); //对pWin窗体内部的所有孩子控件进行同样的r剪切计算 } //4.调整pRect->x0到所有存在剪切关系的控件窗口的最右边rWinClipped.x1+1处,即:"r.x0靠右计算" Find_x0: r.x1 = r.x0; { hParent = GUI_Context.hAWin; } for (; hParent; hParent = pParent->hParent) { pParent = WM_H2P(hParent); if (_Findx0(pParent->hNext, &r, NULL)) { goto Find_x0; //如果pParent的窗口Z序上层中存在和该r剪切区域,调整r.x0到 } //相应窗口Z序对应窗口的x1+1处,goto Find_x0继续计算接下来的pParent } //就这样一直将r.x0调整到所有与r存在剪切关系的窗口Z序上层窗体的右边x1+1处 /* Check all children */ if (_Findx0(pAWin->hFirstChild, &r, NULL)) { //在自己hWin窗体内的所有孩子控件进行r.x0靠右计算 goto Find_x0; } //5.如果r.x0 > r.x1即,r的右边框,那么该窄条剪切域完毕.goto NextStripe去找下一个窄条剪切域 r.x1 = _ClipContext.ClientRect.x1; //恢复"r.x0靠右计算"中破坏了的r.x1 if (r.x1 < r.x0) { _ClipContext.CurRect = r; goto NextStripe; //x0过界,计算下一个需要重绘窄条剪切域 } //6.调整pRect->x1到所有存在剪切关系的控件窗口的最左边rWinClipped.x0-1处,即:"r.x1靠左计算" { hParent = GUI_Context.hAWin; } for (; hParent; hParent = pParent->hParent) { pParent = WM_H2P(hParent); _Findx1(pParent->hNext, &r, NULL); } /* Check all children */ _Findx1(pAWin->hFirstChild, &r, NULL); //在自己hWin窗体内的所有孩子控件进行r.x1靠左计算 if (_ClipContext.Cnt >200) { return 0; //最大嵌套数目 } _ClipContext.CurRect = r; return 1; //2007-07-11 gliethttp计算完毕;通过WM__ActivateClipRect()将会使 } //_ClipContext.CurRect传递给GUI_Context.ClipRect,来影响绘图的有效空间 ------------------------------------------------------------------------------------- 6._Findy1()函数 gui/wm/WM.c static void _Findy1(WM_HWIN iWin, GUI_RECT* pRect, GUI_RECT* pParentRect) { WM_Obj* pWin; //_Findy1就是寻找pRect->y1这个下边沿的最小位置 for (; iWin; iWin = pWin->hNext) { //窗体z序比hWin高的所有窗体中,剪切遮盖住本pRect的部分[gliethttp]:找y1. int Status = (pWin = WM_H2P(iWin))->Status; if (Status & WM_SF_ISVIS) { GUI_RECT rWinClipped; if (pParentRect) { //指定了剪切参考 GUI__IntersectRects(&rWinClipped, &pWin->Rect, pParentRect); } else { rWinClipped = pWin->Rect; //pWin->Rect作为剪切参考 } if (GUI_RectsIntersect(pRect, &rWinClipped)) { if ((Status & WM_SF_HASTRANS) == 0) { if (pWin->Rect.y0 > pRect->y0) { ASSIGN_IF_LESS(pRect->y1, rWinClipped.y0 - 1); //将pRect->y1和rWinClipped矩形的上边延y0对齐 } else { ASSIGN_IF_LESS(pRect->y1, rWinClipped.y1); //将pRect->y1和rWinClipped矩形的下边延y1对齐 } } else { /* Check all children*/ WM_HWIN hChild; WM_Obj* pChild; for (hChild = pWin->hFirstChild; hChild; hChild = pChild->hNext) { pChild = WM_H2P(hChild); _Findy1(hChild, pRect, &rWinClipped); } } } } } } ------------------------------------------------------------------------------------- 6._Findx0()函数 gui/wm/WM.c static int _Findx0(WM_HWIN hWin, GUI_RECT* pRect, GUI_RECT* pParentRect) { WM_Obj* pWin; int r = 0; for (; hWin; hWin = pWin->hNext) { //窗体z序比hWin高的所有窗体中,剪切遮盖住本pRect的部分[gliethttp]:找x0. int Status = (pWin = WM_H2P(hWin))->Status; if (Status & WM_SF_ISVIS) { GUI_RECT rWinClipped; if (pParentRect) { GUI__IntersectRects(&rWinClipped, &pWin->Rect, pParentRect); } else { rWinClipped = pWin->Rect; } if (GUI_RectsIntersect(pRect, &rWinClipped)) { if ((Status & WM_SF_HASTRANS) == 0) { pRect->x0 = rWinClipped.x1+1; //调整pRect->x0到所有存在剪切关系的控件窗口的右边rWinClipped.x1+1处 r = 1; } else { /* Check all children */ //半透明处理 WM_HWIN hChild; WM_Obj* pChild; for (hChild = pWin->hFirstChild; hChild; hChild = pChild->hNext) { pChild = WM_H2P(hChild); if (_Findx0(hChild, pRect, &rWinClipped)) { r = 1; } } } } } } return r; } ------------------------------------------------------------------------------------- 6._Findx1()函数 static void _Findx1(WM_HWIN hWin, GUI_RECT* pRect, GUI_RECT* pParentRect) { WM_Obj* pWin; for (; hWin; hWin = pWin->hNext) { //窗体z序比hWin高的所有窗体中,剪切遮盖住本pRect的部分[gliethttp]:找x1. int Status = (pWin = WM_H2P(hWin))->Status; if (Status & WM_SF_ISVIS) { GUI_RECT rWinClipped; if (pParentRect) { GUI__IntersectRects(&rWinClipped, &pWin->Rect, pParentRect); } else { rWinClipped = pWin->Rect; } if (GUI_RectsIntersect(pRect, &rWinClipped)) { if ((Status & WM_SF_HASTRANS) == 0) { pRect->x1 = rWinClipped.x0-1; //调整pRect->x1到所有存在剪切关系的控件窗口的左边rWinClipped.x0-1处 } else { /* Check all children */ //半透明处理 WM_HWIN hChild; WM_Obj* pChild; for (hChild = pWin->hFirstChild; hChild; hChild = pChild->hNext) { pChild = WM_H2P(hChild); _Findx1(hChild, pRect, &rWinClipped); } } } } } }
|