public static void Crop(Glyph glyph) { // Crop the top. while ((glyph.Subrect.Height > 1) && BitmapUtils.IsAlphaEntirely(0, glyph.Bitmap, new Rectangle(glyph.Subrect.X, glyph.Subrect.Y, glyph.Subrect.Width, 1))) { glyph.Subrect.Y++; glyph.Subrect.Height--; glyph.YOffset++; } // Crop the bottom. while ((glyph.Subrect.Height > 1) && BitmapUtils.IsAlphaEntirely(0, glyph.Bitmap, new Rectangle(glyph.Subrect.X, glyph.Subrect.Bottom - 1, glyph.Subrect.Width, 1))) { glyph.Subrect.Height--; } // Crop the left. while ((glyph.Subrect.Width > 1) && BitmapUtils.IsAlphaEntirely(0, glyph.Bitmap, new Rectangle(glyph.Subrect.X, glyph.Subrect.Y, 1, glyph.Subrect.Height))) { glyph.Subrect.X++; glyph.Subrect.Width--; glyph.XOffset++; } // Crop the right. while ((glyph.Subrect.Width > 1) && BitmapUtils.IsAlphaEntirely(0, glyph.Bitmap, new Rectangle(glyph.Subrect.Right - 1, glyph.Subrect.Y, 1, glyph.Subrect.Height))) { glyph.Subrect.Width--; glyph.XAdvance++; } }
/// <summary> /// This method for loading/saving a font file generated from MakeSpriteFont. /// </summary> /// <param name="serializer">The binary serializer to use.</param> /// <returns></returns> private void SerializeMakeSpriteFont(BinarySerializer serializer) { FourCC magicCode2 = "font"; serializer.Serialize(ref magicCode2); if (magicCode2 != "font") return; // Writes the version if (serializer.Mode == SerializerMode.Read) { int version = serializer.Reader.ReadInt32(); if (version != Version) { throw new NotSupportedException(string.Format("SpriteFontData version [0x{0:X}] is not supported. Expecting [0x{1:X}]", version, Version)); } } else { serializer.Writer.Write(Version); } // Deserialize Glyphs int glyphCount = 0; serializer.Serialize(ref glyphCount); // For MakeSpriteFont, there is only one GlyphPage. Glyphs = new Glyph[glyphCount]; for (int i = 0; i < glyphCount; i++) { serializer.Serialize(ref Glyphs[i].Character); serializer.Serialize(ref Glyphs[i].Subrect); serializer.Serialize(ref Glyphs[i].Offset); serializer.Serialize(ref Glyphs[i].XAdvance); // Fix XAdvance with Right/Left for MakeSpriteFont Glyphs[i].XAdvance += Glyphs[i].Subrect.Right - Glyphs[i].Subrect.Left; } serializer.Serialize(ref LineSpacing); serializer.Serialize(ref DefaultCharacter); var image = new BitmapData(); Bitmaps = new Bitmap[1] {new Bitmap()}; Bitmaps[0].Data = image; serializer.Serialize(ref image.Width); serializer.Serialize(ref image.Height); serializer.SerializeEnum(ref image.PixelFormat); serializer.Serialize(ref image.RowStride); serializer.Serialize(ref image.CompressedHeight); serializer.Serialize(ref image.Data, image.RowStride * image.CompressedHeight); }
public static Bitmap ArrangeGlyphs(Glyph[] sourceGlyphs) { // Build up a list of all the glyphs needing to be arranged. List<ArrangedGlyph> glyphs = new List<ArrangedGlyph>(); for (int i = 0; i < sourceGlyphs.Length; i++) { ArrangedGlyph glyph = new ArrangedGlyph(); glyph.Source = sourceGlyphs[i]; // Leave a one pixel border around every glyph in the output bitmap. glyph.Width = sourceGlyphs[i].Subrect.Width + 2; glyph.Height = sourceGlyphs[i].Subrect.Height + 2; glyphs.Add(glyph); } // Sort so the largest glyphs get arranged first. glyphs.Sort(CompareGlyphSizes); // Work out how big the output bitmap should be. int outputWidth = GuessOutputWidth(sourceGlyphs); int outputHeight = 0; // Choose positions for each glyph, one at a time. for (int i = 0; i < glyphs.Count; i++) { PositionGlyph(glyphs, i, outputWidth); outputHeight = Math.Max(outputHeight, glyphs[i].Y + glyphs[i].Height); } // Create the merged output bitmap. outputHeight = MakeValidTextureSize(outputHeight, false); return CopyGlyphsToOutput(glyphs, outputWidth, outputHeight); }
/// <summary> /// This method for loading/saving a font file generated from MakeSpriteFont. /// </summary> /// <param name="serializer">The binaryserializer to use.</param> /// <returns></returns> private void SerializeMakeSpriteFont(BinarySerializer serializer) { FourCC magicCode2 = "font"; serializer.Serialize(ref magicCode2); if (magicCode2 != "font") return; // Deserialize Glyphs int glyphCount = 0; serializer.Serialize(ref glyphCount); // For MakeSpriteFont, there is only one GlyphPage. Glyphs = new Glyph[glyphCount]; for (int i = 0; i < glyphCount; i++) { serializer.Serialize(ref Glyphs[i].Character); serializer.Serialize(ref Glyphs[i].Subrect); serializer.Serialize(ref Glyphs[i].Offset); serializer.Serialize(ref Glyphs[i].XAdvance); // Fix XAdvance with Right/Left for MakeSpriteFont Glyphs[i].XAdvance += Glyphs[i].Subrect.Right - Glyphs[i].Subrect.Left; } serializer.Serialize(ref LineSpacing); serializer.Serialize(ref DefaultCharacter); var image = new BitmapData(); Bitmaps = new Bitmap[1] {new Bitmap()}; Bitmaps[0].Data = image; serializer.Serialize(ref image.Width); serializer.Serialize(ref image.Height); serializer.SerializeEnum(ref image.PixelFormat); serializer.Serialize(ref image.RowStride); serializer.Serialize(ref image.CompressedHeight); serializer.Serialize(ref image.Data, image.RowStride * image.CompressedHeight); }
// Heuristic guesses what might be a good output width for a list of glyphs. static int GuessOutputWidth(Glyph[] sourceGlyphs) { int maxWidth = 0; int totalSize = 0; foreach (Glyph glyph in sourceGlyphs) { maxWidth = Math.Max(maxWidth, glyph.Subrect.Width); totalSize += glyph.Subrect.Width * glyph.Subrect.Height; } int width = Math.Max((int)Math.Sqrt(totalSize), maxWidth); return MakeValidTextureSize(width, true); }
public static void WriteSpriteFont(FontDescription options, string outputFilename, Glyph[] glyphs, float lineSpacing, Bitmap bitmap) { using (var stream = new NativeFileStream(outputFilename, NativeFileMode.Create, NativeFileAccess.Write)) { WriteSpriteFont(options, stream, glyphs, lineSpacing, bitmap); } }
static void WriteGlyphs(BinaryWriter writer, Glyph[] glyphs) { writer.Write(glyphs.Length); foreach (Glyph glyph in glyphs) { writer.Write((int)glyph.Character); writer.Write(glyph.Subrect.Left); writer.Write(glyph.Subrect.Top); writer.Write(glyph.Subrect.Right); writer.Write(glyph.Subrect.Bottom); writer.Write(glyph.XOffset); writer.Write(glyph.YOffset); writer.Write(glyph.XAdvance); } }
public static void WriteSpriteFont(FontDescription options, Stream outputStream, Glyph[] glyphs, float lineSpacing, Bitmap bitmap) { var writer = new BinaryWriter(outputStream); WriteMagic(writer); writer.Write(SpriteFontData.Version); WriteGlyphs(writer, glyphs); writer.Write(lineSpacing); writer.Write(options.DefaultCharacter); WriteBitmap(writer, options, bitmap); writer.Flush(); }
private Glyph ImportGlyph(Factory factory, FontFace fontFace, char character, FontMetrics fontMetrics, float fontSize, bool activateAntiAliasDetection) { var indices = fontFace.GetGlyphIndices(new int[] {character}); var metrics = fontFace.GetDesignGlyphMetrics(indices, false); var metric = metrics[0]; var width = (float)(metric.AdvanceWidth - metric.LeftSideBearing - metric.RightSideBearing) / fontMetrics.DesignUnitsPerEm * fontSize; var height = (float)(metric.AdvanceHeight - metric.TopSideBearing - metric.BottomSideBearing) / fontMetrics.DesignUnitsPerEm * fontSize; var xOffset = (float)metric.LeftSideBearing / fontMetrics.DesignUnitsPerEm * fontSize; var yOffset = (float)(metric.TopSideBearing - metric.VerticalOriginY) / fontMetrics.DesignUnitsPerEm * fontSize; var advanceWidth = (float)(metric.AdvanceWidth) / fontMetrics.DesignUnitsPerEm * fontSize; var advanceHeight = (float)(metric.AdvanceHeight) / fontMetrics.DesignUnitsPerEm * fontSize; var pixelWidth = (int)Math.Ceiling(width + 2); var pixelHeight = (int)Math.Ceiling(height + 2); Bitmap bitmap; if(char.IsWhiteSpace(character)) { bitmap = new Bitmap(1, 1, PixelFormat.Format32bppArgb); } else { var glyphRun = new GlyphRun() { FontFace = fontFace, Advances = new[] { (float)Math.Round(advanceWidth) }, FontSize = fontSize, BidiLevel = 0, Indices = indices, IsSideways = false, Offsets = new[] {new GlyphOffset()} }; var matrix = SharpDX.Matrix.Identity; matrix.M41 = -(float)Math.Floor(xOffset - 1); matrix.M42 = -(float)Math.Floor(yOffset - 1); RenderingMode renderingMode; if (activateAntiAliasDetection) { var rtParams = new RenderingParams(factory); renderingMode = fontFace.GetRecommendedRenderingMode(fontSize, 1.0f, MeasuringMode.Natural, rtParams); rtParams.Dispose(); } else { renderingMode = RenderingMode.Aliased; } using(var runAnalysis = new GlyphRunAnalysis(factory, glyphRun, 1.0f, matrix, renderingMode, MeasuringMode.Natural, 0.0f, 0.0f)) { var bounds = new SharpDX.Rectangle(0, 0, pixelWidth, pixelHeight); bitmap = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb); if(renderingMode == RenderingMode.Aliased) { var texture = new byte[bounds.Width * bounds.Height]; runAnalysis.CreateAlphaTexture(TextureType.Aliased1x1, bounds, texture, texture.Length); bitmap = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb); for (int y = 0; y < bounds.Height; y++) { for (int x = 0; x < bounds.Width; x++) { int pixelX = y * bounds.Width + x; var grey = texture[pixelX]; var color = Color.FromArgb(grey, grey, grey); bitmap.SetPixel(x, y, color); } } } else { var texture = new byte[bounds.Width * bounds.Height * 3]; runAnalysis.CreateAlphaTexture(TextureType.Cleartype3x1, bounds, texture, texture.Length); for (int y = 0; y < bounds.Height; y++) { for (int x = 0; x < bounds.Width; x++) { int pixelX = (y * bounds.Width + x) * 3; var red = texture[pixelX]; var green = texture[pixelX + 1]; var blue = texture[pixelX + 2]; var color = Color.FromArgb(red, green, blue); bitmap.SetPixel(x, y, color); } } } //var positionUnderline = (float)fontMetrics.UnderlinePosition / fontMetrics.DesignUnitsPerEm * fontSize; //var positionUnderlineSize = (float)fontMetrics.UnderlineThickness / fontMetrics.DesignUnitsPerEm * fontSize; } } var glyph = new Glyph(character, bitmap) { XOffset = (float)Math.Floor(xOffset-1), XAdvance = (float)Math.Round(advanceWidth), YOffset = (float)Math.Floor(yOffset-1), }; return glyph; }
/// <summary> /// This method for loading/saving a font file generated from AngelCode BMFont. /// </summary> /// <param name="serializer">The binaryserializer to use.</param> private void SerializeBMFFont(BinarySerializer serializer) { // ---------------------------------------------------------- // Read block Info (1) // ---------------------------------------------------------- byte blockType = 1; serializer.Serialize(ref blockType); if (blockType != 1) return; // Skip Info block int expectedBlockSize = 0; serializer.Serialize(ref expectedBlockSize); serializer.Stream.Seek(expectedBlockSize, SeekOrigin.Current); // ---------------------------------------------------------- // Read block Common (2) // ---------------------------------------------------------- serializer.Serialize(ref blockType); if (blockType != 2) return; var common = new BMFCommon(); common.Serialize(serializer); // Copy the base offset. BaseOffset = common.Base; LineSpacing = common.LineHeight; // ---------------------------------------------------------- // Read block page names (3) // ---------------------------------------------------------- serializer.Serialize(ref blockType); if (blockType != 3) return; serializer.Serialize(ref expectedBlockSize); // Create bitmap array. Bitmaps = new Bitmap[common.PageCount]; for (int i = 0; i < Bitmaps.Length; i++) { string name = null; serializer.Serialize(ref name, true); // Store the name in data Bitmaps[i] = new Bitmap { Data = name }; } // ---------------------------------------------------------- // Read block glyphs (4) // ---------------------------------------------------------- serializer.Serialize(ref blockType); if (blockType != 4) return; serializer.Serialize(ref expectedBlockSize); int countChars = expectedBlockSize/20; var bmfGlyph = new BMFGlyph(); Glyphs = new Glyph[countChars]; for (int i = 0; i < Glyphs.Length; i++) { bmfGlyph.Serialize(serializer); Glyphs[i] = new Glyph { Character = bmfGlyph.Id, Subrect = new Rectangle(bmfGlyph.X, bmfGlyph.Y, bmfGlyph.Width, bmfGlyph.Height), Offset = {X = bmfGlyph.OffsetX, Y = bmfGlyph.OffsetY}, XAdvance = bmfGlyph.AdvanceX, BitmapIndex = bmfGlyph.PageIndex }; } // ---------------------------------------------------------- // Read block kernings (5) optional // ---------------------------------------------------------- if (serializer.Stream.Position < serializer.Stream.Length) { // If there is still some data to read, there is probably some kernings serializer.Serialize(ref blockType); if (blockType != 5) return; serializer.Serialize(ref expectedBlockSize); int kernelCount = expectedBlockSize/10; Kernings = new Kerning[kernelCount]; for (int i = 0; i < Kernings.Length; i++) { serializer.Serialize(ref Kernings[i].First); serializer.Serialize(ref Kernings[i].Second); short offset = 0; serializer.Serialize(ref offset); Kernings[i].Offset = offset; } } }
public static void WriteSpriteFont(CommandLineOptions options, Glyph[] glyphs, float lineSpacing, Bitmap bitmap) { using (FileStream file = File.OpenWrite(options.OutputFile)) using (BinaryWriter writer = new BinaryWriter(file)) { WriteMagic(writer); WriteGlyphs(writer, glyphs); writer.Write(lineSpacing); writer.Write(options.DefaultCharacter); WriteBitmap(writer, options, bitmap); } }
private Glyph ImportGlyph(Factory factory, FontFace fontFace, char character, FontMetrics fontMetrics, float fontSize, FontAntiAliasMode antiAliasMode) { var indices = fontFace.GetGlyphIndices(new int[] { character }); var metrics = fontFace.GetDesignGlyphMetrics(indices, false); var metric = metrics[0]; var width = (float)(metric.AdvanceWidth - metric.LeftSideBearing - metric.RightSideBearing) / fontMetrics.DesignUnitsPerEm * fontSize; var height = (float)(metric.AdvanceHeight - metric.TopSideBearing - metric.BottomSideBearing) / fontMetrics.DesignUnitsPerEm * fontSize; var xOffset = (float)metric.LeftSideBearing / fontMetrics.DesignUnitsPerEm * fontSize; var yOffset = (float)(metric.TopSideBearing - metric.VerticalOriginY) / fontMetrics.DesignUnitsPerEm * fontSize; var advanceWidth = (float)metric.AdvanceWidth / fontMetrics.DesignUnitsPerEm * fontSize; var advanceHeight = (float)metric.AdvanceHeight / fontMetrics.DesignUnitsPerEm * fontSize; var pixelWidth = (int)Math.Ceiling(width + 4); var pixelHeight = (int)Math.Ceiling(height + 4); var matrix = Matrix.Identity; matrix.M41 = -(float)Math.Floor(xOffset) + 1; matrix.M42 = -(float)Math.Floor(yOffset) + 1; Bitmap bitmap; if (char.IsWhiteSpace(character)) { bitmap = new Bitmap(1, 1, PixelFormat.Format32bppArgb); } else { var glyphRun = new GlyphRun() { FontFace = fontFace, Advances = new[] { (float)Math.Ceiling(advanceWidth) }, FontSize = fontSize, BidiLevel = 0, Indices = indices, IsSideways = false, Offsets = new[] { new GlyphOffset() } }; RenderingMode renderingMode; if (antiAliasMode != FontAntiAliasMode.Aliased) { var rtParams = new RenderingParams(factory); renderingMode = fontFace.GetRecommendedRenderingMode(fontSize, 1.0f, MeasuringMode.Natural, rtParams); rtParams.Dispose(); } else { renderingMode = RenderingMode.Aliased; } using (var runAnalysis = new GlyphRunAnalysis(factory, glyphRun, 1.0f, (Matrix3x2)matrix, renderingMode, MeasuringMode.Natural, 0.0f, 0.0f)) { var bounds = new SharpDX.Rectangle(0, 0, pixelWidth, pixelHeight); bitmap = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb); if (renderingMode == RenderingMode.Aliased) { var texture = new byte[bounds.Width * bounds.Height]; runAnalysis.CreateAlphaTexture(TextureType.Aliased1x1, bounds, texture, texture.Length); for (int y = 0; y < bounds.Height; y++) { for (int x = 0; x < bounds.Width; x++) { int pixelX = y * bounds.Width + x; var grey = texture[pixelX]; var color = Color.FromArgb(grey, grey, grey); bitmap.SetPixel(x, y, color); } } } else { var texture = new byte[bounds.Width * bounds.Height * 3]; runAnalysis.CreateAlphaTexture(TextureType.Cleartype3x1, bounds, texture, texture.Length); for (int y = 0; y < bounds.Height; y++) { for (int x = 0; x < bounds.Width; x++) { int pixelX = (y * bounds.Width + x) * 3; var red = LinearToGamma(texture[pixelX]); var green = LinearToGamma(texture[pixelX + 1]); var blue = LinearToGamma(texture[pixelX + 2]); var color = Color.FromArgb(red, green, blue); bitmap.SetPixel(x, y, color); } } } } } var glyph = new Glyph(character, bitmap) { XOffset = -matrix.M41, XAdvance = advanceWidth, YOffset = -matrix.M42, }; return(glyph); }
/// <summary> /// This method for loading/saving a font file generated from AngelCode BMFont. /// </summary> /// <param name="serializer">The binaryserializer to use.</param> private void SerializeBMFFont(BinarySerializer serializer) { // ---------------------------------------------------------- // Read block Info (1) // ---------------------------------------------------------- byte blockType = 1; serializer.Serialize(ref blockType); if (blockType != 1) { return; } // Skip Info block int expectedBlockSize = 0; serializer.Serialize(ref expectedBlockSize); serializer.Stream.Seek(expectedBlockSize, SeekOrigin.Current); // ---------------------------------------------------------- // Read block Common (2) // ---------------------------------------------------------- serializer.Serialize(ref blockType); if (blockType != 2) { return; } var common = new BMFCommon(); common.Serialize(serializer); // Copy the base offset. BaseOffset = common.Base; LineSpacing = common.LineHeight; // ---------------------------------------------------------- // Read block page names (3) // ---------------------------------------------------------- serializer.Serialize(ref blockType); if (blockType != 3) { return; } serializer.Serialize(ref expectedBlockSize); // Create bitmap array. Bitmaps = new Bitmap[common.PageCount]; for (int i = 0; i < Bitmaps.Length; i++) { string name = null; serializer.Serialize(ref name, true); // Store the name in data Bitmaps[i] = new Bitmap { Data = name }; } // ---------------------------------------------------------- // Read block glyphs (4) // ---------------------------------------------------------- serializer.Serialize(ref blockType); if (blockType != 4) { return; } serializer.Serialize(ref expectedBlockSize); int countChars = expectedBlockSize / 20; var bmfGlyph = new BMFGlyph(); Glyphs = new Glyph[countChars]; for (int i = 0; i < Glyphs.Length; i++) { bmfGlyph.Serialize(serializer); Glyphs[i] = new Glyph { Character = bmfGlyph.Id, Subrect = new Rectangle(bmfGlyph.X, bmfGlyph.Y, bmfGlyph.Width, bmfGlyph.Height), Offset = { X = bmfGlyph.OffsetX, Y = bmfGlyph.OffsetY }, XAdvance = bmfGlyph.AdvanceX, BitmapIndex = bmfGlyph.PageIndex }; } // ---------------------------------------------------------- // Read block kernings (5) optional // ---------------------------------------------------------- if (serializer.Stream.Position < serializer.Stream.Length) { // If there is still some data to read, there is probably some kernings serializer.Serialize(ref blockType); if (blockType != 5) { return; } serializer.Serialize(ref expectedBlockSize); int kernelCount = expectedBlockSize / 10; Kernings = new Kerning[kernelCount]; for (int i = 0; i < Kernings.Length; i++) { serializer.Serialize(ref Kernings[i].First); serializer.Serialize(ref Kernings[i].Second); short offset = 0; serializer.Serialize(ref offset); Kernings[i].Offset = offset; } } }