Wince/VC高效PNG贴图,自定义Alpha算法
2013-06-22 23:13
183 查看
工作中,做一些炫点的界面都需要用到PNG图片,Wince里面微软也提供了PNG图片的支持,不过Alpha的混合速度比较慢,所以自己实现了一个Alpha的混合运算接口,经过测试,要比微软AlphaBlend快4、5倍。当然Alpha混合的方法也适合window下的VC使用。下面有测试的数据。
原创博文,需要转载,请标明出处:/article/4889609.html
1、创建兼容32位位图。
一般界面贴图,我们都是使用微软的兼容DC和兼容位图进行处理。不过这里我需要创建一张32位的设备无关位图。(有关DIB位图相关知识,不了解的可以百度一下,这是和兼容位图对应的一种位图格式。这里就不多说了)。
上面的函数是创建一张指定大小的DIB位图,而且返回这张位图的数据指针(指向位图数据的首指针)。
这张位图,一般就是用来当做背景位图,我们一般贴图使用的双缓冲方式,需要前后两张位图,这个DIB位图就是用做内存DC的位图。
2、加载PNG图片
PNG图片加载,这里还是使用微软的IImage解码PNG图片,使用过pnglib解码库,实际解码速度还比不上IImage,也可能是我编译的libpng库没有做优化吧。所以暂时放弃使用libpng。后续有时间会研究一下libpng解码的原理和嵌入式上优化方式。Android上也是使用libpng解码png,所以解码速度应该还是可以的,不过估计需要做一些嵌入式平台的化,比较大部分嵌入式平台机器CPU主频都不高,(现在的手机例外 -_-!),我工作接触的一般也就400M到800M的范围。下面是解码部分代码,主要是从IImage解码PNG图片,然后把PNG图片构造一个DIB位图,获取一个可以操作图片的指针。
上面主要m_pImgDataBuf 就是一个Byte *的指针,一个指向PNG图片数据的起始指针。我们利用这个指针就可以操作我们刚刚解码的PNG图片的数据。
3、Alpha混合
首先说说Alpha混合,Alpha是指PNG图片的透明度,一般我们使用PNG图片就是为了可以有半透明的显示效果,大部分比较炫的界面,都有这些半透明特效使用,Alpha就是指定图片的透明度,一般图片的透明度我们可以分为两种。
第一:整张图片的Alpha值,图片每个像素使用同一个Alpha值。
第二:每个像素使用独立的Alpha值。
对于32位的PNG图片来说,每个像素值,都保存了自己的Alpha值,32位PNG的像素格式:ARGB8888就是,每个值都是一个8位值。
Alpha混合的基本公式如下:AlphaResult = ALPHA * srcPixel + ( 1 - ALPHA ) * destPixel
下面给出一个Alpha的混合算法例子。
背景图和需要显示的PNG图片,两张图片对应像素值,根据Alpha混合混合,就可以得到最终的图片,这个就是整个显示过程。
这里的方法不仅仅针对PNG图片,其实对于jpg和bmp也是有效的,只是这两种图片,一般是不包含alpha信息的,所以只能控制整张图片的透明度,而不能控制每个像素的透明度。
上面的运算公式经过一些处理,主要是减少运算和减少乘除法的使用,比较cpu运行乘除法比不上加减法。
下面是我自己测试的数据结果:
这个是我在一台主频是600M的CPU上面运行的速度对比,速度单位是毫秒,可以对比,使用自己的Alpha混合要比微软的AlpBlend快不少,特别是针对大图片来说。
查过一些资料,大部分人的观点是微软的AlphaBlend里面处理过多的异常处理和对图片伸缩进行操作,导致比较慢。
有需要朋友可以自己根据我上面说的封装一个png贴图类,我前面写了一篇使用微软ALphBlend贴图的文章,里面给出了完整代码。
这个自定义的方法,代码比较多,我就不贴出来的。
微软AlphaBlend贴图类:/article/4889608.html
如果有需要的朋友可以根据自己情况封装不同的接口处理,如果有不了解地方,可以留言。
新建的讨论群,有兴趣可以加入
VC/Wince群:87053214
原创博文,需要转载,请标明出处:/article/4889609.html
1、创建兼容32位位图。
一般界面贴图,我们都是使用微软的兼容DC和兼容位图进行处理。不过这里我需要创建一张32位的设备无关位图。(有关DIB位图相关知识,不了解的可以百度一下,这是和兼容位图对应的一种位图格式。这里就不多说了)。
/* *hDC:兼容的DC指针 *nWidth:需要创建DIB位图宽 *nHeight:创建位图高 *pBitmapData:这是我自己定义的数据类型,就是一个unsigned char * 类型 */ HBITMAP CPngBitBlt::Create32Bitmap(HDC hDC, int nWidth, int nHeight,HBMDC &pBitmapData) { if(hDC == NULL) { return NULL; } if(nWidth==0 || nHeight==0) { return NULL; } BITMAPINFO *pbinfo = new BITMAPINFO; if(!pbinfo) { return NULL; } ZeroMemory(pbinfo, sizeof(BITMAPINFO)); pbinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pbinfo->bmiHeader.biWidth = nWidth; pbinfo->bmiHeader.biHeight = -nHeight;//位图翻转 pbinfo->bmiHeader.biPlanes = 1; pbinfo->bmiHeader.biBitCount = 32;//32位位图 BYTE* lpBitmapBits = NULL; HBITMAP hDIBmp = CreateDIBSection(hDC, pbinfo, DIB_RGB_COLORS, (void **)&lpBitmapBits, NULL, 0); pBitmapData = lpBitmapBits; delete pbinfo; if(hDIBmp == NULL) { return NULL; } return hDIBmp; }
上面的函数是创建一张指定大小的DIB位图,而且返回这张位图的数据指针(指向位图数据的首指针)。
这张位图,一般就是用来当做背景位图,我们一般贴图使用的双缓冲方式,需要前后两张位图,这个DIB位图就是用做内存DC的位图。
2、加载PNG图片
PNG图片加载,这里还是使用微软的IImage解码PNG图片,使用过pnglib解码库,实际解码速度还比不上IImage,也可能是我编译的libpng库没有做优化吧。所以暂时放弃使用libpng。后续有时间会研究一下libpng解码的原理和嵌入式上优化方式。Android上也是使用libpng解码png,所以解码速度应该还是可以的,不过估计需要做一些嵌入式平台的化,比较大部分嵌入式平台机器CPU主频都不高,(现在的手机例外 -_-!),我工作接触的一般也就400M到800M的范围。下面是解码部分代码,主要是从IImage解码PNG图片,然后把PNG图片构造一个DIB位图,获取一个可以操作图片的指针。
BOOL CImageData::LoadImageFromFile(const TCHAR *fileName) { // 参数有效性 if (fileName == NULL) { return FALSE; } // 创建COM实例 HRESULT hr = NULL; if(FAILED(hr = ::CoCreateInstance(CLSID_ImagingFactory,NULL,CLSCTX_INPROC_SERVER,IID_IImagingFactory,(void**) &m_pImagingFactory))) { return FALSE; }// 从文件中创建图片 if(FAILED(hr = m_pImagingFactory->CreateImageFromFile(fileName, &m_pImage))) { return FALSE; } // 得到图片信息 if(FAILED(hr = m_pImage->GetImageInfo(&m_ImageInfo))) { return FALSE; } // 获取原始数据 if (ReadImageData() == FALSE) { return FALSE; } //......// 成功获得图片信息 return TRUE; }
BOOL CImageData::ReadImageData() { // 用于返回结果 BOOL bRet = TRUE; // 参数有效性 if (m_pImage==NULL || m_pImagingFactory==NULL) { return FALSE; } // 取得图片原始数据 RECT rect = {0, 0, m_ImageInfo.Width, m_ImageInfo.Height}; BitmapData bitmapData; bitmapData.Width = m_ImageInfo.Width; bitmapData.Height = m_ImageInfo.Height; bitmapData.PixelFormat = m_ImageInfo.PixelFormat; IBitmapImage *pBitmapImage = NULL; m_pImagingFactory->CreateBitmapFromImage(m_pImage, m_ImageInfo.Width, m_ImageInfo.Height, PIXFMT_32BPP_ARGB, InterpolationHintDefault, &pBitmapImage); pBitmapImage->LockBits(&rect, ImageLockModeRead, PIXFMT_32BPP_ARGB, &bitmapData); // 释放旧数据 if (m_pImgDataBuf != NULL) { delete[] m_pImgDataBuf; m_pImgDataBuf = NULL; } // 申请新空间 bRet = TRUE; m_pImgDataBuf = new unsigned char[m_ImageInfo.Width * m_ImageInfo.Height * 4]; if (m_pImgDataBuf == NULL) { bRet = FALSE; goto ERROR_END_FUNCTION; } // 拷贝数据 bRet = TRUE; memcpy(m_pImgDataBuf, bitmapData.Scan0, m_ImageInfo.Width * m_ImageInfo.Height * 4); ERROR_END_FUNCTION: pBitmapImage->UnlockBits(&bitmapData); pBitmapImage->Release(); return bRet; }
上面主要m_pImgDataBuf 就是一个Byte *的指针,一个指向PNG图片数据的起始指针。我们利用这个指针就可以操作我们刚刚解码的PNG图片的数据。
3、Alpha混合
首先说说Alpha混合,Alpha是指PNG图片的透明度,一般我们使用PNG图片就是为了可以有半透明的显示效果,大部分比较炫的界面,都有这些半透明特效使用,Alpha就是指定图片的透明度,一般图片的透明度我们可以分为两种。
第一:整张图片的Alpha值,图片每个像素使用同一个Alpha值。
第二:每个像素使用独立的Alpha值。
对于32位的PNG图片来说,每个像素值,都保存了自己的Alpha值,32位PNG的像素格式:ARGB8888就是,每个值都是一个8位值。
Alpha混合的基本公式如下:AlphaResult = ALPHA * srcPixel + ( 1 - ALPHA ) * destPixel
下面给出一个Alpha的混合算法例子。
//支持自定义Alpha //pBackDSSrcBmp:我们背景DIB位图的数据指针,也就是上面创建的DIB位图返回的指针pBitmapData //m_ImgDataBuf:上面解码图片,获取的需要显示的PNG图片的数据指针
//支持自定义Alpha void CImageData::DrawImage(HBMDC pBackDCSrcBmp, const int DstX, const int DstY, const int Alpha, DWORD DstBMPWidth) { //进行Alpha混合运算 BYTE btAlphaSRC = 0; DWORD iSrcPos = 0; int iDstPos = 0; for(DWORD i=0; i<m_ImageHeigh; i++) { DWORD SrcData = (i+DstY)*DstBMPWidth + DstX; DWORD DstData = i*m_ImageWidth; for(int j=0; j<m_ImageWidth; j++) { // 计算源图像数据索引和像素点ALPHA iSrcPos = (SrcData + j) << 2; iDstPos = (DstData + j) << 2; btAlphaSRC = m_pImgDataBuf[iDstPos+3]; btAlphaSRC = btAlphaSRC*Alpha >> 8; //ALPHA混合基本公式result = ALPHA * srcPixel + ( 1 - ALPHA ) * destPixel pBackDCSrcBmp[iSrcPos] += (btAlphaSRC*(m_pImgDataBuf[iDstPos]-pBackDCSrcBmp[iSrcPos]) >> 8); pBackDCSrcBmp[iSrcPos+1] += (btAlphaSRC*(m_pImgDataBuf[iDstPos+1]-pBackDCSrcBmp[iSrcPos+1]) >> 8); pBackDCSrcBmp[iSrcPos+2] += (btAlphaSRC*(m_pImgDataBuf[iDstPos+2]-pBackDCSrcBmp[iSrcPos+2]) >> 8); } } }
背景图和需要显示的PNG图片,两张图片对应像素值,根据Alpha混合混合,就可以得到最终的图片,这个就是整个显示过程。
这里的方法不仅仅针对PNG图片,其实对于jpg和bmp也是有效的,只是这两种图片,一般是不包含alpha信息的,所以只能控制整张图片的透明度,而不能控制每个像素的透明度。
上面的运算公式经过一些处理,主要是减少运算和减少乘除法的使用,比较cpu运行乘除法比不上加减法。
下面是我自己测试的数据结果:
这个是我在一台主频是600M的CPU上面运行的速度对比,速度单位是毫秒,可以对比,使用自己的Alpha混合要比微软的AlpBlend快不少,特别是针对大图片来说。
查过一些资料,大部分人的观点是微软的AlphaBlend里面处理过多的异常处理和对图片伸缩进行操作,导致比较慢。
有需要朋友可以自己根据我上面说的封装一个png贴图类,我前面写了一篇使用微软ALphBlend贴图的文章,里面给出了完整代码。
这个自定义的方法,代码比较多,我就不贴出来的。
微软AlphaBlend贴图类:/article/4889608.html
如果有需要的朋友可以根据自己情况封装不同的接口处理,如果有不了解地方,可以留言。
新建的讨论群,有兴趣可以加入
VC/Wince群:87053214
相关文章推荐
- Wince/VC高效PNG贴图,自定义Alpha算法
- wince的png贴图
- 在VC中加载自定义资源类型,以PNG文件为例
- Wince贴图 显示png wince GDI图片显示半透明
- [vc] mfc 对话框采用png的自定义格式
- VC中使用自定义消息在进程间通讯
- vc中使用SendMessage正确发送自定义消息的方法
- Windows Mobile的高效贴图
- 编写高效的C#图像处理程序(4) Rgb=>Lab,C# vs. C++(VC)
- 在VC下手工为对话框添加自定义消息
- unity3D片面使用png材质贴图透明
- 编写高效的C#图像处理程序(4) Rgb=>Lab,C# vs. C++(VC)
- 基于VC++ PNG按钮的实现【3】
- Windows Mobile的高效贴图
- MFC使用自定义资源加载PNG
- VC++中添加自定义消息的响应 (BEGIN_MESSAGE_MAP ON_MESSAGE)
- MFC使用自定义资源加载PNG
- VC 自定义消息
- VC/MFC中如何自定义消息
- vc 6.0和vs2008中添加自定义消息的区别