예제 #1
0
        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());
        }
예제 #2
0
        /// <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));
        }
예제 #3
0
 public static int WriteTree(HuffmanNode tree, DataStream stream)
 {
     throw new NotImplementedException();
 }