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

https://i.stack.imgur.com/5khCg.png

  • What is `invertArray()`? – Mark Adler Jul 06 '22 at 03:14
  • a function which return an inverted array – Igor Mogou Jul 06 '22 at 06:30
  • I still don't know what it does. All you did is repeat the name. Put the code for that function in the question. Also include the code for building the header and other chunks. If you provide partial code, then we have no way to find the problem if the problem is in code that hasn't been provided. – Mark Adler Jul 06 '22 at 13:02
  • i edited the post ans uploaded the invertArray() function – Igor Mogou Jul 07 '22 at 07:18
  • So why in the world would you want to reverse the bytes in your data? That makes no sense. Remove the call to `invertArray()`. – Mark Adler Jul 07 '22 at 21:37
  • by this i tried to flip the pixel buffer, Opengl store pixels from left to right and bottom to top, but png needs top to bottom scanlines. Sure i was wrong while simply invert the array, i updated the original post for showing the real flipping method – Igor Mogou Jul 11 '22 at 21:14

1 Answers1

0

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.

Mark Adler
  • 101,978
  • 13
  • 118
  • 158
  • no, the color mode is in rgb true color (2) – Igor Mogou Jul 06 '22 at 06:30
  • Then the PNG file you provided in your "my PNG Output" link (r88TR.png) is not your actual output. That PNG file has color type 6. – Mark Adler Jul 06 '22 at 12:59
  • it's the original png output that i compressed with ms.paint for bypass limit of 2mb imge size – Igor Mogou Jul 06 '22 at 20:32
  • Then generate a smaller example of the problem for which you can post the actual png image. – Mark Adler Jul 07 '22 at 03:45
  • I edited my post, and replaced this image by a 800X600 ver of the same file – Igor Mogou Jul 07 '22 at 06:53
  • That PNG file has an IDAT chunk with 228 extraneous bytes at the end and an incorrect CRC. First, fix your chunk formatting and compression. You're still not showing the code that builds the PNG file. – Mark Adler Jul 07 '22 at 21:42
  • thanks, my scanlines generating method was effectively wrong, i corrected the method and changed the deflate() call and getting bound and its worked perfectly, going from a 13mb file to 14ko is just insane. – Igor Mogou Jul 11 '22 at 21:22