A palette base PNG with IDAT that has BTYPE=00 for

2019-05-30 10:37发布

问题:

I am working on some code that builds simple palette based PNG files without libpng. The output file, at this stage only has IHDR, PLTE, IDAT(x3) and IEND chunks. The only thing which is possibly a bit different in that the pixel index values in the IDAT chunk are not compressed, that is the various zlib / block header bytes are as follows.

  • CMF = 0x78.
  • FLG = 0x9C (have also a few other values here but always with bit 5 clear).
  • Block header byte = 0x01 (BFINAL = 1, BTYPE = 00).

From what I can see the code builds the file correctly, however some image viewers refuse to display the image completely, if at all.

  • MS Paint is happy.
  • GIMP is happy.
  • LibreOffice Draw is happy.
  • Ristretto >> Fatal error reading PNG image file: Not enough compressed data.
  • ImageMagick >> identify: Not enough image data `20160317_PNG_064.png' @ error/png.c/MagickPNGErrorHandler/1645.
  • Eye of Gnome >> not enough image data.

I have put the file through a few different tools, again with mixed results.

  • optipng >> not enough image data.
  • pngchunks does not report any errors.
  • pngcheck does not report any errors.

Here is the hex view of the file 20160317_PNG_064.png

The picture it does generate is this small 8x8 pixel image.

So I am at a bit of a dead end as to what to try next. Any and all assistance is appreciated.

EDIT_000 Having narrowed down the issue to the Adler32 calculation here is, as requested by @Mark Adler, the code that I am using to calculate the Adler32 value with test data in the main function. Btw, it is not fancy and I code very verbose.

#include <stdio.h>

#define     DEBUG

static const unsigned long GC_ADLER32_BASE = 0xFFF1;   // Largest prime smaller than 65536 is 65521.

unsigned long Adler32_Update
        (
        unsigned long Adler32,
        unsigned char *Buffer,
        unsigned int BufferLength
        )
{
    unsigned long   ulW0;
    unsigned long   ulW1;
    unsigned int    uiW0;
#ifdef DEBUG
    printf("\n");
    printf("        Incoming Adler32 value.................0x%.8X\n", Adler32);
#endif
    ulW0 = Adler32 & 0xFFFF;
    ulW1 = (Adler32 >> 0x0010) & 0xFFFF;
#ifdef DEBUG
    printf("        Inital sum values are..................0x%.8X, 0x%.8X\n", ulW0, ulW1);
#endif
    for (uiW0 = 0x0000; uiW0 < BufferLength; uiW0 = uiW0 + 0x0001)
        {
        ulW0 = (ulW0 + Buffer[uiW0]) % GC_ADLER32_BASE;
        ulW1 = (ulW1 + ulW0) % GC_ADLER32_BASE;
        }
#ifdef DEBUG
    printf("        Final sum values are...................0x%.8X, 0x%.8X\n", ulW0, ulW1);
#endif
    Adler32 = (ulW1 << 0x0010) | ulW0;
#ifdef DEBUG
    printf("        Outgoing Adler32 value.................0x%.8X\n", Adler32);
#endif
    return (Adler32);
}


unsigned long Adler32_Get
        (
        unsigned char *Buffer,
        unsigned int BufferLength
        )
{
    unsigned long   Adler32;

    Adler32 = 0x00000001L;
    Adler32 = Adler32_Update(Adler32, Buffer, BufferLength);
    return (Adler32);
}


int main
    (
    unsigned int    argc,
    unsigned char   *arg[]
    )
{
    unsigned long   Adler32;
    unsigned char data[272] = 
        {
    0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
    0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 
    0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 
    0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 
    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 
    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 
    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 
    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01,
    0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 
    0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 
    0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 
    0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02
        };
    Adler32 = Adler32_Get(data, sizeof(data));
    printf("\n");
    printf("The Adler32 value is ..........................0x%.8X\n", Adler32);
    return(0x00);
}

回答1:

Whatever is calculating the Adler-32 value is incorrect. The correct Adler-32 value for the data is 0x10080061, which should be stored in the stream as 10 08 00 61. If I fix that in the linked file, and make a new CRC for that chunk, then all is good.

The fixed image is:



回答2:

As a co-author of the PNG spec, I believe this file is entirely compliant. The PNG spec defers definition of "deflate" to RFC 1951, and that RFC explicitly allows BTYPE=0 sections. You may want to contact the authors of those programs that have trouble with it and report a bug, attaching your file as sample input.