文章转自:http://www.gymsaga.com/project/604.html
这节说一下QQ的聊天面板
由于这两天事情比较多,聊天面板也只是搭建完了界面,明天把内容充实一下,代码也将于明天公布,敬请期待!
先说一下修改的内容:
1:目前发现,当拖动窗口时,程序会出现卡顿的情况,同时cpu会显著提升,经测试,此问题的根源在于GDI+的DrawImage,此函数造成整个效率的下降,为什么会出现这种问题,当Bitmap被创建之后,Bitmap的格式可能DrawImage时不一样,当不一样的时候,他就会根据其类型进行转码,期间需要消耗一部分的时间,我们知道GDI的BltBit函数效果是很高的,虽然说鱼和熊掌不可兼得,但是想同时兼顾GDI的高效和GDI+的易用,其实是有办法的。
1:创建Bitmap的缓存,由Bitmap Clone出一个新的Bitmap,这个时候就可以指定图像的格式,当绘制DrawImage的时候,会节约一部分时间。不过效率并不会提高多少
2:将Bitmap对象通过CreateDIBSection转换成BITMAPINFO对象,这样就可以通过BltBit绘制了,效率也会提高不少,不过对于图片资源比较多的,最好使用多线程,且将CreateDIBSection放在初始化中
不过,为了节约时间,本次我通过CImage类完成,效率比之前的GDI+提高了不少,不过这里需要注意一下,采用CImage绘制时,如果对图像进行缩放,尤其是缩小处理的时候,会出现像素丢失失真的情况,此时通过pDC->SetStretchBltMode(HALFTONE);即可解决问题
CImage对于Png这种带有Alpha通道的图像,需要进行Alpha预乘,代码如下
bool CImageEx::SetAlphaBit() { ASSERT(IsNull() == false); if(IsNull())return FALSE; if ( GetBPP() == 32 )//png图像 { LPVOID pBitsSrc = NULL; BYTE * psrc = NULL; BITMAP stBmpInfo; HBITMAP hBmp = (HBITMAP)*this; ::GetObject(hBmp, sizeof(BITMAP), &stBmpInfo); if (32 != stBmpInfo.bmBitsPixel || NULL == stBmpInfo.bmBits) return FALSE; psrc = (BYTE *) stBmpInfo.bmBits; for (int nPosY = 0; nPosY < abs(stBmpInfo.bmHeight); nPosY++) { for (int nPosX = stBmpInfo.bmWidth; nPosX > 0; nPosX--) { BYTE alpha = psrc[3]; psrc[0] = (BYTE)((psrc[0] * alpha) / 255); psrc[1] = (BYTE)((psrc[1] * alpha) / 255); psrc[2] = (BYTE)((psrc[2] * alpha) / 255); psrc += 4; } } } return TRUE; }
在说一下窗口的三个系统按钮,最大化,最小化,关闭,我们发现QQ,不同的窗口,系统按钮的多少是不确定的,有的只有关闭,有的没有最大化,有的一个按钮也没有(比如QQ更新的时候)如果我们每个窗口都进行这些按钮的加载处理又太过于繁琐,所以,我们在基类中完成这项工作,我们定义一个枚举,定义刚才的这些情况
enum AFX_WND_STYLE { en_Wnd_Normal=0, //关闭,最大化,最小化同时存在 en_Wnd_MinimizeBox, //无最大化按钮 en_Wnd_CloseBox, //只有关闭按钮 en_Wnd_None //无按钮 };
我们将构造函数添加一个可选参数,默认,三种按钮同时存在
CSkinManager(UINT nIDTemplate,CWnd* pParent = NULL,AFX_WND_STYLE Style = en_Wnd_Normal); // 标准构造函数
此时,我们在初始化的时候进行判断处理
CRect rcClient; GetClientRect(&rcClient); if ( m_enWndStyle != en_Wnd_None ) { m_btClose.Create(NULL,WS_VISIBLE|WS_CHILD,CRect(rcClient.Width()-41,0,0,0),this,IDCANCEL); m_btClose.SetBackImage(TEXT("\\QQ\\Button\\btn_close_normal.png"),TEXT("\\QQ\\Button\\btn_close_highlight.png"),TEXT("\\QQ\\Button\\btn_close_down.png"),TEXT("\\QQ\\Button\\btn_close_normal.png")); m_btClose.SetButtonType(en_PushButton); m_btClose.SetParentBack(hParentDC); m_btClose.SetSize(39,20); if ( m_enWndStyle != en_Wnd_CloseBox ) { if( m_enWndStyle != en_Wnd_MinimizeBox ) { m_btMax.Create(NULL,WS_VISIBLE|WS_CHILD,CRect(rcClient.Width()-69,0,0,0),this,IDC_WNDMAX); m_btMax.SetBackImage(TEXT("\\QQ\\Button\\btn_max_normal.png"),TEXT("\\QQ\\Button\\btn_max_highlight.png"),TEXT("\\QQ\\Button\\btn_max_down.png"),TEXT("\\QQ\\Button\\btn_max_normal.png")); m_btMax.SetButtonType(en_PushButton); m_btMax.SetParentBack(hParentDC); m_btMax.SetSize(28,20); } m_btMin.Create(NULL,WS_VISIBLE|WS_CHILD,CRect(rcClient.Width()-69-(m_enWndStyle==en_Wnd_Normal?28:0),0,0,0),this,IDC_WNDMIN); m_btMin.SetBackImage(TEXT("\\QQ\\Button\\btn_mini_normal.png"),TEXT("\\QQ\\Button\\btn_mini_highlight.png"),TEXT("\\QQ\\Button\\btn_mini_down.png"),TEXT("\\QQ\\Button\\btn_mini_normal.png")); m_btMin.SetButtonType(en_PushButton); m_btMin.SetParentBack(hParentDC); m_btMin.SetSize(28,20); } }
在WM_SIZE消息内,在分别处理一下按钮的位置,这样,我们就不用在每个子类里分别创建这些按钮了。
关于最大化,可能说到最大化,你第一反应是ShowWindow(SW_MAXIMIZE),对,这个的确可以最大化,但是你发现没有,他貌似实现的是全屏,我们并不希望把任务栏给盖住,那么我们就需要获取窗口能在桌面显示的大小
void CSkinManager::OnBnClickWindowMax() { static CRect rcClient(0,0,0,0); if ( m_bIsZoomed ) { m_btMax.SetBackImage(TEXT("\\QQ\\Button\\btn_max_normal.png"),TEXT("\\QQ\\Button\\btn_max_highlight.png"),TEXT("\\QQ\\Button\\btn_max_down.png"),TEXT("\\QQ\\Button\\btn_max_normal.png")); MoveWindow(&rcClient); m_bIsZoomed = false; } else { GetWindowRect(&rcClient); m_btMax.SetBackImage(TEXT("\\QQ\\Button\\btn_restore_normal.png"),TEXT("\\QQ\\Button\\btn_restore_highlight.png"),TEXT("\\QQ\\Button\\btn_restore_down.png"),TEXT("\\QQ\\Button\\btn_restore_normal.png")); CRect rc; SystemParametersInfo(SPI_GETWORKAREA,0,&rc,0); MoveWindow(&rc); m_bIsZoomed = true; } }
我们通过SystemParametersInfo函数就可以轻松实现这个功能,这样我们就不需要获取任务栏的位置,任务栏的高度或者宽度,通过这些属性去计算了
QQ聊天面板,关于工具栏,我们通过CWnd模拟出一个工具栏,这样我们可以解决原有工具栏图像支持单一的问题,明天我们再讲一下工具栏的绘制和使用