Question

I'm working on a function that can uncompress the deflate compression, so i can read/draw png files in my c++ program. However, the deflate specification isn't very clear on some things.

So my main question is: Paragraph 3.2.7. Compression with dynamic Huffman codes (BTYPE=10) of the specification state that the distance code follows the literal/length

But it does not state how many bits the distance code occupy, is it an entire byte? And how does the distance code relate?.. whats its use, really?

Any one have a general explanation? since the specification is kinda lacking in clarity. The specification i found here: http://www.ietf.org/rfc/rfc1951.txt

Edit (Here is my following code to use with puff inflate code.)

First the header (ConceptApp.h)

#include "resource.h"

#ifdef _WIN64
typedef unsigned long long SIZE_PTR;
#else
typedef unsigned long SIZE_PTR;
#endif

typedef struct _IMAGE {
DWORD Width;           //Width in pixels.
DWORD Height;          //Height in pixels.
DWORD BitsPerPixel;    //24 (RGB), 32 (RGBA).
DWORD Planes;          //Count of color planes
PBYTE Pixels;          //Pointer to the first pixel of the image.
} IMAGE, *PIMAGE;

typedef DWORD LodePNGColorType;

typedef struct _LodePNGColorMode {
DWORD colortype;
DWORD bitdepth;
} LodePNGColorMode;

typedef struct LodePNGInfo
{
/*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/
unsigned compression_method;/*compression method of the original file. Always 0.*/
unsigned filter_method;     /*filter method of the original file*/
unsigned interlace_method;  /*interlace method of the original file*/
LodePNGColorMode color;     /*color type and bits, palette and transparency of the PNG file*/
} LodePNGInfo;

typedef struct _ZLIB {
BYTE CMF;
BYTE FLG;
//DWORD DICTID; //if FLG.FDICT (Bit 5) is set, this variable follows.
//Compressed data here...
} ZLIB, *PZLIB;

typedef struct _PNG_IHDR {
DWORD Width;
DWORD Height;
BYTE BitDepth;
BYTE ColourType;
BYTE CompressionMethod;
BYTE FilterMethod;
BYTE InterlaceMethod;
} PNG_IHDR, *PPNG_IHDR;

typedef struct _PNG_CHUNK {
DWORD Length;
CHAR ChuckType[4];
} PNG_CHUNK, *PPNG_CHUNK;

typedef struct _PNG {
BYTE Signature[8];
PNG_CHUNK FirstChunk;
} PNG, *PPNG;

And the code .cpp file: The main function can be found at the bottom of the file (LoadPng)

BYTE LoadPng(PPNG PngFile, PIMAGE ImageData)
{
PDWORD Pixel = 0;
DWORD ChunkSize = 0;
PPNG_IHDR PngIhdr = (PPNG_IHDR) ((SIZE_PTR) &PngFile->FirstChunk + sizeof(PNG_CHUNK));
DWORD Png_Width = Png_ReadDword((PBYTE)&PngIhdr->Width);
DWORD Png_Height = Png_ReadDword((PBYTE)&PngIhdr->Height);
DWORD BufferSize = (Png_Width*Png_Height) * 8; //This just a guess right now, havent done the math yet. !!!
ChunkSize = Png_ReadDword((PBYTE)&PngFile->FirstChunk.Length);
PPNG_CHUNK ThisChunk = (PPNG_CHUNK) ((SIZE_PTR)&PngFile->FirstChunk + ChunkSize + 12); //12 is the length var itself, Chunktype and CRC.
PPNG_CHUNK NextChunk;
PBYTE UncompressedData = (PBYTE) malloc(BufferSize);
INT RetValue = 0;

do
{
    ChunkSize = Png_ReadDword((PBYTE)&ThisChunk->Length);
    NextChunk = (PPNG_CHUNK) ((SIZE_PTR)ThisChunk + ChunkSize + 12); //12 is the length var itself, Chunktype and CRC.

    if (Png_IsChunk(ThisChunk->ChuckType, "IDAT")) //Is IDAT ?
    {
        PZLIB iData = (PZLIB) ((SIZE_PTR)ThisChunk + 8); //8 is the length and chunkType.

        PBYTE FirstBlock; //ponter to the first 3 bits of the deflate stuff.

        if ((iData->CMF & 8) == 8) //deflate compression method.
        {
            if ((iData->FLG & 0x20) == 0x20)
            {
                FirstBlock = (PBYTE) ((SIZE_PTR)iData + 6); //DICTID Present.
            }
            else FirstBlock = (PBYTE) ((SIZE_PTR)iData + 2); //DICTID Not present.

            RetValue = puff(UncompressedData, &BufferSize, FirstBlock, &ChunkSize); //I belive chunksize should be fine.

            if (RetValue != 0)
            {
                WCHAR ErrorText[100];
                swprintf_s(ErrorText, 100, L"%u", RetValue); //Convert data into string.
                MessageBox(NULL, ErrorText, NULL, MB_OK);
            }
        }
    }
    ThisChunk = NextChunk;
} while (!Png_IsChunk(ThisChunk->ChuckType, "IEND"));

//LodePNGInfo ImageInfo;
//PBYTE Png_Real_Image = (PBYTE) malloc(BufferSize);
//ImageInfo.compression_method = PngIhdr->CompressionMethod;
//ImageInfo.filter_method = PngIhdr->FilterMethod;
//ImageInfo.interlace_method = PngIhdr->InterlaceMethod;
//ImageInfo.color.bitdepth = PngIhdr->BitDepth;
//ImageInfo.color.colortype = PngIhdr->ColourType;

//Remove Filter/crap blah blah.
//postProcessScanlines(Png_Real_Image, UncompressedData, Png_Width, Png_Height, &ImageInfo);

ImageData->Width = Png_Width;
ImageData->Height = Png_Height;
ImageData->Planes = 0; //Will need changed later.
ImageData->BitsPerPixel = 32; //Will need changed later.

ImageData->Pixels = 0;
//ImageData->Pixels = Png_Real_Image; //image not uncompressed yet.

return TRUE; //ret true for now. fix later.
}
Was it helpful?

Solution 2

The problem is not with puff. All the IDAT chunks in the png file need to be put together before calling puff.

It should look something like this:

BYTE LoadPng(PPNG PngFile, PIMAGE ImageData)
{
PDWORD Pixel = 0;
DWORD ChunkSize = 0;
PPNG_IHDR PngIhdr = (PPNG_IHDR) ((SIZE_PTR) &PngFile->FirstChunk + sizeof(PNG_CHUNK));
DWORD Png_Width = Png_ReadDword((PBYTE)&PngIhdr->Width);
DWORD Png_Height = Png_ReadDword((PBYTE)&PngIhdr->Height);
DWORD BufferSize = (Png_Width*Png_Height) * 8; //This just a guess right now, havent done the math yet. !!!
ChunkSize = Png_ReadDword((PBYTE)&PngFile->FirstChunk.Length);
PPNG_CHUNK ThisChunk = (PPNG_CHUNK) ((SIZE_PTR)&PngFile->FirstChunk + ChunkSize + 12); //12 is the length var itself, Chunktype and CRC.
PPNG_CHUNK NextChunk;
PBYTE UncompressedData = (PBYTE) malloc(BufferSize);
PBYTE TempBuffer = (PBYTE) malloc(BufferSize); //Put all idat chunks together befor uncompressing.
DWORD DeflateSize = 0; //All IDAT Chunks Added.
PZLIB iData = NULL;
PBYTE FirstBlock = NULL; //ponter to the first 3 bits of the deflate stuff.
INT RetValue = 0;

do
{
    ChunkSize = Png_ReadDword((PBYTE)&ThisChunk->Length);
    NextChunk = (PPNG_CHUNK) ((SIZE_PTR)ThisChunk + ChunkSize + 12); //12 is the length var itself, Chunktype and CRC.

    if (Png_IsChunk(ThisChunk->ChuckType, "IDAT")) //Is IDAT ?
    {
        CopyMemory(&TempBuffer[DeflateSize], (PBYTE) ((SIZE_PTR)ThisChunk + 8), ChunkSize); //8 is the length and chunkType.
        DeflateSize += ChunkSize;
    }
    ThisChunk = NextChunk;
} while (!Png_IsChunk(ThisChunk->ChuckType, "IEND"));

iData = (PZLIB) TempBuffer;
if ((iData->CMF & 8) == 8) //deflate compression method.
{
    if ((iData->FLG & 0x20) == 0x20)
    {
        FirstBlock = (PBYTE) ((SIZE_PTR)iData + 6); //DICTID Present.
    }
    else FirstBlock = (PBYTE) ((SIZE_PTR)iData + 2); //DICTID Not present.
}

RetValue = puff(UncompressedData, &BufferSize, FirstBlock, &DeflateSize); //I belive chunksize should be fine.

if (RetValue != 0)
{
    WCHAR ErrorText[100];
    swprintf_s(ErrorText, 100, L"%u", RetValue);
    MessageBox(NULL, ErrorText, NULL, MB_OK);
}

//LodePNGInfo ImageInfo;
//PBYTE Png_Real_Image = (PBYTE) malloc(BufferSize);
//ImageInfo.compression_method = PngIhdr->CompressionMethod;
//ImageInfo.filter_method = PngIhdr->FilterMethod;
//ImageInfo.interlace_method = PngIhdr->InterlaceMethod;
//ImageInfo.color.bitdepth = PngIhdr->BitDepth;
//ImageInfo.color.colortype = PngIhdr->ColourType;

//Remove Filter/crap blah blah.
//postProcessScanlines(Png_Real_Image, UncompressedData, Png_Width, Png_Height, &ImageInfo);

ImageData->Width = Png_Width;
ImageData->Height = Png_Height;
ImageData->Planes = 0; //Will need changed later.
ImageData->BitsPerPixel = 32; //Will need changed later.

ImageData->Pixels = 0;
//ImageData->Pixels = Png_Real_Image; //image not uncompressed yet.

return TRUE; //ret true for now. fix later.
}

OTHER TIPS

I just hope to make clearer what is stated before--Huffman coding is a method for encoding values using a variable number of bits. In, say, ASCII coding, every letter gets the same number of bits no matter how frequently it is used. In Huffman coding, you could make "e" have fewer bits than an "X".

The trick in huffman coding is how the codes are prefixed. After reading each bit, the decoder knows, unambiguously, whether it has a value or needs to read another bit.

To comprehend the deflate process you need to understand LZ algorithm and Huffman coding.

On their own, both techniques are simple. The complexity comes from how they are put together.

LZ compresses by finding previous occurrences of a string. When a string has occurred previously, it is compressed by referencing the previous occurrence. The Distance is the offset to the previous occurrence. Distance and length specify that occurrence.

You need to first read up on compression, since there is a lot of basic stuff that you're not getting. E.g. The Data Compression Book, by Nelson and Gailly.

Since it's a code, specifically a Huffman code, by definition the number of bits are variable.

If you don't know what the distance is for, then you need to first understand the LZ77 compression approach.

Lastly, aside from curiosity and self-education, there is no need for you to understand the deflate specification or to write your own inflate code. That's what zlib is for.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top