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

一、概述

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。希望通过此文能增进与大家交流。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 当使用GDI加载PNG图片时,有时会发生图片失真的情况。这通常是由于PNG图片中包含了透明度信息,而GDI在处理透明度时出现了问题导致的。 具体来说,PNG图片格式支持alpha通道,用于控制图像的透明度。而GDI在处理PNG图片时,只能处理RGB通道,无法处理alpha通道信息。当PNG图片中存在alpha通道时,GDI会将alpha通道信息丢失,只保留RGB通道信息,因而导致了图片失真。 为了解决这个问题,可以使用其他方式加载PNG图片,比如使用第三方库或者使用GDI+。其中,GDI+是Microsoft开发的一种图形处理API,支持PNG图片的读取和处理,可以有效地解决PNG图片失真的问题。 总之,PNG图片失真的问题主要是由于GDI无法处理PNG图片透明度信息所导致的。使用第三方库或者GDI+可以有效地解决这个问题。 ### 回答2: GDI矢量图像和PNG位图有着不同的特点。GDI矢量图像是由数学公式生成的,精度高,可以无限放大而不失真;而PNG位图则是由像素点组成的,当放大图片时会出现锯齿和失真。所以,如果将一个PNG图片放大,在没有适当的滤波算法和图像处理技术的情况下,就会导致图片的失真。为了避免PNG图片的失真,我们可以按照目标尺寸重新制作一张高分辨率的图片,或者使用专业的图像处理软件对图片进行处理和压缩,以便在放大时保持图片质量。此外,还可以采用一些防止PNG图片失真的技巧,如在导出PNG图片时使用抗锯齿的选项,或者使用无损压缩来减小图片文件的大小。总之,PNG图片失真问题可以通过改善图片的质量、使用专业的图像处理软件和优化导出设置等手段来解决。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值