/// <summary> /// Small function that takes a Huffman tree & a TKMK00CommandWriter and writes out the tree construction information to the writer. /// </summary> /// <param name="node"></param> /// <param name="writer"></param> private static void WriteHuffmanTree(TKMK00HuffmanTreeNode node, TKMK00CommandWriter writer) { if (!node.IsEndNode) { writer.Write(1, 1); WriteHuffmanTree(node.Left, writer); WriteHuffmanTree(node.Right, writer); } else { writer.Write(0, 1); writer.Write(node.Value, 5); } }
/// <summary> /// Creates a Huffman-style binary tree that holds the 5-bit color data referenced in the /// TKMK00 decoding process. /// </summary> private static TKMK00HuffmanTreeNode SetUpHuffmanTree(uint val, byte[] data, TKMK00CommandReader commandReader) { TKMK00HuffmanTreeNode newTree = new TKMK00HuffmanTreeNode(); int command = commandReader.ReadBits(1);//1 - Make new branches, 0 - End current branch if (command != 0) { newTree.Value = (int)val; //Not used, but can't hurt to number it val++; newTree.Left = SetUpHuffmanTree(val, data, commandReader); newTree.Right = SetUpHuffmanTree(val, data, commandReader); return newTree; } //Else, return a node with a value int value = 0; int bitCount = 5; do { command = commandReader.ReadBits(1); value = value * 2 + command; //basically bitshifts s0 to the left and adds v0, aka it's loading 5 bytes straight from the comandReader bitCount -= 1; } while (bitCount > 0); newTree.Value = value; return newTree; }
/// <summary> /// Uses the existing Huffman tree and the command that is being looked for, and saves the combination of bits used to /// reach that command in the Huffman tree into outCommands (the value) and outBits (# of bits used) /// </summary> private static void GetHuffmanTreeTraversalCommands(TKMK00HuffmanTreeNode node, int commandTotal, int bitCount, int[] outCommands, int[] outBits) { if (node.IsEndNode) { outCommands[node.Value] = commandTotal; outBits[node.Value] = bitCount; } else { bitCount++; commandTotal <<= 1; GetHuffmanTreeTraversalCommands(node.Left, commandTotal, bitCount, outCommands, outBits); commandTotal++; GetHuffmanTreeTraversalCommands(node.Right, commandTotal, bitCount, outCommands, outBits); } }
//Only called when creating new rgba value. Traverses the Huffman tree to return the correct value private static int RetrieveHuffmanTreeValue(byte[] data, TKMK00HuffmanTreeNode currentNode, TKMK00CommandReader commandReader) { while (!currentNode.IsEndNode)/*currentNode.Value >= 0x20*/ { int command = commandReader.ReadBits(1); // 0 - left, 1 - right if (command == 0) currentNode = currentNode.Left; else currentNode = currentNode.Right; } return currentNode.Value; }
/// <summary> /// Returns the differential code used to turn the predicted color to the actual color /// </summary> private static TKMK00HuffmanTreeNode ConstructTree(int[] colorCounts) { //Link to Huffman balancing document: https://www.siggraph.org/education/materials/HyperGraph/video/mpeg/mpegfaq/huffman_tutorial.html List<Tuple<int, TKMK00HuffmanTreeNode>> frequenciesAndValues = new List<Tuple<int, TKMK00HuffmanTreeNode>>(); for (int i = 0; i < colorCounts.Length; i++) { TKMK00HuffmanTreeNode node = new TKMK00HuffmanTreeNode(); node.Value = i; frequenciesAndValues.Add(new Tuple<int, TKMK00HuffmanTreeNode>(colorCounts[i], node)); } while (frequenciesAndValues.Count > 1) { frequenciesAndValues.Sort((s1, s2) => s2.Item1.CompareTo(s1.Item1)); //Merge two into a new Huffman tree (NOTE: LEFT IS LESS THAN RIGHT) TKMK00HuffmanTreeNode node = new TKMK00HuffmanTreeNode(); node.Left = frequenciesAndValues[frequenciesAndValues.Count - 1].Item2; node.Right = frequenciesAndValues[frequenciesAndValues.Count - 2].Item2; node.Value = 64; //Invalid value to denote a traversal node rather than an end node int newCount = frequenciesAndValues[frequenciesAndValues.Count - 1].Item1 + frequenciesAndValues[frequenciesAndValues.Count - 2].Item1; frequenciesAndValues.RemoveAt(frequenciesAndValues.Count - 1); frequenciesAndValues.RemoveAt(frequenciesAndValues.Count - 1); frequenciesAndValues.Add(new Tuple<int, TKMK00HuffmanTreeNode>(newCount, node)); } return frequenciesAndValues[0].Item2; }