2012-07-25 58 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