private Bitmap GetGlyphBitmap(Image fontImage, XfCharMap charMap, XfCharSizeInfo charSizeInfo) { // Destination points var destPoints = new[] { new PointF(charSizeInfo.offsetX, charSizeInfo.offsetY), new PointF(charSizeInfo.glyphWidth + charSizeInfo.offsetX, charSizeInfo.offsetY), new PointF(charSizeInfo.offsetX, charSizeInfo.glyphHeight + charSizeInfo.offsetY) }; // Source rectangle var srcRect = new RectangleF( charMap.imageInformation.imageOffsetX, charMap.imageInformation.imageOffsetY, charSizeInfo.glyphWidth, charSizeInfo.glyphHeight); // Color matrix var imageAttributes = new ImageAttributes(); imageAttributes.SetColorMatrix(_colorMatrices[charMap.imageInformation.colorChannel]); // Draw the glyph from the master texture var glyph = new Bitmap( Math.Max(1, Math.Max(charMap.charInformation.charWidth, charSizeInfo.glyphWidth + charSizeInfo.offsetX)), Math.Max(1, charSizeInfo.glyphHeight + charSizeInfo.offsetY)); var gfx = Graphics.FromImage(glyph); gfx.DrawImage(fontImage, destPoints, srcRect, GraphicsUnit.Pixel, imageAttributes); return(glyph); }
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); }