您的位置:首页 > 其它

用PNG透明图片和GDI+做不规则透明窗体

2014-05-05 17:42 447 查看
一、概述

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。希望通过此文能增进与大家交流。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: