65.9K
CodeProject 正在变化。 阅读更多。
Home

一个显示透明 PNG 文件的简单方法

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.65/5 (29投票s)

2007年7月16日

CPOL

1分钟阅读

viewsIcon

207418

downloadIcon

2689

介绍一种显示透明PNG文件的方法。

Screenshot - snapshot.gif

引言

如果您正在寻找一个用于显示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

    首次提交。

© . All rights reserved.