public static HuffmanNode MakeTree(byte[] data) { // First get the frequencies and create the leafs var leafs = new Dictionary <byte, HuffmanNode>(); foreach (var d in data) { if (!leafs.ContainsKey(d)) { leafs.Add(d, new HuffmanNode(d)); } leafs[d].Frequency++; } // Now create the tree var nodes = new SortedSet <HuffmanNode>(leafs.Values, new FrequencyComparer()); while (nodes.Count > 1) { // Take the two elements with lower frequency var lowest1 = nodes.Last(); nodes.Remove(lowest1); var lowest2 = nodes.Last(); nodes.Remove(lowest2); // Create a new node that contains both of them var parent = new HuffmanNode(0); parent.Frequency = lowest1.Frequency + lowest2.Frequency; parent.LeftLeaf = lowest1; parent.RightLeaf = lowest2; // Add the node to the list nodes.Add(parent); } return(nodes.First()); }
/// <summary> /// Convert the specified font into binary format.. /// </summary> /// <returns>Binary representation of the font.</returns> /// <param name="source">Font to convert.</param> public BinaryFormat Convert(GameFont source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } var stream = new DataStream(new MemoryStream(), 0, 0); var writer = new DataWriter(stream); int numGlyphs = source.Glyphs.Count; int pixGlyph = source.CharWidth * source.CharHeight; // Main header // Hard-coded: 4 bpp with flags 0x010101 writer.Write(0x20); writer.Write(0x00); // will set later writer.Write(0x010101); writer.Write((ushort)0x00); writer.Write((ushort)numGlyphs); writer.Write((ushort)source.CharWidth); writer.Write((ushort)source.CharHeight); writer.Write((ushort)(pixGlyph / 2)); writer.Write((ushort)0x01); stream.WriteTimes(0x00, 8); // Palette foreach (var color in source.GetPalette()) { writer.Write((byte)color.Red); writer.Write((byte)color.Green); writer.Write((byte)color.Blue); if (color.Red > 0 && color.Green > 0 && color.Blue > 0) { writer.Write((byte)0x80); } else { writer.Write((byte)0x00); } } // Variable Width Table (VWT) int vwtSize = numGlyphs * 2; writer.Write(vwtSize); foreach (var glyph in source.Glyphs) { writer.Write((byte)glyph.BearingX); writer.Write((byte)glyph.Advance); } // Reserved space stream.WriteTimes(0x00, 4 + (4 * numGlyphs)); // Glyphs long glyphSection = stream.RelativePosition; stream.WriteTimes(0x00, 0x20); // header // Flatten the glyph bytes var rawData = new byte[numGlyphs * pixGlyph / 2]; int rawDataIdx = 0; foreach (var glyph in source.Glyphs) { int[,] glyphImg = glyph.GetImage(); for (int h = 0; h < source.CharHeight; h++) { for (int w = 0; w < source.CharWidth; w++) { if (rawDataIdx % 2 == 0) { rawData[rawDataIdx / 2] = (byte)glyphImg[w, h]; } else { rawData[rawDataIdx / 2] |= (byte)(glyphImg[w, h] << 4); } rawDataIdx++; } } } // Get Huffman tree HuffmanNode huffmanTree = Huffman.MakeTree(rawData); int huffmanTreeSize = Huffman.WriteTree(huffmanTree, stream); var codewords = Huffman.GetCodewords(huffmanTree); // Write empty position table long positionTableSection = stream.RelativePosition; stream.WriteTimes(0x00, numGlyphs * 4L); var compressed = new DataStream(stream, stream.RelativePosition, 0); int compressedPos = 0; rawDataIdx = 0; for (int i = 0; i < numGlyphs; i++) { stream.Seek(positionTableSection + (i * 4L), SeekMode.Origin); writer.Write(compressedPos); Huffman.Compress(codewords, rawData, rawDataIdx, pixGlyph, compressed, ref compressedPos); rawDataIdx += pixGlyph; } // ..Header stream.Seek(glyphSection, SeekMode.Origin); writer.Write(0x20); writer.Write(huffmanTreeSize); writer.Write((uint)compressed.Length); writer.Write(compressedPos); writer.Write(pixGlyph / 2); writer.Write(numGlyphs + 1); writer.Write((numGlyphs + 1) * 4); writer.Write(rawData.Length); compressed.Dispose(); // Set the unknown field size stream.Seek(0x04, SeekMode.Origin); writer.Write((uint)(stream.Length - vwtSize - 7)); return(new BinaryFormat(stream)); }
public static int WriteTree(HuffmanNode tree, DataStream stream) { throw new NotImplementedException(); }