一、概述
GDI+的应用使得平面图形图象编程变的更加容易,本文以一个基于对话框的时钟程序为例,在VC6.0中调用*.png图片实现半透明渐变窗口,该程序实现了指针式和数字式两种时钟显示方式。窗口实现了半透明渐变窗口、窗口拖动无移动矩形框、隐藏了任务栏窗体按钮等。
效果图如下:
图一 程序执行后与WindowXP桌面背景效果图
二、准备工作
1、图片资源准备工作。首先在Photoshop中编辑好时钟的背景、时针、分针以及数字时钟显示方式的所有图片,如下图:将这些图片保存成为带透明通道的.png格式(GDI+调用显示时能够透明调背景)。这样程序中图片资源就准备好了。
2、下面开始做好在VC6.0下展开此项工作的基本准备工作。
(1)、下载gdiplus forVC6.0的SDK,(总共两兆多)
(2)、在C盘建立文件夹“GDI+”将开发包拷贝在里面,亦即建立如下路径,以便例子代码顺利编译(当然你可以放到任意你喜欢的地方,只要在你的Project中正确包含路径即可!)。
C:\GDI+\Includes
C:\GDI+\Lib
C:\GDI+\gdiplus.dl
(3)在stdAfx.h中添加对GDI+环境的设置
1.
#define UNICODE
2.
#ifndef ULONG_PTR
3.
#define ULONG_PTR unsigned long*
4.
#endif
5.
#include "c:\gdi+\includes\gdiplus.h" 请修改为你的头文件路径
6.
using
namespace
Gdiplus;
7.
#pragma comment(lib, "c:\\gdi+\\lib\\gdiplus.lib") 请修改为你的.lib文件路径
(4)在GDIPClock.cpp中编辑app的InitInstance()中添加如下代码进行GDI+的初始化工作
1.
GdiplusStartupInput gdiplusStartupInput;
2.
ULONG_PTR
gdiplusToken;
3.
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
4.
......
5.
//在对话框程序结束后
6.
//关闭gdiplus的环境
7.
GdiplusShutdown(gdiplusToken);
三、程序的实现全过程
1、建立一个基于对话框的Project,这里的名称为GDIPClock
2、在GDIPClockDlg.h中定义所有类成员变量,包括所有图片的指针和图片的长宽尺寸信息。
01.
Image *m_pImageClock;
02.
Image *m_pImageClock1;
03.
Image *m_pImageHHour;
04.
Image *m_pImageHMinu;
05.
Image *m_pImageHSec;
06.
Image *m_pImageNum;
07.
int
m_BakWidth , m_BakHeight ;
08.
int
m_HourWidth, m_HourHeight;
09.
int
m_MinuWidth , m_MinuHeight;
10.
int
m_SecWidth , m_SecHeight ;
11.
HINSTANCE
hFuncInst ;
12.
Typedef
BOOL
(WINAPI*MYFUNC)(
HWND
,
HDC
,POINT*,SIZE*,
HDC
,POINT*,
13.
COLORREF
,BLENDFUNCTION*,
DWORD
);
14.
MYFUNC UpdateLayeredWindow;
在这一步中需要特别说明的是,在创建透明窗口式需要调用一个Windows API函数UpdateLayeredWindow(),该函数在.net以上的版本的SDK中有申明,但是在VC6.0下要调用要么下载200多兆的高版本SDK,要么从动态链接库“User32.dll”中调用,这里选择从“User32.dll”中调用。以上定义中后三项就是为此作准备的。
3、在对话框的OnCreate()中添加如下代码:对2的函数和成员变量进行初始化!(其中ImageFromIDResource()函数为从资源中载入Png图像的一个方法!)
01.
int
CGDIPClockDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
02.
{
03.
if
(CDialog::OnCreate(lpCreateStruct) == -1)
04.
return
-1;
05.
hFuncInst = LoadLibrary(
"User32.DLL"
);
06.
BOOL
bRet=FALSE;
07.
if
(hFuncInst)
08.
UpdateLayeredWindow=(MYFUNC)GetProcAddress(hFuncInst,
"UpdateLayeredWindow"
);
09.
else
10.
{
11.
AfxMessageBox(
"User32.dll ERROR!"
);
12.
exit
(0);
13.
}
14.
//初始化gdiplus的环境
15.
// Initialize GDI+.
16.
m_Blend.BlendOp=0;
//theonlyBlendOpdefinedinWindows2000
17.
m_Blend.BlendFlags=0;
//nothingelseisspecial...
18.
m_Blend.AlphaFormat=1;
//...
19.
m_Blend.SourceConstantAlpha=255;
//AC_SRC_ALPHA
20.
21.
// png图片添加到资源中了在"PNG"下:所以这里可以从资源中调用,
22.
// 这里Image没有提供字节调用资源中图像的函数,
23.
// ImageFromIDResource()是通过资源名称"PNG"和资源ID号将图像
24.
// 的Image指针传递给指针应用。来完成的。
25.
26.
ImageFromIDResource(IDR_PNGBAK1,
"PNG"
,m_pImageClock1);
27.
ImageFromIDResource(IDR_PNGNUM,
"PNG"
,m_pImageNum);
28.
ImageFromIDResource(IDR_PNGBAK,
"PNG"
,m_pImageClock);
29.
ImageFromIDResource(IDR_PNGHOUR,
"PNG"
,m_pImageHHour);
30.
ImageFromIDResource(IDR_PNGMIN,
"PNG"
,m_pImageHMinu);
31.
ImageFromIDResource(IDR_PNGSEC,
"PNG"
,m_pImageHSec);
32.
m_BakWidth =m_pImageClock->GetWidth();
33.
m_BakHeight =m_pImageClock->GetHeight();
34.
m_HourWidth =m_pImageHHour->GetWidth();
35.
m_HourHeight=m_pImageHHour->GetHeight();
36.
m_MinuWidth =m_pImageHMinu->GetWidth();
37.
m_MinuHeight=m_pImageHMinu->GetHeight();
38.
m_SecWidth =m_pImageHSec->GetWidth();
39.
m_SecHeight =m_pImageHSec->GetHeight();
40.
::SetWindowPos(m_hWnd, HWND_TOPMOST,0,0,m_BakWidth,m_BakHeight,
41.
SWP_NOSIZE|SWP_NOMOVE);
42.
return
0;
43.
}
4.在OnInitDialog()种添加如下代码对调用透明窗体初始化和设置时钟进行刷新,代码意义有注解:
01.
//启动后立刻更新窗口样式为透明窗体
02.
UpdateClockDisplay();
03.
SetTimer(1,500,NULL);
04.
//去除任务栏窗口对应按钮
05.
ModifyStyleEx (WS_EX_APPWINDOW,WS_EX_TOOLWINDOW );
06.
void
CGDIPClockDlg::OnTimer(
UINT
nIDEvent)
07.
{
08.
// TODO: Add your message handler code here and/or call default
09.
UpdateClockDisplay();
10.
CDialog::OnTimer(nIDEvent);
11.
}
5、透明窗体创建于刷新,均调用以下函数完成,函数的参数表示整个窗体的透明度
在该函数中包括了GDI+中对Image.DrawImage()函数的集中重载方式的使用,还有在GDI+中图像变换矩阵的使用初步研究。
001.
BOOL
CGDIPClockDlg::UpdateClockDisplay(
int
Transparent)
002.
{
003.
HDC
hdcTemp=GetDC()->m_hDC;
004.
m_hdcMemory=CreateCompatibleDC(hdcTemp);
005.
HBITMAP
hBitMap=CreateCompatibleBitmap(hdcTemp,m_BakWidth,m_BakHeight);
006.
SelectObject(m_hdcMemory,hBitMap);
007.
if
(Transparent<0||Transparent>100) Transparent=100;
008.
009.
m_Blend.SourceConstantAlpha=
int
(Transparent*2.55);
010.
HDC
hdcScreen=::GetDC (m_hWnd);
011.
RECT rct;
012.
GetWindowRect(&rct);
013.
POINT ptWinPos={rct.left,rct.top};
014.
Graphics graph(m_hdcMemory);
015.
016.
Point points[] = { Point(0, 0),
017.
Point(m_BakWidth, 0),
018.
Point(0, m_BakHeight)
019.
};
020.
static
bool
bFly=
false
;
021.
bFly?graph.DrawImage(m_pImageClock, points, 3): graph.DrawImage(m_pImageClock1, points, 3);
022.
bFly=!bFly;
023.
int
OxyX=140;
//m_BakWidth/2+8;
024.
int
OxyY=90;
//m_BakHeight/2+10;
025.
SYSTEMTIME SystemTime;
// address of system time structure
026.
GetLocalTime(&SystemTime);
027.
028.
// 定义一个单位矩阵,坐标原点在表盘中央
029.
Matrix matrixH(1,0,0,1,OxyX,OxyY);
030.
// 时针旋转的角度度
031.
matrixH.Rotate(SystemTime.wHour*30+SystemTime.wMinute/2.0-180);
032.
Point pointsH[] = { Point(0, 0),Point(m_HourWidth, 0),Point(0, m_HourHeight)};
033.
matrixH.Translate(-m_HourWidth/2,-m_HourHeight/6);
034.
// 用该矩阵转换points
035.
matrixH.TransformPoints( pointsH, 3);
036.
graph.DrawImage (m_pImageHHour,pointsH, 3);
037.
038.
// 定义一个单位矩阵,坐标原点在表盘中央
039.
Matrix matrixM(1,0,0,1,OxyX,OxyY);
040.
// 分针旋转的角度度
041.
matrixM.Rotate(SystemTime.wMinute*6-180);
042.
Point pointsM[] = { Point(0, 0),Point(m_MinuWidth, 0),Point(0, m_MinuHeight)};
043.
matrixM.Translate(-m_MinuWidth/2,-m_MinuHeight/6);
044.
// 用该矩阵转换pointsM
045.
matrixM.TransformPoints( pointsM, 3);
046.
graph.DrawImage (m_pImageHMinu,pointsM, 3);
047.
048.
// 定义一个单位矩阵,坐标原点在表盘中央
049.
Matrix matrix(1,0,0,1,OxyX,OxyY);
050.
// 秒针旋转的角度度
051.
matrix.Rotate(SystemTime.wSecond*6-180);
052.
Point pointsS[] = { Point(0, 0),Point( m_SecWidth,0),Point(0,m_SecHeight )};
053.
matrix.Translate(-m_SecWidth/2,-m_SecHeight/7);
054.
// 用该矩阵转换pointsS
055.
matrix.TransformPoints( pointsS, 3);
056.
graph.DrawImage (m_pImageHSec,pointsS, 3);
057.
//HH:MM:SS
058.
059.
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
060.
graph.DrawImage(m_pImageNum,0, 0, 14*(SystemTime.wHour/10), 0,14,23,UnitPixel);
061.
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
062.
graph.DrawImage(m_pImageNum,20,0, 14*(SystemTime.wHour%10), 0,14,23,UnitPixel);
063.
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
064.
graph.DrawImage(m_pImageNum,20*2,0, 140, 0,14,23,UnitPixel);
065.
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
066.
graph.DrawImage(m_pImageNum,20*3, 0, 14*(SystemTime.wMinute/10), 0,14,23,UnitPixel);
067.
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
068.
graph.DrawImage(m_pImageNum,20*4,0, 14*(SystemTime.wMinute%10), 0,14,23,UnitPixel);
069.
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
070.
graph.DrawImage(m_pImageNum,20*5,0, 140, 0,14,23,UnitPixel);
071.
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
072.
graph.DrawImage(m_pImageNum,20*6, 0, 14*(SystemTime.wSecond/10), 0,14,23,UnitPixel);
073.
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
074.
graph.DrawImage(m_pImageNum,20*7,0, 14*(SystemTime.wSecond%10), 0,14,23,UnitPixel);
075.
076.
SIZE sizeWindow={m_BakWidth,m_BakHeight};
077.
POINT ptSrc={0,0};
078.
DWORD
dwExStyle=GetWindowLong(m_hWnd,GWL_EXSTYLE);
079.
if
((dwExStyle&0x80000)!=0x80000)
080.
SetWindowLong(m_hWnd,GWL_EXSTYLE,dwExStyle^0x80000);
081.
082.
BOOL
bRet=FALSE;
083.
bRet= UpdateLayeredWindow( m_hWnd,hdcScreen,&ptWinPos,
084.
&sizeWindow,m_hdcMemory,&ptSrc,0,&m_Blend,2);
085.
graph.ReleaseHDC(m_hdcMemory);
086.
::ReleaseDC(m_hWnd,hdcScreen);
087.
hdcScreen=NULL;
088.
::ReleaseDC(m_hWnd,hdcTemp);
089.
hdcTemp=NULL;
090.
DeleteObject(hBitMap);
091.
DeleteDC(m_hdcMemory);
092.
m_hdcMemory=NULL;
093.
return
bRet;
094.
}
095.
BOOL
CGDIPClockDlg::ImageFromIDResource(
UINT
nID,
LPCTSTR
sTR,Image * &pImg)
096.
{
097.
HINSTANCE
hInst = AfxGetResourceHandle();
098.
HRSRC
hRsrc = ::FindResource (hInst,MAKEINTRESOURCE(nID),sTR);
// type
099.
if
(!hRsrc)
100.
return
FALSE;
101.
// load resource into memory
102.
DWORD
len = SizeofResource(hInst, hRsrc);
103.
BYTE
* lpRsrc = (
BYTE
*)LoadResource(hInst, hRsrc);
104.
if
(!lpRsrc)
105.
return
FALSE;
106.
// Allocate global memory on which to create stream
107.
HGLOBAL
m_hMem = GlobalAlloc(GMEM_FIXED, len);
108.
BYTE
* pmem = (
BYTE
*)GlobalLock(m_hMem);
109.
memcpy
(pmem,lpRsrc,len);
110.
IStream* pstm;
111.
CreateStreamOnHGlobal(m_hMem,FALSE,&pstm);
112.
// load from stream
113.
pImg=Gdiplus::Image::FromStream(pstm);
114.
// free/release stuff
115.
GlobalUnlock(m_hMem);
116.
pstm->Release();
117.
FreeResource(lpRsrc);
118.
}
119.
void
CGDIPClockDlg::OnLButtonDown(
UINT
nFlags, CPoint point)
120.
{
121.
//禁止显示移动矩形窗体框
122.
::SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,TRUE,NULL,0);
123.
//非标题栏移动整个窗口
124.
SendMessage(WM_SYSCOMMAND,0xF012,0);
125.
// PostMessage(WM_NCLBUTTONDOWN,HTCAPTION,MAKELPARAM(point.x,point.y));
126.
CDialog::OnLButtonDown(nFlags, point);
127.
}
详细实现过程请参考源代码!
四、结束语
编写该程序的主要动力来自于对GDI+图像、图形功能的好奇,网上好多例子和文章都是关于C#或delphi等语言的。本人一直以来习惯于使用VC6.0。希望通过此文能增进与大家交流。