public void Pack_AdjustedGlyphs_All() { // Arrange var glyphs = new List <Bitmap>(); glyphs.Add(new Bitmap(5, 5)); glyphs[0].SetPixel(2, 1, Color.White); glyphs[0].SetPixel(2, 2, Color.White); glyphs[0].SetPixel(2, 3, Color.White); glyphs[0].SetPixel(1, 2, Color.White); glyphs[0].SetPixel(3, 2, Color.White); glyphs.Add(new Bitmap(5, 5)); glyphs[1].SetPixel(2, 2, Color.White); glyphs[1].SetPixel(2, 1, Color.White); glyphs[1].SetPixel(3, 2, Color.White); glyphs.Add(new Bitmap(2, 2)); glyphs[2].SetPixel(0, 0, Color.White); glyphs[2].SetPixel(1, 0, Color.White); var adjustedGlyphs = FontMeasurement.MeasureWhiteSpace(glyphs); var binPacker = new BinPacker(new Size(5, 3)); // Act var boxes = binPacker.Pack(adjustedGlyphs).ToArray(); // Assert boxes.Length.Should().Be(3); boxes[0].position.Should().Be(Point.Empty); boxes[1].position.Should().Be(new Point(3, 0)); boxes[2].position.Should().Be(new Point(3, 2)); }
public void Pack_AdjustedGlyphs_Space() { // Arrange var glyphs = new List <Bitmap>(); glyphs.Add(new Bitmap(5, 5)); var adjustedGlyphs = FontMeasurement.MeasureWhiteSpace(glyphs); var binPacker = new BinPacker(new Size(6, 6)); // Act var boxes = binPacker.Pack(adjustedGlyphs).ToArray(); // Assert boxes.Length.Should().Be(1); boxes[0].position.Should().Be(new Point(1, 1)); }
public void Measure_WhiteSpace_Works() { // Arrange var glyphs = new List <Bitmap>(); glyphs.Add(new Bitmap(5, 5)); glyphs[0].SetPixel(2, 2, Color.White); glyphs.Add(new Bitmap(5, 5)); glyphs[1].SetPixel(2, 2, Color.White); glyphs[1].SetPixel(2, 1, Color.White); // Act var adjustedGlyphs = FontMeasurement.MeasureWhiteSpace(glyphs).ToArray(); // Assert adjustedGlyphs.Length.Should().Be(2); adjustedGlyphs[0].WhiteSpaceAdjustment.GlyphSize.Should().Be(new Size(1, 1)); adjustedGlyphs[1].WhiteSpaceAdjustment.GlyphSize.Should().Be(new Size(1, 2)); adjustedGlyphs[0].WhiteSpaceAdjustment.GlyphPosition.Should().Be(new Point(2, 2)); adjustedGlyphs[1].WhiteSpaceAdjustment.GlyphPosition.Should().Be(new Point(2, 1)); }
public (Stream fontStream, Image fontImage) Save(List <CharacterInfo> characterInfos, Size imageSize) { // Generating font textures var adjustedGlyphs = FontMeasurement.MeasureWhiteSpace(characterInfos.Select(x => (Bitmap)x.Glyph)).ToList(); // Adjust image size for at least the biggest letter var height = Math.Max(adjustedGlyphs.Max(x => x.WhiteSpaceAdjustment.GlyphSize.Height), imageSize.Height); var width = Math.Max(adjustedGlyphs.Max(x => x.WhiteSpaceAdjustment.GlyphSize.Width), imageSize.Width); imageSize = new Size(width, height); var generator = new FontTextureGenerator(imageSize, 0); var textureInfos = generator.GenerateFontTextures(adjustedGlyphs, 3).ToList(); // Join important lists var joinedCharacters = characterInfos.OrderBy(x => x.CodePoint).Join(adjustedGlyphs, c => c.Glyph, ag => ag.Glyph, (c, ag) => new { character = c, adjustedGlyph = ag }) .Select(cag => new { cag.character, cag.adjustedGlyph, textureIndex = textureInfos.FindIndex(x => x.Glyphs.Any(y => y.Item1 == cag.adjustedGlyph.Glyph)), texturePosition = textureInfos.SelectMany(x => x.Glyphs).FirstOrDefault(x => x.Item1 == cag.adjustedGlyph.Glyph).Item2 }); // Create character information var charMaps = new List <(AdjustedGlyph, XfCharMap)>(adjustedGlyphs.Count); var charSizeInfos = new List <XfCharSizeInfo>(); foreach (var joinedCharacter in joinedCharacters) { if (joinedCharacter.textureIndex == -1) { continue; } var charSizeInfo = new XfCharSizeInfo { offsetX = (sbyte)joinedCharacter.adjustedGlyph.WhiteSpaceAdjustment.GlyphPosition.X, offsetY = (sbyte)joinedCharacter.adjustedGlyph.WhiteSpaceAdjustment.GlyphPosition.Y, glyphWidth = (byte)joinedCharacter.adjustedGlyph.WhiteSpaceAdjustment.GlyphSize.Width, glyphHeight = (byte)joinedCharacter.adjustedGlyph.WhiteSpaceAdjustment.GlyphSize.Height }; if (!charSizeInfos.Contains(charSizeInfo)) { charSizeInfos.Add(charSizeInfo); } // Only used for Time Travelers var codePoint = ConvertChar(joinedCharacter.character.CodePoint); //var codePoint = joinedCharacter.character.CodePoint; var charInformation = new XfCharInformation { charSizeInfoIndex = charSizeInfos.IndexOf(charSizeInfo), charWidth = char.IsWhiteSpace((char)codePoint) ? joinedCharacter.character.CharacterSize.Width : joinedCharacter.character.CharacterSize.Width - charSizeInfo.offsetX }; charMaps.Add((joinedCharacter.adjustedGlyph, new XfCharMap { codePoint = (ushort)codePoint, charInformation = charInformation, imageInformation = new XfImageInformation { colorChannel = joinedCharacter.textureIndex, imageOffsetX = joinedCharacter.texturePosition.X, imageOffsetY = joinedCharacter.texturePosition.Y } })); if (codePoint != joinedCharacter.character.CodePoint) { charInformation = new XfCharInformation { charSizeInfoIndex = charSizeInfos.IndexOf(charSizeInfo), charWidth = char.IsWhiteSpace((char)joinedCharacter.character.CodePoint) ? joinedCharacter.character.CharacterSize.Width : joinedCharacter.character.CharacterSize.Width - charSizeInfo.offsetX }; charMaps.Add((joinedCharacter.adjustedGlyph, new XfCharMap { codePoint = (ushort)joinedCharacter.character.CodePoint, charInformation = charInformation, imageInformation = new XfImageInformation { colorChannel = joinedCharacter.textureIndex, imageOffsetX = joinedCharacter.texturePosition.X, imageOffsetY = joinedCharacter.texturePosition.Y } })); } } // Set escape characters var escapeIndex = charMaps.FindIndex(x => x.Item2.codePoint == '?'); Header.largeEscapeCharacter = escapeIndex < 0 ? (short)0 : (short)escapeIndex; Header.smallEscapeCharacter = 0; // Minimize top value and line height Header.largeCharHeight = (short)charSizeInfos.Max(x => x.glyphHeight + x.offsetY); Header.smallCharHeight = 0; // Draw textures var img = new Bitmap(imageSize.Width, imageSize.Height); var gfx = Graphics.FromImage(img); for (var i = 0; i < textureInfos.Count; i++) { var destPoints = new[] { new PointF(0, 0), new PointF(textureInfos[i].FontTexture.Width, 0), new PointF(0, textureInfos[i].FontTexture.Height) }; var rect = new RectangleF(0, 0, textureInfos[i].FontTexture.Width, textureInfos[i].FontTexture.Height); var attr = new ImageAttributes(); attr.SetColorMatrix(_inverseColorMatrices[i]); gfx.DrawImage(textureInfos[i].FontTexture, destPoints, rect, GraphicsUnit.Pixel, attr); } // Save fnt.bin var savedFntBin = new MemoryStream(); using (var bw = new BinaryWriterX(savedFntBin, true)) { //Table0 bw.BaseStream.Position = 0x28; Header.charSizeCount = (short)charSizeInfos.Count; WriteMultipleCompressed(bw, charSizeInfos, _t0Comp); bw.WriteAlignment(4); //Table1 Header.largeCharOffset = (short)(bw.BaseStream.Position >> 2); Header.largeCharCount = (short)charMaps.Count; WriteMultipleCompressed(bw, charMaps.OrderBy(x => x.Item2.codePoint).Select(x => x.Item2).ToArray(), _t1Comp); bw.WriteAlignment(4); //Table2 Header.smallCharOffset = (short)(bw.BaseStream.Position >> 2); Header.smallCharCount = 0; WriteMultipleCompressed(bw, Array.Empty <XfCharMap>(), _t2Comp); bw.WriteAlignment(4); //Header bw.BaseStream.Position = 0; bw.WriteType(Header); } return(savedFntBin, img); }