Skip to content

Instantly share code, notes, and snippets.

@reZach
Created November 26, 2018 03:20
Show Gist options
  • Save reZach/28247e4616c2596fc282b6167740e3f8 to your computer and use it in GitHub Desktop.
Save reZach/28247e4616c2596fc282b6167740e3f8 to your computer and use it in GitHub Desktop.
C# .gif lzw
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