我的PNG IDAT块数据有什么问题?
我正在尝试使用OpenGL手动创建PNG文件的学习目的 所有其他块都是OKK(IHDR,PHY,IEND)。
首先,我读了OpenGl的Pixels:
int s_width = glutGet(GLUT_SCREEN_WIDTH), s_height = glutGet(GLUT_SCREEN_HEIGHT);
int pixelArraySize = s_width*s_height*_glColorChannels;
unsigned char *pixelsArrayInfo = (unsigned char*)malloc(pixelArraySize);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadBuffer(GL_FRONT);
glReadPixels(0, 0, (unsigned short)s_width, (unsigned short)s_height, GL_RGB, GL_UNSIGNED_BYTE, pixelsArrayInfo);
然后,我创建了生成这样的扫描线的函数: “每个扫描线都是一个屏幕行中的RGB值数组,然后是'0'”
unsigned char *generateScanlines(unsigned char *pixels, int s_width, int s_height)
{
int eachScanlineLength = 1 + s_width*3;
unsigned char *finalOutput = (unsigned char*)malloc(s_height*eachScanlineLength);
for(int i=0; i<s_height; i++){
finalOutput[i*eachScanlineLength] = 0;
copyElement(finalOutput, pixels, i*eachScanlineLength, (i+1)*eachScanlineLength, i*eachScanlineLength+1);
}
return finalOutput;
}
void copyElement(unsigned char *dest, unsigned char *src, int src_debut, int src_fin, int dest_debut)
{
for(int i=src_debut, j=dest_debut; i<src_fin; i++, j++){
dest[j] = src[i];
}
}
unsigned char *deflateDatas(unsigned char *pixels, int s_width, int s_height, int *deflatedDataLength)
{
unsigned char *deflated = (unsigned char*)malloc(compressBound(s_height*(1 + s_width*3)));
unsigned char *scanlines = invertArray(generateScanlines(pixels, s_width, s_height), s_height*(1 + s_width*3));
z_stream defstream;
defstream.zalloc = Z_NULL;
defstream.zfree = Z_NULL;
defstream.opaque = Z_NULL;
defstream.avail_in = (uInt)(s_height*(1 + s_width*3));
defstream.next_in = (Bytef *)scanlines;
defstream.avail_out = (uInt)(compressBound(s_height*(1 + s_width*3)));
defstream.next_out = (Bytef *)deflated;
deflateInit(&defstream, 0);
deflate(&defstream, Z_FINISH);
deflateEnd(&defstream);
*deflatedDataLength = compressBound(s_height*(1 + s_width*3));
return deflated;
}
,然后看来它可以工作,但是当我对其进行测试时,我的OpenGL程序会得到:
[小PNG输出] [1]
我也创建了一个基本的BMP文件,它可以完美地奏效,
我尝试找到是否有任何错误,也许是在扫描线的生成或误解PNG文件格式中。
invertarray()代码:
unsigned char *invertArray(unsigned char *myArray, int arrayEnd)
{ unsigned char *invertedtableau = (unsigned char*)malloc(arrayEnd*sizeof(unsigned char));
for(int i=0 ; i<=arrayEnd ; i++)
{ invertedtableau[i] = myArray[arrayEnd-i];
}
return invertedtableau; }
解决方案 我发现错误来自何处,AccordInd到Mark Adler,Scanlines Gemeration方法是错误的。 另外,由于OpenGL仅与左下角兼容,因此文件被倒置,但是PNG是左上方格式,然后我们需要在生成scanlines之前将像素缓冲液反转(EHAT我使用interverArray()方法)。
最后一个错误是调用方法和存储温度的长度也是错误的。
整个放气代码:
// generating scanline function
unsigned char *generateScanlines(unsigned char *pixels, int s_width, int s_height, int colorChannel)
{
int eachScanlineLength = 1 + s_width * colorChannel, i = 1, j = 0; // one scanline length
unsigned char *scanlines = (unsigned char *)malloc(s_height * eachScanlineLength); // memory allocation for the scanline output
memset(scanlines, 0, s_height * eachScanlineLength * sizeof(char)); // we set all the output values to 0
// then we copy pixels elements in the output, skipping the fisrt output values, that should ever be 0
for (i = 1, j = 0; i < s_height && j < s_height; i++, j++)
memcpy(scanlines + 1 + (i - 1) * eachScanlineLength, pixels + j * (eachScanlineLength - 1), eachScanlineLength - 1);
memcpy(scanlines + 1 + (i - 1) * eachScanlineLength, pixels + j * (eachScanlineLength - 1), eachScanlineLength - 1);
return scanlines;
}
// deflating IDAT CHUNK data algorithm
unsigned char *deflateDatas(unsigned char *pixels, int s_width, int s_height, int colorChannel, int *deflatedLen)
{
unsigned long inLen = s_height * (1 + s_width * colorChannel), tmpLen = 0; // input len of scanlines datas
unsigned char *scanlines = generateScanlines(pixels, s_width, s_height, colorChannel); // generating scanlines from the pixels
unsigned char *deflatedDatas = NULL; // setting up the deflated datas output
int result = 0;
// initialising zlib
z_stream defstream;
defstream.zalloc = Z_NULL;
defstream.zfree = Z_NULL;
defstream.opaque = Z_NULL;
defstream.avail_in = inLen;
defstream.next_in = (Bytef *)scanlines;
defstream.avail_out = 0;
defstream.next_out = (Bytef *)deflatedDatas;
if ((result = deflateInit(&defstream, Z_DEFAULT_COMPRESSION)) == Z_OK)
{
// calculate the actual length and update zlib structure
unsigned long estimateLen = deflateBound(&defstream, inLen);
deflatedDatas = (unsigned char *)malloc(estimateLen);
if (deflatedDatas != NULL)
{
// updation zlib configuration
defstream.avail_out = (uInt)estimateLen;
defstream.next_out = (Bytef *)deflatedDatas;
// do the compression
deflate(&defstream, Z_FINISH);
tmpLen = (unsigned char *)defstream.next_out - deflatedDatas;
}
}
deflateEnd(&defstream); // end of deflating algorithm
*deflatedLen = tmpLen; // copying the defalted data length to the IDAT->length
free(scanlines);
return deflatedDatas;
}
左下到左上的PixelBuffer翻转代码:
void flipPixels(unsigned char *pixelsArray, int s_width, int s_heigth, int colorChannel)
{
int totalLength = s_width * s_heigth * colorChannel;
int oneLineLength = s_width * colorChannel;
unsigned char *tmp = (unsigned char *)malloc(totalLength * sizeof(unsigned char));
memcpy(tmp, pixelsArray, totalLength);
for (int i = 0; i < s_heigth; i++)
memcpy(pixelsArray + oneLineLength * i, tmp + totalLength - oneLineLength * (i + 1), oneLineLength);
free(tmp);
}
[1]:
i'm trying for learning purpose to create manually a png file from with OpenGL
All other CHUNKS are okk (IHDR, pHY, IEND).
firstly, I read pixels by Opengl :
int s_width = glutGet(GLUT_SCREEN_WIDTH), s_height = glutGet(GLUT_SCREEN_HEIGHT);
int pixelArraySize = s_width*s_height*_glColorChannels;
unsigned char *pixelsArrayInfo = (unsigned char*)malloc(pixelArraySize);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadBuffer(GL_FRONT);
glReadPixels(0, 0, (unsigned short)s_width, (unsigned short)s_height, GL_RGB, GL_UNSIGNED_BYTE, pixelsArrayInfo);
then, I created a function of generating scanlines like this:
"each scanline is an array of RGB values in one screen line preceded by '0' "
unsigned char *generateScanlines(unsigned char *pixels, int s_width, int s_height)
{
int eachScanlineLength = 1 + s_width*3;
unsigned char *finalOutput = (unsigned char*)malloc(s_height*eachScanlineLength);
for(int i=0; i<s_height; i++){
finalOutput[i*eachScanlineLength] = 0;
copyElement(finalOutput, pixels, i*eachScanlineLength, (i+1)*eachScanlineLength, i*eachScanlineLength+1);
}
return finalOutput;
}
void copyElement(unsigned char *dest, unsigned char *src, int src_debut, int src_fin, int dest_debut)
{
for(int i=src_debut, j=dest_debut; i<src_fin; i++, j++){
dest[j] = src[i];
}
}
unsigned char *deflateDatas(unsigned char *pixels, int s_width, int s_height, int *deflatedDataLength)
{
unsigned char *deflated = (unsigned char*)malloc(compressBound(s_height*(1 + s_width*3)));
unsigned char *scanlines = invertArray(generateScanlines(pixels, s_width, s_height), s_height*(1 + s_width*3));
z_stream defstream;
defstream.zalloc = Z_NULL;
defstream.zfree = Z_NULL;
defstream.opaque = Z_NULL;
defstream.avail_in = (uInt)(s_height*(1 + s_width*3));
defstream.next_in = (Bytef *)scanlines;
defstream.avail_out = (uInt)(compressBound(s_height*(1 + s_width*3)));
defstream.next_out = (Bytef *)deflated;
deflateInit(&defstream, 0);
deflate(&defstream, Z_FINISH);
deflateEnd(&defstream);
*deflatedDataLength = compressBound(s_height*(1 + s_width*3));
return deflated;
}
then, it seem it work, but when I test it my OpenGL program I get this :
[small png output][1]
also, i created a basic bmp File and it work perfectly
i try to find if it's any error, maybe it's in scanlines generation or misunderstanding with the PNG file format.
the invertArray() code :
unsigned char *invertArray(unsigned char *myArray, int arrayEnd)
{ unsigned char *invertedtableau = (unsigned char*)malloc(arrayEnd*sizeof(unsigned char));
for(int i=0 ; i<=arrayEnd ; i++)
{ invertedtableau[i] = myArray[arrayEnd-i];
}
return invertedtableau; }
SOLUTION
I found where the error comes from, accordind to Mark Adler, the scanlines gemeration method was, wrong.
Also, file was inverted because Opengl is only compatible with bottom left gormat, but png is a top left format, then we need to invert the pixel buffer before generating scanlines(ehat i tried with invertArray() method).
The last error was that the calling of deflate method and storing the deflated length was also wrong.
the whole deflating code :
// generating scanline function
unsigned char *generateScanlines(unsigned char *pixels, int s_width, int s_height, int colorChannel)
{
int eachScanlineLength = 1 + s_width * colorChannel, i = 1, j = 0; // one scanline length
unsigned char *scanlines = (unsigned char *)malloc(s_height * eachScanlineLength); // memory allocation for the scanline output
memset(scanlines, 0, s_height * eachScanlineLength * sizeof(char)); // we set all the output values to 0
// then we copy pixels elements in the output, skipping the fisrt output values, that should ever be 0
for (i = 1, j = 0; i < s_height && j < s_height; i++, j++)
memcpy(scanlines + 1 + (i - 1) * eachScanlineLength, pixels + j * (eachScanlineLength - 1), eachScanlineLength - 1);
memcpy(scanlines + 1 + (i - 1) * eachScanlineLength, pixels + j * (eachScanlineLength - 1), eachScanlineLength - 1);
return scanlines;
}
// deflating IDAT CHUNK data algorithm
unsigned char *deflateDatas(unsigned char *pixels, int s_width, int s_height, int colorChannel, int *deflatedLen)
{
unsigned long inLen = s_height * (1 + s_width * colorChannel), tmpLen = 0; // input len of scanlines datas
unsigned char *scanlines = generateScanlines(pixels, s_width, s_height, colorChannel); // generating scanlines from the pixels
unsigned char *deflatedDatas = NULL; // setting up the deflated datas output
int result = 0;
// initialising zlib
z_stream defstream;
defstream.zalloc = Z_NULL;
defstream.zfree = Z_NULL;
defstream.opaque = Z_NULL;
defstream.avail_in = inLen;
defstream.next_in = (Bytef *)scanlines;
defstream.avail_out = 0;
defstream.next_out = (Bytef *)deflatedDatas;
if ((result = deflateInit(&defstream, Z_DEFAULT_COMPRESSION)) == Z_OK)
{
// calculate the actual length and update zlib structure
unsigned long estimateLen = deflateBound(&defstream, inLen);
deflatedDatas = (unsigned char *)malloc(estimateLen);
if (deflatedDatas != NULL)
{
// updation zlib configuration
defstream.avail_out = (uInt)estimateLen;
defstream.next_out = (Bytef *)deflatedDatas;
// do the compression
deflate(&defstream, Z_FINISH);
tmpLen = (unsigned char *)defstream.next_out - deflatedDatas;
}
}
deflateEnd(&defstream); // end of deflating algorithm
*deflatedLen = tmpLen; // copying the defalted data length to the IDAT->length
free(scanlines);
return deflatedDatas;
}
the bottom left to top left pixelbuffer flipping code :
void flipPixels(unsigned char *pixelsArray, int s_width, int s_heigth, int colorChannel)
{
int totalLength = s_width * s_heigth * colorChannel;
int oneLineLength = s_width * colorChannel;
unsigned char *tmp = (unsigned char *)malloc(totalLength * sizeof(unsigned char));
memcpy(tmp, pixelsArray, totalLength);
for (int i = 0; i < s_heigth; i++)
memcpy(pixelsArray + oneLineLength * i, tmp + totalLength - oneLineLength * (i + 1), oneLineLength);
free(tmp);
}
[1]:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我们没有看到所有相关的代码,但是首先,您似乎通过逆转字节来故意损坏您的图像数据。不要那样做。删除对
invertarray()
的调用。其次,您没有返回压缩数据的大小。您正在返回对此的上限估计。
使用
deflateBound()
(调用deflateInit()
),而不是compressbound()
。调用deflateBound()
一次(而不是三次),然后将答案保存在无签名的长期绑定
中。返回压缩数据的大小,即BOND -defstream.avail_out
。最后,您没有压缩!
deflateInit()
的第二个参数应为z_default_compression
或0
以外的其他内容,这意味着没有压缩。您的示例对
idat
块有不正确的CRC,因此您在构造该块的代码中没有其他问题。即使我撤消
invertarray()
(本身有另一个错误),我得到的仍然是混乱的图像,似乎在每行上删除了一个字节。除非您提供代码的全部,否则我们无法帮助您。有很多错误,您不知道它们在哪里。
We don't see all the relevant code, but first, you seem to be deliberately corrupting your image data by reversing the bytes. Don't do that. Remove the call to
invertArray()
.Second, you are not returning the size of the compressed data. You are returning the upper-bound estimate of that.
Use
deflateBound()
(after callingdeflateInit()
), notcompressBound()
. CalldeflateBound()
once (instead of three times) and save the answer inunsigned long bound
. Return the size of the compressed data, which isbound - defstream.avail_out
.Lastly, you are not compressing! The second argument to
deflateInit()
should beZ_DEFAULT_COMPRESSION
or something else other than0
, which means no compression.Your example has an incorrect CRC for the
IDAT
chunk, so there's something else wrong in the code you don't show for constructing that chunk.Even if I undo the
invertArray()
(which itself has another error), what I get is still messed-up image that appears to have a byte deleted on each row.We can't help you unless you provide all of your code. There are many errors, and you don't know where they are.