第一章 第八小节Duilib的绘制基础类CRenderEngine和CRenderClip
duilib是一个直接绘制的界面库,所以它的核心之一当然还是怎么绘制的问题,在Duilib里采用的绘制的基础库是win32函数gui和gdiplus两者,为了后面详细阐述后面的绘制消息流程,先将一部分基础预先讲解,这样大家理解起来的难度就会大大降低。第一个类是CRenderClip,源码如下所示:
class UILIB_API CRenderClip
{
public:
~CRenderClip();
RECT rcItem;
HDC hDC;
HRGN hRgn;
HRGN hOldRgn;
static void GenerateClip(HDC hDC, RECT rc, CRenderClip& clip);
static void GenerateRoundClip(HDC hDC, RECT rc, RECT rcItem, int width, int height, CRenderClip& clip);
static void UseOldClipBegin(HDC hDC, CRenderClip& clip);//当前区域
static void UseOldClipEnd(HDC hDC, CRenderClip& clip);//回复上一个区域
};
void CRenderClip::GenerateClip(HDC hDC, RECT rc, CRenderClip& clip)
{
RECT rcClip = { 0 };
::GetClipBox(hDC, &rcClip);
clip.hOldRgn = ::CreateRectRgnIndirect(&rcClip);
clip.hRgn = ::CreateRectRgnIndirect(&rc);
::ExtSelectClipRgn(hDC, clip.hRgn, RGN_AND);
clip.hDC = hDC;
clip.rcItem = rc;
}
void CRenderClip::GenerateRoundClip(HDC hDC, RECT rc, RECT rcItem, int width, int height, CRenderClip& clip)
{
RECT rcClip = { 0 };
::GetClipBox(hDC, &rcClip);
clip.hOldRgn = ::CreateRectRgnIndirect(&rcClip);
clip.hRgn = ::CreateRectRgnIndirect(&rc);
HRGN hRgnItem = ::CreateRoundRectRgn(rcItem.left, rcItem.top, rcItem.right + 1, rcItem.bottom + 1, width, height);
::CombineRgn(clip.hRgn, clip.hRgn, hRgnItem, RGN_AND);
::ExtSelectClipRgn(hDC, clip.hRgn, RGN_AND);
clip.hDC = hDC;
clip.rcItem = rc;
::DeleteObject(hRgnItem);
}
阅读到此处的读者,需要对win32的gdi有一些基础知识的了解,比如HDC是一个设备上下文句柄,它代表一个绘制信息对象的索引。所以要改变一些绘制状态都需要提供这个句柄。ExtSelectClipRgn是一个系统api,该函数通过特定的方式把一个特定的区域与当前的剪切区域合并在一起。SelectClipRgn是另一个系统api,该函数选择一个区域作为指定设备环境的当前剪切区域。虽然他起名为CRenderClip,其实就是一个矩形剪辑区域或者圆角矩形剪辑区域。
另外一个类是CRenderEngine,它绘制一些常见的元素(如文字,形状),及图像数据的读取,源码如下所示:
class UILIB_API CRenderEngine
{
public:
static DWORD AdjustColor(DWORD dwColor, short H, short S, short L);
static HBITMAP CreateARGB32Bitmap(HDC hDC, int cx, int cy, BYTE** pBits);
static void AdjustImage(bool bUseHSL, TImageInfo* imageInfo, short H, short S, short L);
static TImageInfo* LoadImage(STRINGorID bitmap, LPCTSTR type = NULL, DWORD mask = 0, HINSTANCE instance = NULL);
#ifdef USE_XIMAGE_EFFECT
static CxImage *LoadGifImageX(STRINGorID bitmap, LPCTSTR type = NULL, DWORD mask = 0);
#endif
static void FreeImage(TImageInfo* bitmap, bool bDelete = true);
static TImageInfo* LoadImage(LPCTSTR pStrImage, LPCTSTR type = NULL, DWORD mask = 0, HINSTANCE instance = NULL);
static TImageInfo* LoadImage(UINT nID, LPCTSTR type = NULL, DWORD mask = 0, HINSTANCE instance = NULL);
static Gdiplus::Image* GdiplusLoadImage(LPCTSTR pstrPath);
static Gdiplus::Image* GdiplusLoadImage(LPVOID pBuf, size_t dwSize);
static bool DrawIconImageString(HDC hDC, CPaintManagerUI* pManager, const RECT& rcItem, const RECT& rcPaint, \
LPCTSTR pStrImage, LPCTSTR pStrModify = NULL);
static bool MakeFitIconDest(const RECT& rcControl,const CDuiSize& szIcon, const CDuiString& sAlign, RECT& rcDest);
static void DrawText(HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText,DWORD dwTextColor, \
int iFont, UINT uStyle, DWORD dwTextBKColor);
static void DrawImage(HDC hDC, HBITMAP hBitmap, const RECT& rc, const RECT& rcPaint, \
const RECT& rcBmpPart, const RECT& rcCorners, bool bAlpha, BYTE uFade = 255,
bool hole = false, bool xtiled = false, bool ytiled = false);
static bool DrawImageInfo(HDC hDC, CPaintManagerUI* pManager, const RECT& rcItem, const RECT& rcPaint, const TDrawInfo* pDrawInfo, HINSTANCE instance = NULL);
static bool DrawImageString(HDC hDC, CPaintManagerUI* pManager, const RECT& rcItem, const RECT& rcPaint, LPCTSTR pStrImage, LPCTSTR pStrModify = NULL, HINSTANCE instance = NULL);
static void DrawColor(HDC hDC, const RECT& rc, DWORD color);
static void DrawGradient(HDC hDC, const RECT& rc, DWORD dwFirst, DWORD dwSecond, bool bVertical, int nSteps);
// 以下函数中的颜色参数alpha值无效
static void DrawLine(HDC hDC, const RECT& rc, int nSize, DWORD dwPenColor,int nStyle = PS_SOLID);
static void DrawRect(HDC hDC, const RECT& rc, int nSize, DWORD dwPenColor,int nStyle = PS_SOLID);
static void DrawRoundRect(HDC hDC, const RECT& rc, int width, int height, int nSize, DWORD dwPenColor,int nStyle = PS_SOLID);
static void DrawText(HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText, \
DWORD dwTextColor, int iFont, UINT uStyle);
static void DrawHtmlText(HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText,
DWORD dwTextColor, RECT* pLinks, CDuiString* sLinks, int& nLinkRects, int iFont, UINT uStyle);
static HBITMAP GenerateBitmap(CPaintManagerUI* pManager, RECT rc, CControlUI* pStopControl = NULL, DWORD dwFilterColor = 0);
static HBITMAP GenerateBitmap(CPaintManagerUI* pManager, CControlUI* pControl, RECT rc, DWORD dwFilterColor = 0);
static SIZE GetTextSize(HDC hDC, CPaintManagerUI* pManager , LPCTSTR pstrText, int iFont, UINT uStyle);
//alpha utilities
static void CheckAlphaColor(DWORD& dwColor);
};
其中大部分是对win32 gdi的封装,笔者对其中稍微复杂的一个函数展开讲解,这个函数是LoadImage函数,它的复杂性在于它支持从文件,exe资源和zip压缩文件中读取图像信息。为了方便讲解,部分内容以注释的形式写在代码中,源码如下所示:
TImageInfo* CRenderEngine::LoadImage(STRINGorID bitmap, LPCTSTR type, DWORD mask, HINSTANCE instance)
{
LPBYTE pData = NULL;
DWORD dwSize = 0;
do
{
if( type == NULL ) {
//获取图片所在路径,读取文件加载入内存
CDuiString sFile = CPaintManagerUI::GetResourcePath();
if( CPaintManagerUI::GetResourceZip().IsEmpty() ) {
sFile += bitmap.m_lpstr;
HANDLE hFile = ::CreateFile(sFile.GetData(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, \
FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE ) break;
dwSize = ::GetFileSize(hFile, NULL);
if( dwSize == 0 ) break;
DWORD dwRead = 0;
pData = new BYTE[ dwSize ];
::ReadFile( hFile, pData, dwSize, &dwRead, NULL );
::CloseHandle( hFile );
if( dwRead != dwSize ) {
delete[] pData;
pData = NULL;
break;
}
}
else {
//获取zip文件路径
sFile += CPaintManagerUI::GetResourceZip();
CDuiString sFilePwd = CPaintManagerUI::GetResourceZipPwd();
HZIP hz = NULL;
//获取ZIP资源句柄
if( CPaintManagerUI::IsCachedResourceZip() ) hz = (HZIP)CPaintManagerUI::GetResourceZipHandle();
else
{
#ifdef UNICODE
char* pwd = w2a((wchar_t*)sFilePwd.GetData());
hz = OpenZip(sFile.GetData(), pwd);
if(pwd) delete[] pwd;
#else
hz = OpenZip(sFile.GetData(), sFilePwd.GetData());
#endif
}
if( hz == NULL ) break;
//从内存中查找对应的文件名,并解压到内存
ZIPENTRY ze;
int i = 0;
CDuiString key = bitmap.m_lpstr;
key.Replace(_T("\\"), _T("/"));
if( FindZipItem(hz, key, true, &i, &ze) != 0 ) break;
dwSize = ze.unc_size;
if( dwSize == 0 ) break;
pData = new BYTE[ dwSize ];
int res = UnzipItem(hz, i, pData, dwSize);
if( res != 0x00000000 && res != 0x00000600) {
delete[] pData;
pData = NULL;
if( !CPaintManagerUI::IsCachedResourceZip() ) CloseZip(hz);
break;
}
if( !CPaintManagerUI::IsCachedResourceZip() ) CloseZip(hz);
}
}
else {
HINSTANCE dllinstance = NULL;
if (instance) {
dllinstance = instance;
}
else {
dllinstance = CPaintManagerUI::GetResourceDll();
}
HRSRC hResource = ::FindResource(dllinstance, bitmap.m_lpstr, type);
if( hResource == NULL ) break;
HGLOBAL hGlobal = ::LoadResource(dllinstance, hResource);
if( hGlobal == NULL ) {
FreeResource(hResource);
break;
}
dwSize = ::SizeofResource(dllinstance, hResource);
if( dwSize == 0 ) break;
pData = new BYTE[ dwSize ];
::CopyMemory(pData, (LPBYTE)::LockResource(hGlobal), dwSize);
::FreeResource(hResource);
}
} while (0);
while (!pData)
{
//读不到图片, 则直接去读取bitmap.m_lpstr指向的路径
HANDLE hFile = ::CreateFile(bitmap.m_lpstr, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, \
FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE ) break;
dwSize = ::GetFileSize(hFile, NULL);
if( dwSize == 0 ) break;
DWORD dwRead = 0;
pData = new BYTE[ dwSize ];
::ReadFile( hFile, pData, dwSize, &dwRead, NULL );
::CloseHandle( hFile );
if( dwRead != dwSize ) {
delete[] pData;
pData = NULL;
}
break;
}
if (!pData)
{
return NULL;
}
//采用stb_image开源库函数从内存中读取图像
LPBYTE pImage = NULL;
int x,y,n;
pImage = stbi_load_from_memory(pData, dwSize, &x, &y, &n, 4);
delete[] pData;
if( !pImage ) {
return NULL;
}
//创建内存位图对象和句柄
BITMAPINFO bmi;
::ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = x;
bmi.bmiHeader.biHeight = -y;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = x * y * 4;
bool bAlphaChannel = false;
LPBYTE pDest = NULL;
HBITMAP hBitmap = ::CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pDest, NULL, 0);
if( !hBitmap ) {
return NULL;
}
//填充数据
for( int i = 0; i < x * y; i++ )
{
pDest[i*4 + 3] = pImage[i*4 + 3];
if( pDest[i*4 + 3] < 255 )
{
pDest[i*4] = (BYTE)(DWORD(pImage[i*4 + 2])*pImage[i*4 + 3]/255);
pDest[i*4 + 1] = (BYTE)(DWORD(pImage[i*4 + 1])*pImage[i*4 + 3]/255);
pDest[i*4 + 2] = (BYTE)(DWORD(pImage[i*4])*pImage[i*4 + 3]/255);
bAlphaChannel = true;
}
else
{
pDest[i*4] = pImage[i*4 + 2];
pDest[i*4 + 1] = pImage[i*4 + 1];
pDest[i*4 + 2] = pImage[i*4];
}
if( *(DWORD*)(&pDest[i*4]) == mask ) {
pDest[i*4] = (BYTE)0;
pDest[i*4 + 1] = (BYTE)0;
pDest[i*4 + 2] = (BYTE)0;
pDest[i*4 + 3] = (BYTE)0;
bAlphaChannel = true;
}
}
stbi_image_free(pImage);
//构建duilib图像信息标签
TImageInfo* data = new TImageInfo;
data->pBits = NULL;
data->pSrcBits = NULL;
data->hBitmap = hBitmap;
data->nX = x;
data->nY = y;
data->bAlpha = bAlphaChannel;
return data;
}
请读者先仔细阅读源码,这部分是从其它位置加载资源的关键,由于采用的zlib库进行资源解压和读取,但是如果采用unicode模式读取,注意编码的转换,它支持的utf-8,而不是双字节的unicode,在传入前需要进行编码转换,否则采用汉字命名的文件名将读取不出来。最后就是这里面一个创建位图的关键api,CreateDIBSection函数,它创建应用程序可以直接写入的、与设备无关的位图(DIB)的函数。
欢迎光临知了软件开发网络平台,本公司定制开发各类软件,主要方向为桌面专业软件开发和插件定制开发,桌面软件主要包括文字图形识别类软件,信息管理类软件,3D打印类软件,视频类软件以及其它涉及专业的各类图形图像处理软件。插件包含AE插件,AI插件,PS插件,PDF插件,3DMAX插件以及Word,Excel等Office插件开发。详情请咨询,微信QQ:312117271,手机:18928899728,邮箱: anjingzhi_sea@163.com.
公司网址:http://www.zhiliaos.com
本文地址:https://blog.csdn.net/weixin_42247427/article/details/107280938