Visual C++ 7.1Visual C++ 8.0ASMVisual Studio 6Visual C++ 7.0Windows 2003Windows 2000Visual C++ 6.0Windows XP中级开发Visual StudioWindowsC++
一个显示透明 PNG 文件的简单方法
介绍一种显示透明PNG文件的方法。
引言
如果您正在寻找一个用于显示PNG32图像的组件,这段代码可能会给您一些提示。
背景
几天前,我需要在我的代码中显示一个透明PNG图像的组件。在CodeProject中用“PNG”作为关键词搜索,我找到一个名为PNGView的演示程序。但是,它不能处理透明PNG图像。所以我决定自己动手实现。
基本思路是模拟AlphaBlend的行为,请参阅以下代码。为了加快显示速度,代码的核心部分通过ASM进行了优化。我不太擅长ASM,所以如果我的代码有任何不足或错误,请告诉我。
希望这能有所帮助。感谢您的关注!
使用代码
我已经将代码封装成了一个静态库。下载内容包括一个演示程序和静态库的源代码。
为了使用这段代码,您应该首先导入lib,然后就可以通过三个步骤显示PNG32图像了。
步骤 1:加载 PNG32 图像
为了简化代码,我只支持 32 位像素深度的 PNG 图像。
char szFileName[MAX_PATH]; strcpy(szFileName,"*.png") PNGINFO *pPng=Png32_Load(szFileName);
步骤 2:在 HDC 中显示图像
Png32_Show(pDC->m_hDC,0,0,pPng->nWidth,pPng->nHeight,pPng,0,0);
步骤 3:释放占用的内存
Png32_Free(pPng);
代码
//display a 32 bit deep png image void Png32_Show(HDC hdc,int xDest,int yDest,int nWidth,int nHeight, PNGINFO *pPngInfo,int xSour,int ySour) { if(xSour+nWidth>(int)pPngInfo->nWidth) nWidth=pPngInfo->nWidth-xSour; if(ySour+nHeight>(int)pPngInfo->nHeight) nHeight=pPngInfo->nHeight-ySour; if(nWidth>0 && nHeight>0) { HDC hmemdc=0; LPBYTE pBitsDest=NULL; HBITMAP hbmpDest=0; HGDIOBJ hOldBmp=0; BITMAPINFO bmi; //sour memory unsigned char ** row_pointers = pPngInfo->ppbyRow+pPngInfo->nHeight-1-(pPngInfo->nHeight-ySour-nHeight); //Do alpla blend int nLineTailDest=WIDTHBYTES(24*nWidth)-3*nWidth; // Initialize header to 0s. ZeroMemory(&bmi, sizeof(bmi)); // Fill out the fields you care about. bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = nWidth; bmi.bmiHeader.biHeight = nHeight; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 24; bmi.bmiHeader.biCompression = BI_RGB; // Create the surface. hmemdc=CreateCompatibleDC(hdc); // Get Dest Rectangle memory hbmpDest = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void **)&pBitsDest, NULL, 0); hOldBmp=SelectObject(hmemdc,hbmpDest); BitBlt(hmemdc,0,0,nWidth,nHeight,hdc,xDest,yDest,SRCCOPY); #ifdef ASM_CORE __asm { push esi push edi push eax push ebx push ecx push edx push es push ds mov ax,ds mov es,ax mov edi,pBitsDest mov ecx,nHeight beginy: mov ebx, row_pointers mov esi,[ebx] ;//offset picture's left margin mov eax,xSour imul eax,4 ;//4 bytes make up one pixel add esi,eax mov eax,nWidth beginx: ;//get alpla value xor ebx,ebx mov bl,[esi+3] ;//blue xor dx,dx mov dl,[edi]; imul dx,bx add dh,[esi+2] mov [edi],dh ;//save result to *pBitsDest inc edi ;//green xor dx,dx mov dl,[edi]; imul dx,bx add dh,[esi+1] mov [edi],dh ;//save result to *pBitsDest inc edi ;//red xor dx,dx mov dl,[edi]; imul dx,bx add dh,[esi] mov [edi],dh ;//save result to *pBitsDest inc edi add esi,4 dec eax cmp eax,0 jne beginx add edi,nLineTailDest sub row_pointers,4 ;//next line loop beginy pop ds pop es pop edx pop ecx pop ebx pop eax pop edi pop esi } #else//ASM_CORE { int i,j; BYTE *p1=pBitsDest; for(i=0;i<nHeight;i++) { BYTE *p2=*(row_pointers--); for(j=0;j<nWidth;j++) { *p1++=((p2[3]*(*p1))>>8)+p2[2]; *p1++=((p2[3]*(*p1))>>8)+p2[1]; *p1++=((p2[3]*(*p1))>>8)+p2[0]; p2+=4; } p1+=nLineTailDest; } } #endif//ASM_CORE //render BitBlt(hdc,xDest,yDest,nWidth,nHeight,hmemdc,0,0,SRCCOPY); SelectObject(hmemdc,hOldBmp); //Free memory DeleteObject(hbmpDest); DeleteDC(hmemdc); } }
备注
新版本增加了一个新的接口,Png32_LoadFromMem
,您可以使用它轻松地从内存加载 32 位 PNG 图像。请参阅 CMfc_demoDlg::OnInitDialog
以获取示例。
要测试此演示程序,您应该首先编译 png32_helper 项目,然后编译 mfc_demo。
历史
- 2008-04-17
添加
Png32_LoadFromMem
接口。 - 2007-09-07
进行了像素预乘。
- 2007-07-16
首次提交。