mirror of https://github.com/buggins/dlangui.git
310 lines
7.2 KiB
D
310 lines
7.2 KiB
D
/*
|
|
Copyright (c) 2015 Timur Gafarov
|
|
|
|
Boost Software License - Version 1.0 - August 17th, 2003
|
|
|
|
Permission is hereby granted, free of charge, to any person or organization
|
|
obtaining a copy of the software and accompanying documentation covered by
|
|
this license (the "Software") to use, reproduce, display, distribute,
|
|
execute, and transmit the Software, and to prepare derivative works of the
|
|
Software, and to permit third-parties to whom the Software is furnished to
|
|
do so, all subject to the following:
|
|
|
|
The copyright notices in the Software and this entire statement, including
|
|
the above license grant, this restriction and the following disclaimer,
|
|
must be included in all copies of the Software, in whole or in part, and
|
|
all derivative works of the Software, unless such copies or derivative
|
|
works are solely in the form of machine-executable object code generated by
|
|
a source language processor.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
// dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
|
|
module dimage.huffman;
|
|
|
|
import dimage.memory;
|
|
import dimage.bitio;
|
|
|
|
//import dlib.core.memory;
|
|
//import dlib.core.bitio;
|
|
|
|
struct HuffmanTreeNode
|
|
{
|
|
HuffmanTreeNode* parent;
|
|
HuffmanTreeNode* left;
|
|
HuffmanTreeNode* right;
|
|
ubyte ch;
|
|
uint freq;
|
|
bool blank = true;
|
|
|
|
this(
|
|
HuffmanTreeNode* leftNode,
|
|
HuffmanTreeNode* rightNode,
|
|
ubyte symbol,
|
|
uint frequency,
|
|
bool isBlank)
|
|
{
|
|
parent = null;
|
|
left = leftNode;
|
|
right = rightNode;
|
|
|
|
if (left !is null)
|
|
left.parent = &this;
|
|
if (right !is null)
|
|
right.parent = &this;
|
|
|
|
ch = symbol;
|
|
freq = frequency;
|
|
blank = isBlank;
|
|
}
|
|
|
|
bool isLeaf()
|
|
{
|
|
return (left is null && right is null);
|
|
}
|
|
|
|
void free()
|
|
{
|
|
if (left !is null)
|
|
{
|
|
left.free();
|
|
Delete(left);
|
|
}
|
|
|
|
if (right !is null)
|
|
{
|
|
right.free();
|
|
Delete(right);
|
|
}
|
|
}
|
|
|
|
/*
|
|
// TODO: implement this without GC
|
|
|
|
void getCodes(ref string[ubyte] table, string code = "")
|
|
{
|
|
if (isLeaf())
|
|
{
|
|
table[ch] = code;
|
|
}
|
|
else
|
|
{
|
|
if (left !is null)
|
|
left.getCodes(table, code ~ '0');
|
|
if (right !is null)
|
|
right.getCodes(table, code ~ '1');
|
|
}
|
|
}
|
|
|
|
void print(string indent = "")
|
|
{
|
|
writefln("%s<%s>%x", indent, freq, ch);
|
|
indent ~= " ";
|
|
if (left !is null)
|
|
left.print(indent);
|
|
if (right !is null)
|
|
right.print(indent);
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
// TODO: implement this without GC
|
|
|
|
HuffmanTreeNode* buildHuffmanTree(ubyte[] data)
|
|
{
|
|
// Count frequencies
|
|
uint[ubyte] freqs;
|
|
foreach(s; data)
|
|
{
|
|
if (s in freqs)
|
|
freqs[s] += 1;
|
|
else
|
|
freqs[s] = 1;
|
|
}
|
|
|
|
// Sort in descending order
|
|
ubyte[] symbols = freqs.keys;
|
|
sort!((a, b) => freqs[a] > freqs[b])(symbols);
|
|
|
|
// Create node list
|
|
auto nodeList = new HuffmanTreeNode*[symbols.length];
|
|
foreach(i, s; symbols)
|
|
nodeList[i] = new HuffmanTreeNode(null, null, s, freqs[s], false);
|
|
|
|
// Build tree
|
|
while (nodeList.length > 1)
|
|
{
|
|
// Pop two nodes with minimal frequencies
|
|
auto n1 = nodeList[$-1];
|
|
auto n2 = nodeList[$-2];
|
|
nodeList.popBack;
|
|
nodeList.popBack;
|
|
|
|
// Insert a new parent node
|
|
uint fsum = n1.freq + n2.freq;
|
|
auto parent = new HuffmanTreeNode(n1, n2, 0, fsum, false);
|
|
nodeList ~= parent;
|
|
sort!((a, b) => a.freq > b.freq)(nodeList);
|
|
}
|
|
|
|
auto root = nodeList[0];
|
|
|
|
return root;
|
|
}
|
|
|
|
void packHuffmanTree(HuffmanTreeNode* node, BitWriter* bw)
|
|
{
|
|
if (node.isLeaf)
|
|
{
|
|
bw.writeBit(true);
|
|
bw.writeByte(node.ch);
|
|
}
|
|
else
|
|
{
|
|
bw.writeBit(false);
|
|
packHuffmanTree(node.left, bw);
|
|
packHuffmanTree(node.right, bw);
|
|
}
|
|
}
|
|
|
|
HuffmanTreeNode* unpackHuffmanTree(BitReader* br)
|
|
{
|
|
if (!br.end)
|
|
{
|
|
bool bit = br.readBit();
|
|
if (bit)
|
|
{
|
|
byte ch = br.readByte();
|
|
return new HuffmanTreeNode(null, null, ch, 0, false);
|
|
}
|
|
else
|
|
{
|
|
HuffmanTreeNode* left = unpackHuffmanTree(br);
|
|
HuffmanTreeNode* right = unpackHuffmanTree(br);
|
|
return new HuffmanTreeNode(left, right, 0, 0, false);
|
|
}
|
|
}
|
|
else return null;
|
|
}
|
|
|
|
ubyte[] encodeHuffman(ubyte[] data, out HuffmanTreeNode* tree)
|
|
{
|
|
// Build Huffman tree
|
|
tree = buildHuffmanTree(data);
|
|
|
|
// Generate binary codes
|
|
string[ubyte] huffTable;
|
|
tree.getCodes(huffTable);
|
|
|
|
// Encode data
|
|
string bitStr;
|
|
foreach(s; data)
|
|
bitStr ~= huffTable[s];
|
|
|
|
// Pack bits to byte array
|
|
uint octetsLen = 0;
|
|
ubyte lastBits = 0;
|
|
if (bitStr.length == 8)
|
|
{
|
|
octetsLen = 1;
|
|
}
|
|
else if (bitStr.length > 8)
|
|
{
|
|
octetsLen = cast(uint)bitStr.length / 8;
|
|
lastBits = cast(ubyte)(bitStr.length % 8);
|
|
if (lastBits != 0)
|
|
octetsLen++;
|
|
}
|
|
else
|
|
{
|
|
octetsLen = 1;
|
|
lastBits = cast(ubyte)(bitStr.length);
|
|
}
|
|
|
|
octetsLen++;
|
|
auto octets = new ubyte[octetsLen];
|
|
octets[0] = lastBits;
|
|
|
|
uint bitPos = 0;
|
|
uint bytePos = 1;
|
|
|
|
foreach(bit; bitStr)
|
|
{
|
|
bool state;
|
|
if (bit == '0')
|
|
state = false;
|
|
else
|
|
state = true;
|
|
|
|
octets[bytePos] = setBit(octets[bytePos], bitPos, state);
|
|
bitPos++;
|
|
|
|
if (bitPos == 8)
|
|
{
|
|
bitPos = 0;
|
|
bytePos++;
|
|
}
|
|
}
|
|
|
|
return octets;
|
|
}
|
|
|
|
ubyte[] decodeHuffman(ubyte[] data, HuffmanTreeNode* tree)
|
|
{
|
|
// Generate binary codes
|
|
string[ubyte] huffTable;
|
|
tree.getCodes(huffTable);
|
|
|
|
//Unpack bits from array
|
|
ubyte[] result;
|
|
bool appendNext = true;
|
|
string code = "";
|
|
ubyte lastBits = data[0];
|
|
foreach(i, b; data[1..$])
|
|
{
|
|
uint len;
|
|
if ((lastBits != 0) && (i == data.length-1))
|
|
len = lastBits;
|
|
else
|
|
len = 8;
|
|
|
|
foreach(bp; 0..len)
|
|
{
|
|
char bitChr = getBit(b, bp)? '1':'0';
|
|
if (appendNext)
|
|
{
|
|
code ~= bitChr;
|
|
foreach(key, val; huffTable)
|
|
{
|
|
if (code == val)
|
|
{
|
|
result ~= key;
|
|
appendNext = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
code = "";
|
|
code ~= bitChr;
|
|
appendNext = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
*/
|
|
|
|
|