2012-07-25 56 views
0

我想直接使用zlib编写PNG。但是,输出文件不正确。看着它,IDAT块显示周期性模式和一大块零。这表明调用zlib时出现错误。代码如下:使用zlib编写PNG

#include "FrameGrabber.h" 
#include "Image.h" 
#include "crc.h" 
#include <zlib.h> 
#include <stdint.h> 

#include <cstdio> 
#include <cassert> 



template<class T> 
void assign(void* addr,T x) 
{ 
T* p_out= (T*)addr ; 
*p_out=x; 
} 

inline int32_t swap_bytes(int32_t x) 
{ 
asm("bswap %1":"=r"(x):"r"(x):); 
return x; 
} 

//Input is BGRx (convert to BGR (should be swapped to RGB later) and add a byte in the beginning) 
void filterApply(const unsigned char* const* scanlines,char* buffer_png,int width,int height) 
{ 
for(int k=0;k<height;++k) 
    { 
    *buffer_png=0; 
    ++buffer_png; 
    for(int l=0;l<width;++l) 
     { 
     assign(buffer_png,((int32_t*)(scanlines[k]) )[l]); 
     buffer_png+=3; 
     } 
    } 
} 

size_t compress(const char* buffer_uncompressed,char* buffer_compressed,size_t length_in) 
{ 
z_stream stream; 
memset(&stream,0,sizeof(stream)); 
// 15 bits=32K? 
deflateInit2(&stream,6,Z_DEFLATED,15,9,Z_FILTERED); 

stream.avail_in=length_in; 
stream.next_in=(unsigned char*)buffer_uncompressed; 
stream.avail_out=length_in; 
stream.next_out =(unsigned char*)buffer_compressed; 
do 
    {  
    deflate(&stream, Z_FINISH); 
    } 
while (stream.avail_out == 0); 
deflateEnd(&stream); 
return stream.total_out; 
} 



const size_t CHUNK_BASE_SIZE=12; 

void Chunk_sizeSet(char* chunk,uint32_t size) 
{assign(chunk,swap_bytes(size));} 

void Chunk_IDSet(char* chunk,uint32_t id) 
{assign(chunk+4,swap_bytes(id));} 

void Chunk_CRCSet(char* chunk,const PngCRC& crc,uint32_t size_chunk_data) 
{assign(chunk+8+size_chunk_data, swap_bytes(crc(chunk+4,size_chunk_data+4)));} 



const size_t IHDR_SIZE=13; 
const int IHDR_COLORTYPE_RGB=2; 

void IHDR_widthSet(char* chunk,int32_t width) 
{assign(chunk+8,swap_bytes(width));} 

void IHDR_heightSet(char* chunk,int32_t height) 
{assign(chunk+12,swap_bytes(height));} 

void IHDR_bitDepthSet(char* chunk,char bd) 
{chunk[16]=bd;} 

void IHDR_colorTypeSet(char* chunk,char ct) 
{chunk[17]=ct;} 

void IHDR_compressionMethodSet(char* chunk,char cmprm) 
{chunk[18]=cmprm;} 

void IHDR_filterMethodSet(char* chunk,char filter) 
{chunk[19]=filter;} 

void IHDR_interlaceMethodSet(char* chunk,char interlace) 
{chunk[20]=interlace;} 



int main() 
{ 
PngCRC crc;        //The CRC code works 
char signature[8]={137,80,78,71,13,10,26,10}; 

char IEND[CHUNK_BASE_SIZE]; 
Chunk_sizeSet(IEND,0); 
Chunk_IDSet(IEND,0x49454e44); 
Chunk_CRCSet(IEND,crc,0); 

FrameGrabber grabber(GetDesktopWindow()); //Grab the desktop (works) 
Image img(grabber); 
grabber.grab(); 

char IHDR[CHUNK_BASE_SIZE+IHDR_SIZE]; 
Chunk_sizeSet(IHDR,CHUNK_BASE_SIZE+IHDR_SIZE); 
Chunk_IDSet(IHDR,0x49484452); 
IHDR_widthSet(IHDR,grabber.widthGet()); 
IHDR_heightSet(IHDR,grabber.heightGet()); 
IHDR_bitDepthSet(IHDR,8); 
IHDR_colorTypeSet(IHDR,IHDR_COLORTYPE_RGB); 
IHDR_compressionMethodSet(IHDR,0); 
IHDR_filterMethodSet(IHDR,0); 
IHDR_interlaceMethodSet(IHDR,0); 
Chunk_CRCSet(IHDR,crc,IHDR_SIZE); 


size_t size_uncompressed=(1+3*grabber.widthGet())*grabber.heightGet(); 
char* img_png_uncompressed=(char*)malloc(size_uncompressed); 
filterApply(img.rowsGet(),img_png_uncompressed,grabber.widthGet(),grabber.heightGet()); 

    //The compressed chunk should not be larger than the uncompressed one. 
char* IDAT=(char*)malloc(size_uncompressed+CHUNK_BASE_SIZE); 

int32_t size_idat=compress(img_png_uncompressed,IDAT+CHUNK_BASE_SIZE,size_uncompressed); 
Chunk_sizeSet(IDAT,size_idat); 
Chunk_IDSet(IDAT,0x49444154); 
Chunk_CRCSet(IDAT,crc,size_idat); 


FILE* file_out=fopen("test.png","wb"); 

fwrite(signature,1,sizeof(signature),file_out); 
fwrite(IHDR,1,sizeof(IHDR),file_out); 
fwrite(IDAT,1,size_idat,file_out); 
fwrite(IEND,1,sizeof(IEND),file_out); 
free(IDAT); 
fclose(file_out); 

return 0; 
} 

编辑:在filterApply中发现一个错误。现在我没有得到任何大的空白块,但文件仍然无效。它只是影响输入数据,所以它是正确的。

+0

你应该只使用libpng(http://www.libpng.org/pub/png/libpng.html)。它的编写是为了高效地编码和解码png文件并支持所有png选项。它当然使用zlib。 – 2012-07-25 16:25:01

+0

支持所有选项会降低性能。 – user877329 2012-07-25 17:18:54

+0

这并不会让它变慢。 libpng已经过多年的优化,所以它的速度可能比你在一天中写的要快。它只是意味着更多的代码链接,你不会使用。它将几百K字节添加到您的可执行文件中。你没有足够的内存来支持它? – 2012-07-25 18:26:20

回答

0

首先,使用libpng,你不必担心这一切。

您正在错误地设置块大小。块大小不包含长度,块ID或crc。你也正在计算crc的错误,关于它的计算结果。并且您正在将压缩数据写入IDAT块的错误位置。我会在那里停下 - 你需要仔细阅读PNG specification

其他评论:

我不知道什么是crc.h,但ZLIB已经提供了crc32()功能,该功能与PNG兼容。您可能从crc.h使用的crc()例程正在计算不同的crc。即使它是相同的crc,crc32()已经被zlib链接了,所以你不妨使用它。

您不应该使用名称compress(),因为该功能已由zlib提供。由于您使用的是zlib,因此使用不同的compress()函数会使读者感到困惑。

while (stream.avail_out == 0);是没有意义的。 deflate()的第一个调用完成avail_out != 0,或者它陷入了一个无限循环,因为再次调用deflate()不会改变它。

如果你想使用Z_FINISH,那么你需要使用deflateBound()来设置输出缓冲区的大小,以保证它完成。源代码中的注释“压缩块不应大于未压缩块”。不保证。不可压缩数据将导致压缩数据比未压缩输入略大。

您不检查deflate功能的任何返回代码!检查返回码。

+0

* libpng太多了 * crc.h包含由png规范建议的crc算法 *您是否阅读过C++中的静态polymorfism?*其余的,我需要一个简单易懂的压缩循环。关于32K规范呢? – user877329 2012-07-25 16:52:43

+1

我不知道你在问什么“32K规范怎么样?”。 – 2012-07-25 18:28:22

+0

“对于PNG压缩方法0,zlib压缩方法/标志代码必须指定方法代码8(”deflate“压缩)和LZ77窗口大小不超过32K。” – user877329 2012-07-26 09:41:07