Duilib源码分析(四)绘制管理器—CPaintManagerUI—(前期准备三)

时间:2023-01-21 23:34:03

  接下来,我们将继续分析UIlib.h文件中其他的文件, UIContainer.h, UIRender.h, WinImplBase.h, UIManager.h,以及其他布局、控件等;

  1. UIRender.h:UI渲染器,其中cpp文件中,定义的ZIP压缩相关的数据结构,以及宏操作,与XUnzip.cpp中一样的(个人认为可以提取出来作为共用的一部分),此外还有stbi_load_from_memory、

        stbi_image_free,涉及到图片加载操作,具体详细细节可参考stb_image.c中开源库JPEG/PNG图片加载器stbi_image(支持的格式也比较多的)。

    CRenderClip:渲染剪切区域类,数据成员:rcItem:渲染剪切区域大小,hDC:设备环境句柄 ,hRgn:标识被剪切的区域句柄,hOldRgn:保存早前剪切区域的句柄,静态成员函数:GenerateClip:

    生成剪切区域,参数rc:新的剪切区域大小,内部调用GetClipBox获取到环境句柄的剪切区域大小,并调用CreateRectRgnIndirect创建剪切区域句柄,此外ExtSelectClipRgn将新的剪切区域与当前

    环境句柄的剪切区域进行扩展、合并,GenerateRoundClip:生成圆角剪切区域,参数rcItem为圆角区域位置大小,width、height:圆角矩形的逻辑高度和宽度,内部通过CreateRoundRectRgn

    创建一个圆角矩形区域句柄,并与当前区域合并,UseOldClipBegin:使用早期的矩形区域作为当前的剪切区域区域,UseOldClipEnd:使用当前区域作为设备环境剪切区域;

    CRenderEngine:渲染引擎类,整个类中基本上均是静态成员函数,AdjustColor:色彩调整,在分析如何处理色彩调整前,先分析静态全局函数RGBtoHSL、HSLtoRGB分别为RGB颜色空间互转为

    HSL色彩空间,RGB和HSL属于不同的色彩空间,此外在不同平台下,HSL表现不一样,即范围不一致,RGB的取值范围在0~255之间,HSL的取值H为0~240,SL在0~1之间,需要对RGB和HSL颜色

    空间有足够的认识可查阅相关资料,算法处理(摘自网络):

  HSL与RGB转换(伪代码表示)
  a) RGB→HSL的算法描述。 
  步骤1:把RGB值转成【0,1】中数值。 
  步骤2:找出R,G和B中的最大值。 
  步骤3:设L=(maxcolor + mincolor)/2 
  步骤4:如果最大和最小的颜色值相同,即表示灰色,那么S定义为0,而H未定义并在程序中通常写成0。 
  步骤5:否则,测试L: 
  If L<0.5, S=(maxcolor-mincolor)/(maxcolor + mincolor) 
  If L>=0.5, S=(maxcolor-mincolor)/(2.0-maxcolor-mincolor) 
  步骤6: If R==maxcolor, H=(G-B)/(maxcolor-mincolor) 
  If G==maxcolor, H=2.0+(B-R)/(maxcolor-mincolor) 
  If B==maxcolor, H=4.0+(R-G)/(maxcolor-mincolor) 
  步骤7:从第6步的计算看,H分成0~6区域。RGB颜色空间是一个立方体而HSL颜色空间是两个六角形锥体,其中的L是RGB立方体的主对角线。因此,RGB立方体的顶点:红、黄、绿、青、蓝

  和品红就成为HSL六角形的顶点,而数值0~6就告诉我们H在哪个部分;H=H*60.0,如果H为负值,则加360。

  b) HSL→RGB的算法描述。 
  步骤1:If S==0,表示灰色,定义R,G和B都为L. 
  步骤2:否则,测试L: 
  If L<0.5,temp2=L*(1.0+S) 
  If L>=0.5,temp2=L+S-L*S 
  步骤3:temp1=2.0*-temp2 
  步骤4:把H转换到0~1。 
  步骤5:对于R,G,B,计算另外的临时值temp3。方法如下: 
  for R, temp3=H+1.0/3.0 
  for G, temp3=H 
  for B, temp3=H-1.0/3.0 
  if temp3<0, temp3=temp3+1.0 
  if temp3>1, temp3=temp3-1.0 
  步骤6:对于R,G,B做如下测试: 
  If 6.0*temp3<1,color=temp1+(temp2-temp1)*6.0*temp3 
  else if 2.0*temp3<1,color=temp2 
  else if 3.0*temp3<2, color=temp1+(temp2-temp1)*((2.0/3.0)-temp3)*6.0 
  else color=temp1。

  有兴趣时可以仔细推导一下算法;

  好啦,我们回到AdjustColor函数中,内部现通过获取到指定颜色RGB值dwColor的HSL值并重新根据传入的参数H、S、L修正HSL值,此后再转化为RGB值;可以看到内部修正HSL值时,将S、L分别/100,

  += (H - 180)?事实上我们可以跟踪发现CPaintManagerUI类中的m_H、m_S、m_L的值预设值为180、100、100;duilib中一般在绘制背景色PaintBkColor时会使用到;

  LoadImage:从文件或可执行应用程序、资源DLL中加载图片,参数bitmap为图片资源ID或资源字符串,type为图片类型或资源类型,mask为掩码,内部实现为:若type为空,若CPaintManagerUI已有

  资源ZIP文件,则从压缩文件中获取指定文件的内容,否则则直接从指定文件中获取内容,若type不为空,则从可执行应用程序或资源DLL中获取内容,得到内容后,再通过stbi_load_from_memory加载

  解析得到pImage的图片内容,此外因pImage与CreateDIBSection创建的BITMAP数据格式不一致,需要转化;此后返回TImageInfo数据结构的内容,释放pImage资源,TImageInfo结构后面会解析

  ,整个图片加载,图片格式因stbi-info支持格式,若需要支持其他格式或是其他处理可以更换图片加载模块,FreeImage:释放加载的图片资源(实时上主要是释放通过LoadImage创建的BITMAP数据资源)

  DrawImage:绘制图片,参数hDC,控件或窗口上下文句柄,hBitmap为位图句柄,rc为窗口区域大小,rcPaint为绘制区域大小,rcBmpPart为图片源窗口区域大小,rcCorners为拐角区域大小,此外

  alphaChannel释放开启了alpha通道,uFade为淡色值(透明度),hole、xtiled、ytiled分别为释放开启凹陷效果、X或Y方向平铺效果;DrawImageString:绘制图片(通过内容字符串解析绘制),内部调用

  DrawImage实现,DrawColor:绘制颜色,参数hDC为上下文句柄,rc为绘制区域,color为绘制颜色值,内部通过创建单一颜色的32bpp的HBITMAP来绘制区域,DrawGradient:渐变绘制,用在绘制

  背景色中,在dulib中提供了三个背景渐变色彩区域见CControlUI中m_dwBackColor、m_dwBackColor2,m_dwBackColor3,参数hDC为上下文句柄,rc为绘制区域,dwFirst、dwSecond为第一色

  、第二色(即首尾色),bVertical为是否为垂直方向,否则为水平,nSteps指定渐变的步进值,函数内部通过双缓存技术实现绘制通过计算step调整每Step各RGB色彩值创建CreateSolidBrush画刷分区域

  地FillRect填充实现绘图,DrawLine:绘制线段,参数hDC为上下文句柄,rc为绘制区域(事实上绘制线段为该区域的左上角至右下角的对角线线段),nSize为画笔宽度,dwPenColor为画笔颜色,nStyle

  为画笔风格,DrawRect:绘制矩形,参数同DrawLine,少了画笔风格的参数,内部调用Rectangle绘制,矩形内部不会被填充,DrawRoundRect:绘制圆角矩形,参数同DrawRect,此外内部调用了

  RoundRect绘制圆角矩形,同样内部不会被填充,DrawText:绘制文本,参数hDC上下文句柄,pManager为绘制管理对象,rc为绘制区域大小,pstrText为绘制文本字符串,dwTextColor为文本颜色,

  iFont为字体索引,通过pManager中获取其字体句柄,uStyle为绘制风格,函数内部调用DrawText实现绘制文本,DrawHtmlText:绘制HTML文本,GenerateBitmap:生成bitmap,参数pManager

  获取对应绘制管理器对象,pControl控件对象,rc为bitmap大小,使用绘制管理对象中绘制句柄对象绘制控件对象区域并返回该绘制后指定大小的bitmap大小;GetTextSize:获取字符串大小(高、宽度)

  参数hDC为上下文句柄,pManager为绘制管理对象提供当前应用于(hDC下的当前所选字体来计算字符串尺寸)该字符串上的字体,pstrText为字符串,iFont为字体索引,uStyle参数暂时未被用到,

  内部使用GetTextExtentPoint32获取到字符串高度和宽度信息。

  2. UIContainer.h:控件容器,一般用在具有子控件的控件中,如:UIList、UICombox、UITreeView、以及布局控件;

    IContainerUI:UI容器基类,提供了对元素项的索引查找和设置,控件添加和移除等操作的接口定义;GetItemAt:获取指定索引的下的控件对象,GetItemIndex:获取指定控件对应的索引值,此外对应

    SetItemIndex:设置控件对象与索引对,对应早期索引下的Item将被移除,GetCount:获取容器中控件数,Add:添加(追加)控件对象至容器,AddAt:在指定索引处添加控件,Remove:移除指定控件

    对象,RemoveAt:移除指定索引下的控件对象,RemoveAll:移除所有容器下对象;

    CContainerUI:继承于IContainerUI、CControlUI,UI容器CContainerUI也为控件UI对象,因该类比较大,从成员函数开始,GetClass:获取当前对象的类名,GetInterface:获取参数pstrName指定

    类名的控件对象指针,接下来的几个函数继承于IContainerUI,此处不再说明,需要说的是成员变量m_items:容纳控件对象,即为控件容器,DoEvent:事件源处理函数,处理各种事件消息,SetVisible:

    设置当前容器对象可见性并设置容器内各控件对象的可见性状态,SetInternVisible:设置控件内部可见性状态,SetMouseEnabled:设置鼠标是否可用,内部若存在垂直、水平滚动条则也被设置,GetInset

    、SetInset:获取/设置内边距(即客户区)m_rcInset,GetChildPadding/SetChildPadding:获取/设置子对象内边距m_iChildPadding(控件边距),SetAutoDestroy/GetAutoDestroy:设置或查询当前

    容器内对象是否自动销毁(事实上内部还有个延迟销毁),SetDelayedDestroy/GetDelayedDestroy:设置/查询是否延迟销毁,SetMouseChildEnabled/GetMouseChildEnabled:设置或查询本控件的子

    控件是否可以响应用户操作,FindSelectable:查找容器中下一个可用控件索引,SetPos:设置控件位置,若存在子控件则若子控件为浮动的,则调整该控件,否则则设置为当前控件区域的大小,DoPaint:

    绘制操作,SetAttribute:设置控件相关的属性,SetManager:设置控件管理器(绘制管理器UI、控件父窗口,是否初始化),FindControl:查找控件并执行相应提供的回调函数,参数pData一般为鼠标

    点击位置或其他的附加值,SetSubControlText/GetSubControlText:设置/获取子控件文本,SetSubControlFixedHeight/GetSubControlFixedHeight/SetSubControlFixedWdith等分别为获取或

    设置子控件高度或宽度,SetSubControlUserData:设置子控件用户数据,FindSubControl:查找子控件对象,GetScrollPos/SetScrollPos:获取/设置滚动条位置,LineUp/LineDown、PageUp、

    HomeUp、EndDown、LineLeft、LineRight、PageLeft、PageRight、HomeLeft、EndRight等均为对滚动条的控制,EnableScrollBar:是否启用垂直滚动条和水平滚动条;GetVerticalScrollBar、

    GetHorizontalScrollBar:获取垂直滚动条、水平滚动条控件对象;SetFloatPos:设置指定容器元素浮动位置显示,ProcessScrollBar:调整滚动条位置;