www.xbdev.net
xbdev - software development
Wednesday September 18, 2024
Home | Contact | Support | Image File Format... Storing Graphics using Bits and Bytes.. | Image Formats A single picture is worth a thousand words...
     
 

Image Formats

A single picture is worth a thousand words...

 

PNG Image File Format Explained

by bkenwright@xbdev.net

 

One format I had to use recently is the PNG image format.  Now at first you'd think, it can't be that hard.  But its a lot harder than you think.  Its file format specification is that of chunks!  All the data in the file is stored in chunks, so you have a description chunk, a image header chunk and an image data chunk etc.  Each chunk is a 4 byte size followed by a 4 byte id, so in principle you can read the whole file, dump out all the chunk types and only use the ones that your file reader can handle.

 

Taking a simple test case, such as a 8x8 image which we can create using any photo package, such as the GIMP...which allows us to set compression on/off.  We can create a simple png file that we can dump and explore.

 

 

 

 

Dump Png - Download Source Code (64k)
/******************************************************************************/
/*                                                                            */
/* File: code.cpp                                                             */
/*                                                                            */
/* Simple png image file format dump program - dumps out the contents of a    */
/* png file to a text output file (output.txt) that you can then look at.     */
/* Only dumps the chunk sizes and ids, then goes on to dump more extended     */
/* information about the IDAT chunk which contains the actual image           */
/* image information                                                          */
/*                                                                            */
/* Quote: Anyone can code!                                                    */
/*                                                                            */
/******************************************************************************/

// Dump Contents of png image file

#include <string.h>        // memset(..)
#include <stdio.h>        // fopen, fread, sprintf etc
#include <stdarg.h>     // So we can use ... (in dprintf)
#include <stdlib.h>


#include "zlib.h"
#pragma comment(lib, "zdll.lib")


// Saving debug information to a txt file
void dprintf(const char *fmt, ...) 
{
    va_list parms;
    char buf[256];

    // Try to print in the allocated space.
    va_start(parms, fmt);
    vsprintf (buf, fmt, parms);
    va_end(parms);

    // Write the information out to a txt file
    FILE *fp = fopen("output.txt", "a+");
    fprintf(fp, "%s", buf);
    fclose(fp);

}// End dprintf(..)


inline void SwapEndian(unsigned short *data)
{
    *data=((*data&0x00ff)<<8)
         |((*data&0xff00)>>8);
}

inline void SwapEndian(short *data)
{
    *data=((*data&0x00ff)<<8)
         |((*data&0xff00)>>8);
}

inline void SwapEndian(unsigned int *data)
{
    *data=((*data&0x000000ff)<<24)
         |((*data&0x0000ff00)<< 8)
         |((*data&0x00ff0000)>> 8)
         |((*data&0xff000000)>>24);
}

inline void SwapEndian(int *data)
{
    *data=((*data&0x000000ff)<<24)
         |((*data&0x0000ff00)<< 8)
         |((*data&0x00ff0000)>> 8)
         |((*data&0xff000000)>>24);
}


static void *png_zalloc(void *opaque, unsigned int items, unsigned int size)
{
    return malloc(items * size);
}

static void png_zfree(void *opaque, void *ptr)
{
    free(ptr);
}


void ReadImageData(FILE *fp, int sizeData)
{
    static z_stream zstream;
    memset(&zstream, 0, sizeof(zstream));

    // init the zlib
    zstream.zalloc = png_zalloc;
    zstream.zfree = png_zfree;
    zstream.opaque = NULL;
    int okay = inflateInit(&zstream);
    dprintf("InflateInit Z_OK(0) : %d\n", okay);


    dprintf("\t[IDAT]Reading Image Data\n");
    dprintf("\t SizeData %d\n", sizeData);

    unsigned char* buf = new unsigned char[sizeData + 100];
    fread(buf, 1, sizeData, fp);

    
    int i = 0;
    dprintf("Compression Flags: 0x%02X\n", buf[i]);    i++;
    dprintf("Additional  Flags: 0x%02X\n", buf[i]);    i++;
    dprintf("Misc Bytes: 0x%02X\n", buf[i]);        i++;
    dprintf("Misc Bytes: 0x%02X\n", buf[i]);        i++;
    dprintf("Misc Bytes: 0x%02X\n", buf[i]);        i++;
    dprintf("Misc Bytes: 0x%02X\n", buf[i]);        i++;
    dprintf("Misc Bytes: 0x%02X\n", buf[i]);        i++;

    for (int y=0; y<8; y++)
    {
        dprintf("Data: ");
        dprintf("0x%02X   - ", buf[i]); i++;

        int lineWidth = 3*8;

        for (int x=0; x<lineWidth; x++)
        {
            dprintf("0x%02X ", buf[i]); i++;
        
        }
        dprintf("\n");
    }


    unsigned int cv = ((unsigned int)buf[i+0] | 
                      ((unsigned int)buf[i+1]<<8) | 
                      ((unsigned int)buf[i+2]<<16) | 
                      ((unsigned int)buf[i+3]<<24));
    SwapEndian(&cv);
    dprintf("Check Value: 0x%08X\n", cv);    i+=4;
    dprintf("i = %d\n\n", i);


    // Decompressed using zlib
    unsigned char outBuf[1024]= {0};

    dprintf("Compressed Data:\n\n");
    unsigned char compressedData[1024] = {0};
    for (int i=0; i<sizeData; i++)
    {
        compressedData[i] = buf[i];

        if (i%(8*3)==0) dprintf("\n");
        dprintf("0x%02X, ", buf[i]);
    }
    dprintf("\n\n");


    int ret = 0;
    zstream.avail_in = sizeData;
    zstream.next_in = compressedData;

    zstream.avail_out = sizeof(outBuf);
    zstream.next_out = (Bytef*)outBuf;


    // decode one line if possible
    while (zstream.avail_in > 0) 
    {
        ret = inflate(&zstream, Z_SYNC_FLUSH); //Z_PARTIAL_FLUSH);
        if (ret != Z_OK && ret != Z_STREAM_END) 
        {
            dprintf("Problem with zlib inflate\n");
            return;
        }
    }

    // Finished!..release zlib
    inflateEnd(&zstream);


    dprintf("-Decompressed Image:\n");
    for (int y=0; y<8; y++)
    {
        int lineWidth = 3*8 + 1;

        dprintf("Data: ");
        dprintf("0x%02X   - ", outBuf[y*lineWidth + 0]);

        for (int x=0; x<lineWidth-1; x++)
        {
            dprintf("0x%02X ", outBuf[y*lineWidth + x + 1]);
        
        }
        dprintf("\n");
    }

    delete[] buf;
}



void ReadPngFile(char* szFileName)
{
    dprintf("Png Dump File: %s\n\n", szFileName);

    FILE *fp;
    fp = fopen(szFileName, "rb");

    // Read 8 byte PNG signature first
    unsigned char sig[8];
    fread(sig, 1, 8, fp);

    // Should be, for a valid PNG file:
    // [0x89, (0x50)'P', (0x4E)'N', (0xD)'G', 0xD, 0xA, 0x1A, 0xA]
    dprintf("First 8 bytes: [0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X]\n",
            sig[0],sig[1],sig[2],sig[3],sig[4],sig[5],sig[6],sig[7]);


    // Next comes the chunks of data which contain the image data
    while (true)
    {
        unsigned int chunkSize;
        unsigned int chunkID;

        
        fread(&chunkSize, 1, sizeof(chunkSize), fp);
        fread(&chunkID,   1, sizeof(chunkID),   fp);

        SwapEndian(&chunkSize); // Get Size of this Chunk
        SwapEndian(&chunkID);    // Get Its Chunk ID Type

        dprintf("[Chunk] ID:0x%X  (Size:% 6d)\n", chunkID, chunkSize);
        bool doneAllChunks = false;

        long chunkPos = ftell(fp);

        switch (chunkID)
        {
            case 0x49444154:    // 'I', 'D', 'A', 'T'
            {
                ReadImageData(fp, chunkSize);
            }
            break;
            case 0x49454e44:    // 'I', 'E', 'N', 'D'
            {
                doneAllChunks=true; // No More Chunks
            }
            break;
            default:
            {
            }
        }

        fseek(fp, chunkPos, SEEK_SET);
        // Jump over this chunk
        fseek(fp, chunkSize, SEEK_CUR); // Skip over its contents

        unsigned int crc;
        fread(&crc,   1, sizeof(crc),   fp); // Read CRC Value
        dprintf("CRC: 0x%08X\n", crc);

        if (doneAllChunks) break;
    }

    // All done extracting information, close file
    fclose(fp);

    dprintf("Finished\n");
}// End ReadPngFile(..)



void main()
{
    ReadPngFile("output.png");
}// End main()

 

output.txt
Png Dump File: cross8x8.png

First 8 bytes: [0x89, 0x50, 0x4E, 0x47, 0xD, 0xA, 0x1A, 0xA]
[Chunk] ID:0x49484452 (Size: 13)
CRC: 0xDC296D4B
[Chunk] ID:0x73524742 (Size: 1)
CRC: 0xE91CCEAE
[Chunk] ID:0x49444154 (Size: 211)
InflateInit Z_OK(0) : 0
[IDAT]Reading Image Data
SizeData 211
Compression Flags: 0x08
Additional Flags: 0x1D
Misc Bytes: 0x01
Misc Bytes: 0xC8
Misc Bytes: 0x00
Misc Bytes: 0x37
Misc Bytes: 0xFF
Data: 0x01 - 0x00 0x00 0x00 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x01 0x01
Data: 0x02 - 0xFF 0xFF 0xFF 0x01 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x01 0x01 0xFF 0xFF 0xFF
Data: 0x02 - 0x00 0x00 0x00 0xFF 0xFF 0xFF 0x01 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x01 0x01 0xFF 0xFF 0xFF 0x00 0x00 0x00
Data: 0x01 - 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x01 0x01 0x00 0x00 0x00 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00
Data: 0x02 - 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Data: 0x02 - 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x01 0x01 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x01 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00
Data: 0x02 - 0x00 0x00 0x00 0x01 0x01 0x01 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0x01 0x01 0x01 0x00 0x00 0x00
Data: 0x01 - 0x00 0x00 0x00 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x01 0x01
Check Value: 0x9F79240B

CRC: 0x33114D4B
[Chunk] ID:0x49454E44 (Size: 0)
CRC: 0x826042AE
Finished

 

Of course most formats start with some magic numbers so that we can identify what file we are dealing with.  The next part is to actually get information about the image file we have, such as image width, compression etc.

 

 

 

Looking at the RAW Png File Contents

 

To really help you visualise what can be in a raw png file, I wrote a very simple program that takes the raw data of a ping and shows it to you at each byte.  It then allows you to run the program and it takes those bytes and creates the file. 

 

Raw PNG File Create - Download Source Code (5k)
/******************************************************************************/
/*                                                                            */
/* File: png_tiny_raw_creator.cpp                                             */
/*                                                                            */
/* Simple tutorial file, that allows you to see whats in a raw png file, and  */
/* then creates it.  Creates a file, and writes out the raw byte data to it.  */
/* The image is a 8x8 image of a cross. Using no compression and RGB values.  */
/*                                                                            */
/******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define WIDTH 0x8
#define HEIGHT 0x8

static
unsigned char head[] = {
/* PNG signature */
        0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,

/* IHDR -- Image header */

        0, 0, 0, 0x0d,
        'I', 'H', 'D', 'R',
        0, 0, 0, WIDTH, // width
        0, 0, 0, HEIGHT,// height
        8,              // bit depth
        2,              // colour type: 2=rgb-colour
        0,              // compression method: 0=deflate
        0,              // filter method
        0,              // interlace method
        0x4B, 0x6D, 0x29, 0xDC, // CRC-32

/* IDAT */

        0, 0, 0, 211,
        'I', 'D', 'A', 'T',

        // CMF = 0x28 
        //  .CM = %1000 = 8 (compression method: 8 deflate)
        //  .CINFO = %0010 (window size: 1024)
        // FLG = 0xcf
        //  .FCHECK %01111 (checksum: 0x28cf = 10447 = 337 * 31 OK)
        //  .FDICT = %0 = no dictionary
        //  .FLEVEL = %11 = max compression

        0x08,
        0x1D,
        0x01,
        0xC8,
        0x00,
        0x37,
        0xFF,

        // Filter Byte  --- >   Raw RGB Pixel Data
        0x01,  0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,
        0x02,  0xFF,0xFF,0xFF,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0xFF,0xFF,0xFF,
        0x02,  0x00,0x00,0x00,0xFF,0xFF,0xFF,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0xFF,0xFF,0xFF,0x00,0x00,0x00,
        0x01,  0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
        0x02,  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x02,  0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
        0x02,  0x00,0x00,0x00,0x01,0x01,0x01,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x01,0x01,0x01,0x00,0x00,0x00,
        0x01,  0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,
                                
        0x9F, 0x79, 0x24, 0x0B, // Adler CRC

        0x43, 0xD1, 0x11, 0x33, // CRC-32

/* IEND */

        0, 0, 0, 0,
        'I', 'E', 'N', 'D',
        // no data
        0xae, 0x42, 0x60, 0x82, // CRC-32
};


int main(void)
{
    FILE *fp = fopen("test.png", "wb");
    fwrite(head, 1, sizeof head, fp);
    fclose (fp);
}

 

 

One thing to note about this simple RAW png file format, is the CRC's...as if you alter any of the data you have to re-do the CRC values...most paint packages don't check these CRC values at the end of chunks so thats not so bad.  But the data adler crc is usually checked to determine if our image data is corrupted.

 

 

Writing PNG Writer

 

Once you know the layout of any format, its not that hard to sit down and write a simple program that can generate them.  Same for our PNG iamge files...now that we sort of grasp things...we can write a simple program that takes raw RGB values and then builds a .png file for us.

 

Tiny Png Write - Download Source Code (7k)
/******************************************************************************/
/*                                                                            */
/* File: tinyPng.cpp                                                          */
/*                                                                            */
/* Simple Png File Write (Exporter) - Takes raw RGB pixel values and then     */
/* writes them out to a png file.                                             */
/* Only does Uncompressed PNG so that its kept as simple as possible, and     */
/* doesn't require any external libs...so you don't need to use zlib..        */
/*                                                                            */
/* Quote: Code can drive you crazy!                                           */
/*                                                                            */
/******************************************************************************/

#include <stdio.h>


void SavePng(char* fileName);
void SaveIhdrChunk();
void SaveChunk(int chunkType, int chunkSize, unsigned char* data);
void SavePhysChunk();
void SaveIendChunk();
void SaveImage();


FILE* g_fp     = NULL;
int   g_width  = 16;
int   g_height = 16;


/******************************************************************************/
/*                                                                            */
/* Program Entry Point                                                        */
/*                                                                            */
/******************************************************************************/
void main()
{
    SavePng("output.png");

}// End main(..)


/******************************************************************************/
/*                                                                            */
/* Various common PNG File Format Defines                                     */
/*                                                                            */
/******************************************************************************/
const int CHUNK_CRC32_IEND = 0xae426082;
const int CHUNK_SIZE_IHDR = 0x0000000d;
const int CHUNK_TYPE_IDAT = 0x49444154;
const int CHUNK_TYPE_IEND = 0x49454e44;
const int CHUNK_TYPE_IHDR = 0x49484452;
const int CHUNK_TYPE_PLTE = 0x504c5445;
const int CHUNK_TYPE_tEXt = 0x74455874;
const int CHUNK_TYPE_pHYs = 0x70485973;
const int COLOR_TYPE_GRAY = 0;
const int COLOR_TYPE_GRAY_ALPHA = 4;
const int COLOR_TYPE_INDEXED = 3;
const int COLOR_TYPE_RGB = 2;
const int COLOR_TYPE_RGB_ALPHA = 6;
const int COLOR_TYPE_ALPHA = 4;
const int FILTER_TYPE_NONE = 0;
const int FILTER_TYPE_SUB = 1;
const int FILTER_TYPE_UP = 2;
const int FILTER_TYPE_AVERAGE = 3;
const int FILTER_TYPE_PAETH = 4;
const int COMPRESSION_DEFLATE = 0;
const int INTERLACING_NONE = 0;
const int INTERLACING_ADAM7 = 1;
const int FILTERING_ADAPTIVE = 0;
const int MAX_TEXT_SIZE = 512;
const int ADAM7_NUM_PASSES = 7;
const int ADAM7_COLUMN_INCREMENT[] = {8, 8, 4, 4, 2, 2, 1};
const int ADAM7_FIRST_COLUMN[] = {0, 4, 0, 2, 0, 1, 0};
const int ADAM7_FIRST_ROW[] = {0, 0, 4, 0, 2, 0, 1};
const int ADAM7_ROW_INCREMENT[] = {8, 8, 8, 4, 4, 2, 2};
const unsigned char MAGIC_BYTES[8] = {0x89, 0x50, 0x4e, 0x47,0x0d, 0x0a, 0x1a, 0x0a};


/******************************************************************************/
/*                                                                            */
/* SwapEndian(..)                                                             */
/* A lot of the data needs to be put into BigEndian, so a common function     */
/* is created to make it easier :)                                            */
/*                                                                            */
/******************************************************************************/
inline void SwapEndian(unsigned int *data)
{
    *data=((*data&0x000000ff)<<24)
         |((*data&0x0000ff00)<< 8)
         |((*data&0x00ff0000)>> 8)
         |((*data&0xff000000)>>24);
}


/******************************************************************************/
/*                                                                            */
/* File Writing Functions                                                     */
/*                                                                            */
/******************************************************************************/
void WriteBytes(const unsigned char* data, int numBytes) 
{
    fwrite(data, 1, numBytes, g_fp);
}// End WriteByte(..)


void WriteDword(const unsigned int data) 
{
    SwapEndian((unsigned int*)&data);
    fwrite(&data, 1, sizeof(unsigned int), g_fp);
}// End WriteByte(..)



void SavePng(char* outputFileName)
{
    g_fp = fopen(outputFileName, "wb+");

    // write 8 byte PNG signature
    WriteBytes(MAGIC_BYTES, sizeof(MAGIC_BYTES));
    // write IHDR (image header) chunk
    SaveIhdrChunk();
    // write IDAT chunk
    SaveImage();
    // write IEND chunk
    SaveIendChunk();

    fclose(g_fp);
}


void SaveIhdrChunk()
{
    unsigned char buffer[CHUNK_SIZE_IHDR];

    *(unsigned int*)&buffer[0] = g_width;
    *(unsigned int*)&buffer[4] = g_height;

    SwapEndian((unsigned int*)&buffer[0]);
    SwapEndian((unsigned int*)&buffer[4]);

    buffer[8]  = 8;                        // 8 bit per component
    buffer[9]  = COLOR_TYPE_RGB;        // rgb 24bit
    buffer[10] = COMPRESSION_DEFLATE;    // non-compressed
    buffer[11] = FILTER_TYPE_NONE;
    buffer[12] = INTERLACING_NONE;

    SaveChunk(CHUNK_TYPE_IHDR, CHUNK_SIZE_IHDR, buffer);
}//End SaveIhdrChunk(..)



unsigned int crctab[256] = {
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
    0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
    0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
    0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
    0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
    0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
    0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
    0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
    0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
    0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
    0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
    0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
    0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
    0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
    0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
    0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
    0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
    0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
    0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
    0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
    0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
    0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};


unsigned int CRC32(unsigned int crc, unsigned char * buf, unsigned int len)
{
    unsigned int sum;
    unsigned int s0 = crc ^ 0xffffffffL;
    for (unsigned int i = 0; i < len; i++) 
    {
        s0 = (s0 >> 8) ^ crctab[(unsigned char) (s0 & 0xFF) ^ buf[i]];
    }
    sum = ~s0;
    return sum;
}

class CRC
{
public:
    unsigned int m_sum;
    void Reset()
    {
        m_sum = ~0;
    }

    void Update(unsigned char * buf, unsigned int len)
    {
        for (unsigned int i = 0; i < len; i++) 
        {
            m_sum = (m_sum >> 8) ^ crctab[(unsigned char) (m_sum & 0xFF) ^ buf[i]];
        }
    }

    unsigned int GetCRC()
    {
        return (~m_sum);
    }
};

void SaveChunk(int chunkType, int chunkDataSize, unsigned char* chunkData)
{
    // Write out the Chunk Size
    WriteDword(chunkDataSize);

    // Write out the Chunk Type
    WriteDword(chunkType);

    // Write out the actual data
    WriteBytes(chunkData, chunkDataSize);

    // Work out the 4 byte CRC value on the end which is generated from
    // the data and the chunkType
    CRC crcVal;
    crcVal.Reset();
    unsigned int chunkTypeEndian = chunkType;
    SwapEndian(&chunkTypeEndian);
    crcVal.Update((unsigned char*)&chunkTypeEndian, 4);
    crcVal.Update(chunkData,  chunkDataSize);
    unsigned int checkSum = crcVal.GetCRC();

    // Or using function based CRC, instead of a class
    unsigned int crc = CRC32(0,   (unsigned char*)&chunkTypeEndian, 4);
                 crc = CRC32(crc, chunkData, chunkDataSize);


    // Writing the Checksum
    WriteDword(checkSum);
}


void SaveIendChunk()
{
    // Write out the 12 byte end chunk
    WriteDword(0);
    WriteDword(CHUNK_TYPE_IEND);
    WriteDword(CHUNK_CRC32_IEND);
}



unsigned int adler = 1; // Reset it back to 1
unsigned int UpdateAdler32(unsigned long adler, unsigned char* buf, int sizeBuf)
{
    const unsigned int BASE = 65521;
    unsigned int s1 = adler & 0xffff;
    unsigned int s2 = (adler>>16) & 0xffff;
    
    for (int n=0; n<sizeBuf; n++)
    {
        s1 = (s1 + buf[n]) % BASE;
        s2 = (s2 + s1)     % BASE;
    }
    return (s2<<16) + s1;
}

/******************************************************************************/
/*                                                                            */
/*  SaveImage()                                                               */
/*                                                                            */
/*  Heart of the work goes here.  As we can't just save out the RGB values    */
/*  as you'd think!  Even thought we've chosen uncompressed RGB it still      */
/*  passes the data to zlib, which adds on some extra information.            */
/*  We can still make our image export raw data, but we have to add a few     */
/*  extra bytes which which the zlib compression would usually add to define  */
/*  how the data is stored, and extra parity checks.                          */
/*                                                                            */
/******************************************************************************/
void SaveImage()
{
    int imageWidth  = g_width;
    int imageHeight = g_height;

    // Image Data is
    int lineWidth      = (imageWidth*3) + 1;            // +1 is the Filter Byte (RGB*width)
    int imageChunkSize = lineWidth * imageHeight;        // Our RGB's are stored in this
    int IDATChunkSize  = imageChunkSize + 2 + 5 + 4;    // +3 is for the Compression Flags
                                                        // +4 Size Chunk and Size Chunks Complement
                                                        // +4 ADLER CheckSum

    // Create a big temp array for our generated data
    unsigned char* imageData = new unsigned char[IDATChunkSize];

    // Create a temp pointer to our array, and increment it as we fill our buffer
    // with data
    unsigned char* destPtr = imageData;
    
    // 3 Bytes
    *destPtr++ = 0x08; // Default compression (CMF)
    *destPtr++ = 0x1D; // 
    *destPtr++ = 0x01; // parity bit (FLG)

    // 4 Bytes
    *((unsigned short *)(destPtr))=imageChunkSize;    destPtr+=2;    // Not endian flipped!
    int sizeComplement = imageChunkSize^0xffffffff;                // Complement follows
    *((unsigned short *)(destPtr))=sizeComplement;    destPtr+=2;    // Not endian flipped!


    // Our Actual Pixle values are wrote out here
    for (int y=0; y<imageHeight; y++)
    {
        // Single Filter byte at the start of each scanline
        *destPtr++ = 0;

        // RGB Image Data For a Row
        for (int x=0; x<imageWidth; x++)
        {
            *destPtr++ = 0x0;
            *destPtr++ = 0xff;
            *destPtr++ = 0x0;
        }
    }


    // +7 is so we Calculate the Adler for the size of our image data (including 
    // filter byte) only
    unsigned int adler32 = UpdateAdler32(adler, imageData+7, imageChunkSize);

    SwapEndian(&adler32);
    *(unsigned int *)destPtr = adler32; // Adler


    // Create our buffer full of data which we'll now save out.
    SaveChunk(CHUNK_TYPE_IDAT, IDATChunkSize, imageData);


    // Delete temp buffer
    delete[] imageData;
}// End SaveImage(..)

 

 

 

 

 

 

 

TODO :

- Various Compression Methods

- Full Dump/ Analysis Utility

- Interlacing

 

 

 

 

 

 

 

 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2024 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.