上回我SHARE了一个将PNG转成BMP的方法,通过直接向Buffer里Draw一个PNG来实现的,今天我想共享给大家的是用自己解码来实现PNG转成BMP的方法,这个方法更正统,不过实现也相对比较麻烦一点.
PNG的格式除了几个头以外,关键是它的数据用GZip做了压缩,所以,要实现PNG的解码,需要先了解的背景知识只有两个:1.PNG/BMP的编码格式,2.GZIP的编码格式.这两种文档的编码格式,可以Google一下,会有很多相关资料,我就不再罗嗦了.把自己的实现说一下吧.
1.解开GZIP的方法,我用拿来主义,将网上流传的一个KJava的代码翻译了一下,改写成C代码就OK了.源文件可以点击这里.
在翻译的过程中,注意一下java与c的语法,大体上就可以了,并没有太大难度,好象就是将byte的数组换成byte的指针,记得malloc就一定要free.
2.解析PNG也不复杂,大家可以参考这个文档(中文的,也说的比较清楚).从头里可以得到一些信息(我因为实用的原因,只处理特定的格式,8bit索引).然后,拿到数据,解压缩得以像素值,就可以构造出一个IDIB结构.我们知道IDIB是BREW中唯一能直接操作像素点的接口(如果想手工操作BMP的像素,它是一个很方便的途径).部分代码如下:
boolean PngDecoder_ToBMP(PngDecoder * pMe,byte * pPngRaw,IDIB * pDIB)
{
byte * block;
byte * data;
int i,j,index;
uint32 len;
Head head;
if(pPngRaw[0]!=0x89 || pPngRaw[1]!=0x50 || pPngRaw[2]!=0x4E || pPngRaw[3]!=0x47 || pPngRaw[4]!=0x0D || pPngRaw[5]!=0x0A)
return FALSE;
//缺省认为都是 0x08(8bits color depth) 0x03(调色板)
IDIB_FlushPalette(pDIB);
pDIB->pPaletteMap=NULL;
pDIB->nDepth=8;
pDIB->nColorScheme=0;
pDIB->nPitch=pDIB->cx;
pDIB->ncTransparent=IBITMAP_RGBToNative(pMe->pScreen,MAKE_RGB(255,255,255));
//调色板
index=33;
readHead(pPngRaw+index,&head);
if(STRCMP(head.type,"PLTE")==0)
{
len=head.len;
index+=8;
pDIB->pRGB=(uint32*)MALLOC(sizeof(uint32)*(len/3));
MEMSET(pDIB->pRGB,0,(len/3)*sizeof(uint32));
for(j=0,i=index;i<index+len;j++,i+=3)
{
//低位
pDIB->pRGB[j]=((uint32)pPngRaw[i]<<16)+((uint32)pPngRaw[i+1]<<8)+(uint32)pPngRaw[i+2];
}
pDIB->cntRGB=j;
index+=len+4;
}
//读透明色
readHead(pPngRaw+index,&head);
if(STRCMP(head.type,"tRNS")==0)
{
len=head.len;
index+=8;
j=(int)pPngRaw[index];
pDIB->ncTransparent=IBITMAP_RGBToNative(pMe->pScreen,(RGBVAL)pDIB->pRGB[j]);
index+=len+4;
}
//Step 2: 读数据放入block里
//数据长
readHead(pPngRaw+index,&head);
if(STRCMP(head.type,"IDAT")==0)
{
len=head.len;
block=(byte*)MALLOC(len);
index+=10; //IDAT块前面两个字节是标志:0x78 0x5E
MEMSET(block,0,len);
MEMCPY(block,&pPngRaw[index],len);
//Step 3:解压缩
data = GZIP_Inflate(pMe->gzip,block,pDIB->cx*pDIB->cy+pDIB->cy);
len=pMe->gzip->uncompressed_index;
pDIB->pBmp=(byte*)MALLOC(pDIB->cx*pDIB->cy);
for(i=0;i<pDIB->cy;i++)
for(j=0;j<pDIB->cx;j++)
{
pDIB->pBmp[i*pDIB->cx+j]=data[i*(pDIB->cx+1)+j+1];
}
FREEIF(block);
block=NULL;
FREEIF(data);
data=NULL;
}
return TRUE;
}
{
byte * block;
byte * data;
int i,j,index;
uint32 len;
Head head;
if(pPngRaw[0]!=0x89 || pPngRaw[1]!=0x50 || pPngRaw[2]!=0x4E || pPngRaw[3]!=0x47 || pPngRaw[4]!=0x0D || pPngRaw[5]!=0x0A)
return FALSE;
//缺省认为都是 0x08(8bits color depth) 0x03(调色板)
IDIB_FlushPalette(pDIB);
pDIB->pPaletteMap=NULL;
pDIB->nDepth=8;
pDIB->nColorScheme=0;
pDIB->nPitch=pDIB->cx;
pDIB->ncTransparent=IBITMAP_RGBToNative(pMe->pScreen,MAKE_RGB(255,255,255));
//调色板
index=33;
readHead(pPngRaw+index,&head);
if(STRCMP(head.type,"PLTE")==0)
{
len=head.len;
index+=8;
pDIB->pRGB=(uint32*)MALLOC(sizeof(uint32)*(len/3));
MEMSET(pDIB->pRGB,0,(len/3)*sizeof(uint32));
for(j=0,i=index;i<index+len;j++,i+=3)
{
//低位
pDIB->pRGB[j]=((uint32)pPngRaw[i]<<16)+((uint32)pPngRaw[i+1]<<8)+(uint32)pPngRaw[i+2];
}
pDIB->cntRGB=j;
index+=len+4;
}
//读透明色
readHead(pPngRaw+index,&head);
if(STRCMP(head.type,"tRNS")==0)
{
len=head.len;
index+=8;
j=(int)pPngRaw[index];
pDIB->ncTransparent=IBITMAP_RGBToNative(pMe->pScreen,(RGBVAL)pDIB->pRGB[j]);
index+=len+4;
}
//Step 2: 读数据放入block里
//数据长
readHead(pPngRaw+index,&head);
if(STRCMP(head.type,"IDAT")==0)
{
len=head.len;
block=(byte*)MALLOC(len);
index+=10; //IDAT块前面两个字节是标志:0x78 0x5E
MEMSET(block,0,len);
MEMCPY(block,&pPngRaw[index],len);
//Step 3:解压缩
data = GZIP_Inflate(pMe->gzip,block,pDIB->cx*pDIB->cy+pDIB->cy);
len=pMe->gzip->uncompressed_index;
pDIB->pBmp=(byte*)MALLOC(pDIB->cx*pDIB->cy);
for(i=0;i<pDIB->cy;i++)
for(j=0;j<pDIB->cx;j++)
{
pDIB->pBmp[i*pDIB->cx+j]=data[i*(pDIB->cx+1)+j+1];
}
FREEIF(block);
block=NULL;
FREEIF(data);
data=NULL;
}
return TRUE;
}
pPngRaw是加载的PNG图像数据,可以来自于文件或网络,转换的结果放到pDIB里.