If you do a search for PackBits and C#, you don’t find much. I found an Objective C implementation, a C implementation, the Apple Guidelines from 1996, etc. but no solid examples in C#. For my implementation, I had to stick to the basic rules (no bigger than 128 byte chunks, in particular) for the print controller that I’m sending data to.
Other compression mechanisms will not work for what I need, since the marker only understands PackBits and Monochrome Bitmaps.
For what I needed, compressed byte arrays work great, and given the nature of the data I’m printing, there are a lot of repeating bits, which yields very good compression (about 90%!).
At anyrate, here’s what I’m using. Yeah, you could probably clean it up a bit more, but it’s good enough. Enjoy.
// -----------------------------------------------------------------------
//
// Copyright (c) VendRx, Inc. 2015
//
//
// Released under the apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0.html
//
// ---------------------------------------------------------------------
namespace VendRx.Common.Compression
{
using System;
using System.Collections.Generic;
using System.Linq;
///
/// Class PackBitsCompressor.
///
public class PackBitsCompressor : IPackBitsCompressor
{
///
/// Packs the specified byte array using the packbits algorithm.
///
/// The source byte[] to pack.
/// A byte[] array that has been compressed.
public byte[] Pack(byte[] source)
{
IList result = new List(source.Length);
const int MaxLength = 127;
IList literals = new List(MaxLength);
for (int i = 0; i < source.Length; i++)
{
byte current = source[i];
if (i + 1 != source.Length)
{
byte next = source[i + 1];
if (next == current)
{
this.AddLiterals(result, literals);
int max = i + MaxLength >= source.Length ? source.Length - i - 1 : MaxLength;
bool hitMax = true;
byte runLength = 1;
for (int j = 2; j <= max; j++)
{
byte run = source[i + j];
if (run != current)
{
hitMax = false;
byte count = (byte)(0 - runLength);
i = i + j - 1;
result.Add(count);
result.Add(current);
break;
}
runLength++;
}
if (hitMax)
{
result.Add((byte)(sbyte)(0 - max));
result.Add(current);
i = i + max;
}
}
else
{
literals.Add(current);
if (literals.Count == MaxLength)
{
this.AddLiterals(result, literals);
}
}
}
else
{
literals.Add(current);
this.AddLiterals(result, literals);
}
}
return result.ToArray();
}
///
/// Unpacks the source byte array
///
/// The source byte array to unpack.
/// Unpacks the source array.
public byte[] UnPack(byte[] source)
{
IList result = new List(source.Length * 2);
for (int i = 0; i < source.Length; i++)
{
sbyte count = (sbyte)source[i];
if (count == -128)
{
// do nothing--skip it.
}
else if (count >= 0)
{
int total = count + 1;
for (int j = 0; j < total; j++)
{
result.Add(source[i + j + 1]);
}
i = i + total;
}
else
{
int total = Math.Abs(count) + 1;
for (int j = 0; j < total; j++)
{
result.Add(source[i + 1]);
}
i = i + 1;
}
}
return result.ToArray();
}
///
/// Adds the literal run to the result
///
/// The result where the literals should be added.
/// The list of literals that will be appeneded to the result.
private void AddLiterals(IList result, IList literals)
{
if (literals.Count > 0)
{
result.Add((byte)(literals.Count - 1));
foreach (byte literal in literals)
{
result.Add(literal);
}
}
literals.Clear();
}
}
}
