Files
SharpPngDecode/TEST_DecodePng/PngDecoder.cs

228 lines
9.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TEST_DecodePng
{
internal class PngDecoder
{
const int PNG_SIGNATURE_LENGTH = 8;
readonly byte[] EXPECTED_PNG_SIGNATURE = new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 };
const int CHUNCK_SIZE_FIELD_LENGTH = 4;
const int CHUNCK_TYPE_FIELD_LENGTH = 4;
const int CHUNCK_CRC_FIELD_LENGTH = 4;
const int IMAGE_WIDTH_FIELD_SIZE = 4;
const int IMAGE_HEIGHT_FIELD_SIZE = 4;
const int GAMA_FILED_LENGTH = 4;
//Critical chunks
const string FIRST_CHUNK_TYPE = "IHDR";
const string LAST_CHUNCK_TYPE = "IEND";
const string DATA_CHUNCK_TYPE = "IDAT";
const string GAMA_CHUNCK_TYPE = "GAMA";
public Stopwatch chrono;
public PngDecoder()
{
chrono = new Stopwatch();
//I start and stop the chronometer so all the methods can just use the Restart method.
chrono.Start(); chrono.Stop();
}
public Bitmap Decode(string filename)
{
//Timing Stuff
chrono.Reset();
chrono.Start();
//Timing Stuff
byte[] content = File.ReadAllBytes(filename);
int cursor = 0;
byte[] pngSignature = new byte[PNG_SIGNATURE_LENGTH];
for (int i = 0; i < PNG_SIGNATURE_LENGTH; i++)
{
pngSignature[i] = content[cursor];
cursor++;
}
//Checks if the file has a valid png signature
if (!compareByteArrays(EXPECTED_PNG_SIGNATURE, pngSignature))
throw (new InvalidPngHeadersFileException("File signature not recognized"));
List<Byte[]> chuncks = new List<Byte[]>();
List<string> chuncksType = new List<string>();
List<int> chuncksSize = new List<int>();
int chunckSize = 0;
string chunckType = "";
while (chunckType.ToLower() != LAST_CHUNCK_TYPE.ToLower())
{
//Reading Chunck Size (Carefull, does not contain the size, type and crc bytes)
byte[] ckln = new byte[] { content[cursor], content[cursor + 1], content[cursor + 2], content[cursor + 3] };
Array.Reverse(ckln);
chunckSize = BitConverter.ToInt32(ckln, 0);
cursor += CHUNCK_SIZE_FIELD_LENGTH;
//Reading the chunck type
byte[] cktp = new byte[] { content[cursor], content[cursor + 1], content[cursor + 2], content[cursor + 3] };
chunckType = System.Text.Encoding.UTF8.GetString(cktp);
cursor += CHUNCK_TYPE_FIELD_LENGTH;
//Reading the chunck data
byte[] chunck = new byte[chunckSize];
for (int i = 0; i < chunckSize; i++)
{
chunck[i] = content[cursor];
cursor++;
}
//Reading the CRC
byte[] ckcrc = new byte[] { content[cursor], content[cursor + 1], content[cursor + 2], content[cursor + 3] };
cursor += CHUNCK_CRC_FIELD_LENGTH;
//PROCESS THE CRC
chuncks.Add(chunck);
chuncksSize.Add(chunckSize);
chuncksType.Add(chunckType);
}
//Now we have a list of all the chuncks in the PNG file
//We can now process it
//The First chunck MUST be a IHDR chunck so we test that
if (chuncksType[0].ToLower() != FIRST_CHUNK_TYPE.ToLower())
throw new InvalidPngHeadersFileException("Critial chunck not found : IHDR");
//The Last chunck MUST be an IEND chunck so we test that
if (chuncksType[chuncksType.Count - 1].ToLower() != LAST_CHUNCK_TYPE.ToLower())
throw new InvalidPngHeadersFileException("Critical chunck not found : IEND");
//There must be at least one IDAT chunck so we check that
int dataChunckcount = 0;
foreach(string cktype in chuncksType)
{
if (cktype.ToLower() == DATA_CHUNCK_TYPE.ToLower())
dataChunckcount++;
}
if (dataChunckcount == 0)
throw new InvalidPngHeadersFileException("No data chuncks where found");
PngMetaData header = null;
float gama = 0;
//Now we can process them
for (int chunckCounter = 0; chunckCounter < chuncks.Count; chunckCounter++)
{
string cktype = chuncksType[chunckCounter];
switch (cktype.ToUpper())
{
//IHDR
case FIRST_CHUNK_TYPE:
int chunckCursor = 0;
byte[] btaImageWidth = new byte[IMAGE_WIDTH_FIELD_SIZE];
byte[] btaImageHeight = new byte[IMAGE_HEIGHT_FIELD_SIZE];
for (int i = 0; i < IMAGE_WIDTH_FIELD_SIZE; i++)
{
btaImageWidth[i] = chuncks[chunckCounter][chunckCursor];
chunckCursor++;
}
for (int i = 0; i < IMAGE_HEIGHT_FIELD_SIZE; i++)
{
btaImageHeight[i] = chuncks[chunckCounter][chunckCursor];
chunckCursor++;
}
//Those are in little endien notation so we need to reverse it
Array.Reverse(btaImageWidth);
Array.Reverse(btaImageHeight);
int imageWidth = BitConverter.ToInt32(btaImageWidth, 0);
int imageHeight = BitConverter.ToInt32(btaImageHeight, 0);
int bitDepth = Convert.ToInt32(chuncks[chunckCounter][chunckCursor]);
chunckCursor++;
int colorType = Convert.ToInt32(chuncks[chunckCounter][chunckCursor]);
chunckCursor++;
int compressionMethod = Convert.ToInt32(chuncks[chunckCounter][chunckCursor]);
chunckCursor++;
int filterMethod = Convert.ToInt32(chuncks[chunckCounter][chunckCursor]);
chunckCursor++;
int interlaceMethod = Convert.ToInt32(chuncks[chunckCounter][chunckCursor]);
chunckCursor++;
header = new PngMetaData(imageHeight, imageWidth, (sbyte)bitDepth, (sbyte)colorType, (sbyte)compressionMethod, (sbyte)filterMethod, (sbyte)interlaceMethod);
if (!header.IsValid())
throw new InvalidPngHeadersFileException("Header invalide");
break;
case GAMA_CHUNCK_TYPE:
byte[] btGamma = new byte[GAMA_FILED_LENGTH];
//btGamma = new byte[] { chuncks[chunckCounter][0], chuncks[chunckCounter][1], chuncks[chunckCounter][2], chuncks[chunckCounter][3] };
for (int i = 0; i < GAMA_FILED_LENGTH; i++)
{
btGamma[i] = chuncks[chunckCounter][i];
}
//btGamma.Reverse();
Array.Reverse(btGamma);
gama = ((float)BitConverter.ToInt32(btGamma,0) / 1000F);
break;
default:
//Unrecognized chunck, as said in the doc should not stop the decoding
break;
}
}
if (header != null)
{
MessageBox.Show("Image width: " + header.Width +
Environment.NewLine + "Image height: " + header.Height +
Environment.NewLine + "byte depth : " + header.BitDepth +
Environment.NewLine + "color type : " + header.ColorType +
Environment.NewLine + "compression method : " + header.CompressionMethod +
Environment.NewLine + "filter method : " + header.FilterMethod +
Environment.NewLine + "interface method : " + header.InterlaceMethod +
Environment.NewLine + "gama : " + gama
);
}
//Timing Stuff
chrono.Stop();
//Timing Stuff
//TO REMOVE
return new Bitmap(100, 100);
}
public bool compareByteArrays(byte[] firstArray, byte[] secondArray)
{
if (firstArray.Length != secondArray.Length)
return false;
bool result = true;
for (int i = 0; i < firstArray.Length; i++)
{
if (firstArray[0] != secondArray[0])
result = false;
}
return result;
}
}
}