GDI+/GDI实现半透贴图动画(png图片,使用updatelayeredwindow)
写在文章前:一个多月前匆匆辞职,本来写好的草稿还没完成就走了,公司都是内网机,自己辛苦经营的成果都弄不出来,这份草稿几乎是仅剩的一点东西(虽然可能也就是入门知识)。现在在自己做yx,想要做些记录时发现这个半成品草稿,有些唏嘘,虽然是半成品,还是发出来便于以后翻看吧。
================================================================================================================================================================================================
最近接到新任务,要把游戏客户端登录界面做出半透动画特效,就像新天龙八部那种客户端贴图会动的效果。要求是要使用updatelayeredwindow,读取png,还要实现放置按钮控件(在这里不记录了)。
说实话对于win的图片显示我完全是个小白,开始真的什么都不懂,之前开发也在封装好的函数上写逻辑,根本没用过winAPI。好在之前有看过一些相关的知识,加上不停的在网上冲浪,两个星期终于是搞定了。工作能学到东西过程再苦也有意义,下面记录一下过程,和我纠结的点,或许会对读者有些启示意义。
先讲讲显示贴图的整体流程吧。
首先,要想实现半透,一定要把窗口的扩展属性设置为WS_EX_LAYERED,你可以在CreatewindowEx的时候指定,也可以在后面用窗口句柄改。
有人说,咦,改完了为什么我窗口都看不见?这是因为在制定了WS_EXLAYERED属性后,窗口将不再发送WS_PAINT消息,这意味窗口不再被绘制,这样当然就什么都看不见了。这样就要用到我们的关键武器updatelayeredwindow去主动绘制窗口。不过搜一下这个函数你就头疼了,这么多参数怎么玩?
别急,下面就讲解使用updatelayered前的准备工作。
这里讲一下显示器显示图片的原理,显示器有一个叫做设备上下文(DC)的东西,你要做的就是把图片能够弄到这里面去显示。但是我们不能直接将图片放入显示器DC中,因为这样相当于机器的领导CPU亲自跟显示器缓存交接。这肯定不行啊,领导只负责指挥,干活就太丢面了。
所以我们要在内存中复制一个DC,然后CPU把图片存入内存中的DC,让内存DC和显示器DC去传输。
[code]HDC hdcScreen = ::GetWindowDC(hwnd); HDC hdcmem = ::CreateCompatibleDC(hdcScreen);
这样就创建了一个和显示器DC相适配的内存DC:hdcmem
现在在内存中有个一片空间,我们可以在里面放图片,但是不可以直接放进去,因为图片要显示都要以bitmap的格式存储在存储空间中,所以我们要先创建一个位图。可以bitmao加载图片再指定内存设备,也先bitmap指定内存设备,再加载图片进内存设备。bitmap的创建方式有很多,但是我只有在这种方式下成功了
[code]BYTE * pBits ; BITMAPINFOHEADER bmih; ZeroMemory( &bmih, sizeof( BITMAPINFO ) ); bmih.biSize = sizeof (BITMAPINFOHEADER) ; bmih.biWidth = rect.Width() ; bmih.biHeight = rect.Height() ; bmih.biPlanes = 1 ; bmih.biBitCount = 32; //这里一定要是32 16位色没有alpha通道 bmih.biCompression = BI_RGB ; bmih.biSizeImage = 0 ; bmih.biXPelsPerMeter = 0 ; bmih.biYPelsPerMeter = 0 ; bmih.biClrUsed = 0 ; bmih.biClrImportant = 0 ; HBITMAP hBitMap = CreateDIBSection (NULL, (BITMAPINFO *) &bmih, 0, (VOID**)&pBits, NULL, 0) ; 原文:https://blog.csdn.net/xuxinhua/article/details/7569586
位图创建好了再将位图和内存DC绑定:
[code]SelectObject(hdcmem, hBitmap);
之后,我们要将图片加载进内存了。直接用bitmap加载png是不行的,单纯的bitmap真的很弱鸡。GDI+的方法很干脆
[code]Graphics gp(hdcMem); gp.DrawImage(&image, 5, 5, 90, 90);//将png图像绘制到后台DC中
没有任何问题,但是如果要求只能用GDI,那么可以用Cimage来加载图。Cimage相当于在GDI中对GDI+做了封装,本质应该是GDI+,不过不需要GDI+那些头和启动结束的代码了。
[code]CImage * image = new CImage; CImage->Draw(hdcmem,rc);
用这种方式运行后图片确实显示了,但是图片失真很严重。到网上查了很多,最终找到了正确的解释
然后配置一个要用到的混合函数
[code] BLENDFUNCTION blend = { 0 }; blend.BlendOp = AC_SRC_OVER; blend.SourceConstantAlpha = 255; blend.AlphaFormat = AC_SRC_ALPHA;//按通道混合
我们要根据png
//然后我之前写到这里了,已经是快两个月的事情,我差不多忘光了,但是我记得是对图片的像素点逐个修正,重新计算每个像素值---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------我找了半天计算的公式,果不其然,我收藏了网址(好习惯啊!)https://blog.csdn.net/houjixin/article/details/8830469就是这个。如果你用网上那种通用的函数不能解决失真就请用用链接中介绍的方法。后面我真的忘了,若有那一天我再拾起这些可能还会补充。
- 点赞
- 收藏
- 分享
- 文章举报
- 岛屿数量问题(用广度优先算法)
- 岛屿数量问题(深度优先)
- DEV-C++支持C99标准设置方法
- 详解socket编程:bind()函数为什么要使用地址类型转化传递参数?
- fedora等linux虚拟机,虚拟机识别了u盘,但在linux系统里却无法找到u盘文件夹
- 国密算法SM4-PHP实现
- C走迷宫源文件,建议使用Dev-C++编译
- 制作坑人的多层文件夹(使用批处理文件)
- 错误:AttributeError: module 'easygui' has no attribute 'msgbox'
- vue.js循环加载图片资源及其他
- 如何用一次循环得到数组中第二大数
- 使用map删除字符串的交集
- Git学习笔记(一)
- Git学习笔记(二)
- Git学习笔记(三)
- scikit-learn机器学习常用算法原理及编程实战(一)
- scikit-learn机器学习常用算法原理及编程实战(二)
- scikit-learn机器学习常用算法原理及编程实战(五)
- Deep Residual Learning for Image Recognition
- JetBrains系列软件用法