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
|