Created
November 26, 2018 03:20
-
-
Save reZach/28247e4616c2596fc282b6167740e3f8 to your computer and use it in GitHub Desktop.
C# .gif lzw
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
using (FileStream fs = new FileStream(@"D:\file.gif", FileMode.Create)) | |
{ | |
using (BinaryWriter outputFile = new BinaryWriter(fs)) | |
{ | |
//outputFile.Seek(0, SeekOrigin.Begin); | |
// http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp | |
// Header | |
outputFile.Write(new char[3] { 'G', 'I', 'F' }); // signature | |
outputFile.Write(new char[3] { '8', '9', 'a' }); // gif format | |
outputFile.Write(Convert.ToInt16(5)); // screen width | |
outputFile.Write(Convert.ToInt16(5)); // screen height | |
BitArray array = new BitArray(new byte[1]); | |
array[7] = true; // global color table flag | |
array[6] = false; array[5] = false; array[4] = true; // color resolution | |
array[3] = false; // sort flag | |
array[2] = false; array[1] = false; array[0] = true; // global color table size | |
byte[] bytes = new byte[1]; | |
array.CopyTo(bytes, 0); | |
outputFile.Write(bytes); | |
outputFile.Write(Convert.ToByte(0)); // background color index | |
outputFile.Write(Convert.ToByte(0)); // pixel aspect ratio | |
// global color table | |
outputFile.Write(Convert.ToByte(255)); // white | |
outputFile.Write(Convert.ToByte(255)); | |
outputFile.Write(Convert.ToByte(255)); | |
outputFile.Write(Convert.ToByte(255)); // red | |
outputFile.Write(Convert.ToByte(0)); | |
outputFile.Write(Convert.ToByte(0)); | |
outputFile.Write(Convert.ToByte(0)); // blue | |
outputFile.Write(Convert.ToByte(0)); | |
outputFile.Write(Convert.ToByte(255)); | |
outputFile.Write(Convert.ToByte(0)); // black | |
outputFile.Write(Convert.ToByte(0)); | |
outputFile.Write(Convert.ToByte(0)); | |
// graphic control extension | |
//outputFile.Write(Convert.ToByte(21)); // extension introducer | |
//outputFile.Write(Convert.ToByte(0xf9)); // graphic control label | |
//outputFile.Write(Convert.ToByte(4)); // byte size | |
//outputFile.Write(Convert.ToByte(0)); // packed field | |
//outputFile.Write(Convert.ToInt16(0)); // delay time | |
//outputFile.Write(Convert.ToByte(0)); // transparent color index | |
//outputFile.Write(Convert.ToByte(0)); // block terminator | |
// image descriptor | |
outputFile.Write(Convert.ToByte(0x2c)); // img separator character | |
outputFile.Write(Convert.ToInt16(0)); // img left position | |
outputFile.Write(Convert.ToInt16(0)); // img top positon | |
outputFile.Write(Convert.ToInt16(5)); // img width | |
outputFile.Write(Convert.ToInt16(5)); // img height | |
array = new BitArray(new byte[1]); | |
array[0] = false; // local color table present | |
array[1] = false; // img not interlaced | |
array[2] = false; // sort flag | |
array[5] = false; // size of local color table | |
array[6] = false; | |
array[7] = false; | |
bytes = new byte[1]; | |
array.CopyTo(bytes, 0); | |
outputFile.Write(bytes); | |
// image data; lzw | |
List<int> pixelData = new List<int>(); | |
pixelData.Add(1); | |
pixelData.Add(1); | |
pixelData.Add(1); | |
pixelData.Add(1); | |
pixelData.Add(1); | |
pixelData.Add(2); | |
pixelData.Add(2); | |
pixelData.Add(2); | |
pixelData.Add(2); | |
pixelData.Add(2); | |
pixelData.Add(1); | |
pixelData.Add(1); | |
pixelData.Add(1); | |
pixelData.Add(1); | |
pixelData.Add(1); | |
pixelData.Add(2); | |
pixelData.Add(2); | |
pixelData.Add(2); | |
pixelData.Add(2); | |
pixelData.Add(2); | |
pixelData.Add(3); | |
pixelData.Add(3); | |
pixelData.Add(3); | |
pixelData.Add(3); | |
pixelData.Add(3); | |
string result = LZW(pixelData); | |
bytes = GetBytes(result); | |
outputFile.Write(Convert.ToByte(2)); // lzw minimum code size | |
outputFile.Write(Convert.ToByte(bytes.Length)); // number of bytes in sub-block | |
for (int i = 0; i < bytes.Length; i++) | |
{ | |
outputFile.Write(bytes[i]); | |
} | |
outputFile.Write(Convert.ToByte(0)); | |
outputFile.Write(Convert.ToByte(0x3b)); // terminator character | |
} | |
} | |
} | |
// https://rosettacode.org/wiki/LZW_compression#C.23 | |
static string LZW(List<int> pixelImageData) | |
{ | |
// build the dictionary | |
Dictionary<string, int> dictionary = new Dictionary<string, int>(); | |
for (int i = 0; i < 4; i++) | |
dictionary.Add(i.ToString(), i); | |
dictionary.Add(5.ToString(), 5); | |
dictionary.Add(6.ToString(), 6); | |
string w = pixelImageData[0].ToString();// string.Empty; | |
List<int> compressed = new List<int>(); | |
string returnMe = string.Empty; | |
double codeSize = 3.0; // this is in bits | |
foreach (int i in pixelImageData) | |
{ | |
string wc = w + i; | |
if (dictionary.ContainsKey(wc)) | |
{ | |
w = wc; | |
} | |
else | |
{ | |
// write w to output | |
compressed.Add(dictionary[w]); | |
returnMe = reverse(Convert.ToString(((byte)dictionary[w]), 2).PadLeft((int)codeSize, '0')) + returnMe; | |
if (Math.Pow(2.0, codeSize) - 1 == dictionary.Count) | |
{ | |
codeSize += 1.0; | |
} | |
// wc is a new sequence; add it to the dictionary | |
dictionary.Add(wc, dictionary.Count); | |
w = i.ToString(); | |
} | |
} | |
// write remaining output if necessary | |
if (!string.IsNullOrEmpty(w)) | |
compressed.Add(dictionary[w]); | |
return returnMe; //compressed; | |
} | |
static string reverse(string s) | |
{ | |
char[] charArray = s.ToCharArray(); | |
Array.Reverse(charArray); | |
return new string(charArray); | |
} | |
static byte[] GetBytes(string bitString) | |
{ | |
byte[] result = Enumerable.Range(0, bitString.Length / 8). | |
Select(pos => Convert.ToByte( | |
bitString.Substring(pos * 8, 8), | |
2) | |
).ToArray(); | |
List<byte> mahByteArray = new List<byte>(); | |
for (int i = result.Length - 1; i >= 0; i--) | |
{ | |
mahByteArray.Add(result[i]); | |
} | |
return mahByteArray.ToArray(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment