1 /*
2 Copyright (c) 2015 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license (the "Software") to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12 
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28 
29 // dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
30 module dimage.huffman;
31 
32 import dimage.memory;
33 import dimage.bitio;
34 
35 //import dlib.core.memory;
36 //import dlib.core.bitio;
37 
38 struct HuffmanTreeNode
39 {
40     HuffmanTreeNode* parent;
41     HuffmanTreeNode* left;
42     HuffmanTreeNode* right;
43     ubyte ch;
44     uint freq;
45     bool blank = true;
46 
47     this(
48         HuffmanTreeNode* leftNode,
49         HuffmanTreeNode* rightNode,
50         ubyte symbol,
51         uint frequency,
52         bool isBlank)
53     {
54         parent = null;
55         left = leftNode;
56         right = rightNode;
57 
58         if (left !is null)
59             left.parent = &this;
60         if (right !is null)
61             right.parent = &this;
62 
63         ch = symbol;
64         freq = frequency;
65         blank = isBlank;
66     }
67 
68     bool isLeaf()
69     {
70         return (left is null && right is null);
71     }
72 
73     void free()
74     {
75         if (left !is null)
76         {
77             left.free();
78             Delete(left);
79         }
80 
81         if (right !is null)
82         {
83             right.free();
84             Delete(right);
85         }
86     }
87 
88 /*
89     // TODO: implement this without GC
90 
91     void getCodes(ref string[ubyte] table, string code = "")
92     {
93         if (isLeaf())
94         {
95             table[ch] = code;
96         }
97         else
98         {
99             if (left !is null)
100                 left.getCodes(table, code ~ '0');
101             if (right !is null)
102                 right.getCodes(table, code ~ '1');
103         }
104     }
105 
106     void print(string indent = "")
107     {
108         writefln("%s<%s>%x", indent, freq, ch);
109         indent ~= " ";
110         if (left !is null)
111             left.print(indent);
112         if (right !is null)
113             right.print(indent);
114     }
115 */
116 }
117 
118 /*
119 // TODO: implement this without GC
120 
121 HuffmanTreeNode* buildHuffmanTree(ubyte[] data)
122 {
123     // Count frequencies
124     uint[ubyte] freqs;
125     foreach(s; data)
126     {
127         if (s in freqs)
128             freqs[s] += 1;
129         else
130             freqs[s] = 1;
131     }
132 
133     // Sort in descending order
134     ubyte[] symbols = freqs.keys;
135     sort!((a, b) => freqs[a] > freqs[b])(symbols);
136 
137     // Create node list
138     auto nodeList = new HuffmanTreeNode*[symbols.length];
139     foreach(i, s; symbols)
140         nodeList[i] = new HuffmanTreeNode(null, null, s, freqs[s], false);
141 
142     // Build tree
143     while (nodeList.length > 1)
144     {
145         // Pop two nodes with minimal frequencies
146         auto n1 = nodeList[$-1];
147         auto n2 = nodeList[$-2];
148         nodeList.popBack;
149         nodeList.popBack;
150 
151         // Insert a new parent node
152         uint fsum = n1.freq + n2.freq;
153         auto parent = new HuffmanTreeNode(n1, n2, 0, fsum, false);
154         nodeList ~= parent;
155         sort!((a, b) => a.freq > b.freq)(nodeList);
156     }
157 
158     auto root = nodeList[0];
159 
160     return root;
161 }
162 
163 void packHuffmanTree(HuffmanTreeNode* node, BitWriter* bw)
164 {
165     if (node.isLeaf)
166     {
167         bw.writeBit(true);
168         bw.writeByte(node.ch);
169     }
170     else
171     {
172         bw.writeBit(false);
173         packHuffmanTree(node.left, bw);
174         packHuffmanTree(node.right, bw);
175     }
176 }
177 
178 HuffmanTreeNode* unpackHuffmanTree(BitReader* br)
179 {
180     if (!br.end)
181     {
182         bool bit = br.readBit();
183         if (bit)
184         {
185             byte ch = br.readByte();
186             return new HuffmanTreeNode(null, null, ch, 0, false);
187         }
188         else
189         {
190             HuffmanTreeNode* left = unpackHuffmanTree(br);
191             HuffmanTreeNode* right = unpackHuffmanTree(br);
192             return new HuffmanTreeNode(left, right, 0, 0, false);
193         }
194     }
195     else return null;
196 }
197 
198 ubyte[] encodeHuffman(ubyte[] data, out HuffmanTreeNode* tree)
199 {
200     // Build Huffman tree
201     tree = buildHuffmanTree(data);
202 
203     // Generate binary codes
204     string[ubyte] huffTable;
205     tree.getCodes(huffTable);
206 
207     // Encode data
208     string bitStr;
209     foreach(s; data)
210         bitStr ~= huffTable[s];
211 
212     // Pack bits to byte array
213     uint octetsLen = 0;
214     ubyte lastBits = 0;
215     if (bitStr.length == 8)
216     {
217         octetsLen = 1;
218     }
219     else if (bitStr.length > 8)
220     {
221         octetsLen = cast(uint)bitStr.length / 8;
222         lastBits = cast(ubyte)(bitStr.length % 8);
223         if (lastBits != 0)
224             octetsLen++;
225     }
226     else
227     {
228         octetsLen = 1;
229         lastBits = cast(ubyte)(bitStr.length);
230     }
231 
232     octetsLen++;
233     auto octets = new ubyte[octetsLen];
234     octets[0] = lastBits;
235 
236     uint bitPos = 0;
237     uint bytePos = 1;
238 
239     foreach(bit; bitStr)
240     {
241         bool state;
242         if (bit == '0')
243             state = false;
244         else
245             state = true;
246 
247         octets[bytePos] = setBit(octets[bytePos], bitPos, state);
248         bitPos++;
249 
250         if (bitPos == 8)
251         {
252             bitPos = 0;
253             bytePos++;
254         }
255     }
256 
257     return octets;
258 }
259 
260 ubyte[] decodeHuffman(ubyte[] data, HuffmanTreeNode* tree)
261 {
262     // Generate binary codes
263     string[ubyte] huffTable;
264     tree.getCodes(huffTable);
265 
266     //Unpack bits from array
267     ubyte[] result;
268     bool appendNext = true;
269     string code = "";
270     ubyte lastBits = data[0];
271     foreach(i, b; data[1..$])
272     {
273         uint len;
274         if ((lastBits != 0) && (i == data.length-1))
275             len = lastBits;
276         else
277             len = 8;
278 
279         foreach(bp; 0..len)
280         {
281             char bitChr = getBit(b, bp)? '1':'0';
282             if (appendNext)
283             {
284                 code ~= bitChr;
285                 foreach(key, val; huffTable)
286                 {
287                     if (code == val)
288                     {
289                         result ~= key;
290                         appendNext = false;
291                         break;
292                     }
293                 }
294             }
295             else
296             {
297                 code = "";
298                 code ~= bitChr;
299                 appendNext = true;
300             }
301         }
302     }
303 
304     return result;
305 }
306 
307 */
308 
309