我的PNG IDAT块数据有什么问题?

发布于 2025-02-13 07:45:07 字数 6245 浏览 2 评论 0原文

我正在尝试使用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]: 

https://i.sstatic.net /5khcg.png

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]: 

https://i.sstatic.net/5khCg.png

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

江城子 2025-02-20 07:45:07

我们没有看到所有相关的代码,但是首先,您似乎通过逆转字节来故意损坏您的图像数据。不要那样做。删除对invertarray()的调用。

其次,您没有返回压缩数据的大小。您正在返回对此的上限估计。

使用deflateBound()(调用deflateInit()),而不是compressbound()。调用deflateBound()一次(而不是三次),然后将答案保存在无签名的长期绑定中。返回压缩数据的大小,即BOND -defstream.avail_out

最后,您没有压缩! deflateInit()的第二个参数应为z_default_compression0以外的其他内容,这意味着没有压缩。

您的示例对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 calling deflateInit()), not compressBound(). Call deflateBound() once (instead of three times) and save the answer in unsigned long bound. Return the size of the compressed data, which is bound - defstream.avail_out.

Lastly, you are not compressing! The second argument to deflateInit() should be Z_DEFAULT_COMPRESSION or something else other than 0, 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文