我正在尝试学习的目的,手动创建一个png文件,从与OpenGL所有其他块是ok(IHDR,pHY,IEND)。
首先,用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);
然后,我创建了一个生成扫描线的函数,像这样:“每条扫描线是一个屏幕行中的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; }
解决方案我找到了错误的来源,根据Mark Adler的说法,扫描线生成方法是错误的。此外,文件被反转,因为OpenGL只兼容左下角的格式,而png是左上角的格式,那么我们需要在生成扫描线之前反转像素缓冲区(我尝试过使用invertArray()方法)。
最后一个错误是调用deflate方法和存储收缩长度也是错误的。
整个紧缩代码:
// 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]:
1条答案
按热度按时间k5ifujac1#
我们没有看到所有相关的代码,但首先,你似乎故意通过反转字节来破坏图像数据。不要这样做。删除对
invertArray()
的调用。第二,返回的不是压缩数据的大小,而是压缩数据的上限估计值。
使用
deflateBound()
(在调用deflateInit()
之后),而不是compressBound()
。调用deflateBound()
一次(而不是三次),并将答案保存在unsigned long bound
中。返回压缩数据的大小,即bound - defstream.avail_out
。最后,你没有压缩!
deflateInit()
的第二个参数应该是Z_DEFAULT_COMPRESSION
或0
以外的其他参数,这意味着没有压缩。您的示例中
IDAT
块的CRC不正确,因此在构造该块时没有显示的代码中还有其他错误。即使我撤消了
invertArray()
(它本身有另一个错误),我得到的仍然是混乱的图像,似乎每行都删除了一个字节。除非您提供 * 所有 * 代码,否则我们无法为您提供帮助。有许多错误,您不知道它们在哪里。