Skip to content

Instantly share code, notes, and snippets.

@greenboxal
Created February 13, 2013 04:20
Show Gist options
  • Save greenboxal/4942235 to your computer and use it in GitHub Desktop.
Save greenboxal/4942235 to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HuffmanTexture
{
public class HuffmanPixelTree
{
public sealed class Node : IListSource
{
public Vector4 Color;
public Node[] Childs;
public bool Leaf;
public Node(Vector4 color)
{
Color = color;
Childs = new Node[4];
Leaf = true;
}
IList IListSource.GetList()
{
return Childs;
}
bool IListSource.ContainsListCollection { get { return !Leaf; } }
}
private readonly Vector4[,] _originalPixels;
private readonly Bitmap _cache;
private bool _dirty;
public int Size { get; private set; }
private Node _mainNode;
public Node MainNode
{
get { return _mainNode; }
}
private Vector4 _nodeThreshold;
public Vector4 NodeThreshold
{
get { return _nodeThreshold; }
set
{
if (_nodeThreshold == value)
return;
_nodeThreshold = value;
Generate();
}
}
public event Action Generated;
public HuffmanPixelTree(Bitmap source)
{
if (source.Width != source.Height || source.Width % 4 != 0)
throw new NotSupportedException("Invalid image");
Size = source.Width;
_nodeThreshold = Vector4.Zero;
_cache = new Bitmap(Size, Size);
_originalPixels = new Vector4[Size, Size];
for (int x = 0; x < source.Width; x++)
for (int y = 0; y < source.Height; y++)
_originalPixels[x, y] = new Vector4(source.GetPixel(x, y));
Generate();
}
private void Generate()
{
_mainNode = new Node(Vector4.Zero);
Generate(_mainNode, 0, 0, Size);
_dirty = true;
if (Generated != null)
Generated();
}
private void Generate(Node father, int startX, int startY, int size)
{
int childSize = size / 2;
if (childSize == 1)
{
Vector4[] colors = new Vector4[4];
Vector4 average;
colors[0] = _originalPixels[startX, startY];
colors[1] = _originalPixels[startX + 1, startY];
colors[2] = _originalPixels[startX, startY + 1];
colors[3] = _originalPixels[startX + 1, startY + 1];
if (!CheckThreshold(colors, out average))
{
father.Childs[0] = new Node(colors[0]);
father.Childs[1] = new Node(colors[1]);
father.Childs[2] = new Node(colors[2]);
father.Childs[3] = new Node(colors[3]);
father.Leaf = false;
}
father.Color = average;
}
else
{
Node[] nodes = new Node[4];
Vector4 average;
nodes[0] = new Node(Vector4.Zero);
Generate(nodes[0], startX, startY, childSize);
nodes[1] = new Node(Vector4.Zero);
Generate(nodes[1], startX + childSize, startY, childSize);
nodes[2] = new Node(Vector4.Zero);
Generate(nodes[2], startX, startY + childSize, childSize);
nodes[3] = new Node(Vector4.Zero);
Generate(nodes[3], startX + childSize, startY + childSize, childSize);
// Check if we can compress
if (!CheckThreshold(new[] { nodes[0].Color, nodes[1].Color, nodes[2].Color, nodes[3].Color }, out average))
{
father.Childs = nodes;
father.Leaf = false;
}
father.Color = average;
}
}
public Bitmap Rebuild()
{
lock (_cache)
{
if (_dirty)
{
Rebuild(_mainNode, 0, 0, Size);
_dirty = true;
}
return new Bitmap(_cache); ;
}
}
private void Rebuild(Node node, int startX, int startY, int size)
{
int childSize = size / 2;
if (node == null)
return;
if (node.Leaf)
{
Color c = node.Color.ToColor();
for (int x = 0; x < size; x++)
for (int y = 0; y < size; y++)
_cache.SetPixel(startX + x, startY + y, c);
}
else
{
Rebuild(node.Childs[0], startX, startY, childSize);
Rebuild(node.Childs[1], startX + childSize, startY, childSize);
Rebuild(node.Childs[2], startX, startY + childSize, childSize);
Rebuild(node.Childs[3], startX + childSize, startY + childSize, childSize);
}
}
private bool CheckThreshold(Vector4[] colors, out Vector4 average)
{
/* int i;
Vector4 a = new Vector4();
Vector4 min = new Vector4(1);
Vector4 max = new Vector4(0);
for (i = 0; i < colors.Length; i++)
{
Vector4 c = colors[i];
Vector4.Min(ref min, ref c, out min);
Vector4.Max(ref max, ref c, out max);
a += c;
}
a /= i;
average = a;
Vector4 cmin = a - _nodeThreshold;
Vector4 cmax = a + _nodeThreshold;
return cmin >= min && cmax <= max;*/
int i;
Vector4 a = new Vector4();
for (i = 0; i < colors.Length; i++)
a += colors[i];
a /= i;
average = a;
return a == colors[0];
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment