ucgui透明效果的实现

项目组最近要实现gtk基于Xserver下透明效果。先研究学习一下ucgui透明效果的实现

目录
一. 透明处理的本质.
二. UCGUI中透明窗体支持.
三. MemDev下实现透明窗体的问题.
四. 透明窗体是否仅绘图时进行透明处理即可?
五. 修改后的代码下载.

内容简述:有很多朋友不太明白UCGUI中如何实现透明窗体,虽然有些朋友曾经讨论过UCGUI中透明窗体的实现,但涉及到对UCGUI核心结构的改动,其实完全是没有必要的,这要更深入的了解UCGUI以及UCGUI中透明窗体的相关支持,实现透明绘图的基本原理是一样的,关键在于如何与UCGUI结合起来,本文将详述UCGUI中透明窗体的支持原理,以及完全将修改局限在底层的LCD图形驱动当中,无须更改UCGUI本身。

一. 透明处理的本质。

1.透明显示的原理.
假设一张图片A与B,如果B是透明的,其意即,透明B可以看到A,但并不是完全看到A,只是看到A的朦胧的影象。在图形处理上,其实就是在A上面再显示B的时候,对B上面的每个点的颜色做了处理,简单的说就是与A的颜色按比例进行了一个混合,混合的时候是按照R、G、G分别进行混合的.

2.透明显示的计算公式
alpha是透明度,亦即显示B时B的每个象素点与A的相同位置的象素点进行混合的比例系数,R(b)_new/G(b)_new/B(b)_new为计算所得的新的B要显示的象素点的R/G/B颜色分量,R(b)/G(b)/B(b)为B本来象素点要显示的象素点的R/G/B颜色分量,R(a)/G(a)/B(a)为A的点的R/G/B颜色分量.
R(b)_new = R(b)*(1-alpha) + R(a)*alpha;
G(b)_new = G(b)*(1-alpha) + G(a)*alpha;
B(b)_new = B(b)*(1-alpha) + B(a)*alpha;

3.实现窗体透明显示面临的问题
经过这样合成之后,就可以获得B透明显示于A,即可以看到A又可以看到B的效果。但是这里其实也暗示了一个问题:
透明的图片B是在A的基础上显示的,即是在A显示之后再处理B,所以考虑一下我们下面要讨论的透明窗体:
[1].窗体要透明,则必须总是在不透明的窗体之后绘制,不然就会被其它窗体挡住了,这是窗体Z序的问题。
[2].透明的窗体不会对一般窗体构成遮挡关系,这是窗体剪切计算时的问题.

下面我们就将讨论一下UCGUI中如何利透明显示的知道实现透明窗体.

二. UCGUI中透明窗体支持。

1.UCGUI中窗体的Z序.

为了帮助理解透明窗体在剪切时的处理,这里有必要介绍一下UCGUI中z序的知识.有的朋友以为UCGUI中没有Z序,其实在窗体的最基本的结构WM_Obj当中,就包含了Z序:

typedef struct {
  GUI_RECTRect;        
  GUI_RECT InvalidRect;
  WM_CALLBACK*cb;      
  WM_HWINhNextLin;    
  WM_HWIN hParent;
  WM_HWIN hFirstChild;
  WM_HWIN hNext;
  U16Status;          
} WM_Obj;

hNextLin----------所有窗体的链表指针,将UCGUI中所有的窗体形成一个键表,便于查找窗体。
hNext-------------兄弟窗体的链表指针,这个成员其实就表示了兄弟窗体的Z序关系,hNext所指窗体Z序高.
hParent-----------父窗体指体指针,父窗体被子孙窗体剪切.
hFirstChild-------第一个子窗体指针,用于快速查找窗体的所有子窗体.
Status------------窗体属性,如透明窗体/窗体是否可称动等.

Z序-----按照通行的解释,就是除平面的X/Y轴,抽象出来表示窗体层次关系的Z轴,沿着Z轴Z序增高,Z序高的窗体挡住Z序低的窗体.
注:依我理解,表达更详细一点以及加深理解, 可以把Z序理解成多层概念:
[1].兄弟窗体之间,Z序高的窗体高于Z序低的窗体的所有子孙窗体.
[2].父子窗体之间,子孙窗体Z序高于父窗体,但子孙窗体的区域不能超过父窗体.

根据UCGUI中的这个Z序,在剪切计算时如下的剪切规则:
[1].窗体被hNext之下的兄弟窗体剪切,以及被其父窗体的hNext之下兄弟窗体剪切,递归处理.
[2].窗体被子窗体剪切,注意只被第一级子窗体剪切[没有必要处理孙辈窗体,他们的区域被父窗体包含].
[3].子窗体不能超出父窗体区域.

2.UCGUI中透明窗体的具体支持.

UCGUI中实现的透明,是完全的透明,意即透明窗体相对于其它一般窗体完全不可见,这种完全不可见的的就是在剪切处理当中实现的,UCGUI中处理剪切时,透明窗体不会对其它一般窗体构成遮挡关系,无论是比其Z序高还是序低的窗体。在UCGUI中实现透明窗体,必须指定如下两个属性:
WM_CF_HASTRANS-----透明属性
WM_CF_STAYONTOP----始终为UCGUI中同一级兄弟窗体中z序最高的窗体,以最后指定该属性的窗体Z序最高.

[1].WM_CF_HASTRANS属性是透明窗体的基本属性,相关窗体的剪切处理部分,凡是指定了这个属性的窗体,在UCGUI中的剪切计算时,不会对其它窗体形成剪切关系;
[2].透明窗体会在其它Z序低的窗体重绘后,重绘透明窗体,以保证透明窗体不会被挡住。
[3].指定透明窗体WM_CF_STAYONTOP属性, 是为了实现透明窗体居于一般窗体的前台,这样透明窗体才不会被一般窗体挡住,保持透明的效果.
[4].因为WM_CF_STAYONTOP属性是相对于同一层级兄弟窗体的,因此透明窗体也是相对于同一层级的兄弟窗体。

3.UCGUI中透明窗体的实现原理.

[1].剪切处理时的支持.
关于剪切计算,是UCGUI中比较难理解的地方,我这里仅仅指出剪切处理时,透明窗体不会对一般窗体构成剪切的地方。
UCGUI390版的剪切计算有三个主要函数_Findy1()/_Findx0()/_Findx1(),这三个函数分别是找出一个剪切矩形的x0/x1/y1点,y0点是已知的.查看这几个函数可以知道,在处理剪切时,首先会判断窗体的透明属性,如果是透明窗体,就再查找透明窗体的所有子窗体。
[2].画窗体时的支持.
透明窗体是不会变成无效窗体的,它的重绘都是其它窗体的重绘引起的,这样处理对于效率是有帮助的.在画窗体时,主要查看voidWM__PaintWinAndOverlays(WM_PAINTINFO*pInfo);这个函数是处理窗体得绘的,它在处理完每个窗体的重绘之后会调用两个函数,这两个函数作用如下:

if (WM__TransWindowCnt != 0) {
_PaintTransChildren(pWin);      
_PaintTransTopSiblings(hWin,pWin);    
}
_PaintTransChildren()-------重绘刚才绘制窗体的所有透明子窗体,保证透明窗体显示在其上层。
_PaintTransTopSiblings()----重绘刚才绘制窗体之上的所有透明窗体,保证位于其上的透明窗体显示在上层。

[注:位于一个窗体之上的窗体,包括其hNext之下的兄弟窗体,以及位于其父辈窗体的hNext之下的兄弟窗体]

[3].UCGUI为了支持透明窗体,已经将窗体绘制与剪切处理复杂处理了,而且还必须处理因为一般窗体重绘而引起透明窗体的重绘(当有相交区域时),因此透明窗体比起一般窗体在效率下是比较低下的.

4.如何增加UCGUI中透明窗体的透明度支持.

综上分析,UCGUI中已经实现了透明窗体所须的各种属性,如此我们所需要做的工作就非常的简单了,仅仅须要提供2D层次实际绘图的透明处理即可,UCGUI中2D图形库是所有绘图的基础,它自己的基础则是更下一层的驱动级的绘点函数,所以仅须实现绘点函数的透明处理,就实现了UCGUI中的透明窗体支持。

以下以模拟器中的处理透明,来说明透明窗体支持(如果是在真实硬件下,其改动也类似,仅须提供相应的Alpah画点函数),改动如下:

[1].增加一个全局变量g_AlphaValue,用于记录当前绘图透明度。
[2].增加设置/获取Alpha透明度的函数SetAlpha()/GetAlpha(),进行透明绘图时SetAlpha(60),完成透明绘图后恢复SetAlpha(0);
[3].增加一个带透明处理的绘点函数LCDSIM_SetPixelIndexAlpha().


透明效果图如下:
[示例是UCGUI390T版提供的示例XEye.c]
http://www.ucgui.com/ucgui/XEye.rar
http://www.ucgui.com/ucgui/image/UCGUI_Alpha.bmp

在透明窗体当中,处理绘图时要增加透明度的设置,如下:

  case WM_PAINT:
    State.x-= WM_GetWindowOrgX(hWin);
    State.y-= WM_GetWindowOrgY(hWin);
SetAlpha(60); //设置要绘图的透明度60%.
GUI_SetBkColor(GUI_BLUE);
GUI_Clear();
    _DrawEye(EYE_X1,EYE_Y, EYE_RX, EYE_RY, State.x, State.y);
    _DrawEye(EYE_X2,EYE_Y, EYE_RX, EYE_RY, State.x, State.y);
SetAlpha(0); //恢复透明度为0,即以下绘图不再进行透明处理.

[特别注意:透明窗体当中的绘制操作,会进行叠加透明处理,因此绘图时要注意相同区域的反复绘画]

5.具体的代码修改说明.

代码改动有三处地方,涉及两个文件LCDWin.c及LCDSim.c,主要是绘点宏定义的更改以及设置透明度的增加,具体的代码改动如下:

[1].在模拟器的LCDWin.c当中,增加如下代码:

//houhh 20061211...
//houhh 20061211...
unsigned g_AlphaValue = 0;
void SetAlpha(unsigned alpha);
void SetAlpha(unsigned alpha)
{
g_AlphaValue = alpha*256/100;
}

[2].在LCDWin.c当中,修改SETPIXEL画点的宏定义成如下:
#ifdef _DEBUG
static int _CheckBound(unsigned int c) {
  unsigned int NumColors =LCD_BITSPERPIXEL > 8 ? 0xffff : (1<< LCD_BITSPERPIXEL) - 1;
  if (c >NumColors) {
    GUI_DEBUG_ERROROUT("LCDWin::SETPIXEL:parameters out of bounds");
    return1;
  }
  return 0;
}


#define SETPIXEL(x, y, c) if (!_CheckBound(c)){\
if(g_AlphaValue == 0){\
  LCDSIM_SetPixelIndex(x, y, c,LCD_DISPLAY_INDEX);}\
else{\
  LCDSIM_SetPixelIndexAlpha(x, y,c, LCD_DISPLAY_INDEX, g_AlphaValue);\
}\
}

#else
//houhh 20061211...
//  #define SETPIXEL(x, y, c)LCDSIM_SetPixelIndex(x, y, c, LCD_DISPLAY_INDEX)
#define SETPIXEL(x, y, c) if(g_AlphaValue == 0){\
LCDSIM_SetPixelIndex(x, y, c, LCD_DISPLAY_INDEX);}\
else{\
LCDSIM_SetPixelIndexAlpha(x, y, c, LCD_DISPLAY_INDEX,g_AlphaValue);\
}

#endif

[3].在LCDSim.c文件中,增加如下代码,相应在LCDSim.h中增加如下LCDSIM_SetPixelIndexAlpha()函数的声明:


//houhh 20061211...
//可参考LCD_MixColors256.c中的LCD_MixColors256()函数.
void LCDSIM_SetPixelIndexAlpha(int x, int y, int Index, intLayerIndex, unsignedAlpha)  {
  RETURN_IF_NOT_INITIALIZED(;);
  #ifdef _DEBUG
    _CheckBreak(x,y, LayerIndex);
  #endif
  ASSERT(x >=0);
  ASSERT(x <_aVXSize[LayerIndex]);
  ASSERT(y >=0);
  ASSERT(y <_aVYSize[LayerIndex]);
  if (_aBPP[LayerIndex]<= 8) {
U32 Color2, Color1;
    Color1= LCD_Index2Color(Index);
Color2 = LCDSIM_GetPixelColor(x, y, LayerIndex);
    Color1= LCD_MixColors256( Color2, Color1, Alpha);
    Index= LCD_Color2Index(Color1);
    *XY2PTR(x,y,LayerIndex) = Index;
  } else {
    U32Index32 = _ColorIndex2COLORREF(Index, LayerIndex);
Index32 = LCD_MixColors256(LCDSIM_GetPixelColor(x, y, LayerIndex),Index32, Alpha);
    *XY2PTR_DWORD(x,y, LayerIndex) = Index32;
  }
  MARK_MODIFIED(LayerIndex);
}


三.MemDev下实现透明窗体的问题

如果一般窗体创建时指定了WM_CF_MEMDEV属性,则实现透明窗体与上面有很大的不同,下面详细谈谈其原因.

1.首先必须明白,MemDev当中的绘图操作是针对一块分配的内存(对应屏幕一块矩形区域),所以绘图过程中不会对屏幕产生影响(避免了闪烁),只是在绘图完成之后,然后才将此块内存区域整块的复制到屏幕上,因此用户看到的就是一次屏幕更新,但实际上绘图时是经过多个基本绘图操作组合的.

2.窗体有WM_CF_MEMDEV属性后,则窗体上的绘图操将在内存当中进行,此块区域大小等于窗口大小,当然窗体重绘完成后就将此块区域复制到窗体在屏幕上的区域,因此的窗体绘制是调用的MemDev当中的基本绘图操作,所以必须更改这些绘图操作,使其支持透明绘制.

3.内存设备当中,支持1/8/16位三种类型(即内存中每个点所占的位数),其中实际显存中象素等于或小于8位的,均在内存当中用八位表示一个点,其实8/16这两种类型是一样的,仅仅是宏定义中的一些数据类型不一样(如定义象素点的数据类型为char或short),所以其代码是共用的.对于单色的情况,没有透明显示,所以我们仅须处理8位的MemDev当中的基本绘图透明支持即可.

4.主要的修改集中在GUIDEV_8.c当中,分别是_SetPixelIndex()/_FillRect()/_DrawVLine()等几个函数的透明化支持:

_SetPixelIndex()---------绘点.
_FillRect()--------------填充矩形(UCGUI中采取的是GUI_memset()赋值,因此这对于透明支持显然不行,因为透明支持必须逐点处理,取出每个点的颜色来处理,所以如此赋值达不到透明效果,必须更改为逐点画,这样做效率是比较低的).
_DrawVLine()-------------竖直线
_DrawHLine()-------------水平线(调用矩形填充).
_DrawBitmap()------------位图(要处理1/2/4/8/16这几种情况的位图).

[特别注意:_FillRect()在MemDev中的加速处理,与透明化支持相背的,所以必须降低效率变填充为逐点画才行]

5.具体的代码修改.

这里不再详细的说明代码修改,要改的地方已经都在第4点中列出,这时只略提几处改的地方.

[1].增加一个函数,用来将指定内存的点转换成透明处理后的颜色索引.

//houhh 20061217...
extern unsigned GetAlpha();
static PIXELINDEX GetAlphaColor(PIXELINDEX* pData, int index)
{
  LCD_COLOR Color1, Color2;
  int Alpha = GetAlpha();
  if(Alpha != 0){
   Color1 =LCD_Index2Color(index);
   Color2 =LCD_Index2Color(*(pData));
   Color2 =LCD_MixColors256(Color1, Color2,Alpha);  
   return(PIXELINDEX)LCD_Color2Index(Color2);
  }
  else
   return index;
}

[2].矩形填充,有一处修改如下:


  int x = x0; //houhh20061218...
  for(; x0 <= x1;x0++){
   pData = _XY2PTR(x0, y0);
   *pData = GetAlphaColor(pData,LCD_COLORINDEX);
  }
  x0 = x;
  
[3].画点函数,仅仅修改了一句,将颜色索引进行透明处理即可,其它的几个位图函数以及画线函数,基本都是类似这个的修改,仅仅修改一句代码即可.

static void _SetPixelIndex(int x, int y, int Index) {
  GUI_MEMDEV* pDev =GUI_MEMDEV_H2P(GUI_Context.hDevData);
  GUI_USAGE_h hUsage =pDev->hUsage;
  PIXELINDEX* pData = _XY2PTR(x,y);
//  *pData = Index;
//houhh 20061217...
  *pData = GetAlphaColor(pData,Index);;
  if (hUsage) {
    GUI_USAGE_AddPixel(GUI_USAGE_H2P(hUsage),x, y);
  }
}

四.透明窗体是否仅绘图时进行透明处理即可?

特别把这一点拿出来单独的讲,是为了更强调这个问题,有些朋友会问,既然如上面所说,将绘图操作进行了透明处理,他们就这样理解:

"窗体的透明设置我觉得可以对cb回调函数中的“WM_PAINT:”进行相应的透明绘图,且不让它填充矩形框及颜色,那它就会显示透明了。"

针对这种想法,可以提出几个问题,如果这几个问题都能回答,那么上面的说明才是可行,否则断不能行:

[关键词:我们称要实现透明的窗体为WIN_Trans]

1.首先指出一点,对于没指定WM_CF_HASTRANS属性的WIN_Trans窗体,是可以变成无效窗体,从而在DrawNext()当中调用窗体的重绘的,这与带WM_CF_HASTRANS属性的窗体的重绘是由其它窗体重绘而引发是截然不同的(此时总是在一般窗体绘制之后进行透明窗体绘制).

2.对于非指定WM_CF_HASTRANS属性的WIN_Trans窗体, 进行绘制后如果慢慢拖动窗体的话,窗体内的原来绘画没有清理将会残留下来,简单的现象就是如果显示一些文字后拖动窗体,可以看到窗体上残留了文字的颜色(这个残留是先前窗体的绘制没被清理,此残留区域在移动前与移动后还包含在窗体内,所以未被清除而残留).

3.对于非指定WM_CF_HASTRANS属性的WIN_Trans窗体, 当拖动其它窗体与本窗体产生相交的区域后,WIN_Trans窗体会进行重绘, 如果不进行清除操作, 那么就会残留下原来窗体的相交区域(当相交窗体移走后).

4.如果给WIN_Trans窗体加上WM_CF_STAYONTOP属性,如果其它窗体移动时会看到在WIN_Trans区域内被挡住,这块区域是WIN_Trans的父窗体的背景色(因为WIN_Trans窗体没有填充背景所至), 这种效果不太符合透明的效果.

5.即使加了WM_CF_STAYONTOP属性,而且在WM_PAINT中绘图进行透明处理,不使用填充矩形框,也是无法实现透明效果的,依据上面我们所讲的,透明处理是找原来显示屏幕上的一点来进行颜色混合,但是拖动WIN_Trans窗体后,WIN_Trans窗体会在重绘中与自己原来位置的屏幕点进行混合,这样根本不符合透明处理的本质了.

6.因此,UCGUI中实现透明窗体其实是有两个最本质的地方的, 在上面我们已经讲过, 这里再重复如下:
[1].透明窗体在一般窗体之后绘制.
[2].透明窗体的重绘均由其它窗体的重绘而引发,永远不会变成无效窗体(即透明窗体重绘不会由DrawNext()直接调用).

只有上面两点满足了,才可以保证透明窗体最基础的一点:透明窗体每个点绘制时与正确的背景点进行颜色混合(透明化).


五.修改后的代码下载.

所有的文中讲述的修改, 在此提供修改后的代码如下:

1.一般窗体带有WM_CF_MEMDEV属性时的透明窗体支持.
http://www.ucgui.com/ucgui/GUIDEV_8.rar
2.一般窗体无WM_CF_MEMDEV属性时的透明窗体支持.
http://www.ucgui.com/ucgui/LCDWin.rar

来自:http://blog.sina.com.cn/s/blog_4b8c53cf01009p2c.html

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值