diff --git a/SpriteFontPlus/BMFontLoader.cs b/SpriteFontPlus/BMFontLoader.cs new file mode 100644 index 000000000..99373be7c --- /dev/null +++ b/SpriteFontPlus/BMFontLoader.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Cyotek.Drawing.BitmapFont; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace SpriteFontPlus +{ + public class BMFontLoader + { + public static SpriteFont LoadXml(string xml, Texture2D texture) + { + var data = new BitmapFont(); + data.LoadXml(xml); + +#if !XENKO + var glyphBounds = new List(); + var cropping = new List(); + var chars = new List(); + var kerning = new List(); + + var characters = data.Characters.Values.OrderBy(c => c.Char); + foreach (var character in characters) + { + var bounds = character.Bounds; + +// bounds.Offset(texture.Bounds.Location); + + glyphBounds.Add(bounds); + cropping.Add(new Rectangle(character.Offset.X, character.Offset.Y, bounds.Width, bounds.Height)); + + chars.Add(character.Char); + + kerning.Add(new Vector3(0, character.Bounds.Width, character.XAdvance - character.Bounds.Width)); + } + + var constructorInfo = typeof(SpriteFont).GetTypeInfo().DeclaredConstructors.First(); + var result = (SpriteFont) constructorInfo.Invoke(new object[] + { + texture, glyphBounds, cropping, + chars, data.LineHeight, 0, kerning, ' ' + }); + + return result; +#else + var textureRegion = textureRegionLoader(data.Pages[0].FileName); + + var glyphs = new List(); + foreach (var pair in data.Characters) + { + var character = pair.Value; + + var bounds = character.Bounds; + bounds.X += textureRegion.Bounds.X; + bounds.Y += textureRegion.Bounds.Y; + var glyph = new Glyph + { + Character = character.Char, + BitmapIndex = 0, + Offset = new Vector2(character.Offset.X, character.Offset.Y), + Subrect = bounds, + XAdvance = character.XAdvance + }; + + glyphs.Add(glyph); + } + + var textures = new List + { + textureRegion.Texture + }; + + return DefaultAssets.FontSystem.NewStatic(data.LineHeight, glyphs, textures, 0, data.LineHeight); +#endif + } + + } +} \ No newline at end of file diff --git a/SpriteFontPlus/CharacterRange.cs b/SpriteFontPlus/CharacterRange.cs new file mode 100644 index 000000000..0cbd05d7b --- /dev/null +++ b/SpriteFontPlus/CharacterRange.cs @@ -0,0 +1,40 @@ +namespace SpriteFontPlus +{ + public struct CharacterRange + { + public static readonly CharacterRange BasicLatin = new CharacterRange((char) 0x0020, (char) 0x007F); + + public static readonly CharacterRange Latin1Supplement = + new CharacterRange((char) 0x00A0, (char) 0x00FF); + + public static readonly CharacterRange LatinExtendedA = + new CharacterRange((char) 0x0100, (char) 0x017F); + + public static readonly CharacterRange LatinExtendedB = + new CharacterRange((char) 0x0180, (char) 0x024F); + + public static readonly CharacterRange Cyrillic = new CharacterRange((char) 0x0400, (char) 0x04FF); + + public static readonly CharacterRange CyrillicSupplement = + new CharacterRange((char) 0x0500, (char) 0x052F); + + public static readonly CharacterRange Hiragana = + new CharacterRange((char) 0x3040, (char) 0x309F); + + public static readonly CharacterRange Katakana = + new CharacterRange((char) 0x30A0, (char) 0x30FF); + + public char Start { get; private set; } + public char End { get; private set; } + + public CharacterRange(char start, char end) + { + Start = start; + End = end; + } + + public CharacterRange(char single): this(single, single) + { + } + } +} \ No newline at end of file diff --git a/SpriteFontPlus/Cyotek.Drawing.Bitmap/BitmapFont.cs b/SpriteFontPlus/Cyotek.Drawing.Bitmap/BitmapFont.cs new file mode 100644 index 000000000..7ec2e5766 --- /dev/null +++ b/SpriteFontPlus/Cyotek.Drawing.Bitmap/BitmapFont.cs @@ -0,0 +1,750 @@ +/* AngelCode bitmap font parsing using C# + * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp + * + * Copyright © 2012-2015 Cyotek Ltd. + * + * Licensed under the MIT License. See license.txt for the full text. + */ + +// Some documentation derived from the BMFont file format specification +// http://www.angelcode.com/products/bmfont/doc/file_format.html + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Text; +using System.Xml; +using Point = Microsoft.Xna.Framework.Point; +using Rectangle = Microsoft.Xna.Framework.Rectangle; + +namespace Cyotek.Drawing.BitmapFont +{ + /// + /// A bitmap font. + /// + /// + internal class BitmapFont : IEnumerable + { + #region Constants + + /// + /// When used with , specifies that no wrapping should occur. + /// + public const int NoMaxWidth = -1; + + #endregion + + #region Properties + + /// + /// Gets or sets the alpha channel. + /// + /// + /// The alpha channel. + /// + /// + /// Set to 0 if the channel holds the glyph data, 1 if it holds the outline, 2 if it holds the glyph and the + /// outline, 3 if its set to zero, and 4 if its set to one. + /// + public int AlphaChannel { get; set; } + + /// + /// Gets or sets the number of pixels from the absolute top of the line to the base of the characters. + /// + /// + /// The number of pixels from the absolute top of the line to the base of the characters. + /// + public int BaseHeight { get; set; } + + /// + /// Gets or sets the blue channel. + /// + /// + /// The blue channel. + /// + /// + /// Set to 0 if the channel holds the glyph data, 1 if it holds the outline, 2 if it holds the glyph and the + /// outline, 3 if its set to zero, and 4 if its set to one. + /// + public int BlueChannel { get; set; } + + /// + /// Gets or sets a value indicating whether the font is bold. + /// + /// + /// true if the font is bold, otherwise false. + /// + public bool Bold { get; set; } + + /// + /// Gets or sets the characters that comprise the font. + /// + /// + /// The characters that comprise the font. + /// + public IDictionary Characters { get; set; } + + /// + /// Gets or sets the name of the OEM charset used. + /// + /// + /// The name of the OEM charset used (when not unicode). + /// + public string Charset { get; set; } + + /// + /// Gets or sets the name of the true type font. + /// + /// + /// The font family name. + /// + public string FamilyName { get; set; } + + /// + /// Gets or sets the size of the font. + /// + /// + /// The size of the font. + /// + public int FontSize { get; set; } + + /// + /// Gets or sets the green channel. + /// + /// + /// The green channel. + /// + /// + /// Set to 0 if the channel holds the glyph data, 1 if it holds the outline, 2 if it holds the glyph and the + /// outline, 3 if its set to zero, and 4 if its set to one. + /// + public int GreenChannel { get; set; } + + /// + /// Gets or sets a value indicating whether the font is italic. + /// + /// + /// true if the font is italic, otherwise false. + /// + public bool Italic { get; set; } + + /// + /// Indexer to get items within this collection using array index syntax. + /// + /// The character. + /// + /// The indexed item. + /// + public Character this[char character] => Characters[character]; + + /// + /// Gets or sets the character kernings for the font. + /// + /// + /// The character kernings for the font. + /// + public IDictionary Kernings { get; set; } + + /// + /// Gets or sets the distance in pixels between each line of text. + /// + /// + /// The distance in pixels between each line of text. + /// + public int LineHeight { get; set; } + + /// + /// Gets or sets the outline thickness for the characters. + /// + /// + /// The outline thickness for the characters. + /// + public int OutlineSize { get; set; } + + /// + /// Gets or sets a value indicating whether the monochrome characters have been packed into each of the texture + /// channels. + /// + /// + /// true if the characters are packed, otherwise false. + /// + /// + /// When packed, the property describes what is stored in each channel. + /// + public bool Packed { get; set; } + + /// + /// Gets or sets the padding for each character. + /// + /// + /// The padding for each character. + /// + public Padding Padding { get; set; } + + /// + /// Gets or sets the texture pages for the font. + /// + /// + /// The pages. + /// + public Page[] Pages { get; set; } + + /// + /// Gets or sets the red channel. + /// + /// + /// The red channel. + /// + /// + /// Set to 0 if the channel holds the glyph data, 1 if it holds the outline, 2 if it holds the glyph and the + /// outline, 3 if its set to zero, and 4 if its set to one. + /// + public int RedChannel { get; set; } + + /// + /// Gets or sets a value indicating whether the font is smoothed. + /// + /// + /// true if the font is smoothed, otherwise false. + /// + public bool Smoothed { get; set; } + + /// + /// Gets or sets the spacing for each character. + /// + /// + /// The spacing for each character. + /// + public Point Spacing { get; set; } + + /// + /// Gets or sets the font height stretch. + /// + /// + /// The font height stretch. + /// + /// 100% means no stretch. + public int StretchedHeight { get; set; } + + /// + /// Gets or sets the level of super sampling used by the font. + /// + /// + /// The super sampling level of the font. + /// + /// A value of 1 indicates no super sampling is in use. + public int SuperSampling { get; set; } + + /// + /// Gets or sets the size of the texture images used by the font. + /// + /// + /// The size of the texture. + /// + public Size TextureSize { get; set; } + + /// + /// Gets or sets a value indicating whether the font is unicode. + /// + /// + /// true if the font is unicode, otherwise false. + /// + public bool Unicode { get; set; } + + #endregion + + #region Methods + + /// + /// Gets the kerning for the specified character combination. + /// + /// The previous character. + /// The current character. + /// + /// The spacing between the specified characters. + /// + public int GetKerning(char previous, char current) + { + Kerning key; + int result; + + key = new Kerning(previous, current, 0); + + if (!Kernings.TryGetValue(key, out result)) result = 0; + + return result; + } + + /// + /// Load font information from the specified . + /// + /// Thrown when one or more required arguments are null. + /// + /// Thrown when one or more arguments have unsupported or + /// illegal values. + /// + /// Thrown when an Invalid Data error condition occurs. + /// The stream to load. + public virtual void Load(Stream stream) + { + byte[] buffer; + string header; + + if (stream == null) throw new ArgumentNullException("stream"); + + if (!stream.CanSeek) + throw new ArgumentException("Stream must be seekable in order to determine file format.", "stream"); + + // read the first five bytes so we can try and work out what the format is + // then reset the position so the format loaders can work + buffer = new byte[5]; + stream.Read(buffer, 0, 5); + stream.Seek(0, SeekOrigin.Begin); + header = Encoding.ASCII.GetString(buffer); + + switch (header) + { + case "info ": + LoadText(stream); + break; + case " + /// Load font information from the specified file. + /// + /// Thrown when one or more required arguments are null. + /// Thrown when the requested file is not present. + /// The file name to load. + public void Load(string fileName) + { + if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName"); + + if (!File.Exists(fileName)) + throw new FileNotFoundException(string.Format("Cannot find file '{0}'.", fileName), fileName); + + using (Stream stream = File.OpenRead(fileName)) + { + Load(stream); + } + + BitmapFontLoader.QualifyResourcePaths(this, Path.GetDirectoryName(fileName)); + } + + /// + /// Loads font information from the specified string. + /// + /// String containing the font to load. + /// The source data must be in BMFont text format. + public void LoadText(string text) + { + using (var reader = new StringReader(text)) + { + LoadText(reader); + } + } + + /// + /// Loads font information from the specified stream. + /// + /// + /// The source data must be in BMFont text format. + /// + /// Thrown when one or more required arguments are null. + /// The stream containing the font to load. + public void LoadText(Stream stream) + { + if (stream == null) throw new ArgumentNullException("stream"); + + using (TextReader reader = new StreamReader(stream)) + { + LoadText(reader); + } + } + + /// + /// Loads font information from the specified . + /// + /// + /// The source data must be in BMFont text format. + /// + /// Thrown when one or more required arguments are null. + /// The TextReader used to feed the data into the font. + public virtual void LoadText(TextReader reader) + { + IDictionary pageData; + IDictionary kerningDictionary; + IDictionary charDictionary; + string line; + + if (reader == null) throw new ArgumentNullException("reader"); + + pageData = new SortedDictionary(); + kerningDictionary = new Dictionary(); + charDictionary = new Dictionary(); + + do + { + line = reader.ReadLine(); + + if (line != null) + { + string[] parts; + + parts = BitmapFontLoader.Split(line, ' '); + + if (parts.Length != 0) + switch (parts[0]) + { + case "info": + FamilyName = BitmapFontLoader.GetNamedString(parts, "face"); + FontSize = BitmapFontLoader.GetNamedInt(parts, "size"); + Bold = BitmapFontLoader.GetNamedBool(parts, "bold"); + Italic = BitmapFontLoader.GetNamedBool(parts, "italic"); + Charset = BitmapFontLoader.GetNamedString(parts, "charset"); + Unicode = BitmapFontLoader.GetNamedBool(parts, "unicode"); + StretchedHeight = BitmapFontLoader.GetNamedInt(parts, "stretchH"); + Smoothed = BitmapFontLoader.GetNamedBool(parts, "smooth"); + SuperSampling = BitmapFontLoader.GetNamedInt(parts, "aa"); + Padding = BitmapFontLoader.ParsePadding( + BitmapFontLoader.GetNamedString(parts, "padding")); + Spacing = BitmapFontLoader.ParsePoint( + BitmapFontLoader.GetNamedString(parts, "spacing")); + OutlineSize = BitmapFontLoader.GetNamedInt(parts, "outline"); + break; + case "common": + LineHeight = BitmapFontLoader.GetNamedInt(parts, "lineHeight"); + BaseHeight = BitmapFontLoader.GetNamedInt(parts, "base"); + TextureSize = new Size(BitmapFontLoader.GetNamedInt(parts, "scaleW"), + BitmapFontLoader.GetNamedInt(parts, "scaleH")); + Packed = BitmapFontLoader.GetNamedBool(parts, "packed"); + AlphaChannel = BitmapFontLoader.GetNamedInt(parts, "alphaChnl"); + RedChannel = BitmapFontLoader.GetNamedInt(parts, "redChnl"); + GreenChannel = BitmapFontLoader.GetNamedInt(parts, "greenChnl"); + BlueChannel = BitmapFontLoader.GetNamedInt(parts, "blueChnl"); + break; + case "page": + int id; + string name; + + id = BitmapFontLoader.GetNamedInt(parts, "id"); + name = BitmapFontLoader.GetNamedString(parts, "file"); + + pageData.Add(id, new Page(id, name)); + break; + case "char": + Character charData; + + charData = new Character + { + Char = (char) BitmapFontLoader.GetNamedInt(parts, "id"), + Bounds = + new Rectangle(BitmapFontLoader.GetNamedInt(parts, "x"), + BitmapFontLoader.GetNamedInt(parts, "y"), + BitmapFontLoader.GetNamedInt(parts, "width"), + BitmapFontLoader.GetNamedInt(parts, "height")), + Offset = + new Point(BitmapFontLoader.GetNamedInt(parts, "xoffset"), + BitmapFontLoader.GetNamedInt(parts, "yoffset")), + XAdvance = BitmapFontLoader.GetNamedInt(parts, "xadvance"), + TexturePage = BitmapFontLoader.GetNamedInt(parts, "page"), + Channel = BitmapFontLoader.GetNamedInt(parts, "chnl") + }; + charDictionary.Add(charData.Char, charData); + break; + case "kerning": + Kerning key; + + key = new Kerning((char) BitmapFontLoader.GetNamedInt(parts, "first"), + (char) BitmapFontLoader.GetNamedInt(parts, "second"), + BitmapFontLoader.GetNamedInt(parts, "amount")); + + if (!kerningDictionary.ContainsKey(key)) kerningDictionary.Add(key, key.Amount); + break; + } + } + } while (line != null); + + Pages = BitmapFontLoader.ToArray(pageData.Values); + Characters = charDictionary; + Kernings = kerningDictionary; + } + + /// + /// Loads font information from the specified string. + /// + /// String containing the font to load. + /// The source data must be in BMFont XML format. + public void LoadXml(string xml) + { + using (var reader = new StringReader(xml)) + { + LoadXml(reader); + } + } + + /// + /// Loads font information from the specified . + /// + /// + /// The source data must be in BMFont XML format. + /// + /// Thrown when one or more required arguments are null. + /// The TextReader used to feed the data into the font. + public virtual void LoadXml(TextReader reader) + { + XmlDocument document; + IDictionary pageData; + IDictionary kerningDictionary; + IDictionary charDictionary; + XmlNode root; + XmlNode properties; + + if (reader == null) throw new ArgumentNullException("reader"); + + document = new XmlDocument(); + pageData = new SortedDictionary(); + kerningDictionary = new Dictionary(); + charDictionary = new Dictionary(); + + document.Load(reader); + root = document.DocumentElement; + + // load the basic attributes + properties = root.SelectSingleNode("info"); + FamilyName = properties.Attributes["face"].Value; + FontSize = Convert.ToInt32(properties.Attributes["size"].Value); + Bold = Convert.ToInt32(properties.Attributes["bold"].Value) != 0; + Italic = Convert.ToInt32(properties.Attributes["italic"].Value) != 0; + Unicode = Convert.ToInt32(properties.Attributes["unicode"].Value) != 0; + StretchedHeight = Convert.ToInt32(properties.Attributes["stretchH"].Value); + Charset = properties.Attributes["charset"].Value; + Smoothed = Convert.ToInt32(properties.Attributes["smooth"].Value) != 0; + SuperSampling = Convert.ToInt32(properties.Attributes["aa"].Value); + Padding = BitmapFontLoader.ParsePadding(properties.Attributes["padding"].Value); + Spacing = BitmapFontLoader.ParsePoint(properties.Attributes["spacing"].Value); + OutlineSize = Convert.ToInt32(properties.Attributes["outline"].Value); + + // common attributes + properties = root.SelectSingleNode("common"); + BaseHeight = Convert.ToInt32(properties.Attributes["base"].Value); + LineHeight = Convert.ToInt32(properties.Attributes["lineHeight"].Value); + TextureSize = new Size(Convert.ToInt32(properties.Attributes["scaleW"].Value), + Convert.ToInt32(properties.Attributes["scaleH"].Value)); + Packed = Convert.ToInt32(properties.Attributes["packed"].Value) != 0; + AlphaChannel = Convert.ToInt32(properties.Attributes["alphaChnl"].Value); + RedChannel = Convert.ToInt32(properties.Attributes["redChnl"].Value); + GreenChannel = Convert.ToInt32(properties.Attributes["greenChnl"].Value); + BlueChannel = Convert.ToInt32(properties.Attributes["blueChnl"].Value); + + // load texture information + foreach (XmlNode node in root.SelectNodes("pages/page")) + { + Page page; + + page = new Page(); + page.Id = Convert.ToInt32(node.Attributes["id"].Value); + page.FileName = node.Attributes["file"].Value; + + pageData.Add(page.Id, page); + } + + Pages = BitmapFontLoader.ToArray(pageData.Values); + + // load character information + foreach (XmlNode node in root.SelectNodes("chars/char")) + { + Character character; + + character = new Character(); + character.Char = (char) Convert.ToInt32(node.Attributes["id"].Value); + character.Bounds = new Rectangle(Convert.ToInt32(node.Attributes["x"].Value), + Convert.ToInt32(node.Attributes["y"].Value), + Convert.ToInt32(node.Attributes["width"].Value), + Convert.ToInt32(node.Attributes["height"].Value)); + character.Offset = new Point(Convert.ToInt32(node.Attributes["xoffset"].Value), + Convert.ToInt32(node.Attributes["yoffset"].Value)); + character.XAdvance = Convert.ToInt32(node.Attributes["xadvance"].Value); + character.TexturePage = Convert.ToInt32(node.Attributes["page"].Value); + character.Channel = Convert.ToInt32(node.Attributes["chnl"].Value); + + charDictionary.Add(character.Char, character); + } + + Characters = charDictionary; + + // loading kerning information + foreach (XmlNode node in root.SelectNodes("kernings/kerning")) + { + Kerning key; + + key = new Kerning((char) Convert.ToInt32(node.Attributes["first"].Value), + (char) Convert.ToInt32(node.Attributes["second"].Value), + Convert.ToInt32(node.Attributes["amount"].Value)); + + if (!kerningDictionary.ContainsKey(key)) kerningDictionary.Add(key, key.Amount); + } + + Kernings = kerningDictionary; + } + + /// + /// Loads font information from the specified stream. + /// + /// + /// The source data must be in BMFont XML format. + /// + /// Thrown when one or more required arguments are null. + /// The stream containing the font to load. + public void LoadXml(Stream stream) + { + if (stream == null) throw new ArgumentNullException("stream"); + + using (TextReader reader = new StreamReader(stream)) + { + LoadXml(reader); + } + } + + /// + /// Provides the size, in pixels, of the specified text when drawn with this font. + /// + /// The text to measure. + /// + /// The , in pixels, of drawn with this font. + /// + public Size MeasureFont(string text) + { + return MeasureFont(text, NoMaxWidth); + } + + /// + /// Provides the size, in pixels, of the specified text when drawn with this font, automatically wrapping to keep + /// within the specified with. + /// + /// The text to measure. + /// The maximum width. + /// + /// The , in pixels, of drawn with this font. + /// + /// + /// The MeasureText method uses the parameter to automatically wrap when determining + /// text size. + /// + public Size MeasureFont(string text, double maxWidth) + { + Size result; + + if (!string.IsNullOrEmpty(text)) + { + char previousCharacter; + int currentLineWidth; + int currentLineHeight; + int blockWidth; + int blockHeight; + int length; + List lineHeights; + + length = text.Length; + previousCharacter = ' '; + currentLineWidth = 0; + currentLineHeight = LineHeight; + blockWidth = 0; + blockHeight = 0; + lineHeights = new List(); + + for (var i = 0; i < length; i++) + { + char character; + + character = text[i]; + + if (character == '\n' || character == '\r') + { + if (character == '\n' || i + 1 == length || text[i + 1] != '\n') + { + lineHeights.Add(currentLineHeight); + blockWidth = Math.Max(blockWidth, currentLineWidth); + currentLineWidth = 0; + currentLineHeight = LineHeight; + } + } + else + { + Character data; + int width; + + data = this[character]; + width = data.XAdvance + GetKerning(previousCharacter, character); + + if (maxWidth != NoMaxWidth && currentLineWidth + width >= maxWidth) + { + lineHeights.Add(currentLineHeight); + blockWidth = Math.Max(blockWidth, currentLineWidth); + currentLineWidth = 0; + currentLineHeight = LineHeight; + } + + currentLineWidth += width; + currentLineHeight = Math.Max(currentLineHeight, data.Bounds.Height + data.Offset.Y); + previousCharacter = character; + } + } + + // finish off the current line if required + if (currentLineHeight != 0) lineHeights.Add(currentLineHeight); + + // reduce any lines other than the last back to the base + for (var i = 0; i < lineHeights.Count - 1; i++) lineHeights[i] = LineHeight; + + // calculate the final block height + foreach (var lineHeight in lineHeights) blockHeight += lineHeight; + + result = new Size(Math.Max(currentLineWidth, blockWidth), blockHeight); + } + else + { + result = Size.Empty; + } + + return result; + } + + #endregion + + #region IEnumerable Interface + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through + /// the collection. + /// + /// + public IEnumerator GetEnumerator() + { + foreach (var pair in Characters) yield return pair.Value; + } + + /// + /// Gets the enumerator. + /// + /// + /// The enumerator. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + } +} \ No newline at end of file diff --git a/SpriteFontPlus/Cyotek.Drawing.Bitmap/BitmapFontLoader.cs b/SpriteFontPlus/Cyotek.Drawing.Bitmap/BitmapFontLoader.cs new file mode 100644 index 000000000..51ac5df28 --- /dev/null +++ b/SpriteFontPlus/Cyotek.Drawing.Bitmap/BitmapFontLoader.cs @@ -0,0 +1,339 @@ +/* AngelCode bitmap font parsing using C# + * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp + * + * Copyright © 2012-2015 Cyotek Ltd. + * + * Licensed under the MIT License. See license.txt for the full text. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Xna.Framework; + +namespace Cyotek.Drawing.BitmapFont +{ + /// + /// Parsing class for bitmap fonts generated by AngelCode BMFont + /// + internal static class BitmapFontLoader + { + #region Static Methods + + /// + /// Loads a bitmap font from a file, attempting to auto detect the file type + /// + /// Thrown when one or more required arguments are null. + /// Thrown when the requested file is not present. + /// Thrown when an Invalid Data error condition occurs. + /// Name of the file to load. + /// + /// A containing the loaded data. + /// + public static BitmapFont LoadFontFromFile(string fileName) + { + BitmapFont result; + + if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName", "File name not specified"); + + if (!File.Exists(fileName)) + throw new FileNotFoundException(string.Format("Cannot find file '{0}'", fileName), fileName); + + using (var file = File.OpenRead(fileName)) + { + using (TextReader reader = new StreamReader(file)) + { + string line; + + line = reader.ReadLine(); + + if (line.StartsWith("info ")) + result = LoadFontFromTextFile(fileName); + else if (line.StartsWith(" + /// Loads a bitmap font from a file containing font data in text format. + /// + /// Thrown when one or more required arguments are null. + /// Thrown when the requested file is not present. + /// Name of the file to load. + /// + /// A containing the loaded data. + /// + public static BitmapFont LoadFontFromTextFile(string fileName) + { + BitmapFont font; + + if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName"); + + if (!File.Exists(fileName)) + throw new FileNotFoundException(string.Format("Cannot find file '{0}'", fileName), fileName); + + font = new BitmapFont(); + + using (Stream stream = File.OpenRead(fileName)) + { + font.LoadText(stream); + } + + QualifyResourcePaths(font, Path.GetDirectoryName(fileName)); + + return font; + } + + /// + /// Loads a bitmap font from a file containing font data in XML format. + /// + /// Thrown when one or more required arguments are null. + /// Thrown when the requested file is not present. + /// Name of the file to load. + /// + /// A containing the loaded data. + /// + public static BitmapFont LoadFontFromXmlFile(string fileName) + { + BitmapFont font; + + if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName"); + + if (!File.Exists(fileName)) + throw new FileNotFoundException(string.Format("Cannot find file '{0}'", fileName), fileName); + + font = new BitmapFont(); + + using (Stream stream = File.OpenRead(fileName)) + { + font.LoadXml(stream); + } + + QualifyResourcePaths(font, Path.GetDirectoryName(fileName)); + + return font; + } + + /// + /// Returns a boolean from an array of name/value pairs. + /// + /// The array of parts. + /// The name of the value to return. + /// Default value(if the key doesnt exist or can't be parsed) + /// + internal static bool GetNamedBool(string[] parts, string name, bool defaultValue = false) + { + var s = GetNamedString(parts, name); + + bool result; + int v; + if (int.TryParse(s, out v)) + result = v > 0; + else + result = defaultValue; + + return result; + } + + /// + /// Returns an integer from an array of name/value pairs. + /// + /// The array of parts. + /// The name of the value to return. + /// Default value(if the key doesnt exist or can't be parsed) + /// + internal static int GetNamedInt(string[] parts, string name, int defaultValue = 0) + { + var s = GetNamedString(parts, name); + + int result; + if (!int.TryParse(s, out result)) result = defaultValue; + + return result; + } + + /// + /// Returns a string from an array of name/value pairs. + /// + /// The array of parts. + /// The name of the value to return. + /// + internal static string GetNamedString(string[] parts, string name) + { + string result; + + result = string.Empty; + + foreach (var part in parts) + { + int nameEndIndex; + + nameEndIndex = part.IndexOf('='); + if (nameEndIndex != -1) + { + string namePart; + string valuePart; + + namePart = part.Substring(0, nameEndIndex); + valuePart = part.Substring(nameEndIndex + 1); + + if (string.Equals(name, namePart, StringComparison.OrdinalIgnoreCase)) + { + int length; + + length = valuePart.Length; + + if (length > 1 && valuePart[0] == '"' && valuePart[length - 1] == '"') + valuePart = valuePart.Substring(1, length - 2); + + result = valuePart; + break; + } + } + } + + return result; + } + + /// + /// Creates a Padding object from a string representation + /// + /// The string. + /// + internal static Padding ParsePadding(string s) + { + string[] parts; + + parts = s.Split(','); + + return new Padding + { + Left = Convert.ToInt32(parts[3].Trim()), + Top = Convert.ToInt32(parts[0].Trim()), + Right = Convert.ToInt32(parts[1].Trim()), + Bottom = Convert.ToInt32(parts[2].Trim()) + }; + } + + /// + /// Creates a Point object from a string representation + /// + /// The string. + /// + internal static Point ParsePoint(string s) + { + string[] parts; + + parts = s.Split(','); + + return new Point + { + X = Convert.ToInt32(parts[0].Trim()), + Y = Convert.ToInt32(parts[1].Trim()) + }; + } + + /// + /// Updates data with a fully qualified path + /// + /// The to update. + /// The path where texture resources are located. + internal static void QualifyResourcePaths(BitmapFont font, string resourcePath) + { + Page[] pages; + + pages = font.Pages; + + for (var i = 0; i < pages.Length; i++) + { + Page page; + + page = pages[i]; + page.FileName = Path.Combine(resourcePath, page.FileName); + pages[i] = page; + } + + font.Pages = pages; + } + + /// + /// Splits the specified string using a given delimiter, ignoring any instances of the delimiter as part of a quoted + /// string. + /// + /// The string to split. + /// The delimiter. + /// + internal static string[] Split(string s, char delimiter) + { + string[] results; + + if (s.IndexOf('"') != -1) + { + List parts; + int partStart; + + partStart = -1; + parts = new List(); + + do + { + int partEnd; + int quoteStart; + int quoteEnd; + bool hasQuotes; + + quoteStart = s.IndexOf('"', partStart + 1); + quoteEnd = s.IndexOf('"', quoteStart + 1); + partEnd = s.IndexOf(delimiter, partStart + 1); + + if (partEnd == -1) partEnd = s.Length; + + hasQuotes = quoteStart != -1 && partEnd > quoteStart && partEnd < quoteEnd; + if (hasQuotes) partEnd = s.IndexOf(delimiter, quoteEnd + 1); + + parts.Add(s.Substring(partStart + 1, partEnd - partStart - 1)); + + if (hasQuotes) partStart = partEnd - 1; + + partStart = s.IndexOf(delimiter, partStart + 1); + } while (partStart != -1); + + results = parts.ToArray(); + } + else + { + results = s.Split(new[] + { + delimiter + }, StringSplitOptions.RemoveEmptyEntries); + } + + return results; + } + + /// + /// Converts the given collection into an array + /// + /// Type of the items in the array + /// The values. + /// + internal static T[] ToArray(ICollection values) + { + T[] result; + + // avoid a forced .NET 3 dependency just for one call to Linq + + result = new T[values.Count]; + values.CopyTo(result, 0); + + return result; + } + + #endregion + } +} \ No newline at end of file diff --git a/SpriteFontPlus/Cyotek.Drawing.Bitmap/Character.cs b/SpriteFontPlus/Cyotek.Drawing.Bitmap/Character.cs new file mode 100644 index 000000000..e2f0c645a --- /dev/null +++ b/SpriteFontPlus/Cyotek.Drawing.Bitmap/Character.cs @@ -0,0 +1,89 @@ +/* AngelCode bitmap font parsing using C# + * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp + * + * Copyright © 2012-2015 Cyotek Ltd. + * + * Licensed under the MIT License. See license.txt for the full text. + */ + +using Microsoft.Xna.Framework; + +namespace Cyotek.Drawing.BitmapFont +{ + /// + /// Represents the definition of a single character in a + /// + internal struct Character + { + #region Properties + + /// + /// Gets or sets the bounds of the character image in the source texture. + /// + /// + /// The bounds of the character image in the source texture. + /// + public Rectangle Bounds { get; set; } + + /// + /// Gets or sets the texture channel where the character image is found. + /// + /// + /// The texture channel where the character image is found. + /// + /// + /// 1 = blue, 2 = green, 4 = red, 8 = alpha, 15 = all channels + /// + public int Channel { get; set; } + + /// + /// Gets or sets the character. + /// + /// + /// The character. + /// + public char Char { get; set; } + + /// + /// Gets or sets the offset when copying the image from the texture to the screen. + /// + /// + /// The offset when copying the image from the texture to the screen. + /// + public Point Offset { get; set; } + + /// + /// Gets or sets the texture page where the character image is found. + /// + /// + /// The texture page where the character image is found. + /// + public int TexturePage { get; set; } + + /// + /// Gets or sets the value used to advance the current position after drawing the character. + /// + /// + /// How much the current position should be advanced after drawing the character. + /// + public int XAdvance { get; set; } + + #endregion + + #region Methods + + /// + /// Returns the fully qualified type name of this instance. + /// + /// + /// A containing a fully qualified type name. + /// + /// + public override string ToString() + { + return Char.ToString(); + } + + #endregion + } +} \ No newline at end of file diff --git a/SpriteFontPlus/Cyotek.Drawing.Bitmap/Kerning.cs b/SpriteFontPlus/Cyotek.Drawing.Bitmap/Kerning.cs new file mode 100644 index 000000000..45e8c91c5 --- /dev/null +++ b/SpriteFontPlus/Cyotek.Drawing.Bitmap/Kerning.cs @@ -0,0 +1,122 @@ +/* AngelCode bitmap font parsing using C# + * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp + * + * Copyright © 2012-2015 Cyotek Ltd. + * + * Licensed under the MIT License. See license.txt for the full text. + */ + +using System; + +namespace Cyotek.Drawing.BitmapFont +{ + /// + /// Represents the font kerning between two characters. + /// + internal struct Kerning : IEquatable + { + #region Constructors + + /// + /// Constructor. + /// + /// The first character. + /// The second character. + /// + /// How much the x position should be adjusted when drawing the second + /// character immediately following the first. + /// + public Kerning(char firstCharacter, char secondCharacter, int amount) + : this() + { + FirstCharacter = firstCharacter; + SecondCharacter = secondCharacter; + Amount = amount; + } + + #endregion + + #region Properties + + /// + /// Gets or sets how much the x position should be adjusted when drawing the second character immediately following the + /// first. + /// + /// + /// How much the x position should be adjusted when drawing the second character immediately following the first. + /// + public int Amount { get; set; } + + /// + /// Gets or sets the first character. + /// + /// + /// The first character. + /// + public char FirstCharacter { get; set; } + + /// + /// Gets or sets the second character. + /// + /// + /// The second character. + /// + public char SecondCharacter { get; set; } + + #endregion + + #region Methods + + /// + /// Returns the fully qualified type name of this instance. + /// + /// + /// A containing a fully qualified type name. + /// + /// + public override string ToString() + { + return string.Format("{0} to {1} = {2}", FirstCharacter, SecondCharacter, Amount); + } + + /// + /// Check if the object represents kerning between the same two characters. + /// + /// + /// + /// Whether or not the object represents kerning between the same two characters. + /// + public override bool Equals(object obj) + { + if (obj == null) return false; + if (obj.GetType() != typeof(Kerning)) return false; + + return Equals((Kerning) obj); + } + + /// + /// Check if the other kerning is between the same two characters. + /// + /// + /// + /// Whether or not the other kerning is between the same two characters. + /// + public bool Equals(Kerning other) + { + return FirstCharacter == other.FirstCharacter && SecondCharacter == other.SecondCharacter; + } + + /// + /// Return the hash code of the kerning between the two characters. + /// + /// + /// A unique hash code of the kerning between the two characters. + /// + public override int GetHashCode() + { + return (FirstCharacter << 16) | SecondCharacter; + } + + #endregion + } +} \ No newline at end of file diff --git a/SpriteFontPlus/Cyotek.Drawing.Bitmap/Padding.cs b/SpriteFontPlus/Cyotek.Drawing.Bitmap/Padding.cs new file mode 100644 index 000000000..b4ec375be --- /dev/null +++ b/SpriteFontPlus/Cyotek.Drawing.Bitmap/Padding.cs @@ -0,0 +1,88 @@ +/* AngelCode bitmap font parsing using C# + * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp + * + * Copyright © 2012-2015 Cyotek Ltd. + * + * Licensed under the MIT License. See license.txt for the full text. + */ + +namespace Cyotek.Drawing.BitmapFont +{ + /// + /// Represents padding or margin information associated with an element. + /// + internal struct Padding + { + #region Constructors + + /// + /// Initializes a new instance of the stricture using a separate padding size for each edge. + /// + /// The padding size, in pixels, for the left edge. + /// The padding size, in pixels, for the top edge. + /// The padding size, in pixels, for the right edge. + /// The padding size, in pixels, for the bottom edge. + public Padding(int left, int top, int right, int bottom) + : this() + { + Top = top; + Left = left; + Right = right; + Bottom = bottom; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the padding value for the bottom edge. + /// + /// + /// The padding, in pixels, for the bottom edge. + /// + public int Bottom { get; set; } + + /// + /// Gets or sets the padding value for the left edge. + /// + /// + /// The padding, in pixels, for the left edge. + /// + public int Left { get; set; } + + /// + /// Gets or sets the padding value for the right edge. + /// + /// + /// The padding, in pixels, for the right edge. + /// + public int Right { get; set; } + + /// + /// Gets or sets the padding value for the top edge. + /// + /// + /// The padding, in pixels, for the top edge. + /// + public int Top { get; set; } + + #endregion + + #region Methods + + /// + /// Returns the fully qualified type name of this instance. + /// + /// + /// A containing a fully qualified type name. + /// + /// + public override string ToString() + { + return string.Format("{0}, {1}, {2}, {3}", Left, Top, Right, Bottom); + } + + #endregion + } +} \ No newline at end of file diff --git a/SpriteFontPlus/Cyotek.Drawing.Bitmap/Page.cs b/SpriteFontPlus/Cyotek.Drawing.Bitmap/Page.cs new file mode 100644 index 000000000..401ccc9cc --- /dev/null +++ b/SpriteFontPlus/Cyotek.Drawing.Bitmap/Page.cs @@ -0,0 +1,70 @@ +/* AngelCode bitmap font parsing using C# + * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp + * + * Copyright © 2012-2015 Cyotek Ltd. + * + * Licensed under the MIT License. See license.txt for the full text. + */ + +using System.IO; + +namespace Cyotek.Drawing.BitmapFont +{ + /// + /// Represents a texture page. + /// + internal struct Page + { + #region Constructors + + /// + /// Creates a texture page using the specified ID and source file name. + /// + /// The identifier. + /// Filename of the texture image. + public Page(int id, string fileName) + : this() + { + FileName = fileName; + Id = id; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the filename of the source texture image. + /// + /// + /// The name of the file containing the source texture image. + /// + public string FileName { get; set; } + + /// + /// Gets or sets the page identifier. + /// + /// + /// The page identifier. + /// + public int Id { get; set; } + + #endregion + + #region Methods + + /// + /// Returns the fully qualified type name of this instance. + /// + /// + /// A containing a fully qualified type name. + /// + /// + public override string ToString() + { + return string.Format("{0} ({1})", Id, Path.GetFileName(FileName)); + } + + #endregion + } +} \ No newline at end of file diff --git a/SpriteFontPlus/DynamicSpriteFont.cs b/SpriteFontPlus/DynamicSpriteFont.cs new file mode 100644 index 000000000..4520e37f2 --- /dev/null +++ b/SpriteFontPlus/DynamicSpriteFont.cs @@ -0,0 +1,126 @@ +using FontStashSharp; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace SpriteFontPlus +{ + public class DynamicSpriteFont + { + private static readonly string DefaultFontName = string.Empty; + + private readonly FontSystem _fontSystem; + private readonly int _defaultFontId; + public Texture2D Texture + { + get { return _fontSystem.Texture; } + } + + public float Size + { + get + { + return _fontSystem.Size; + } + set + { + _fontSystem.Size = value; + } + } + + public float Spacing + { + get + { + return _fontSystem.Spacing; + } + set + { + _fontSystem.Spacing = value; + } + } + + public int FontId + { + get + { + return _fontSystem.FontId; + } + set + { + _fontSystem.FontId = value; + } + } + + public int DefaultFontId + { + get + { + return _defaultFontId; + } + } + + private DynamicSpriteFont(byte[] ttf, float defaultSize, int textureWidth, int textureHeight) + { + var fontParams = new FontSystemParams + { + Width = textureWidth, + Height = textureHeight, + Flags = FontSystem.FONS_ZERO_TOPLEFT + }; + + _fontSystem = new FontSystem(fontParams); + _fontSystem.Alignment = FontSystem.FONS_ALIGN_TOP; + + _defaultFontId = _fontSystem.AddFontMem(DefaultFontName, ttf); + Size = defaultSize; + } + + public float DrawString(SpriteBatch batch, string text, Vector2 pos, Color color) + { + return DrawString(batch, text, pos, color, Vector2.One, 0f); + } + + public float DrawString(SpriteBatch batch, string text, Vector2 pos, Color color, Vector2 scale, float depth) + { + _fontSystem.Color = color; + _fontSystem.Scale = scale; + + var result = _fontSystem.DrawText(batch, pos.X, pos.Y, text, depth); + + _fontSystem.Scale = Vector2.One; + + return result; + } + + public int AddTtf(string name, byte[] ttf) + { + return _fontSystem.AddFontMem(name, ttf); + } + + public int? GetFontIdByName(string name) + { + return _fontSystem.GetFontByName(name); + } + + public Vector2 MeasureString(string text) + { + Bounds bounds = new Bounds(); + _fontSystem.TextBounds(0, 0, text, ref bounds); + + return new Vector2(bounds.X2, bounds.Y2); + } + + public Rectangle GetTextBounds(Vector2 position, string text) + { + Bounds bounds = new Bounds(); + _fontSystem.TextBounds(position.X, position.Y, text, ref bounds); + + return new Rectangle((int)bounds.X, (int)bounds.Y, (int)(bounds.X2 - bounds.X), (int)(bounds.Y2 - bounds.Y)); + } + + public static DynamicSpriteFont FromTtf(byte[] ttf, float defaultSize, int textureWidth = 1024, int textureHeight = 1024) + { + return new DynamicSpriteFont(ttf, defaultSize, textureWidth, textureHeight); + } + } +} \ No newline at end of file diff --git a/SpriteFontPlus/FontStashSharp/Bounds.cs b/SpriteFontPlus/FontStashSharp/Bounds.cs new file mode 100644 index 000000000..84a974a45 --- /dev/null +++ b/SpriteFontPlus/FontStashSharp/Bounds.cs @@ -0,0 +1,7 @@ +namespace FontStashSharp +{ + internal struct Bounds + { + public float X, Y, X2, Y2; + } +} diff --git a/SpriteFontPlus/FontStashSharp/Font.cs b/SpriteFontPlus/FontStashSharp/Font.cs new file mode 100644 index 000000000..d2b18563f --- /dev/null +++ b/SpriteFontPlus/FontStashSharp/Font.cs @@ -0,0 +1,20 @@ +using StbSharp; + +namespace FontStashSharp +{ + internal unsafe class Font + { + public StbTrueType.stbtt_fontinfo _font = new StbTrueType.stbtt_fontinfo(); + public string Name; + public byte[] Data; + public float Ascent; + public float Ascender; + public float Descender; + public float LineHeight; + public FontGlyph[] Glyphs; + public int GlyphsNumber; + public int[] Lut = new int[256]; + public int[] Fallbacks = new int[20]; + public int FallbacksCount; + } +} diff --git a/SpriteFontPlus/FontStashSharp/FontAtlas.cs b/SpriteFontPlus/FontStashSharp/FontAtlas.cs new file mode 100644 index 000000000..2b74153f5 --- /dev/null +++ b/SpriteFontPlus/FontStashSharp/FontAtlas.cs @@ -0,0 +1,162 @@ +using System; +using System.Runtime.InteropServices; +using StbSharp; + +namespace FontStashSharp +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe class FontAtlas + { + public int NodesCount; + public int Height; + public int NodesNumber; + public FontAtlasNode* Nodes; + public int Width; + + public FontAtlas(int w, int h, int count) + { + Width = w; + Height = h; + Nodes = (FontAtlasNode*)CRuntime.malloc((ulong)(sizeof(FontAtlasNode) * count)); + CRuntime.memset(Nodes, 0, (ulong)(sizeof(FontAtlasNode) * count)); + count = 0; + NodesCount = count; + Nodes[0].X = 0; + Nodes[0].Y = 0; + Nodes[0].Width = (short)w; + NodesNumber++; + } + + public int InsertNode(int idx, int x, int y, int w) + { + if (NodesNumber + 1 > NodesCount) + { + NodesCount = NodesCount == 0 ? 8 : NodesCount * 2; + Nodes = (FontAtlasNode*)CRuntime.realloc(Nodes, (ulong)(sizeof(FontAtlasNode) * NodesCount)); + if (Nodes == null) + return 0; + } + + for (var i = NodesNumber; i > idx; i--) Nodes[i] = Nodes[i - 1]; + Nodes[idx].X = (short)x; + Nodes[idx].Y = (short)y; + Nodes[idx].Width = (short)w; + NodesNumber++; + return 1; + } + + public void RemoveNode(int idx) + { + if (NodesNumber == 0) + return; + for (var i = idx; i < NodesNumber - 1; i++) Nodes[i] = Nodes[i + 1]; + NodesNumber--; + } + + public void Expand(int w, int h) + { + if (w > Width) + InsertNode(NodesNumber, Width, 0, w - Width); + Width = w; + Height = h; + } + + public void Reset(int w, int h) + { + Width = w; + Height = h; + NodesNumber = 0; + Nodes[0].X = 0; + Nodes[0].Y = 0; + Nodes[0].Width = (short)w; + NodesNumber++; + } + + public int AddSkylineLevel(int idx, int x, int y, int w, int h) + { + if (InsertNode(idx, x, y + h, w) == 0) + return 0; + for (var i = idx + 1; i < NodesNumber; i++) + if (Nodes[i].X < Nodes[i - 1].X + Nodes[i - 1].Width) + { + var shrink = Nodes[i - 1].X + Nodes[i - 1].Width - Nodes[i].X; + Nodes[i].X += (short)shrink; + Nodes[i].Width -= (short)shrink; + if (Nodes[i].Width <= 0) + { + RemoveNode(i); + i--; + } + else + { + break; + } + } + else + { + break; + } + + for (var i = 0; i < NodesNumber - 1; i++) + if (Nodes[i].Y == Nodes[i + 1].Y) + { + Nodes[i].Width += Nodes[i + 1].Width; + RemoveNode(i + 1); + i--; + } + + return 1; + } + + public int RectFits(int i, int w, int h) + { + var x = (int)Nodes[i].X; + var y = (int)Nodes[i].Y; + if (x + w > Width) + return -1; + var spaceLeft = w; + while (spaceLeft > 0) + { + if (i == NodesNumber) + return -1; + y = Math.Max(y, Nodes[i].Y); + if (y + h > Height) + return -1; + spaceLeft -= Nodes[i].Width; + ++i; + } + + return y; + } + + public int AddRect(int rw, int rh, int* rx, int* ry) + { + var besth = Height; + var bestw = Width; + var besti = -1; + var bestx = -1; + var besty = -1; + for (var i = 0; i < NodesNumber; i++) + { + var y = RectFits(i, rw, rh); + if (y != -1) + if (y + rh < besth || y + rh == besth && Nodes[i].Width < bestw) + { + besti = i; + bestw = Nodes[i].Width; + besth = y + rh; + bestx = Nodes[i].X; + besty = y; + } + } + + if (besti == -1) + return 0; + if (AddSkylineLevel(besti, bestx, besty, rw, rh) == 0) + return 0; + *rx = bestx; + *ry = besty; + return 1; + } + } +} \ No newline at end of file diff --git a/SpriteFontPlus/FontStashSharp/FontAtlasNode.cs b/SpriteFontPlus/FontStashSharp/FontAtlasNode.cs new file mode 100644 index 000000000..f4a9f7de2 --- /dev/null +++ b/SpriteFontPlus/FontStashSharp/FontAtlasNode.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace FontStashSharp +{ + [StructLayout(LayoutKind.Sequential)] + internal struct FontAtlasNode + { + public short X; + public short Y; + public short Width; + } +} diff --git a/SpriteFontPlus/FontStashSharp/FontGlyph.cs b/SpriteFontPlus/FontStashSharp/FontGlyph.cs new file mode 100644 index 000000000..706a9f27b --- /dev/null +++ b/SpriteFontPlus/FontStashSharp/FontGlyph.cs @@ -0,0 +1,21 @@ +using System.Runtime.InteropServices; + +namespace FontStashSharp +{ + [StructLayout(LayoutKind.Sequential)] + internal class FontGlyph + { + public int Codepoint; + public int Index; + public int Next; + public int Size; + public int Blur; + public int X0; + public int Y0; + public int X1; + public int Y1; + public int XAdvance; + public int XOffset; + public int YOffset; + } +} diff --git a/SpriteFontPlus/FontStashSharp/FontGlyphSquad.cs b/SpriteFontPlus/FontStashSharp/FontGlyphSquad.cs new file mode 100644 index 000000000..4f2cea948 --- /dev/null +++ b/SpriteFontPlus/FontStashSharp/FontGlyphSquad.cs @@ -0,0 +1,17 @@ +using System.Runtime.InteropServices; + +namespace FontStashSharp +{ + [StructLayout(LayoutKind.Sequential)] + internal struct FontGlyphSquad + { + public float X0; + public float Y0; + public float S0; + public float T0; + public float X1; + public float Y1; + public float S1; + public float T1; + } +} diff --git a/SpriteFontPlus/FontStashSharp/FontSystem.cs b/SpriteFontPlus/FontStashSharp/FontSystem.cs new file mode 100644 index 000000000..e0cd21b54 --- /dev/null +++ b/SpriteFontPlus/FontStashSharp/FontSystem.cs @@ -0,0 +1,860 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using SpriteFontPlus; +using StbSharp; + +namespace FontStashSharp +{ + internal unsafe class FontSystem + { + public const int FONS_ZERO_TOPLEFT = 1; + public const int FONS_ZERO_BOTTOMLEFT = 2; + public const int FONS_ALIGN_LEFT = 1 << 0; + public const int FONS_ALIGN_CENTER = 1 << 1; + public const int FONS_ALIGN_RIGHT = 1 << 2; + public const int FONS_ALIGN_TOP = 1 << 3; + public const int FONS_ALIGN_MIDDLE = 1 << 4; + public const int FONS_ALIGN_BOTTOM = 1 << 5; + public const int FONS_ALIGN_BASELINE = 1 << 6; + public const int FONS_GLYPH_BITMAP_OPTIONAL = 1; + public const int FONS_GLYPH_BITMAP_REQUIRED = 2; + public const int FONS_ATLAS_FULL = 1; + public const int FONS_SCRATCH_FULL = 2; + public const int FONS_STATES_OVERFLOW = 3; + public const int FONS_STATES_UNDERFLOW = 4; + + private FontSystemParams _params_ = new FontSystemParams(); + private FontAtlas _atlas; + private Color[] _colors = new Color[1024]; + private int[] _dirtyRect = new int[4]; + private Font[] _fonts; + private int _fontsNumber; + private float _ith; + private float _itw; + private int _vertsNumber; + private Rectangle[] _textureCoords = new Rectangle[1024 * 2]; + private byte[] _texData; + private Color[] _colorData; + private Rectangle[] _verts = new Rectangle[1024 * 2]; + + public int FontId; + public int Alignment; + public float Size; + public Color Color; + public float BlurValue; + public float Spacing; + public Vector2 Scale; + + public FontSystem(FontSystemParams p) + { + _params_ = p; + + _atlas = new FontAtlas(_params_.Width, _params_.Height, 256); + _fonts = new Font[4]; + _fontsNumber = 0; + _itw = 1.0f / _params_.Width; + _ith = 1.0f / _params_.Height; + _texData = new byte[_params_.Width * _params_.Height]; + _colorData = new Color[_params_.Width * _params_.Height]; + Array.Clear(_texData, 0, _texData.Length); + _dirtyRect[0] = _params_.Width; + _dirtyRect[1] = _params_.Height; + _dirtyRect[2] = 0; + _dirtyRect[3] = 0; + AddWhiteRect(2, 2); + ClearState(); + } + + public Texture2D Texture { get; private set; } + + public void AddWhiteRect(int w, int h) + { + var x = 0; + var y = 0; + var gx = 0; + var gy = 0; + if (_atlas.AddRect(w, h, &gx, &gy) == 0) + return; + fixed (byte* dst2 = &_texData[gx + gy * _params_.Width]) + { + var dst = dst2; + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) dst[x] = 0xff; + dst += _params_.Width; + } + } + + _dirtyRect[0] = Math.Min(_dirtyRect[0], gx); + _dirtyRect[1] = Math.Min(_dirtyRect[1], gy); + _dirtyRect[2] = Math.Max(_dirtyRect[2], gx + w); + _dirtyRect[3] = Math.Max(_dirtyRect[3], gy + h); + } + + public int AddFallbackFont(int _base_, int fallback) + { + var baseFont = _fonts[_base_]; + if (baseFont.FallbacksCount < 20) + { + baseFont.Fallbacks[baseFont.FallbacksCount++] = fallback; + return 1; + } + + return 0; + } + public void ClearState() + { + Size = 12.0f; + Color = Color.White; + FontId = 0; + BlurValue = 0; + Spacing = 0; + Alignment = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE; + } + + public int AddFontMem(string name, byte[] data) + { + var i = 0; + var ascent = 0; + var descent = 0; + var fh = 0; + var lineGap = 0; + Font font; + var idx = AllocFont(); + if (idx == -1) + return -1; + font = _fonts[idx]; + font.Name = name; + for (i = 0; i < 256; ++i) font.Lut[i] = -1; + font.Data = data; + fixed (byte* ptr = data) + { + if (LoadFont(font._font, ptr, data.Length) == 0) + goto error; + } + + font._font.fons__tt_getFontVMetrics(&ascent, &descent, &lineGap); + fh = ascent - descent; + font.Ascent = ascent; + font.Ascender = ascent / (float)fh; + font.Descender = descent / (float)fh; + font.LineHeight = (fh + lineGap) / (float)fh; + return idx; + error:; + _fontsNumber--; + return -1; + } + + public int? GetFontByName(string name) + { + var i = 0; + for (i = 0; i < _fontsNumber; i++) + if (_fonts[i].Name == name) + return i; + return null; + } + + public float DrawText(SpriteBatch batch, float x, float y, StringSegment str, float depth) + { + if (str.IsNullOrEmpty) return 0.0f; + + FontGlyph glyph = null; + var q = new FontGlyphSquad(); + var prevGlyphIndex = -1; + var isize = (int)(Size * 10.0f); + var iblur = (int)BlurValue; + float scale = 0; + Font font; + float width = 0; + if (FontId < 0 || FontId >= _fontsNumber) + return x; + font = _fonts[FontId]; + if (font.Data == null) + return x; + scale = font._font.fons__tt_getPixelHeightScale(isize / 10.0f); + + if ((Alignment & FONS_ALIGN_LEFT) != 0) + { + } + else if ((Alignment & FONS_ALIGN_RIGHT) != 0) + { + var bounds = new Bounds(); + width = TextBounds(x, y, str, ref bounds); + x -= width; + } + else if ((Alignment & FONS_ALIGN_CENTER) != 0) + { + var bounds = new Bounds(); + width = TextBounds(x, y, str, ref bounds); + x -= width * 0.5f; + } + + float originX = 0.0f; + float originY = 0.0f; + + originY += GetVertAlign(font, Alignment, isize); + for (int i = 0; i < str.Length; i += Char.IsSurrogatePair(str.String, i + str.Location) ? 2 : 1) + { + var codepoint = Char.ConvertToUtf32(str.String, i + str.Location); + glyph = GetGlyph(font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_REQUIRED); + if (glyph != null) + { + GetQuad(font, prevGlyphIndex, glyph, scale, Spacing, ref originX, ref originY, &q); + if (_vertsNumber + 6 > 1024) + { + Flush(batch, depth); + } + + q.X0 = (int)(q.X0 * Scale.X); + q.X1 = (int)(q.X1 * Scale.X); + q.Y0 = (int)(q.Y0 * Scale.Y); + q.Y1 = (int)(q.Y1 * Scale.Y); + + AddVertex(new Rectangle((int)(x + q.X0), + (int)(y + q.Y0), + (int)(q.X1 - q.X0), + (int)(q.Y1 - q.Y0)), + new Rectangle((int)(q.S0 * _params_.Width), + (int)(q.T0 * _params_.Height), + (int)((q.S1 - q.S0) * _params_.Width), + (int)((q.T1 - q.T0) * _params_.Height)), + Color); + } + + prevGlyphIndex = glyph != null ? glyph.Index : -1; + } + + Flush(batch, depth); + return x; + } + + public float TextBounds(float x, float y, StringSegment str, ref Bounds bounds) + { + var q = new FontGlyphSquad(); + FontGlyph glyph = null; + var prevGlyphIndex = -1; + var isize = (int)(Size * 10.0f); + var iblur = (int)BlurValue; + float scale = 0; + Font font; + float startx = 0; + float advance = 0; + float minx = 0; + float miny = 0; + float maxx = 0; + float maxy = 0; + if (FontId < 0 || FontId >= _fontsNumber) + return 0; + font = _fonts[FontId]; + if (font.Data == null) + return 0; + scale = font._font.fons__tt_getPixelHeightScale(isize / 10.0f); + y += GetVertAlign(font, Alignment, isize); + minx = maxx = x; + miny = maxy = y; + startx = x; + for (int i = 0; i < str.Length; i += char.IsSurrogatePair(str.String, i + str.Location) ? 2 : 1) + { + var codepoint = char.ConvertToUtf32(str.String, i + str.Location); + glyph = GetGlyph(font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL); + if (glyph != null) + { + GetQuad(font, prevGlyphIndex, glyph, scale, Spacing, ref x, ref y, &q); + if (q.X0 < minx) + minx = q.X0; + if (x > maxx) + maxx = x; + if ((_params_.Flags & FONS_ZERO_TOPLEFT) != 0) + { + if (q.Y0 < miny) + miny = q.Y0; + if (q.Y1 > maxy) + maxy = q.Y1; + } + else + { + if (q.Y1 < miny) + miny = q.Y1; + if (q.Y0 > maxy) + maxy = q.Y0; + } + } + + prevGlyphIndex = glyph != null ? glyph.Index : -1; + } + + advance = x - startx; + if ((Alignment & FONS_ALIGN_LEFT) != 0) + { + } + else if ((Alignment & FONS_ALIGN_RIGHT) != 0) + { + minx -= advance; + maxx -= advance; + } + else if ((Alignment & FONS_ALIGN_CENTER) != 0) + { + minx -= advance * 0.5f; + maxx -= advance * 0.5f; + } + + bounds.X = minx; + bounds.Y = miny; + bounds.X2 = maxx; + bounds.Y2 = maxy; + + return advance; + } + + public void VertMetrics(out float ascender, out float descender, out float lineh) + { + ascender = descender = lineh = 0; + Font font; + int isize = 0; + if (FontId < 0 || FontId >= _fontsNumber) + return; + font = _fonts[FontId]; + isize = (int)(Size * 10.0f); + if (font.Data == null) + return; + + ascender = font.Ascender * isize / 10.0f; + descender = font.Descender * isize / 10.0f; + lineh = font.LineHeight * isize / 10.0f; + } + + public void LineBounds(float y, ref float miny, ref float maxy) + { + Font font; + int isize = 0; + if (FontId < 0 || FontId >= _fontsNumber) + return; + font = _fonts[FontId]; + isize = (int)(Size * 10.0f); + if (font.Data == null) + return; + y += GetVertAlign(font, Alignment, isize); + if ((_params_.Flags & FONS_ZERO_TOPLEFT) != 0) + { + miny = y - font.Ascender * isize / 10.0f; + maxy = miny + font.LineHeight * isize / 10.0f; + } + else + { + maxy = y + font.Descender * isize / 10.0f; + miny = maxy - font.LineHeight * isize / 10.0f; + } + } + + public byte[] GetTextureData(out int width, out int height) + { + width = _params_.Width; + height = _params_.Height; + return _texData; + } + + public int ValidateTexture(int* dirty) + { + if (_dirtyRect[0] < _dirtyRect[2] && _dirtyRect[1] < _dirtyRect[3]) + { + dirty[0] = _dirtyRect[0]; + dirty[1] = _dirtyRect[1]; + dirty[2] = _dirtyRect[2]; + dirty[3] = _dirtyRect[3]; + _dirtyRect[0] = _params_.Width; + _dirtyRect[1] = _params_.Height; + _dirtyRect[2] = 0; + _dirtyRect[3] = 0; + return 1; + } + + return 0; + } + + public void GetAtlasSize(out int width, out int height) + { + width = _params_.Width; + height = _params_.Height; + } + + public int ExpandAtlas(int width, int height) + { + var i = 0; + var maxy = 0; + width = Math.Max(width, _params_.Width); + height = Math.Max(height, _params_.Height); + if (width == _params_.Width && height == _params_.Height) + return 1; + + var data = new byte[width * height]; + for (i = 0; i < _params_.Height; i++) + fixed (byte* dst = &data[i * width]) + { + fixed (byte* src = &_texData[i * _params_.Width]) + { + CRuntime.memcpy(dst, src, (ulong)_params_.Width); + if (width > _params_.Width) + CRuntime.memset(dst + _params_.Width, 0, (ulong)(width - _params_.Width)); + } + } + + if (height > _params_.Height) + Array.Clear(data, _params_.Height * width, (height - _params_.Height) * width); + + _texData = data; + + _colorData = new Color[width * height]; + for(i = 0; i < width * height; ++i) + { + _colorData[i].R = _texData[i]; + _colorData[i].G = _texData[i]; + _colorData[i].B = _texData[i]; + _colorData[i].A = _texData[i]; + } + + _atlas.Expand(width, height); + for (i = 0; i < _atlas.NodesNumber; i++) maxy = Math.Max(maxy, _atlas.Nodes[i].Y); + _dirtyRect[0] = 0; + _dirtyRect[1] = 0; + _dirtyRect[2] = _params_.Width; + _dirtyRect[3] = maxy; + _params_.Width = width; + _params_.Height = height; + _itw = 1.0f / _params_.Width; + _ith = 1.0f / _params_.Height; + return 1; + } + + public int ResetAtlas(int width, int height) + { + var i = 0; + var j = 0; + + _atlas.Reset(width, height); + _texData = new byte[width * height]; + Array.Clear(_texData, 0, _texData.Length); + _dirtyRect[0] = width; + _dirtyRect[1] = height; + _dirtyRect[2] = 0; + _dirtyRect[3] = 0; + for (i = 0; i < _fontsNumber; i++) + { + var font = _fonts[i]; + font.GlyphsNumber = 0; + for (j = 0; j < 256; j++) font.Lut[j] = -1; + } + + _params_.Width = width; + _params_.Height = height; + _itw = 1.0f / _params_.Width; + _ith = 1.0f / _params_.Height; + AddWhiteRect(2, 2); + return 1; + } + + private int LoadFont(StbTrueType.stbtt_fontinfo font, byte* data, int dataSize) + { + var stbError = 0; + font.userdata = this; + stbError = StbTrueType.stbtt_InitFont(font, data, 0); + return stbError; + } + + private int AllocFont() + { + Font font = null; + if (_fontsNumber + 1 > _fonts.Length) + { + var newFonts = new Font[_fonts.Length * 2]; + + for (var i = 0; i < _fonts.Length; ++i) + { + newFonts[i] = _fonts[i]; + } + + _fonts = newFonts; + } + + font = new Font + { + Glyphs = new FontGlyph[256], + GlyphsNumber = 0 + }; + + _fonts[_fontsNumber++] = font; + return _fontsNumber - 1; + } + + private void Blur(byte* dst, int w, int h, int dstStride, int blur) + { + var alpha = 0; + float sigma = 0; + if (blur < 1) + return; + sigma = blur * 0.57735f; + alpha = (int)((1 << 16) * (1.0f - Math.Exp(-2.3f / (sigma + 1.0f)))); + BlurRows(dst, w, h, dstStride, alpha); + BlurCols(dst, w, h, dstStride, alpha); + BlurRows(dst, w, h, dstStride, alpha); + BlurCols(dst, w, h, dstStride, alpha); + } + + private FontGlyph GetGlyph(Font font, int codepoint, int isize, int iblur, int bitmapOption) + { + var i = 0; + var g = 0; + var advance = 0; + var lsb = 0; + var x0 = 0; + var y0 = 0; + var x1 = 0; + var y1 = 0; + var gw = 0; + var gh = 0; + var gx = 0; + var gy = 0; + var x = 0; + var y = 0; + float scale = 0; + FontGlyph glyph = null; + int h = 0; + var size = isize / 10.0f; + var pad = 0; + var added = 0; + var renderFont = font; + if (isize < 2) + return null; + if (iblur > 20) + iblur = 20; + pad = iblur + 2; + h = HashInt(codepoint) & (256 - 1); + i = font.Lut[h]; + while (i != -1) + { + if (font.Glyphs[i].Codepoint == codepoint && font.Glyphs[i].Size == isize && + font.Glyphs[i].Blur == iblur) + { + glyph = font.Glyphs[i]; + if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL || glyph.X0 >= 0 && glyph.Y0 >= 0) return glyph; + break; + } + + i = font.Glyphs[i].Next; + } + + g = font._font.fons__tt_getGlyphIndex((int)codepoint); + if (g == 0) + for (i = 0; i < font.FallbacksCount; ++i) + { + var fallbackFont = _fonts[font.Fallbacks[i]]; + var fallbackIndex = fallbackFont._font.fons__tt_getGlyphIndex((int)codepoint); + if (fallbackIndex != 0) + { + g = fallbackIndex; + renderFont = fallbackFont; + break; + } + } + + scale = renderFont._font.fons__tt_getPixelHeightScale(size); + renderFont._font.fons__tt_buildGlyphBitmap(g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); + gw = x1 - x0 + pad * 2; + gh = y1 - y0 + pad * 2; + if (bitmapOption == FONS_GLYPH_BITMAP_REQUIRED) + { + added = _atlas.AddRect(gw, gh, &gx, &gy); + if (added == 0) + throw new Exception("FONS_ATLAS_FULL"); + } + else + { + gx = -1; + gy = -1; + } + + if (glyph == null) + { + glyph = AllocGlyph(font); + glyph.Codepoint = codepoint; + glyph.Size = isize; + glyph.Blur = iblur; + glyph.Next = 0; + glyph.Next = font.Lut[h]; + font.Lut[h] = font.GlyphsNumber - 1; + } + + glyph.Index = g; + glyph.X0 = (int)gx; + glyph.Y0 = (int)gy; + glyph.X1 = (int)(glyph.X0 + gw); + glyph.Y1 = (int)(glyph.Y0 + gh); + glyph.XAdvance = (int)(scale * advance * 10.0f); + glyph.XOffset = (int)(x0 - pad); + glyph.YOffset = (int)(y0 - pad); + if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL) return glyph; + + fixed (byte* dst = &_texData[glyph.X0 + pad + (glyph.Y0 + pad) * _params_.Width]) + { + renderFont._font.fons__tt_renderGlyphBitmap(dst, gw - pad * 2, gh - pad * 2, _params_.Width, scale, + scale, g); + } + + fixed (byte* dst = &_texData[glyph.X0 + glyph.Y0 * _params_.Width]) + { + for (y = 0; y < gh; y++) + { + dst[y * _params_.Width] = 0; + dst[gw - 1 + y * _params_.Width] = 0; + } + + for (x = 0; x < gw; x++) + { + dst[x] = 0; + dst[x + (gh - 1) * _params_.Width] = 0; + } + } + + if (iblur > 0) + { + fixed (byte* bdst = &_texData[glyph.X0 + glyph.Y0 * _params_.Width]) + { + Blur(bdst, gw, gh, _params_.Width, iblur); + } + } + + _dirtyRect[0] = Math.Min(_dirtyRect[0], glyph.X0); + _dirtyRect[1] = Math.Min(_dirtyRect[1], glyph.Y0); + _dirtyRect[2] = Math.Max(_dirtyRect[2], glyph.X1); + _dirtyRect[3] = Math.Max(_dirtyRect[3], glyph.Y1); + return glyph; + } + + private void GetQuad(Font font, int prevGlyphIndex, FontGlyph glyph, float scale, + float spacing, ref float x, ref float y, FontGlyphSquad* q) + { + + if (prevGlyphIndex != -1) + { + var adv = font._font.fons__tt_getGlyphKernAdvance(prevGlyphIndex, glyph.Index) * scale; + x += (int)(adv + spacing + 0.5f); + } + + float rx = 0; + float ry = 0; + + if ((_params_.Flags & FONS_ZERO_TOPLEFT) != 0) + { + rx = x + glyph.XOffset; + ry = y + glyph.YOffset; + q->X0 = rx; + q->Y0 = ry; + q->X1 = rx + (glyph.X1 - glyph.X0); + q->Y1 = ry + (glyph.Y1 - glyph.Y0); + q->S0 = glyph.X0 * _itw; + q->T0 = glyph.Y0 * _ith; + q->S1 = glyph.X1 * _itw; + q->T1 = glyph.Y1 * _ith; + } + else + { + rx = x + glyph.XOffset; + ry = y - glyph.YOffset; + q->X0 = rx; + q->Y0 = ry; + q->X1 = rx + (glyph.X1 - glyph.X0); + q->Y1 = ry - (glyph.Y1 + glyph.Y0); + q->S0 = glyph.X0 * _itw; + q->T0 = glyph.Y0 * _ith; + q->S1 = glyph.X1 * _itw; + q->T1 = glyph.Y1 * _ith; + } + + x += (int)(glyph.XAdvance / 10.0f + 0.5f); + } + + //Add Depth Parameter + private void Flush(SpriteBatch batch, float depth) + { + if (Texture == null) Texture = new Texture2D(batch.GraphicsDevice, _params_.Width, _params_.Height); + + if (_dirtyRect[0] < _dirtyRect[2] && _dirtyRect[1] < _dirtyRect[3]) + { + if (_texData != null) + { + var x = _dirtyRect[0]; + var y = _dirtyRect[1]; + var w = _dirtyRect[2] - x; + var h = _dirtyRect[3] - y; + var sz = w * h; + for (var xx = x; xx < x + w; ++xx) + { + for (var yy = y; yy < y + h; ++yy) + { + var destPos = yy * _params_.Width + xx; + + var c = _texData[destPos]; + _colorData[destPos].R = c; + _colorData[destPos].G = c; + _colorData[destPos].B = c; + _colorData[destPos].A = c; + } + } + + Texture.SetData(_colorData); + } + + _dirtyRect[0] = _params_.Width; + _dirtyRect[1] = _params_.Height; + _dirtyRect[2] = 0; + _dirtyRect[3] = 0; + } + + if (_vertsNumber > 0) + { + for (var i = 0; i < _vertsNumber; ++i) + { + //Add Depth Parameter + batch.Draw(Texture, _verts[i], _textureCoords[i], _colors[i], 0f, Vector2.Zero, SpriteEffects.None, depth); + } + + _vertsNumber = 0; + } + } + + private void AddVertex(Rectangle destRect, Rectangle srcRect, Color c) + { + _verts[_vertsNumber] = destRect; + _textureCoords[_vertsNumber] = srcRect; + _colors[_vertsNumber] = c; + _vertsNumber++; + } + + private float GetVertAlign(Font font, int align, int isize) + { + float result = 0.0f; ; + if ((_params_.Flags & FONS_ZERO_TOPLEFT) != 0) + { + if ((align & FONS_ALIGN_TOP) != 0) + { + result = font.Ascender * isize / 10.0f; + } + else if ((align & FONS_ALIGN_MIDDLE) != 0) + { + result = (font.Ascender + font.Descender) / 2.0f * isize / 10.0f; + } + else if ((align & FONS_ALIGN_BASELINE) != 0) + { + } + else if ((align & FONS_ALIGN_BOTTOM) != 0) + { + result = font.Descender * isize / 10.0f; + } + } + else + { + if ((align & FONS_ALIGN_TOP) != 0) + { + result = -font.Ascender * isize / 10.0f; + } + else + if ((align & FONS_ALIGN_MIDDLE) != 0) + { + result = -(font.Ascender + font.Descender) / 2.0f * isize / 10.0f; + } + else + if ((align & FONS_ALIGN_BASELINE) != 0) + { + } + else if ((align & FONS_ALIGN_BOTTOM) != 0) + { + result = -font.Descender * isize / 10.0f; + } + } + + return result; + } + + private static FontGlyph AllocGlyph(Font font) + { + if (font.GlyphsNumber + 1 > font.Glyphs.Length) + { + var oldGlyphs = font.Glyphs; + font.Glyphs = new FontGlyph[font.Glyphs.Length * 2]; + + for (var i = 0; i < oldGlyphs.Length; ++i) + { + font.Glyphs[i] = oldGlyphs[i]; + } + } + + font.Glyphs[font.GlyphsNumber] = new FontGlyph(); + + font.GlyphsNumber++; + return font.Glyphs[font.GlyphsNumber - 1]; + } + + private static void BlurCols(byte* dst, int w, int h, int dstStride, int alpha) + { + var x = 0; + var y = 0; + for (y = 0; y < h; y++) + { + var z = 0; + for (x = 1; x < w; x++) + { + z += (alpha * ((dst[x] << 7) - z)) >> 16; + dst[x] = (byte)(z >> 7); + } + + dst[w - 1] = 0; + z = 0; + for (x = w - 2; x >= 0; x--) + { + z += (alpha * ((dst[x] << 7) - z)) >> 16; + dst[x] = (byte)(z >> 7); + } + + dst[0] = 0; + dst += dstStride; + } + } + + private static void BlurRows(byte* dst, int w, int h, int dstStride, int alpha) + { + var x = 0; + var y = 0; + for (x = 0; x < w; x++) + { + var z = 0; + for (y = dstStride; y < h * dstStride; y += dstStride) + { + z += (alpha * ((dst[y] << 7) - z)) >> 16; + dst[y] = (byte)(z >> 7); + } + + dst[(h - 1) * dstStride] = 0; + z = 0; + for (y = (h - 2) * dstStride; y >= 0; y -= dstStride) + { + z += (alpha * ((dst[y] << 7) - z)) >> 16; + dst[y] = (byte)(z >> 7); + } + + dst[0] = 0; + dst++; + } + } + + private static int HashInt(int a) + { + a += ~(a << 15); + a ^= a >> 10; + a += a << 3; + a ^= a >> 6; + a += ~(a << 11); + a ^= a >> 16; + return a; + } + } +} \ No newline at end of file diff --git a/SpriteFontPlus/FontStashSharp/FontSystemParams.cs b/SpriteFontPlus/FontStashSharp/FontSystemParams.cs new file mode 100644 index 000000000..02442236d --- /dev/null +++ b/SpriteFontPlus/FontStashSharp/FontSystemParams.cs @@ -0,0 +1,9 @@ +namespace FontStashSharp +{ + internal class FontSystemParams + { + public int Width; + public int Height; + public byte Flags; + } +} \ No newline at end of file diff --git a/SpriteFontPlus/FontStashSharp/GlyphPosition.cs b/SpriteFontPlus/FontStashSharp/GlyphPosition.cs new file mode 100644 index 000000000..52a046dbc --- /dev/null +++ b/SpriteFontPlus/FontStashSharp/GlyphPosition.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace FontStashSharp +{ + [StructLayout(LayoutKind.Sequential)] + internal struct GlyphPosition + { + public StringSegment Str; + public float X; + public float MinX; + public float MaxX; + } +} diff --git a/SpriteFontPlus/FontStashSharp/StringSegment.cs b/SpriteFontPlus/FontStashSharp/StringSegment.cs new file mode 100644 index 000000000..26dc31bc0 --- /dev/null +++ b/SpriteFontPlus/FontStashSharp/StringSegment.cs @@ -0,0 +1,79 @@ +namespace FontStashSharp +{ + internal struct StringSegment + { + public static readonly StringSegment Null; + + public string String; + public int Location; + public int Length; + + public char this[int index] + { + get + { + return String[Location + index]; + } + } + + public bool IsNullOrEmpty + { + get + { + if (String == null) + { + return true; + } + + return Location >= String.Length; + } + } + + public StringSegment(StringSegment s, int location) + { + String = s.String; + Length = s.Length; + Location = location; + } + + public StringSegment(StringSegment s, int location, int length) + { + String = s.String; + Length = length; + Location = location; + } + + public static implicit operator StringSegment(string value) + { + return new StringSegment + { + String = value, + Location = 0, + Length = value != null ? value.Length : 0 + }; + } + + public static bool operator ==(StringSegment a, StringSegment b) + { + return object.ReferenceEquals(a.String, b.String) && + a.Location == b.Location && + a.Length == b.Length; + } + + public static bool operator !=(StringSegment a, StringSegment b) + { + return !(a == b); + } + + public static StringSegment operator +(StringSegment a, int loc) + { + return new StringSegment(a, a.Location + loc); + } + + public void Reset() + { + String = null; + Location = Length = 0; + } + } +} diff --git a/SpriteFontPlus/GlyphInfo.cs b/SpriteFontPlus/GlyphInfo.cs new file mode 100644 index 000000000..4d18a7891 --- /dev/null +++ b/SpriteFontPlus/GlyphInfo.cs @@ -0,0 +1,9 @@ +namespace SpriteFontPlus +{ + public struct GlyphInfo + { + public int X, Y, Width, Height; + public int XOffset, YOffset; + public int XAdvance; + } +} \ No newline at end of file diff --git a/SpriteFontPlus/SpriteBatchExtensions.cs b/SpriteFontPlus/SpriteBatchExtensions.cs new file mode 100644 index 000000000..7e7413a61 --- /dev/null +++ b/SpriteFontPlus/SpriteBatchExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace SpriteFontPlus +{ + public static class SpriteBatchExtensions + { + public static float DrawString(this SpriteBatch batch, DynamicSpriteFont font, + string _string_, Vector2 pos, Color color) + { + return font.DrawString(batch, _string_, pos, color); + } + + public static float DrawString(this SpriteBatch batch, DynamicSpriteFont font, + string _string_, Vector2 pos, Color color, Vector2 scale) + { + return font.DrawString(batch, _string_, pos, color, scale, 0f); + } + } +} \ No newline at end of file diff --git a/SpriteFontPlus/SpriteFontPlus.csproj b/SpriteFontPlus/SpriteFontPlus.csproj new file mode 100644 index 000000000..30cdcc9b5 --- /dev/null +++ b/SpriteFontPlus/SpriteFontPlus.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + true + + + + + + + diff --git a/SpriteFontPlus/StbTrueType/CRuntime.cs b/SpriteFontPlus/StbTrueType/CRuntime.cs new file mode 100644 index 000000000..f84aaf19b --- /dev/null +++ b/SpriteFontPlus/StbTrueType/CRuntime.cs @@ -0,0 +1,316 @@ +using System; +using System.Runtime.InteropServices; + +namespace StbSharp +{ + internal static unsafe class CRuntime + { + public const long DBL_EXP_MASK = 0x7ff0000000000000L; + public const int DBL_MANT_BITS = 52; + public const long DBL_SGN_MASK = -1 - 0x7fffffffffffffffL; + public const long DBL_MANT_MASK = 0x000fffffffffffffL; + public const long DBL_EXP_CLR_MASK = DBL_SGN_MASK | DBL_MANT_MASK; + + public static void* malloc(ulong size) + { + return malloc((long) size); + } + + public static void* malloc(long size) + { + var ptr = Marshal.AllocHGlobal((int) size); + + return ptr.ToPointer(); + } + + public static void memcpy(void* a, void* b, long size) + { + var ap = (byte*) a; + var bp = (byte*) b; + for (long i = 0; i < size; ++i) + { + *ap++ = *bp++; + } + } + + public static void memcpy(void* a, void* b, ulong size) + { + memcpy(a, b, (long) size); + } + + public static void memmove(void* a, void* b, long size) + { + void* temp = null; + + try + { + temp = malloc(size); + memcpy(temp, b, size); + memcpy(a, temp, size); + } + + finally + { + if (temp != null) + { + free(temp); + } + } + } + + public static void memmove(void* a, void* b, ulong size) + { + memmove(a, b, (long) size); + } + + public static int memcmp(void* a, void* b, long size) + { + var result = 0; + var ap = (byte*) a; + var bp = (byte*) b; + for (long i = 0; i < size; ++i) + { + if (*ap != *bp) + { + result += 1; + } + + ap++; + bp++; + } + + return result; + } + + public static int memcmp(void* a, void* b, ulong size) + { + return memcmp(a, b, (long) size); + } + + public static int memcmp(byte* a, byte[] b, ulong size) + { + fixed (void* bptr = b) + { + return memcmp(a, bptr, (long) size); + } + } + + public static void free(void* a) + { + var ptr = new IntPtr(a); + Marshal.FreeHGlobal(ptr); + } + + public static void memset(void* ptr, int value, long size) + { + byte* bptr = (byte*) ptr; + var bval = (byte) value; + for (long i = 0; i < size; ++i) + { + *bptr++ = bval; + } + } + + public static void memset(void* ptr, int value, ulong size) + { + memset(ptr, value, (long) size); + } + + public static uint _lrotl(uint x, int y) + { + return (x << y) | (x >> (32 - y)); + } + + public static void* realloc(void* a, long newSize) + { + if (a == null) + { + return malloc(newSize); + } + + var ptr = new IntPtr(a); + var result = Marshal.ReAllocHGlobal(ptr, new IntPtr(newSize)); + + return result.ToPointer(); + } + + public static void* realloc(void* a, ulong newSize) + { + return realloc(a, (long) newSize); + } + + public static int abs(int v) + { + return Math.Abs(v); + } + + /// + /// This code had been borrowed from here: https://github.com/MachineCognitis/C.math.NET + /// + /// + /// + /// + public static double frexp(double number, int* exponent) + { + var bits = BitConverter.DoubleToInt64Bits(number); + var exp = (int) ((bits & DBL_EXP_MASK) >> DBL_MANT_BITS); + *exponent = 0; + + if (exp == 0x7ff || number == 0D) + number += number; + else + { + // Not zero and finite. + *exponent = exp - 1022; + if (exp == 0) + { + // Subnormal, scale number so that it is in [1, 2). + number *= BitConverter.Int64BitsToDouble(0x4350000000000000L); // 2^54 + bits = BitConverter.DoubleToInt64Bits(number); + exp = (int) ((bits & DBL_EXP_MASK) >> DBL_MANT_BITS); + *exponent = exp - 1022 - 54; + } + + // Set exponent to -1 so that number is in [0.5, 1). + number = BitConverter.Int64BitsToDouble((bits & DBL_EXP_CLR_MASK) | 0x3fe0000000000000L); + } + + return number; + } + + public static double pow(double a, double b) + { + return Math.Pow(a, b); + } + + public static float fabs(double a) + { + return (float) Math.Abs(a); + } + + public static double ceil(double a) + { + return Math.Ceiling(a); + } + + + public static double floor(double a) + { + return Math.Floor(a); + } + + public static double log(double value) + { + return Math.Log(value); + } + + public static double exp(double value) + { + return Math.Exp(value); + } + + public static double cos(double value) + { + return Math.Cos(value); + } + + public static double acos(double value) + { + return Math.Acos(value); + } + + public static double sin(double value) + { + return Math.Sin(value); + } + + public static double ldexp(double number, int exponent) + { + return number * Math.Pow(2, exponent); + } + + public delegate int QSortComparer(void* a, void* b); + + private static void qsortSwap(byte* data, long size, long pos1, long pos2) + { + var a = data + size * pos1; + var b = data + size * pos2; + + for (long k = 0; k < size; ++k) + { + var tmp = *a; + *a = *b; + *b = tmp; + + a++; + b++; + } + } + + private static long qsortPartition(byte* data, long size, QSortComparer comparer, long left, long right) + { + void* pivot = data + size * left; + var i = left - 1; + var j = right + 1; + for (;;) + { + do + { + ++i; + } while (comparer(data + size * i, pivot) < 0); + + do + { + --j; + } while (comparer(data + size * j, pivot) > 0); + + if (i >= j) + { + return j; + } + + qsortSwap(data, size, i, j); + } + } + + + private static void qsortInternal(byte* data, long size, QSortComparer comparer, long left, long right) + { + if (left < right) + { + var p = qsortPartition(data, size, comparer, left, right); + + qsortInternal(data, size, comparer, left, p); + qsortInternal(data, size, comparer, p + 1, right); + } + } + + public static void qsort(void* data, ulong count, ulong size, QSortComparer comparer) + { + qsortInternal((byte*) data, (long) size, comparer, 0, (long) count - 1); + } + + public static double sqrt(double val) + { + return Math.Sqrt(val); + } + + public static double fmod(double x, double y) + { + return x % y; + } + + public static ulong strlen(sbyte* str) + { + ulong res = 0; + var ptr = str; + + while (*ptr != '\0') + { + ptr++; + } + + return ((ulong) ptr - (ulong) str - 1); + } + } +} \ No newline at end of file diff --git a/SpriteFontPlus/StbTrueType/StbTrueType.Generated.cs b/SpriteFontPlus/StbTrueType/StbTrueType.Generated.cs new file mode 100644 index 000000000..e19df33cb --- /dev/null +++ b/SpriteFontPlus/StbTrueType/StbTrueType.Generated.cs @@ -0,0 +1,3852 @@ +using System; +using System.Runtime.InteropServices; + +namespace StbSharp +{ + unsafe partial class StbTrueType + { + public const int STBTT_vmove = 1; + public const int STBTT_vline = 2; + public const int STBTT_vcurve = 3; + public const int STBTT_vcubic = 4; + public const int STBTT_PLATFORM_ID_UNICODE = 0; + public const int STBTT_PLATFORM_ID_MAC = 1; + public const int STBTT_PLATFORM_ID_ISO = 2; + public const int STBTT_PLATFORM_ID_MICROSOFT = 3; + public const int STBTT_UNICODE_EID_UNICODE_1_0 = 0; + public const int STBTT_UNICODE_EID_UNICODE_1_1 = 1; + public const int STBTT_UNICODE_EID_ISO_10646 = 2; + public const int STBTT_UNICODE_EID_UNICODE_2_0_BMP = 3; + public const int STBTT_UNICODE_EID_UNICODE_2_0_FULL = 4; + public const int STBTT_MS_EID_SYMBOL = 0; + public const int STBTT_MS_EID_UNICODE_BMP = 1; + public const int STBTT_MS_EID_SHIFTJIS = 2; + public const int STBTT_MS_EID_UNICODE_FULL = 10; + public const int STBTT_MAC_EID_ROMAN = 0; + public const int STBTT_MAC_EID_ARABIC = 4; + public const int STBTT_MAC_EID_JAPANESE = 1; + public const int STBTT_MAC_EID_HEBREW = 5; + public const int STBTT_MAC_EID_CHINESE_TRAD = 2; + public const int STBTT_MAC_EID_GREEK = 6; + public const int STBTT_MAC_EID_KOREAN = 3; + public const int STBTT_MAC_EID_RUSSIAN = 7; + public const int STBTT_MS_LANG_ENGLISH = 0x0409; + public const int STBTT_MS_LANG_ITALIAN = 0x0410; + public const int STBTT_MS_LANG_CHINESE = 0x0804; + public const int STBTT_MS_LANG_JAPANESE = 0x0411; + public const int STBTT_MS_LANG_DUTCH = 0x0413; + public const int STBTT_MS_LANG_KOREAN = 0x0412; + public const int STBTT_MS_LANG_FRENCH = 0x040c; + public const int STBTT_MS_LANG_RUSSIAN = 0x0419; + public const int STBTT_MS_LANG_GERMAN = 0x0407; + public const int STBTT_MS_LANG_SPANISH = 0x0409; + public const int STBTT_MS_LANG_HEBREW = 0x040d; + public const int STBTT_MS_LANG_SWEDISH = 0x041D; + public const int STBTT_MAC_LANG_ENGLISH = 0; + public const int STBTT_MAC_LANG_JAPANESE = 11; + public const int STBTT_MAC_LANG_ARABIC = 12; + public const int STBTT_MAC_LANG_KOREAN = 23; + public const int STBTT_MAC_LANG_DUTCH = 4; + public const int STBTT_MAC_LANG_RUSSIAN = 32; + public const int STBTT_MAC_LANG_FRENCH = 1; + public const int STBTT_MAC_LANG_SPANISH = 6; + public const int STBTT_MAC_LANG_GERMAN = 2; + public const int STBTT_MAC_LANG_SWEDISH = 5; + public const int STBTT_MAC_LANG_HEBREW = 10; + public const int STBTT_MAC_LANG_CHINESE_SIMPLIFIED = 33; + public const int STBTT_MAC_LANG_ITALIAN = 3; + public const int STBTT_MAC_LANG_CHINESE_TRAD = 19; + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__buf + { + public byte* data; + public int cursor; + public int size; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt_bakedchar + { + public ushort x0; + public ushort y0; + public ushort x1; + public ushort y1; + public float xoff; + public float yoff; + public float xadvance; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt_aligned_quad + { + public float x0; + public float y0; + public float s0; + public float t0; + public float x1; + public float y1; + public float s1; + public float t1; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt_packedchar + { + public ushort x0; + public ushort y0; + public ushort x1; + public ushort y1; + public float xoff; + public float yoff; + public float xadvance; + public float xoff2; + public float yoff2; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt_pack_range + { + public float font_size; + public int first_unicode_codepoint_in_range; + public int* array_of_unicode_codepoints; + public int num_chars; + public stbtt_packedchar* chardata_for_range; + public byte h_oversample; + public byte v_oversample; + } + + [StructLayout(LayoutKind.Sequential)] + public class stbtt_pack_context + { + public object user_allocator_context; + public void* pack_info; + public int width; + public int height; + public int stride_in_bytes; + public int padding; + public uint h_oversample; + public uint v_oversample; + public byte* pixels; + public void* nodes; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe class stbtt_fontinfo + { + public object userdata; + public byte* data; + public int fontstart; + public int numGlyphs; + public int loca; + public int head; + public int glyf; + public int hhea; + public int hmtx; + public int kern; + public int gpos; + public int index_map; + public int indexToLocFormat; + public stbtt__buf cff; + public stbtt__buf charstrings; + public stbtt__buf gsubrs; + public stbtt__buf subrs; + public stbtt__buf fontdicts; + public stbtt__buf fdselect; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt_vertex + { + public short x; + public short y; + public short cx; + public short cy; + public short cx1; + public short cy1; + public byte type; + public byte padding; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__bitmap + { + public int w; + public int h; + public int stride; + public byte* pixels; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__csctx + { + public int bounds; + public int started; + public float first_x; + public float first_y; + public float x; + public float y; + public int min_x; + public int max_x; + public int min_y; + public int max_y; + public stbtt_vertex* pvertices; + public int num_vertices; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__hheap_chunk + { + public stbtt__hheap_chunk* next; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__hheap + { + public stbtt__hheap_chunk* head; + public void* first_free; + public int num_remaining_in_head_chunk; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__edge + { + public float x0; + public float y0; + public float x1; + public float y1; + public int invert; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__active_edge + { + public stbtt__active_edge* next; + public float fx; + public float fdx; + public float fdy; + public float direction; + public float sy; + public float ey; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__point + { + public float x; + public float y; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbrp_context + { + public int width; + public int height; + public int x; + public int y; + public int bottom_y; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbrp_node + { + public byte x; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbrp_rect + { + public int x; + public int y; + public int id; + public int w; + public int h; + public int was_packed; + } + + public static byte stbtt__buf_get8(stbtt__buf* b) + { + if ((b->cursor) >= (b->size)) + return (byte)(0); + return (byte)(b->data[b->cursor++]); + } + + public static byte stbtt__buf_peek8(stbtt__buf* b) + { + if ((b->cursor) >= (b->size)) + return (byte)(0); + return (byte)(b->data[b->cursor]); + } + + public static void stbtt__buf_seek(stbtt__buf* b, int o) + { + b->cursor = (int)((((o) > (b->size)) || ((o) < (0))) ? b->size : o); + } + + public static void stbtt__buf_skip(stbtt__buf* b, int o) + { + stbtt__buf_seek(b, (int)(b->cursor + o)); + } + + public static uint stbtt__buf_get(stbtt__buf* b, int n) + { + uint v = (uint)(0); + int i; + for (i = (int)(0); (i) < (n); i++) + { + v = (uint)((v << 8) | stbtt__buf_get8(b)); + } + return (uint)(v); + } + + public static stbtt__buf stbtt__new_buf(void* p, ulong size) + { + stbtt__buf r = new stbtt__buf(); + r.data = (byte*)(p); + r.size = ((int)(size)); + r.cursor = (int)(0); + return (stbtt__buf)(r); + } + + public static stbtt__buf stbtt__buf_range(stbtt__buf* b, int o, int s) + { + stbtt__buf r = (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + if (((((o) < (0)) || ((s) < (0))) || ((o) > (b->size))) || ((s) > (b->size - o))) + return (stbtt__buf)(r); + r.data = b->data + o; + r.size = (int)(s); + return (stbtt__buf)(r); + } + + public static stbtt__buf stbtt__cff_get_index(stbtt__buf* b) + { + int count; + int start; + int offsize; + start = (int)(b->cursor); + count = (int)(stbtt__buf_get((b), (int)(2))); + if ((count) != 0) + { + offsize = (int)(stbtt__buf_get8(b)); + stbtt__buf_skip(b, (int)(offsize * count)); + stbtt__buf_skip(b, (int)(stbtt__buf_get(b, (int)(offsize)) - 1)); + } + + return (stbtt__buf)(stbtt__buf_range(b, (int)(start), (int)(b->cursor - start))); + } + + public static uint stbtt__cff_int(stbtt__buf* b) + { + int b0 = (int)(stbtt__buf_get8(b)); + if (((b0) >= (32)) && (b0 <= 246)) + return (uint)(b0 - 139); + else if (((b0) >= (247)) && (b0 <= 250)) + return (uint)((b0 - 247) * 256 + stbtt__buf_get8(b) + 108); + else if (((b0) >= (251)) && (b0 <= 254)) + return (uint)(-(b0 - 251) * 256 - stbtt__buf_get8(b) - 108); + else if ((b0) == (28)) + return (uint)(stbtt__buf_get((b), (int)(2))); + else if ((b0) == (29)) + return (uint)(stbtt__buf_get((b), (int)(4))); + return (uint)(0); + } + + public static void stbtt__cff_skip_operand(stbtt__buf* b) + { + int v; + int b0 = (int)(stbtt__buf_peek8(b)); + if ((b0) == (30)) + { + stbtt__buf_skip(b, (int)(1)); + while ((b->cursor) < (b->size)) + { + v = (int)(stbtt__buf_get8(b)); + if (((v & 0xF) == (0xF)) || ((v >> 4) == (0xF))) + break; + } + } + else + { + stbtt__cff_int(b); + } + + } + + public static stbtt__buf stbtt__dict_get(stbtt__buf* b, int key) + { + stbtt__buf_seek(b, (int)(0)); + while ((b->cursor) < (b->size)) + { + int start = (int)(b->cursor); + int end; + int op; + while ((stbtt__buf_peek8(b)) >= (28)) + { + stbtt__cff_skip_operand(b); + } + end = (int)(b->cursor); + op = (int)(stbtt__buf_get8(b)); + if ((op) == (12)) + op = (int)(stbtt__buf_get8(b) | 0x100); + if ((op) == (key)) + return (stbtt__buf)(stbtt__buf_range(b, (int)(start), (int)(end - start))); + } + return (stbtt__buf)(stbtt__buf_range(b, (int)(0), (int)(0))); + } + + public static void stbtt__dict_get_ints(stbtt__buf* b, int key, int outcount, uint* _out_) + { + int i; + stbtt__buf operands = (stbtt__buf)(stbtt__dict_get(b, (int)(key))); + for (i = (int)(0); ((i) < (outcount)) && ((operands.cursor) < (operands.size)); i++) + { + _out_[i] = (uint)(stbtt__cff_int(&operands)); + } + } + + public static int stbtt__cff_index_count(stbtt__buf* b) + { + stbtt__buf_seek(b, (int)(0)); + return (int)(stbtt__buf_get((b), (int)(2))); + } + + public static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) + { + int count; + int offsize; + int start; + int end; + stbtt__buf_seek(&b, (int)(0)); + count = (int)(stbtt__buf_get((&b), (int)(2))); + offsize = (int)(stbtt__buf_get8(&b)); + stbtt__buf_skip(&b, (int)(i * offsize)); + start = (int)(stbtt__buf_get(&b, (int)(offsize))); + end = (int)(stbtt__buf_get(&b, (int)(offsize))); + return (stbtt__buf)(stbtt__buf_range(&b, (int)(2 + (count + 1) * offsize + start), (int)(end - start))); + } + + public static ushort ttUSHORT(byte* p) + { + return (ushort)(p[0] * 256 + p[1]); + } + + public static short ttSHORT(byte* p) + { + return (short)(p[0] * 256 + p[1]); + } + + public static uint ttULONG(byte* p) + { + return (uint)((p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]); + } + + public static int ttLONG(byte* p) + { + return (int)((p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]); + } + + public static int stbtt__isfont(byte* font) + { + if (((((((font)[0]) == ('1')) && (((font)[1]) == (0))) && (((font)[2]) == (0))) && (((font)[3]) == (0)))) + return (int)(1); + if (((((((font)[0]) == ("typ1"[0])) && (((font)[1]) == ("typ1"[1]))) && (((font)[2]) == ("typ1"[2]))) && (((font)[3]) == ("typ1"[3])))) + return (int)(1); + if (((((((font)[0]) == ("OTTO"[0])) && (((font)[1]) == ("OTTO"[1]))) && (((font)[2]) == ("OTTO"[2]))) && (((font)[3]) == ("OTTO"[3])))) + return (int)(1); + if (((((((font)[0]) == (0)) && (((font)[1]) == (1))) && (((font)[2]) == (0))) && (((font)[3]) == (0)))) + return (int)(1); + if (((((((font)[0]) == ("true"[0])) && (((font)[1]) == ("true"[1]))) && (((font)[2]) == ("true"[2]))) && (((font)[3]) == ("true"[3])))) + return (int)(1); + return (int)(0); + } + + public static int stbtt_GetFontOffsetForIndex_internal(byte* font_collection, int index) + { + if ((stbtt__isfont(font_collection)) != 0) + return (int)((index) == (0) ? 0 : -1); + if (((((((font_collection)[0]) == ("ttcf"[0])) && (((font_collection)[1]) == ("ttcf"[1]))) && (((font_collection)[2]) == ("ttcf"[2]))) && (((font_collection)[3]) == ("ttcf"[3])))) + { + if (((ttULONG(font_collection + 4)) == (0x00010000)) || ((ttULONG(font_collection + 4)) == (0x00020000))) + { + int n = (int)(ttLONG(font_collection + 8)); + if ((index) >= (n)) + return (int)(-1); + return (int)(ttULONG(font_collection + 12 + index * 4)); + } + } + + return (int)(-1); + } + + public static int stbtt_GetNumberOfFonts_internal(byte* font_collection) + { + if ((stbtt__isfont(font_collection)) != 0) + return (int)(1); + if (((((((font_collection)[0]) == ("ttcf"[0])) && (((font_collection)[1]) == ("ttcf"[1]))) && (((font_collection)[2]) == ("ttcf"[2]))) && (((font_collection)[3]) == ("ttcf"[3])))) + { + if (((ttULONG(font_collection + 4)) == (0x00010000)) || ((ttULONG(font_collection + 4)) == (0x00020000))) + { + return (int)(ttLONG(font_collection + 8)); + } + } + + return (int)(0); + } + + public static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) + { + uint subrsoff = (uint)(0); + uint* private_loc = stackalloc uint[2]; + private_loc[0] = (uint)(0); + private_loc[1] = (uint)(0); + + stbtt__buf pdict = new stbtt__buf(); + stbtt__dict_get_ints(&fontdict, (int)(18), (int)(2), private_loc); + if ((private_loc[1] == 0) || (private_loc[0] == 0)) + return (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + pdict = (stbtt__buf)(stbtt__buf_range(&cff, (int)(private_loc[1]), (int)(private_loc[0]))); + stbtt__dict_get_ints(&pdict, (int)(19), (int)(1), &subrsoff); + if (subrsoff == 0) + return (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + stbtt__buf_seek(&cff, (int)(private_loc[1] + subrsoff)); + return (stbtt__buf)(stbtt__cff_get_index(&cff)); + } + + public static int stbtt_InitFont_internal(stbtt_fontinfo info, byte* data, int fontstart) + { + uint cmap; + uint t; + int i; + int numTables; + info.data = data; + info.fontstart = (int)(fontstart); + info.cff = (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + cmap = (uint)(stbtt__find_table(data, (uint)(fontstart), "cmap")); + info.loca = (int)(stbtt__find_table(data, (uint)(fontstart), "loca")); + info.head = (int)(stbtt__find_table(data, (uint)(fontstart), "head")); + info.glyf = (int)(stbtt__find_table(data, (uint)(fontstart), "glyf")); + info.hhea = (int)(stbtt__find_table(data, (uint)(fontstart), "hhea")); + info.hmtx = (int)(stbtt__find_table(data, (uint)(fontstart), "hmtx")); + info.kern = (int)(stbtt__find_table(data, (uint)(fontstart), "kern")); + info.gpos = (int)(stbtt__find_table(data, (uint)(fontstart), "GPOS")); + if ((((cmap == 0) || (info.head == 0)) || (info.hhea == 0)) || (info.hmtx == 0)) + return (int)(0); + if ((info.glyf) != 0) + { + if (info.loca == 0) + return (int)(0); + } + else + { + stbtt__buf b = new stbtt__buf(); + stbtt__buf topdict = new stbtt__buf(); + stbtt__buf topdictidx = new stbtt__buf(); + uint cstype = (uint)(2); + uint charstrings = (uint)(0); + uint fdarrayoff = (uint)(0); + uint fdselectoff = (uint)(0); + uint cff; + cff = (uint)(stbtt__find_table(data, (uint)(fontstart), "CFF ")); + if (cff == 0) + return (int)(0); + info.fontdicts = (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + info.fdselect = (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + info.cff = (stbtt__buf)(stbtt__new_buf(data + cff, (ulong)(512 * 1024 * 1024))); + b = (stbtt__buf)(info.cff); + stbtt__buf_skip(&b, (int)(2)); + stbtt__buf_seek(&b, (int)(stbtt__buf_get8(&b))); + stbtt__cff_get_index(&b); + topdictidx = (stbtt__buf)(stbtt__cff_get_index(&b)); + topdict = (stbtt__buf)(stbtt__cff_index_get((stbtt__buf)(topdictidx), (int)(0))); + stbtt__cff_get_index(&b); + info.gsubrs = (stbtt__buf)(stbtt__cff_get_index(&b)); + stbtt__dict_get_ints(&topdict, (int)(17), (int)(1), &charstrings); + stbtt__dict_get_ints(&topdict, (int)(0x100 | 6), (int)(1), &cstype); + stbtt__dict_get_ints(&topdict, (int)(0x100 | 36), (int)(1), &fdarrayoff); + stbtt__dict_get_ints(&topdict, (int)(0x100 | 37), (int)(1), &fdselectoff); + info.subrs = (stbtt__buf)(stbtt__get_subrs((stbtt__buf)(b), (stbtt__buf)(topdict))); + if (cstype != 2) + return (int)(0); + if ((charstrings) == (0)) + return (int)(0); + if ((fdarrayoff) != 0) + { + if (fdselectoff == 0) + return (int)(0); + stbtt__buf_seek(&b, (int)(fdarrayoff)); + info.fontdicts = (stbtt__buf)(stbtt__cff_get_index(&b)); + info.fdselect = (stbtt__buf)(stbtt__buf_range(&b, (int)(fdselectoff), (int)(b.size - fdselectoff))); + } + stbtt__buf_seek(&b, (int)(charstrings)); + info.charstrings = (stbtt__buf)(stbtt__cff_get_index(&b)); + } + + t = (uint)(stbtt__find_table(data, (uint)(fontstart), "maxp")); + if ((t) != 0) + info.numGlyphs = (int)(ttUSHORT(data + t + 4)); + else + info.numGlyphs = (int)(0xffff); + numTables = (int)(ttUSHORT(data + cmap + 2)); + info.index_map = (int)(0); + for (i = (int)(0); (i) < (numTables); ++i) + { + uint encoding_record = (uint)(cmap + 4 + 8 * i); + switch (ttUSHORT(data + encoding_record)) + { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data + encoding_record + 2)) + { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + info.index_map = (int)(cmap + ttULONG(data + encoding_record + 4)); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + info.index_map = (int)(cmap + ttULONG(data + encoding_record + 4)); + break; + } + } + if ((info.index_map) == (0)) + return (int)(0); + info.indexToLocFormat = (int)(ttUSHORT(data + info.head + 50)); + return (int)(1); + } + + public static int stbtt_FindGlyphIndex(stbtt_fontinfo info, int unicode_codepoint) + { + byte* data = info.data; + uint index_map = (uint)(info.index_map); + ushort format = (ushort)(ttUSHORT(data + index_map + 0)); + if ((format) == (0)) + { + int bytes = (int)(ttUSHORT(data + index_map + 2)); + if ((unicode_codepoint) < (bytes - 6)) + return (int)(*(data + index_map + 6 + unicode_codepoint)); + return (int)(0); + } + else if ((format) == (6)) + { + uint first = (uint)(ttUSHORT(data + index_map + 6)); + uint count = (uint)(ttUSHORT(data + index_map + 8)); + if ((((uint)(unicode_codepoint)) >= (first)) && (((uint)(unicode_codepoint)) < (first + count))) + return (int)(ttUSHORT(data + index_map + 10 + (unicode_codepoint - first) * 2)); + return (int)(0); + } + else if ((format) == (2)) + { + return (int)(0); + } + else if ((format) == (4)) + { + ushort segcount = (ushort)(ttUSHORT(data + index_map + 6) >> 1); + ushort searchRange = (ushort)(ttUSHORT(data + index_map + 8) >> 1); + ushort entrySelector = (ushort)(ttUSHORT(data + index_map + 10)); + ushort rangeShift = (ushort)(ttUSHORT(data + index_map + 12) >> 1); + uint endCount = (uint)(index_map + 14); + uint search = (uint)(endCount); + if ((unicode_codepoint) > (0xffff)) + return (int)(0); + if ((unicode_codepoint) >= (ttUSHORT(data + search + rangeShift * 2))) + search += (uint)(rangeShift * 2); + search -= (uint)(2); + while ((entrySelector) != 0) + { + ushort end; + searchRange >>= 1; + end = (ushort)(ttUSHORT(data + search + searchRange * 2)); + if ((unicode_codepoint) > (end)) + search += (uint)(searchRange * 2); + --entrySelector; + } + search += (uint)(2); + { + ushort offset; + ushort start; + ushort item = (ushort)((search - endCount) >> 1); + start = (ushort)(ttUSHORT(data + index_map + 14 + segcount * 2 + 2 + 2 * item)); + if ((unicode_codepoint) < (start)) + return (int)(0); + offset = (ushort)(ttUSHORT(data + index_map + 14 + segcount * 6 + 2 + 2 * item)); + if ((offset) == (0)) + return (int)((ushort)(unicode_codepoint + ttSHORT(data + index_map + 14 + segcount * 4 + 2 + 2 * item))); + return (int)(ttUSHORT(data + offset + (unicode_codepoint - start) * 2 + index_map + 14 + segcount * 6 + 2 + 2 * item)); + } + } + else if (((format) == (12)) || ((format) == (13))) + { + uint ngroups = (uint)(ttULONG(data + index_map + 12)); + int low; + int high; + low = (int)(0); + high = ((int)(ngroups)); + while ((low) < (high)) + { + int mid = (int)(low + ((high - low) >> 1)); + uint start_char = (uint)(ttULONG(data + index_map + 16 + mid * 12)); + uint end_char = (uint)(ttULONG(data + index_map + 16 + mid * 12 + 4)); + if (((uint)(unicode_codepoint)) < (start_char)) + high = (int)(mid); + else if (((uint)(unicode_codepoint)) > (end_char)) + low = (int)(mid + 1); + else + { + uint start_glyph = (uint)(ttULONG(data + index_map + 16 + mid * 12 + 8)); + if ((format) == (12)) + return (int)(start_glyph + unicode_codepoint - start_char); + else + return (int)(start_glyph); + } + } + return (int)(0); + } + + return (int)(0); + } + + public static int stbtt_GetCodepointShape(stbtt_fontinfo info, int unicode_codepoint, stbtt_vertex** vertices) + { + return (int)(stbtt_GetGlyphShape(info, (int)(stbtt_FindGlyphIndex(info, (int)(unicode_codepoint))), vertices)); + } + + public static void stbtt_setvertex(stbtt_vertex* v, byte type, int x, int y, int cx, int cy) + { + v->type = (byte)(type); + v->x = ((short)(x)); + v->y = ((short)(y)); + v->cx = ((short)(cx)); + v->cy = ((short)(cy)); + } + + public static int stbtt__GetGlyfOffset(stbtt_fontinfo info, int glyph_index) + { + int g1; + int g2; + if ((glyph_index) >= (info.numGlyphs)) + return (int)(-1); + if ((info.indexToLocFormat) >= (2)) + return (int)(-1); + if ((info.indexToLocFormat) == (0)) + { + g1 = (int)(info.glyf + ttUSHORT(info.data + info.loca + glyph_index * 2) * 2); + g2 = (int)(info.glyf + ttUSHORT(info.data + info.loca + glyph_index * 2 + 2) * 2); + } + else + { + g1 = (int)(info.glyf + ttULONG(info.data + info.loca + glyph_index * 4)); + g2 = (int)(info.glyf + ttULONG(info.data + info.loca + glyph_index * 4 + 4)); + } + + return (int)((g1) == (g2) ? -1 : g1); + } + + public static int stbtt_GetGlyphBox(stbtt_fontinfo info, int glyph_index, int* x0, int* y0, int* x1, int* y1) + { + if ((info.cff.size) != 0) + { + stbtt__GetGlyphInfoT2(info, (int)(glyph_index), x0, y0, x1, y1); + } + else + { + int g = (int)(stbtt__GetGlyfOffset(info, (int)(glyph_index))); + if ((g) < (0)) + return (int)(0); + if ((x0) != null) + *x0 = (int)(ttSHORT(info.data + g + 2)); + if ((y0) != null) + *y0 = (int)(ttSHORT(info.data + g + 4)); + if ((x1) != null) + *x1 = (int)(ttSHORT(info.data + g + 6)); + if ((y1) != null) + *y1 = (int)(ttSHORT(info.data + g + 8)); + } + + return (int)(1); + } + + public static int stbtt_GetCodepointBox(stbtt_fontinfo info, int codepoint, int* x0, int* y0, int* x1, int* y1) + { + return (int)(stbtt_GetGlyphBox(info, (int)(stbtt_FindGlyphIndex(info, (int)(codepoint))), x0, y0, x1, y1)); + } + + public static int stbtt_IsGlyphEmpty(stbtt_fontinfo info, int glyph_index) + { + short numberOfContours; + int g; + if ((info.cff.size) != 0) + return (int)((stbtt__GetGlyphInfoT2(info, (int)(glyph_index), (null), (null), (null), (null))) == (0) ? 1 : 0); + g = (int)(stbtt__GetGlyfOffset(info, (int)(glyph_index))); + if ((g) < (0)) + return (int)(1); + numberOfContours = (short)(ttSHORT(info.data + g)); + return (int)((numberOfContours) == (0) ? 1 : 0); + } + + public static int stbtt__close_shape(stbtt_vertex* vertices, int num_vertices, int was_off, int start_off, int sx, int sy, int scx, int scy, int cx, int cy) + { + if ((start_off) != 0) + { + if ((was_off) != 0) + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vcurve), (int)((cx + scx) >> 1), (int)((cy + scy) >> 1), (int)(cx), (int)(cy)); + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vcurve), (int)(sx), (int)(sy), (int)(scx), (int)(scy)); + } + else + { + if ((was_off) != 0) + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vcurve), (int)(sx), (int)(sy), (int)(cx), (int)(cy)); + else + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vline), (int)(sx), (int)(sy), (int)(0), (int)(0)); + } + + return (int)(num_vertices); + } + + public static int stbtt__GetGlyphShapeTT(stbtt_fontinfo info, int glyph_index, stbtt_vertex** pvertices) + { + short numberOfContours; + byte* endPtsOfContours; + byte* data = info.data; + stbtt_vertex* vertices = null; + int num_vertices = (int)(0); + int g = (int)(stbtt__GetGlyfOffset(info, (int)(glyph_index))); + *pvertices = (null); + if ((g) < (0)) + return (int)(0); + numberOfContours = (short)(ttSHORT(data + g)); + if ((numberOfContours) > (0)) + { + byte flags = (byte)(0); + byte flagcount; + int ins; + int i; + int j = (int)(0); + int m; + int n; + int next_move; + int was_off = (int)(0); + int off; + int start_off = (int)(0); + int x; + int y; + int cx; + int cy; + int sx; + int sy; + int scx; + int scy; + byte* points; + endPtsOfContours = (data + g + 10); + ins = (int)(ttUSHORT(data + g + 10 + numberOfContours * 2)); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + n = (int)(1 + ttUSHORT(endPtsOfContours + numberOfContours * 2 - 2)); + m = (int)(n + 2 * numberOfContours); + vertices = (stbtt_vertex*)(CRuntime.malloc((ulong)(m * sizeof(stbtt_vertex)))); + if ((vertices) == (null)) + return (int)(0); + next_move = (int)(0); + flagcount = (byte)(0); + off = (int)(m - n); + for (i = (int)(0); (i) < (n); ++i) + { + if ((flagcount) == (0)) + { + flags = (byte)(*points++); + if ((flags & 8) != 0) + flagcount = (byte)(*points++); + } + else + --flagcount; + vertices[off + i].type = (byte)(flags); + } + x = (int)(0); + for (i = (int)(0); (i) < (n); ++i) + { + flags = (byte)(vertices[off + i].type); + if ((flags & 2) != 0) + { + short dx = (short)(*points++); + x += (int)((flags & 16) != 0 ? dx : -dx); + } + else + { + if ((flags & 16) == 0) + { + x = (int)(x + (short)(points[0] * 256 + points[1])); + points += 2; + } + } + vertices[off + i].x = ((short)(x)); + } + y = (int)(0); + for (i = (int)(0); (i) < (n); ++i) + { + flags = (byte)(vertices[off + i].type); + if ((flags & 4) != 0) + { + short dy = (short)(*points++); + y += (int)((flags & 32) != 0 ? dy : -dy); + } + else + { + if ((flags & 32) == 0) + { + y = (int)(y + (short)(points[0] * 256 + points[1])); + points += 2; + } + } + vertices[off + i].y = ((short)(y)); + } + num_vertices = (int)(0); + sx = (int)(sy = (int)(cx = (int)(cy = (int)(scx = (int)(scy = (int)(0)))))); + for (i = (int)(0); (i) < (n); ++i) + { + flags = (byte)(vertices[off + i].type); + x = (int)(vertices[off + i].x); + y = (int)(vertices[off + i].y); + if ((next_move) == (i)) + { + if (i != 0) + num_vertices = (int)(stbtt__close_shape(vertices, (int)(num_vertices), (int)(was_off), (int)(start_off), (int)(sx), (int)(sy), (int)(scx), (int)(scy), (int)(cx), (int)(cy))); + start_off = ((flags & 1) != 0 ? 0 : 1); + if ((start_off) != 0) + { + scx = (int)(x); + scy = (int)(y); + if ((vertices[off + i + 1].type & 1) == 0) + { + sx = (int)((x + (int)(vertices[off + i + 1].x)) >> 1); + sy = (int)((y + (int)(vertices[off + i + 1].y)) >> 1); + } + else + { + sx = ((int)(vertices[off + i + 1].x)); + sy = ((int)(vertices[off + i + 1].y)); + ++i; + } + } + else + { + sx = (int)(x); + sy = (int)(y); + } + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vmove), (int)(sx), (int)(sy), (int)(0), (int)(0)); + was_off = (int)(0); + next_move = (int)(1 + ttUSHORT(endPtsOfContours + j * 2)); + ++j; + } + else + { + if ((flags & 1) == 0) + { + if ((was_off) != 0) + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vcurve), (int)((cx + x) >> 1), (int)((cy + y) >> 1), (int)(cx), (int)(cy)); + cx = (int)(x); + cy = (int)(y); + was_off = (int)(1); + } + else + { + if ((was_off) != 0) + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vcurve), (int)(x), (int)(y), (int)(cx), (int)(cy)); + else + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vline), (int)(x), (int)(y), (int)(0), (int)(0)); + was_off = (int)(0); + } + } + } + num_vertices = (int)(stbtt__close_shape(vertices, (int)(num_vertices), (int)(was_off), (int)(start_off), (int)(sx), (int)(sy), (int)(scx), (int)(scy), (int)(cx), (int)(cy))); + } + else if ((numberOfContours) == (-1)) + { + int more = (int)(1); + byte* comp = data + g + 10; + num_vertices = (int)(0); + vertices = null; + while ((more) != 0) + { + ushort flags; + ushort gidx; + int comp_num_verts = (int)(0); + int i; + stbtt_vertex* comp_verts = null; + stbtt_vertex* tmp = null; + float* mtx = stackalloc float[6]; + mtx[0] = (float)(1); + mtx[1] = (float)(0); + mtx[2] = (float)(0); + mtx[3] = (float)(1); + mtx[4] = (float)(0); + mtx[5] = (float)(0); + float m; + float n; + flags = (ushort)(ttSHORT(comp)); + comp += 2; + gidx = (ushort)(ttSHORT(comp)); + comp += 2; + if ((flags & 2) != 0) + { + if ((flags & 1) != 0) + { + mtx[4] = (float)(ttSHORT(comp)); + comp += 2; + mtx[5] = (float)(ttSHORT(comp)); + comp += 2; + } + else + { + mtx[4] = (float)(*(sbyte*)(comp)); + comp += 1; + mtx[5] = (float)(*(sbyte*)(comp)); + comp += 1; + } + } + else + { + } + if ((flags & (1 << 3)) != 0) + { + mtx[0] = (float)(mtx[3] = (float)(ttSHORT(comp) / 16384.0f)); + comp += 2; + mtx[1] = (float)(mtx[2] = (float)(0)); + } + else if ((flags & (1 << 6)) != 0) + { + mtx[0] = (float)(ttSHORT(comp) / 16384.0f); + comp += 2; + mtx[1] = (float)(mtx[2] = (float)(0)); + mtx[3] = (float)(ttSHORT(comp) / 16384.0f); + comp += 2; + } + else if ((flags & (1 << 7)) != 0) + { + mtx[0] = (float)(ttSHORT(comp) / 16384.0f); + comp += 2; + mtx[1] = (float)(ttSHORT(comp) / 16384.0f); + comp += 2; + mtx[2] = (float)(ttSHORT(comp) / 16384.0f); + comp += 2; + mtx[3] = (float)(ttSHORT(comp) / 16384.0f); + comp += 2; + } + m = ((float)(CRuntime.sqrt((double)(mtx[0] * mtx[0] + mtx[1] * mtx[1])))); + n = ((float)(CRuntime.sqrt((double)(mtx[2] * mtx[2] + mtx[3] * mtx[3])))); + comp_num_verts = (int)(stbtt_GetGlyphShape(info, (int)(gidx), &comp_verts)); + if ((comp_num_verts) > (0)) + { + for (i = (int)(0); (i) < (comp_num_verts); ++i) + { + stbtt_vertex* v = &comp_verts[i]; + short x; + short y; + x = (short)(v->x); + y = (short)(v->y); + v->x = ((short)(m * (mtx[0] * x + mtx[2] * y + mtx[4]))); + v->y = ((short)(n * (mtx[1] * x + mtx[3] * y + mtx[5]))); + x = (short)(v->cx); + y = (short)(v->cy); + v->cx = ((short)(m * (mtx[0] * x + mtx[2] * y + mtx[4]))); + v->cy = ((short)(n * (mtx[1] * x + mtx[3] * y + mtx[5]))); + } + tmp = (stbtt_vertex*)(CRuntime.malloc((ulong)((num_vertices + comp_num_verts) * sizeof(stbtt_vertex)))); + if (tmp == null) + { + if ((vertices) != null) + CRuntime.free(vertices); + if ((comp_verts) != null) + CRuntime.free(comp_verts); + return (int)(0); + } + if ((num_vertices) > (0)) + CRuntime.memcpy(tmp, vertices, (ulong)(num_vertices * sizeof(stbtt_vertex))); + CRuntime.memcpy(tmp + num_vertices, comp_verts, (ulong)(comp_num_verts * sizeof(stbtt_vertex))); + if ((vertices) != null) + CRuntime.free(vertices); + vertices = tmp; + CRuntime.free(comp_verts); + num_vertices += (int)(comp_num_verts); + } + more = (int)(flags & (1 << 5)); + } + } + else if ((numberOfContours) < (0)) + { + } + else + { + } + + *pvertices = vertices; + return (int)(num_vertices); + } + + public static void stbtt__track_vertex(stbtt__csctx* c, int x, int y) + { + if (((x) > (c->max_x)) || (c->started == 0)) + c->max_x = (int)(x); + if (((y) > (c->max_y)) || (c->started == 0)) + c->max_y = (int)(y); + if (((x) < (c->min_x)) || (c->started == 0)) + c->min_x = (int)(x); + if (((y) < (c->min_y)) || (c->started == 0)) + c->min_y = (int)(y); + c->started = (int)(1); + } + + public static void stbtt__csctx_v(stbtt__csctx* c, byte type, int x, int y, int cx, int cy, int cx1, int cy1) + { + if ((c->bounds) != 0) + { + stbtt__track_vertex(c, (int)(x), (int)(y)); + if ((type) == (STBTT_vcubic)) + { + stbtt__track_vertex(c, (int)(cx), (int)(cy)); + stbtt__track_vertex(c, (int)(cx1), (int)(cy1)); + } + } + else + { + stbtt_setvertex(&c->pvertices[c->num_vertices], (byte)(type), (int)(x), (int)(y), (int)(cx), (int)(cy)); + c->pvertices[c->num_vertices].cx1 = ((short)(cx1)); + c->pvertices[c->num_vertices].cy1 = ((short)(cy1)); + } + + c->num_vertices++; + } + + public static void stbtt__csctx_close_shape(stbtt__csctx* ctx) + { + if ((ctx->first_x != ctx->x) || (ctx->first_y != ctx->y)) + stbtt__csctx_v(ctx, (byte)(STBTT_vline), (int)(ctx->first_x), (int)(ctx->first_y), (int)(0), (int)(0), (int)(0), (int)(0)); + } + + public static void stbtt__csctx_rmove_to(stbtt__csctx* ctx, float dx, float dy) + { + stbtt__csctx_close_shape(ctx); + ctx->first_x = (float)(ctx->x = (float)(ctx->x + dx)); + ctx->first_y = (float)(ctx->y = (float)(ctx->y + dy)); + stbtt__csctx_v(ctx, (byte)(STBTT_vmove), (int)(ctx->x), (int)(ctx->y), (int)(0), (int)(0), (int)(0), (int)(0)); + } + + public static void stbtt__csctx_rline_to(stbtt__csctx* ctx, float dx, float dy) + { + ctx->x += (float)(dx); + ctx->y += (float)(dy); + stbtt__csctx_v(ctx, (byte)(STBTT_vline), (int)(ctx->x), (int)(ctx->y), (int)(0), (int)(0), (int)(0), (int)(0)); + } + + public static void stbtt__csctx_rccurve_to(stbtt__csctx* ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) + { + float cx1 = (float)(ctx->x + dx1); + float cy1 = (float)(ctx->y + dy1); + float cx2 = (float)(cx1 + dx2); + float cy2 = (float)(cy1 + dy2); + ctx->x = (float)(cx2 + dx3); + ctx->y = (float)(cy2 + dy3); + stbtt__csctx_v(ctx, (byte)(STBTT_vcubic), (int)(ctx->x), (int)(ctx->y), (int)(cx1), (int)(cy1), (int)(cx2), (int)(cy2)); + } + + public static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) + { + int count = (int)(stbtt__cff_index_count(&idx)); + int bias = (int)(107); + if ((count) >= (33900)) + bias = (int)(32768); + else if ((count) >= (1240)) + bias = (int)(1131); + n += (int)(bias); + if (((n) < (0)) || ((n) >= (count))) + return (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + return (stbtt__buf)(stbtt__cff_index_get((stbtt__buf)(idx), (int)(n))); + } + + public static stbtt__buf stbtt__cid_get_glyph_subrs(stbtt_fontinfo info, int glyph_index) + { + stbtt__buf fdselect = (stbtt__buf)(info.fdselect); + int nranges; + int start; + int end; + int v; + int fmt; + int fdselector = (int)(-1); + int i; + stbtt__buf_seek(&fdselect, (int)(0)); + fmt = (int)(stbtt__buf_get8(&fdselect)); + if ((fmt) == (0)) + { + stbtt__buf_skip(&fdselect, (int)(glyph_index)); + fdselector = (int)(stbtt__buf_get8(&fdselect)); + } + else if ((fmt) == (3)) + { + nranges = (int)(stbtt__buf_get((&fdselect), (int)(2))); + start = (int)(stbtt__buf_get((&fdselect), (int)(2))); + for (i = (int)(0); (i) < (nranges); i++) + { + v = (int)(stbtt__buf_get8(&fdselect)); + end = (int)(stbtt__buf_get((&fdselect), (int)(2))); + if (((glyph_index) >= (start)) && ((glyph_index) < (end))) + { + fdselector = (int)(v); + break; + } + start = (int)(end); + } + } + + if ((fdselector) == (-1)) + stbtt__new_buf((null), (ulong)(0)); + return (stbtt__buf)(stbtt__get_subrs((stbtt__buf)(info.cff), (stbtt__buf)(stbtt__cff_index_get((stbtt__buf)(info.fontdicts), (int)(fdselector))))); + } + + public static int stbtt__run_charstring(stbtt_fontinfo info, int glyph_index, stbtt__csctx* c) + { + int in_header = (int)(1); + int maskbits = (int)(0); + int subr_stack_height = (int)(0); + int sp = (int)(0); + int v; + int i; + int b0; + int has_subrs = (int)(0); + int clear_stack; + float* s = stackalloc float[48]; + stbtt__buf* subr_stack = stackalloc stbtt__buf[10]; + stbtt__buf subrs = (stbtt__buf)(info.subrs); + stbtt__buf b = new stbtt__buf(); + float f; + b = (stbtt__buf)(stbtt__cff_index_get((stbtt__buf)(info.charstrings), (int)(glyph_index))); + while ((b.cursor) < (b.size)) + { + i = (int)(0); + clear_stack = (int)(1); + b0 = (int)(stbtt__buf_get8(&b)); + switch (b0) + { + case 0x13: + case 0x14: + if ((in_header) != 0) + maskbits += (int)(sp / 2); + in_header = (int)(0); + stbtt__buf_skip(&b, (int)((maskbits + 7) / 8)); + break; + case 0x01: + case 0x03: + case 0x12: + case 0x17: + maskbits += (int)(sp / 2); + break; + case 0x15: + in_header = (int)(0); + if ((sp) < (2)) + return (int)(0); + stbtt__csctx_rmove_to(c, (float)(s[sp - 2]), (float)(s[sp - 1])); + break; + case 0x04: + in_header = (int)(0); + if ((sp) < (1)) + return (int)(0); + stbtt__csctx_rmove_to(c, (float)(0), (float)(s[sp - 1])); + break; + case 0x16: + in_header = (int)(0); + if ((sp) < (1)) + return (int)(0); + stbtt__csctx_rmove_to(c, (float)(s[sp - 1]), (float)(0)); + break; + case 0x05: + if ((sp) < (2)) + return (int)(0); + for (; (i + 1) < (sp); i += (int)(2)) + { + stbtt__csctx_rline_to(c, (float)(s[i]), (float)(s[i + 1])); + } + break; + case 0x07: + case 0x06: + { + int gotoVlineTo = (int)(0); + if ((b0) == (0x07)) + { + if ((sp) < (1)) + return (int)(0); + gotoVlineTo = (int)(1); + } + if ((sp) < (1)) + return (int)(0); + for (; ; ) + { + if (gotoVlineTo == 0) + { + if ((i) >= (sp)) + break; + stbtt__csctx_rline_to(c, (float)(s[i]), (float)(0)); + i++; + } + else + { + gotoVlineTo = (int)(0); + } + if ((i) >= (sp)) + break; + stbtt__csctx_rline_to(c, (float)(0), (float)(s[i])); + i++; + } + } + break; + case 0x1F: + case 0x1E: + { + int gotoHcurveTo = (int)(0); + if ((b0) == (0x1F)) + { + if ((sp) < (4)) + return (int)(0); + gotoHcurveTo = (int)(1); + } + for (; ; ) + { + if (gotoHcurveTo == 0) + { + if ((i + 3) >= (sp)) + break; + stbtt__csctx_rccurve_to(c, (float)(0), (float)(s[i]), (float)(s[i + 1]), (float)(s[i + 2]), (float)(s[i + 3]), (float)(((sp - i) == (5)) ? s[i + 4] : 0.0f)); + i += (int)(4); + } + else + { + gotoHcurveTo = (int)(0); + } + if ((i + 3) >= (sp)) + break; + stbtt__csctx_rccurve_to(c, (float)(s[i]), (float)(0), (float)(s[i + 1]), (float)(s[i + 2]), (float)(((sp - i) == (5)) ? s[i + 4] : 0.0f), (float)(s[i + 3])); + i += (int)(4); + } + } + break; + case 0x08: + if ((sp) < (6)) + return (int)(0); + for (; (i + 5) < (sp); i += (int)(6)) + { + stbtt__csctx_rccurve_to(c, (float)(s[i]), (float)(s[i + 1]), (float)(s[i + 2]), (float)(s[i + 3]), (float)(s[i + 4]), (float)(s[i + 5])); + } + break; + case 0x18: + if ((sp) < (8)) + return (int)(0); + for (; (i + 5) < (sp - 2); i += (int)(6)) + { + stbtt__csctx_rccurve_to(c, (float)(s[i]), (float)(s[i + 1]), (float)(s[i + 2]), (float)(s[i + 3]), (float)(s[i + 4]), (float)(s[i + 5])); + } + if ((i + 1) >= (sp)) + return (int)(0); + stbtt__csctx_rline_to(c, (float)(s[i]), (float)(s[i + 1])); + break; + case 0x19: + if ((sp) < (8)) + return (int)(0); + for (; (i + 1) < (sp - 6); i += (int)(2)) + { + stbtt__csctx_rline_to(c, (float)(s[i]), (float)(s[i + 1])); + } + if ((i + 5) >= (sp)) + return (int)(0); + stbtt__csctx_rccurve_to(c, (float)(s[i]), (float)(s[i + 1]), (float)(s[i + 2]), (float)(s[i + 3]), (float)(s[i + 4]), (float)(s[i + 5])); + break; + case 0x1A: + case 0x1B: + if ((sp) < (4)) + return (int)(0); + f = (float)(0.0); + if ((sp & 1) != 0) + { + f = (float)(s[i]); + i++; + } + for (; (i + 3) < (sp); i += (int)(4)) + { + if ((b0) == (0x1B)) + stbtt__csctx_rccurve_to(c, (float)(s[i]), (float)(f), (float)(s[i + 1]), (float)(s[i + 2]), (float)(s[i + 3]), (float)(0.0)); + else + stbtt__csctx_rccurve_to(c, (float)(f), (float)(s[i]), (float)(s[i + 1]), (float)(s[i + 2]), (float)(0.0), (float)(s[i + 3])); + f = (float)(0.0); + } + break; + case 0x0A: + case 0x1D: + if ((b0) == (0x0A)) + { + if (has_subrs == 0) + { + if ((info.fdselect.size) != 0) + subrs = (stbtt__buf)(stbtt__cid_get_glyph_subrs(info, (int)(glyph_index))); + has_subrs = (int)(1); + } + } + if ((sp) < (1)) + return (int)(0); + v = ((int)(s[--sp])); + if ((subr_stack_height) >= (10)) + return (int)(0); + subr_stack[subr_stack_height++] = (stbtt__buf)(b); + b = (stbtt__buf)(stbtt__get_subr((stbtt__buf)((b0) == (0x0A) ? subrs : info.gsubrs), (int)(v))); + if ((b.size) == (0)) + return (int)(0); + b.cursor = (int)(0); + clear_stack = (int)(0); + break; + case 0x0B: + if (subr_stack_height <= 0) + return (int)(0); + b = (stbtt__buf)(subr_stack[--subr_stack_height]); + clear_stack = (int)(0); + break; + case 0x0E: + stbtt__csctx_close_shape(c); + return (int)(1); + case 0x0C: + { + float dx1; + float dx2; + float dx3; + float dx4; + float dx5; + float dx6; + float dy1; + float dy2; + float dy3; + float dy4; + float dy5; + float dy6; + float dx; + float dy; + int b1 = (int)(stbtt__buf_get8(&b)); + switch (b1) + { + case 0x22: + if ((sp) < (7)) + return (int)(0); + dx1 = (float)(s[0]); + dx2 = (float)(s[1]); + dy2 = (float)(s[2]); + dx3 = (float)(s[3]); + dx4 = (float)(s[4]); + dx5 = (float)(s[5]); + dx6 = (float)(s[6]); + stbtt__csctx_rccurve_to(c, (float)(dx1), (float)(0), (float)(dx2), (float)(dy2), (float)(dx3), (float)(0)); + stbtt__csctx_rccurve_to(c, (float)(dx4), (float)(0), (float)(dx5), (float)(-dy2), (float)(dx6), (float)(0)); + break; + case 0x23: + if ((sp) < (13)) + return (int)(0); + dx1 = (float)(s[0]); + dy1 = (float)(s[1]); + dx2 = (float)(s[2]); + dy2 = (float)(s[3]); + dx3 = (float)(s[4]); + dy3 = (float)(s[5]); + dx4 = (float)(s[6]); + dy4 = (float)(s[7]); + dx5 = (float)(s[8]); + dy5 = (float)(s[9]); + dx6 = (float)(s[10]); + dy6 = (float)(s[11]); + stbtt__csctx_rccurve_to(c, (float)(dx1), (float)(dy1), (float)(dx2), (float)(dy2), (float)(dx3), (float)(dy3)); + stbtt__csctx_rccurve_to(c, (float)(dx4), (float)(dy4), (float)(dx5), (float)(dy5), (float)(dx6), (float)(dy6)); + break; + case 0x24: + if ((sp) < (9)) + return (int)(0); + dx1 = (float)(s[0]); + dy1 = (float)(s[1]); + dx2 = (float)(s[2]); + dy2 = (float)(s[3]); + dx3 = (float)(s[4]); + dx4 = (float)(s[5]); + dx5 = (float)(s[6]); + dy5 = (float)(s[7]); + dx6 = (float)(s[8]); + stbtt__csctx_rccurve_to(c, (float)(dx1), (float)(dy1), (float)(dx2), (float)(dy2), (float)(dx3), (float)(0)); + stbtt__csctx_rccurve_to(c, (float)(dx4), (float)(0), (float)(dx5), (float)(dy5), (float)(dx6), (float)(-(dy1 + dy2 + dy5))); + break; + case 0x25: + if ((sp) < (11)) + return (int)(0); + dx1 = (float)(s[0]); + dy1 = (float)(s[1]); + dx2 = (float)(s[2]); + dy2 = (float)(s[3]); + dx3 = (float)(s[4]); + dy3 = (float)(s[5]); + dx4 = (float)(s[6]); + dy4 = (float)(s[7]); + dx5 = (float)(s[8]); + dy5 = (float)(s[9]); + dx6 = (float)(dy6 = (float)(s[10])); + dx = (float)(dx1 + dx2 + dx3 + dx4 + dx5); + dy = (float)(dy1 + dy2 + dy3 + dy4 + dy5); + if ((CRuntime.fabs((double)(dx))) > (CRuntime.fabs((double)(dy)))) + dy6 = (float)(-dy); + else + dx6 = (float)(-dx); + stbtt__csctx_rccurve_to(c, (float)(dx1), (float)(dy1), (float)(dx2), (float)(dy2), (float)(dx3), (float)(dy3)); + stbtt__csctx_rccurve_to(c, (float)(dx4), (float)(dy4), (float)(dx5), (float)(dy5), (float)(dx6), (float)(dy6)); + break; + default: + return (int)(0); + } + } + break; + default: + if (((b0 != 255) && (b0 != 28)) && (((b0) < (32)) || ((b0) > (254)))) + return (int)(0); + if ((b0) == (255)) + { + f = (float)((float)(stbtt__buf_get((&b), (int)(4))) / 0x10000); + } + else + { + stbtt__buf_skip(&b, (int)(-1)); + f = ((float)((short)(stbtt__cff_int(&b)))); + } + if ((sp) >= (48)) + return (int)(0); + s[sp++] = (float)(f); + clear_stack = (int)(0); + break; + } + if ((clear_stack) != 0) + sp = (int)(0); + } + return (int)(0); + } + + public static int stbtt__GetGlyphShapeT2(stbtt_fontinfo info, int glyph_index, stbtt_vertex** pvertices) + { + stbtt__csctx count_ctx = new stbtt__csctx(); + count_ctx.bounds = (int)(1); + stbtt__csctx output_ctx = new stbtt__csctx(); + if ((stbtt__run_charstring(info, (int)(glyph_index), &count_ctx)) != 0) + { + *pvertices = (stbtt_vertex*)(CRuntime.malloc((ulong)(count_ctx.num_vertices * sizeof(stbtt_vertex)))); + output_ctx.pvertices = *pvertices; + if ((stbtt__run_charstring(info, (int)(glyph_index), &output_ctx)) != 0) + { + return (int)(output_ctx.num_vertices); + } + } + + *pvertices = (null); + return (int)(0); + } + + public static int stbtt__GetGlyphInfoT2(stbtt_fontinfo info, int glyph_index, int* x0, int* y0, int* x1, int* y1) + { + stbtt__csctx c = new stbtt__csctx(); + c.bounds = (int)(1); + int r = (int)(stbtt__run_charstring(info, (int)(glyph_index), &c)); + if ((x0) != null) + *x0 = (int)((r) != 0 ? c.min_x : 0); + if ((y0) != null) + *y0 = (int)((r) != 0 ? c.min_y : 0); + if ((x1) != null) + *x1 = (int)((r) != 0 ? c.max_x : 0); + if ((y1) != null) + *y1 = (int)((r) != 0 ? c.max_y : 0); + return (int)((r) != 0 ? c.num_vertices : 0); + } + + public static int stbtt_GetGlyphShape(stbtt_fontinfo info, int glyph_index, stbtt_vertex** pvertices) + { + if (info.cff.size == 0) + return (int)(stbtt__GetGlyphShapeTT(info, (int)(glyph_index), pvertices)); + else + return (int)(stbtt__GetGlyphShapeT2(info, (int)(glyph_index), pvertices)); + } + + public static void stbtt_GetGlyphHMetrics(stbtt_fontinfo info, int glyph_index, int* advanceWidth, int* leftSideBearing) + { + ushort numOfLongHorMetrics = (ushort)(ttUSHORT(info.data + info.hhea + 34)); + if ((glyph_index) < (numOfLongHorMetrics)) + { + if ((advanceWidth) != null) + *advanceWidth = (int)(ttSHORT(info.data + info.hmtx + 4 * glyph_index)); + if ((leftSideBearing) != null) + *leftSideBearing = (int)(ttSHORT(info.data + info.hmtx + 4 * glyph_index + 2)); + } + else + { + if ((advanceWidth) != null) + *advanceWidth = (int)(ttSHORT(info.data + info.hmtx + 4 * (numOfLongHorMetrics - 1))); + if ((leftSideBearing) != null) + *leftSideBearing = (int)(ttSHORT(info.data + info.hmtx + 4 * numOfLongHorMetrics + 2 * (glyph_index - numOfLongHorMetrics))); + } + + } + + public static int stbtt__GetGlyphKernInfoAdvance(stbtt_fontinfo info, int glyph1, int glyph2) + { + byte* data = info.data + info.kern; + uint needle; + uint straw; + int l; + int r; + int m; + if (info.kern == 0) + return (int)(0); + if ((ttUSHORT(data + 2)) < (1)) + return (int)(0); + if (ttUSHORT(data + 8) != 1) + return (int)(0); + l = (int)(0); + r = (int)(ttUSHORT(data + 10) - 1); + needle = (uint)(glyph1 << 16 | glyph2); + while (l <= r) + { + m = (int)((l + r) >> 1); + straw = (uint)(ttULONG(data + 18 + (m * 6))); + if ((needle) < (straw)) + r = (int)(m - 1); + else if ((needle) > (straw)) + l = (int)(m + 1); + else + return (int)(ttSHORT(data + 22 + (m * 6))); + } + return (int)(0); + } + + public static int stbtt__GetCoverageIndex(byte* coverageTable, int glyph) + { + ushort coverageFormat = (ushort)(ttUSHORT(coverageTable)); + switch (coverageFormat) + { + case 1: + { + ushort glyphCount = (ushort)(ttUSHORT(coverageTable + 2)); + int l = (int)(0); + int r = (int)(glyphCount - 1); + int m; + int straw; + int needle = (int)(glyph); + while (l <= r) + { + byte* glyphArray = coverageTable + 4; + ushort glyphID; + m = (int)((l + r) >> 1); + glyphID = (ushort)(ttUSHORT(glyphArray + 2 * m)); + straw = (int)(glyphID); + if ((needle) < (straw)) + r = (int)(m - 1); + else if ((needle) > (straw)) + l = (int)(m + 1); + else + { + return (int)(m); + } + } + } + break; + case 2: + { + ushort rangeCount = (ushort)(ttUSHORT(coverageTable + 2)); + byte* rangeArray = coverageTable + 4; + int l = (int)(0); + int r = (int)(rangeCount - 1); + int m; + int strawStart; + int strawEnd; + int needle = (int)(glyph); + while (l <= r) + { + byte* rangeRecord; + m = (int)((l + r) >> 1); + rangeRecord = rangeArray + 6 * m; + strawStart = (int)(ttUSHORT(rangeRecord)); + strawEnd = (int)(ttUSHORT(rangeRecord + 2)); + if ((needle) < (strawStart)) + r = (int)(m - 1); + else if ((needle) > (strawEnd)) + l = (int)(m + 1); + else + { + ushort startCoverageIndex = (ushort)(ttUSHORT(rangeRecord + 4)); + return (int)(startCoverageIndex + glyph - strawStart); + } + } + } + break; + default: + { + } + break; + } + + return (int)(-1); + } + + public static int stbtt__GetGlyphClass(byte* classDefTable, int glyph) + { + ushort classDefFormat = (ushort)(ttUSHORT(classDefTable)); + switch (classDefFormat) + { + case 1: + { + ushort startGlyphID = (ushort)(ttUSHORT(classDefTable + 2)); + ushort glyphCount = (ushort)(ttUSHORT(classDefTable + 4)); + byte* classDef1ValueArray = classDefTable + 6; + if (((glyph) >= (startGlyphID)) && ((glyph) < (startGlyphID + glyphCount))) + return (int)(ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID))); + classDefTable = classDef1ValueArray + 2 * glyphCount; + } + break; + case 2: + { + ushort classRangeCount = (ushort)(ttUSHORT(classDefTable + 2)); + byte* classRangeRecords = classDefTable + 4; + int l = (int)(0); + int r = (int)(classRangeCount - 1); + int m; + int strawStart; + int strawEnd; + int needle = (int)(glyph); + while (l <= r) + { + byte* classRangeRecord; + m = (int)((l + r) >> 1); + classRangeRecord = classRangeRecords + 6 * m; + strawStart = (int)(ttUSHORT(classRangeRecord)); + strawEnd = (int)(ttUSHORT(classRangeRecord + 2)); + if ((needle) < (strawStart)) + r = (int)(m - 1); + else if ((needle) > (strawEnd)) + l = (int)(m + 1); + else + return (int)(ttUSHORT(classRangeRecord + 4)); + } + classDefTable = classRangeRecords + 6 * classRangeCount; + } + break; + default: + { + } + break; + } + + return (int)(-1); + } + + public static int stbtt__GetGlyphGPOSInfoAdvance(stbtt_fontinfo info, int glyph1, int glyph2) + { + ushort lookupListOffset; + byte* lookupList; + ushort lookupCount; + byte* data; + int i; + if (info.gpos == 0) + return (int)(0); + data = info.data + info.gpos; + if (ttUSHORT(data + 0) != 1) + return (int)(0); + if (ttUSHORT(data + 2) != 0) + return (int)(0); + lookupListOffset = (ushort)(ttUSHORT(data + 8)); + lookupList = data + lookupListOffset; + lookupCount = (ushort)(ttUSHORT(lookupList)); + for (i = (int)(0); (i) < (lookupCount); ++i) + { + ushort lookupOffset = (ushort)(ttUSHORT(lookupList + 2 + 2 * i)); + byte* lookupTable = lookupList + lookupOffset; + ushort lookupType = (ushort)(ttUSHORT(lookupTable)); + ushort subTableCount = (ushort)(ttUSHORT(lookupTable + 4)); + byte* subTableOffsets = lookupTable + 6; + switch (lookupType) + { + case 2: + { + int sti; + for (sti = (int)(0); (sti) < (subTableCount); sti++) + { + ushort subtableOffset = (ushort)(ttUSHORT(subTableOffsets + 2 * sti)); + byte* table = lookupTable + subtableOffset; + ushort posFormat = (ushort)(ttUSHORT(table)); + ushort coverageOffset = (ushort)(ttUSHORT(table + 2)); + int coverageIndex = (int)(stbtt__GetCoverageIndex(table + coverageOffset, (int)(glyph1))); + if ((coverageIndex) == (-1)) + continue; + switch (posFormat) + { + case 1: + { + int l; + int r; + int m; + int straw; + int needle; + ushort valueFormat1 = (ushort)(ttUSHORT(table + 4)); + ushort valueFormat2 = (ushort)(ttUSHORT(table + 6)); + int valueRecordPairSizeInBytes = (int)(2); + ushort pairSetCount = (ushort)(ttUSHORT(table + 8)); + ushort pairPosOffset = (ushort)(ttUSHORT(table + 10 + 2 * coverageIndex)); + byte* pairValueTable = table + pairPosOffset; + ushort pairValueCount = (ushort)(ttUSHORT(pairValueTable)); + byte* pairValueArray = pairValueTable + 2; + if (valueFormat1 != 4) + return (int)(0); + if (valueFormat2 != 0) + return (int)(0); + needle = (int)(glyph2); + r = (int)(pairValueCount - 1); + l = (int)(0); + while (l <= r) + { + ushort secondGlyph; + byte* pairValue; + m = (int)((l + r) >> 1); + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = (ushort)(ttUSHORT(pairValue)); + straw = (int)(secondGlyph); + if ((needle) < (straw)) + r = (int)(m - 1); + else if ((needle) > (straw)) + l = (int)(m + 1); + else + { + short xAdvance = (short)(ttSHORT(pairValue + 2)); + return (int)(xAdvance); + } + } + } + break; + case 2: + { + ushort valueFormat1 = (ushort)(ttUSHORT(table + 4)); + ushort valueFormat2 = (ushort)(ttUSHORT(table + 6)); + ushort classDef1Offset = (ushort)(ttUSHORT(table + 8)); + ushort classDef2Offset = (ushort)(ttUSHORT(table + 10)); + int glyph1class = (int)(stbtt__GetGlyphClass(table + classDef1Offset, (int)(glyph1))); + int glyph2class = (int)(stbtt__GetGlyphClass(table + classDef2Offset, (int)(glyph2))); + ushort class1Count = (ushort)(ttUSHORT(table + 12)); + ushort class2Count = (ushort)(ttUSHORT(table + 14)); + if (valueFormat1 != 4) + return (int)(0); + if (valueFormat2 != 0) + return (int)(0); + if (((((glyph1class) >= (0)) && ((glyph1class) < (class1Count))) && ((glyph2class) >= (0))) && ((glyph2class) < (class2Count))) + { + byte* class1Records = table + 16; + byte* class2Records = class1Records + 2 * (glyph1class * class2Count); + short xAdvance = (short)(ttSHORT(class2Records + 2 * glyph2class)); + return (int)(xAdvance); + } + } + break; + default: + { + break; + } + } + } + break; + } + default: + break; + } + } + return (int)(0); + } + + public static int stbtt_GetGlyphKernAdvance(stbtt_fontinfo info, int g1, int g2) + { + int xAdvance = (int)(0); + if ((info.gpos) != 0) + xAdvance += (int)(stbtt__GetGlyphGPOSInfoAdvance(info, (int)(g1), (int)(g2))); + if ((info.kern) != 0) + xAdvance += (int)(stbtt__GetGlyphKernInfoAdvance(info, (int)(g1), (int)(g2))); + return (int)(xAdvance); + } + + public static int stbtt_GetCodepointKernAdvance(stbtt_fontinfo info, int ch1, int ch2) + { + if ((info.kern == 0) && (info.gpos == 0)) + return (int)(0); + return (int)(stbtt_GetGlyphKernAdvance(info, (int)(stbtt_FindGlyphIndex(info, (int)(ch1))), (int)(stbtt_FindGlyphIndex(info, (int)(ch2))))); + } + + public static void stbtt_GetCodepointHMetrics(stbtt_fontinfo info, int codepoint, int* advanceWidth, int* leftSideBearing) + { + stbtt_GetGlyphHMetrics(info, (int)(stbtt_FindGlyphIndex(info, (int)(codepoint))), advanceWidth, leftSideBearing); + } + + public static void stbtt_GetFontVMetrics(stbtt_fontinfo info, int* ascent, int* descent, int* lineGap) + { + if ((ascent) != null) + *ascent = (int)(ttSHORT(info.data + info.hhea + 4)); + if ((descent) != null) + *descent = (int)(ttSHORT(info.data + info.hhea + 6)); + if ((lineGap) != null) + *lineGap = (int)(ttSHORT(info.data + info.hhea + 8)); + } + + public static int stbtt_GetFontVMetricsOS2(stbtt_fontinfo info, int* typoAscent, int* typoDescent, int* typoLineGap) + { + int tab = (int)(stbtt__find_table(info.data, (uint)(info.fontstart), "OS/2")); + if (tab == 0) + return (int)(0); + if ((typoAscent) != null) + *typoAscent = (int)(ttSHORT(info.data + tab + 68)); + if ((typoDescent) != null) + *typoDescent = (int)(ttSHORT(info.data + tab + 70)); + if ((typoLineGap) != null) + *typoLineGap = (int)(ttSHORT(info.data + tab + 72)); + return (int)(1); + } + + public static void stbtt_GetFontBoundingBox(stbtt_fontinfo info, int* x0, int* y0, int* x1, int* y1) + { + *x0 = (int)(ttSHORT(info.data + info.head + 36)); + *y0 = (int)(ttSHORT(info.data + info.head + 38)); + *x1 = (int)(ttSHORT(info.data + info.head + 40)); + *y1 = (int)(ttSHORT(info.data + info.head + 42)); + } + + public static float stbtt_ScaleForPixelHeight(stbtt_fontinfo info, float height) + { + int fheight = (int)(ttSHORT(info.data + info.hhea + 4) - ttSHORT(info.data + info.hhea + 6)); + return (float)(height / fheight); + } + + public static float stbtt_ScaleForMappingEmToPixels(stbtt_fontinfo info, float pixels) + { + int unitsPerEm = (int)(ttUSHORT(info.data + info.head + 18)); + return (float)(pixels / unitsPerEm); + } + + public static void stbtt_FreeShape(stbtt_fontinfo info, stbtt_vertex* v) + { + CRuntime.free(v); + } + + public static void stbtt_GetGlyphBitmapBoxSubpixel(stbtt_fontinfo font, int glyph, float scale_x, float scale_y, float shift_x, float shift_y, int* ix0, int* iy0, int* ix1, int* iy1) + { + int x0 = (int)(0); + int y0 = (int)(0); + int x1; + int y1; + if (stbtt_GetGlyphBox(font, (int)(glyph), &x0, &y0, &x1, &y1) == 0) + { + if ((ix0) != null) + *ix0 = (int)(0); + if ((iy0) != null) + *iy0 = (int)(0); + if ((ix1) != null) + *ix1 = (int)(0); + if ((iy1) != null) + *iy1 = (int)(0); + } + else + { + if ((ix0) != null) + *ix0 = ((int)(CRuntime.floor((double)(x0 * scale_x + shift_x)))); + if ((iy0) != null) + *iy0 = ((int)(CRuntime.floor((double)(-y1 * scale_y + shift_y)))); + if ((ix1) != null) + *ix1 = ((int)(CRuntime.ceil((double)(x1 * scale_x + shift_x)))); + if ((iy1) != null) + *iy1 = ((int)(CRuntime.ceil((double)(-y0 * scale_y + shift_y)))); + } + + } + + public static void stbtt_GetGlyphBitmapBox(stbtt_fontinfo font, int glyph, float scale_x, float scale_y, int* ix0, int* iy0, int* ix1, int* iy1) + { + stbtt_GetGlyphBitmapBoxSubpixel(font, (int)(glyph), (float)(scale_x), (float)(scale_y), (float)(0.0f), (float)(0.0f), ix0, iy0, ix1, iy1); + } + + public static void stbtt_GetCodepointBitmapBoxSubpixel(stbtt_fontinfo font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int* ix0, int* iy0, int* ix1, int* iy1) + { + stbtt_GetGlyphBitmapBoxSubpixel(font, (int)(stbtt_FindGlyphIndex(font, (int)(codepoint))), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), ix0, iy0, ix1, iy1); + } + + public static void stbtt_GetCodepointBitmapBox(stbtt_fontinfo font, int codepoint, float scale_x, float scale_y, int* ix0, int* iy0, int* ix1, int* iy1) + { + stbtt_GetCodepointBitmapBoxSubpixel(font, (int)(codepoint), (float)(scale_x), (float)(scale_y), (float)(0.0f), (float)(0.0f), ix0, iy0, ix1, iy1); + } + + public static void* stbtt__hheap_alloc(stbtt__hheap* hh, ulong size, object userdata) + { + if ((hh->first_free) != null) + { + void* p = hh->first_free; + hh->first_free = *(void**)(p); + return p; + } + else + { + if ((hh->num_remaining_in_head_chunk) == (0)) + { + int count = (int)((size) < (32) ? 2000 : (size) < (128) ? 800 : 100); + stbtt__hheap_chunk* c = (stbtt__hheap_chunk*)(CRuntime.malloc((ulong)((ulong)sizeof(stbtt__hheap_chunk) + size * (ulong)(count)))); + if ((c) == (null)) + return (null); + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = (int)(count); + } + --hh->num_remaining_in_head_chunk; + return (sbyte*)(hh->head) + sizeof(stbtt__hheap_chunk) + size * (ulong)hh->num_remaining_in_head_chunk; + } + + } + + public static void stbtt__hheap_free(stbtt__hheap* hh, void* p) + { + *(void**)(p) = hh->first_free; + hh->first_free = p; + } + + public static void stbtt__hheap_cleanup(stbtt__hheap* hh, object userdata) + { + stbtt__hheap_chunk* c = hh->head; + while ((c) != null) + { + stbtt__hheap_chunk* n = c->next; + CRuntime.free(c); + c = n; + } + } + + public static stbtt__active_edge* stbtt__new_active(stbtt__hheap* hh, stbtt__edge* e, int off_x, float start_point, object userdata) + { + stbtt__active_edge* z = (stbtt__active_edge*)(stbtt__hheap_alloc(hh, (ulong)(sizeof(stbtt__active_edge)), userdata)); + float dxdy = (float)((e->x1 - e->x0) / (e->y1 - e->y0)); + if (z == null) + return z; + z->fdx = (float)(dxdy); + z->fdy = (float)(dxdy != 0.0f ? (1.0f / dxdy) : 0.0f); + z->fx = (float)(e->x0 + dxdy * (start_point - e->y0)); + z->fx -= (float)(off_x); + z->direction = (float)((e->invert) != 0 ? 1.0f : -1.0f); + z->sy = (float)(e->y0); + z->ey = (float)(e->y1); + z->next = null; + return z; + } + + public static void stbtt__handle_clipped_edge(float* scanline, int x, stbtt__active_edge* e, float x0, float y0, float x1, float y1) + { + if ((y0) == (y1)) + return; + if ((y0) > (e->ey)) + return; + if ((y1) < (e->sy)) + return; + if ((y0) < (e->sy)) + { + x0 += (float)((x1 - x0) * (e->sy - y0) / (y1 - y0)); + y0 = (float)(e->sy); + } + + if ((y1) > (e->ey)) + { + x1 += (float)((x1 - x0) * (e->ey - y1) / (y1 - y0)); + y1 = (float)(e->ey); + } + + if ((x0) == (x)) + { + } + else if ((x0) == (x + 1)) + { + } + else if (x0 <= x) + { + } + else if ((x0) >= (x + 1)) + { + } + else + { + } + + if ((x0 <= x) && (x1 <= x)) + { + scanline[x] += (float)(e->direction * (y1 - y0)); + } + else if (((x0) >= (x + 1)) && ((x1) >= (x + 1))) + { + } + else + { + scanline[x] += (float)(e->direction * (y1 - y0) * (1 - ((x0 - x) + (x1 - x)) / 2)); + } + + } + + public static void stbtt__fill_active_edges_new(float* scanline, float* scanline_fill, int len, stbtt__active_edge* e, float y_top) + { + float y_bottom = (float)(y_top + 1); + while ((e) != null) + { + if ((e->fdx) == (0)) + { + float x0 = (float)(e->fx); + if ((x0) < (len)) + { + if ((x0) >= (0)) + { + stbtt__handle_clipped_edge(scanline, (int)(x0), e, (float)(x0), (float)(y_top), (float)(x0), (float)(y_bottom)); + stbtt__handle_clipped_edge(scanline_fill - 1, (int)((int)(x0) + 1), e, (float)(x0), (float)(y_top), (float)(x0), (float)(y_bottom)); + } + else + { + stbtt__handle_clipped_edge(scanline_fill - 1, (int)(0), e, (float)(x0), (float)(y_top), (float)(x0), (float)(y_bottom)); + } + } + } + else + { + float x0 = (float)(e->fx); + float dx = (float)(e->fdx); + float xb = (float)(x0 + dx); + float x_top; + float x_bottom; + float sy0; + float sy1; + float dy = (float)(e->fdy); + if ((e->sy) > (y_top)) + { + x_top = (float)(x0 + dx * (e->sy - y_top)); + sy0 = (float)(e->sy); + } + else + { + x_top = (float)(x0); + sy0 = (float)(y_top); + } + if ((e->ey) < (y_bottom)) + { + x_bottom = (float)(x0 + dx * (e->ey - y_top)); + sy1 = (float)(e->ey); + } + else + { + x_bottom = (float)(xb); + sy1 = (float)(y_bottom); + } + if (((((x_top) >= (0)) && ((x_bottom) >= (0))) && ((x_top) < (len))) && ((x_bottom) < (len))) + { + if (((int)(x_top)) == ((int)(x_bottom))) + { + float height; + int x = (int)(x_top); + height = (float)(sy1 - sy0); + scanline[x] += (float)(e->direction * (1 - ((x_top - x) + (x_bottom - x)) / 2) * height); + scanline_fill[x] += (float)(e->direction * height); + } + else + { + int x; + int x1; + int x2; + float y_crossing; + float step; + float sign; + float area; + if ((x_top) > (x_bottom)) + { + float t; + sy0 = (float)(y_bottom - (sy0 - y_top)); + sy1 = (float)(y_bottom - (sy1 - y_top)); + t = (float)(sy0); + sy0 = (float)(sy1); + sy1 = (float)(t); + t = (float)(x_bottom); + x_bottom = (float)(x_top); + x_top = (float)(t); + dx = (float)(-dx); + dy = (float)(-dy); + t = (float)(x0); + x0 = (float)(xb); + xb = (float)(t); + } + x1 = ((int)(x_top)); + x2 = ((int)(x_bottom)); + y_crossing = (float)((x1 + 1 - x0) * dy + y_top); + sign = (float)(e->direction); + area = (float)(sign * (y_crossing - sy0)); + scanline[x1] += (float)(area * (1 - ((x_top - x1) + (x1 + 1 - x1)) / 2)); + step = (float)(sign * dy); + for (x = (int)(x1 + 1); (x) < (x2); ++x) + { + scanline[x] += (float)(area + step / 2); + area += (float)(step); + } + y_crossing += (float)(dy * (x2 - (x1 + 1))); + scanline[x2] += (float)(area + sign * (1 - ((x2 - x2) + (x_bottom - x2)) / 2) * (sy1 - y_crossing)); + scanline_fill[x2] += (float)(sign * (sy1 - sy0)); + } + } + else + { + int x; + for (x = (int)(0); (x) < (len); ++x) + { + float y0 = (float)(y_top); + float x1 = (float)(x); + float x2 = (float)(x + 1); + float x3 = (float)(xb); + float y3 = (float)(y_bottom); + float y1 = (float)((x - x0) / dx + y_top); + float y2 = (float)((x + 1 - x0) / dx + y_top); + if (((x0) < (x1)) && ((x3) > (x2))) + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x1), (float)(y1)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x1), (float)(y1), (float)(x2), (float)(y2)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x2), (float)(y2), (float)(x3), (float)(y3)); + } + else if (((x3) < (x1)) && ((x0) > (x2))) + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x2), (float)(y2)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x2), (float)(y2), (float)(x1), (float)(y1)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x1), (float)(y1), (float)(x3), (float)(y3)); + } + else if (((x0) < (x1)) && ((x3) > (x1))) + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x1), (float)(y1)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x1), (float)(y1), (float)(x3), (float)(y3)); + } + else if (((x3) < (x1)) && ((x0) > (x1))) + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x1), (float)(y1)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x1), (float)(y1), (float)(x3), (float)(y3)); + } + else if (((x0) < (x2)) && ((x3) > (x2))) + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x2), (float)(y2)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x2), (float)(y2), (float)(x3), (float)(y3)); + } + else if (((x3) < (x2)) && ((x0) > (x2))) + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x2), (float)(y2)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x2), (float)(y2), (float)(x3), (float)(y3)); + } + else + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x3), (float)(y3)); + } + } + } + } + e = e->next; + } + } + + public static void stbtt__rasterize_sorted_edges(stbtt__bitmap* result, stbtt__edge* e, int n, int vsubsample, int off_x, int off_y, object userdata) + { + stbtt__hheap hh; + stbtt__active_edge* active = (null); + int y; + int j = (int)(0); + int i; + float* scanline_data = stackalloc float[129]; + float* scanline; + float* scanline2; + if ((result->w) > (64)) + scanline = (float*)(CRuntime.malloc((ulong)((result->w * 2 + 1) * sizeof(float)))); + else + scanline = scanline_data; + scanline2 = scanline + result->w; + y = (int)(off_y); + e[n].y0 = (float)((float)(off_y + result->h) + 1); + while ((j) < (result->h)) + { + float scan_y_top = (float)(y + 0.0f); + float scan_y_bottom = (float)(y + 1.0f); + stbtt__active_edge** step = &active; + CRuntime.memset(scanline, (int)(0), (ulong)(result->w * sizeof(float))); + CRuntime.memset(scanline2, (int)(0), (ulong)((result->w + 1) * sizeof(float))); + while ((*step) != null) + { + stbtt__active_edge* z = *step; + if (z->ey <= scan_y_top) + { + *step = z->next; + z->direction = (float)(0); + stbtt__hheap_free(&hh, z); + } + else + { + step = &((*step)->next); + } + } while (e->y0 <= scan_y_bottom) + { + if (e->y0 != e->y1) + { + stbtt__active_edge* z = stbtt__new_active(&hh, e, (int)(off_x), (float)(scan_y_top), userdata); + if (z != (null)) + { + z->next = active; + active = z; + } + } + ++e; + } + if ((active) != null) + stbtt__fill_active_edges_new(scanline, scanline2 + 1, (int)(result->w), active, (float)(scan_y_top)); + { + float sum = (float)(0); + for (i = (int)(0); (i) < (result->w); ++i) + { + float k; + int m; + sum += (float)(scanline2[i]); + k = (float)(scanline[i] + sum); + k = (float)((float)(CRuntime.fabs((double)(k))) * 255 + 0.5f); + m = ((int)(k)); + if ((m) > (255)) + m = (int)(255); + result->pixels[j * result->stride + i] = ((byte)(m)); + } + } + step = &active; + while ((*step) != null) + { + stbtt__active_edge* z = *step; + z->fx += (float)(z->fdx); + step = &((*step)->next); + } + ++y; + ++j; + } + stbtt__hheap_cleanup(&hh, userdata); + if (scanline != scanline_data) + CRuntime.free(scanline); + } + + public static void stbtt__sort_edges_ins_sort(stbtt__edge* p, int n) + { + int i; + int j; + for (i = (int)(1); (i) < (n); ++i) + { + stbtt__edge t = (stbtt__edge)(p[i]); + stbtt__edge* a = &t; + j = (int)(i); + while ((j) > (0)) + { + stbtt__edge* b = &p[j - 1]; + int c = (int)(a->y0 < b->y0 ? 1 : 0); + if (c == 0) + break; + p[j] = (stbtt__edge)(p[j - 1]); + --j; + } + if (i != j) + p[j] = (stbtt__edge)(t); + } + } + + public static void stbtt__sort_edges_quicksort(stbtt__edge* p, int n) + { + while ((n) > (12)) + { + stbtt__edge t = new stbtt__edge(); + int c01; + int c12; + int c; + int m; + int i; + int j; + m = (int)(n >> 1); + c01 = (int)(((&p[0])->y0) < ((&p[m])->y0) ? 1 : 0); + c12 = (int)(((&p[m])->y0) < ((&p[n - 1])->y0) ? 1 : 0); + if (c01 != c12) + { + int z; + c = (int)(((&p[0])->y0) < ((&p[n - 1])->y0) ? 1 : 0); + z = (int)(((c) == (c12)) ? 0 : n - 1); + t = (stbtt__edge)(p[z]); + p[z] = (stbtt__edge)(p[m]); + p[m] = (stbtt__edge)(t); + } + t = (stbtt__edge)(p[0]); + p[0] = (stbtt__edge)(p[m]); + p[m] = (stbtt__edge)(t); + i = (int)(1); + j = (int)(n - 1); + for (; ; ) + { + for (i = (int)(i); ; ++i) + { + if (!(((&p[i])->y0) < ((&p[0])->y0))) + break; + } + for (j = (int)(j); ; --j) + { + if (!(((&p[0])->y0) < ((&p[j])->y0))) + break; + } + if ((i) >= (j)) + break; + t = (stbtt__edge)(p[i]); + p[i] = (stbtt__edge)(p[j]); + p[j] = (stbtt__edge)(t); + ++i; + --j; + } + if ((j) < (n - i)) + { + stbtt__sort_edges_quicksort(p, (int)(j)); + p = p + i; + n = (int)(n - i); + } + else + { + stbtt__sort_edges_quicksort(p + i, (int)(n - i)); + n = (int)(j); + } + } + } + + public static void stbtt__sort_edges(stbtt__edge* p, int n) + { + stbtt__sort_edges_quicksort(p, (int)(n)); + stbtt__sort_edges_ins_sort(p, (int)(n)); + } + + public static void stbtt__rasterize(stbtt__bitmap* result, stbtt__point* pts, int* wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, object userdata) + { + float y_scale_inv = (float)((invert) != 0 ? -scale_y : scale_y); + stbtt__edge* e; + int n; + int i; + int j; + int k; + int m; + int vsubsample = (int)(1); + n = (int)(0); + for (i = (int)(0); (i) < (windings); ++i) + { + n += (int)(wcount[i]); + } + e = (stbtt__edge*)(CRuntime.malloc((ulong)(sizeof(stbtt__edge) * (n + 1)))); + if ((e) == (null)) + return; + n = (int)(0); + m = (int)(0); + for (i = (int)(0); (i) < (windings); ++i) + { + stbtt__point* p = pts + m; + m += (int)(wcount[i]); + j = (int)(wcount[i] - 1); + for (k = (int)(0); (k) < (wcount[i]); j = (int)(k++)) + { + int a = (int)(k); + int b = (int)(j); + if ((p[j].y) == (p[k].y)) + continue; + e[n].invert = (int)(0); + if ((((invert) != 0) && ((p[j].y) > (p[k].y))) || ((invert == 0) && ((p[j].y) < (p[k].y)))) + { + e[n].invert = (int)(1); + a = (int)(j); + b = (int)(k); + } + e[n].x0 = (float)(p[a].x * scale_x + shift_x); + e[n].y0 = (float)((p[a].y * y_scale_inv + shift_y) * vsubsample); + e[n].x1 = (float)(p[b].x * scale_x + shift_x); + e[n].y1 = (float)((p[b].y * y_scale_inv + shift_y) * vsubsample); + ++n; + } + } + stbtt__sort_edges(e, (int)(n)); + stbtt__rasterize_sorted_edges(result, e, (int)(n), (int)(vsubsample), (int)(off_x), (int)(off_y), userdata); + CRuntime.free(e); + } + + public static void stbtt__add_point(stbtt__point* points, int n, float x, float y) + { + if (points == null) + return; + points[n].x = (float)(x); + points[n].y = (float)(y); + } + + public static int stbtt__tesselate_curve(stbtt__point* points, int* num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) + { + float mx = (float)((x0 + 2 * x1 + x2) / 4); + float my = (float)((y0 + 2 * y1 + y2) / 4); + float dx = (float)((x0 + x2) / 2 - mx); + float dy = (float)((y0 + y2) / 2 - my); + if ((n) > (16)) + return (int)(1); + if ((dx * dx + dy * dy) > (objspace_flatness_squared)) + { + stbtt__tesselate_curve(points, num_points, (float)(x0), (float)(y0), (float)((x0 + x1) / 2.0f), (float)((y0 + y1) / 2.0f), (float)(mx), (float)(my), (float)(objspace_flatness_squared), (int)(n + 1)); + stbtt__tesselate_curve(points, num_points, (float)(mx), (float)(my), (float)((x1 + x2) / 2.0f), (float)((y1 + y2) / 2.0f), (float)(x2), (float)(y2), (float)(objspace_flatness_squared), (int)(n + 1)); + } + else + { + stbtt__add_point(points, (int)(*num_points), (float)(x2), (float)(y2)); + *num_points = (int)(*num_points + 1); + } + + return (int)(1); + } + + public static void stbtt__tesselate_cubic(stbtt__point* points, int* num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) + { + float dx0 = (float)(x1 - x0); + float dy0 = (float)(y1 - y0); + float dx1 = (float)(x2 - x1); + float dy1 = (float)(y2 - y1); + float dx2 = (float)(x3 - x2); + float dy2 = (float)(y3 - y2); + float dx = (float)(x3 - x0); + float dy = (float)(y3 - y0); + float longlen = (float)(CRuntime.sqrt((double)(dx0 * dx0 + dy0 * dy0)) + CRuntime.sqrt((double)(dx1 * dx1 + dy1 * dy1)) + CRuntime.sqrt((double)(dx2 * dx2 + dy2 * dy2))); + float shortlen = (float)(CRuntime.sqrt((double)(dx * dx + dy * dy))); + float flatness_squared = (float)(longlen * longlen - shortlen * shortlen); + if ((n) > (16)) + return; + if ((flatness_squared) > (objspace_flatness_squared)) + { + float x01 = (float)((x0 + x1) / 2); + float y01 = (float)((y0 + y1) / 2); + float x12 = (float)((x1 + x2) / 2); + float y12 = (float)((y1 + y2) / 2); + float x23 = (float)((x2 + x3) / 2); + float y23 = (float)((y2 + y3) / 2); + float xa = (float)((x01 + x12) / 2); + float ya = (float)((y01 + y12) / 2); + float xb = (float)((x12 + x23) / 2); + float yb = (float)((y12 + y23) / 2); + float mx = (float)((xa + xb) / 2); + float my = (float)((ya + yb) / 2); + stbtt__tesselate_cubic(points, num_points, (float)(x0), (float)(y0), (float)(x01), (float)(y01), (float)(xa), (float)(ya), (float)(mx), (float)(my), (float)(objspace_flatness_squared), (int)(n + 1)); + stbtt__tesselate_cubic(points, num_points, (float)(mx), (float)(my), (float)(xb), (float)(yb), (float)(x23), (float)(y23), (float)(x3), (float)(y3), (float)(objspace_flatness_squared), (int)(n + 1)); + } + else + { + stbtt__add_point(points, (int)(*num_points), (float)(x3), (float)(y3)); + *num_points = (int)(*num_points + 1); + } + + } + + public static stbtt__point* stbtt_FlattenCurves(stbtt_vertex* vertices, int num_verts, float objspace_flatness, int** contour_lengths, int* num_contours, object userdata) + { + stbtt__point* points = null; + int num_points = (int)(0); + float objspace_flatness_squared = (float)(objspace_flatness * objspace_flatness); + int i; + int n = (int)(0); + int start = (int)(0); + int pass; + for (i = (int)(0); (i) < (num_verts); ++i) + { + if ((vertices[i].type) == (STBTT_vmove)) + ++n; + } + *num_contours = (int)(n); + if ((n) == (0)) + return null; + *contour_lengths = (int*)(CRuntime.malloc((ulong)(sizeof(int) * n))); + if ((*contour_lengths) == (null)) + { + *num_contours = (int)(0); + return null; + } + + for (pass = (int)(0); (pass) < (2); ++pass) + { + float x = (float)(0); + float y = (float)(0); + if ((pass) == (1)) + { + points = (stbtt__point*)(CRuntime.malloc((ulong)(num_points * sizeof(stbtt__point)))); + if ((points) == (null)) + goto error; + } + num_points = (int)(0); + n = (int)(-1); + for (i = (int)(0); (i) < (num_verts); ++i) + { + switch (vertices[i].type) + { + case STBTT_vmove: + if ((n) >= (0)) + (*contour_lengths)[n] = (int)(num_points - start); + ++n; + start = (int)(num_points); + x = (float)(vertices[i].x); + y = (float)(vertices[i].y); + stbtt__add_point(points, (int)(num_points++), (float)(x), (float)(y)); + break; + case STBTT_vline: + x = (float)(vertices[i].x); + y = (float)(vertices[i].y); + stbtt__add_point(points, (int)(num_points++), (float)(x), (float)(y)); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, (float)(x), (float)(y), (float)(vertices[i].cx), (float)(vertices[i].cy), (float)(vertices[i].x), (float)(vertices[i].y), (float)(objspace_flatness_squared), (int)(0)); + x = (float)(vertices[i].x); + y = (float)(vertices[i].y); + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, (float)(x), (float)(y), (float)(vertices[i].cx), (float)(vertices[i].cy), (float)(vertices[i].cx1), (float)(vertices[i].cy1), (float)(vertices[i].x), (float)(vertices[i].y), (float)(objspace_flatness_squared), (int)(0)); + x = (float)(vertices[i].x); + y = (float)(vertices[i].y); + break; + } + } (*contour_lengths)[n] = (int)(num_points - start); + } + return points; + error: + ; + CRuntime.free(points); + CRuntime.free(*contour_lengths); + *contour_lengths = null; + *num_contours = (int)(0); + return (null); + } + + public static void stbtt_Rasterize(stbtt__bitmap* result, float flatness_in_pixels, stbtt_vertex* vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, object userdata) + { + float scale = (float)((scale_x) > (scale_y) ? scale_y : scale_x); + int winding_count = (int)(0); + int* winding_lengths = (null); + stbtt__point* windings = stbtt_FlattenCurves(vertices, (int)(num_verts), (float)(flatness_in_pixels / scale), &winding_lengths, &winding_count, userdata); + if ((windings) != null) + { + stbtt__rasterize(result, windings, winding_lengths, (int)(winding_count), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(x_off), (int)(y_off), (int)(invert), userdata); + CRuntime.free(winding_lengths); + CRuntime.free(windings); + } + + } + + public static void stbtt_FreeBitmap(byte* bitmap, object userdata) + { + CRuntime.free(bitmap); + } + + public static byte* stbtt_GetGlyphBitmapSubpixel(stbtt_fontinfo info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int* width, int* height, int* xoff, int* yoff) + { + int ix0; + int iy0; + int ix1; + int iy1; + stbtt__bitmap gbm = new stbtt__bitmap(); + stbtt_vertex* vertices; + int num_verts = (int)(stbtt_GetGlyphShape(info, (int)(glyph), &vertices)); + if ((scale_x) == (0)) + scale_x = (float)(scale_y); + if ((scale_y) == (0)) + { + if ((scale_x) == (0)) + { + CRuntime.free(vertices); + return (null); + } + scale_y = (float)(scale_x); + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, (int)(glyph), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), &ix0, &iy0, &ix1, &iy1); + gbm.w = (int)(ix1 - ix0); + gbm.h = (int)(iy1 - iy0); + gbm.pixels = (null); + if ((width) != null) + *width = (int)(gbm.w); + if ((height) != null) + *height = (int)(gbm.h); + if ((xoff) != null) + *xoff = (int)(ix0); + if ((yoff) != null) + *yoff = (int)(iy0); + if (((gbm.w) != 0) && ((gbm.h) != 0)) + { + gbm.pixels = (byte*)(CRuntime.malloc((ulong)(gbm.w * gbm.h))); + if ((gbm.pixels) != null) + { + gbm.stride = (int)(gbm.w); + stbtt_Rasterize(&gbm, (float)(0.35f), vertices, (int)(num_verts), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(ix0), (int)(iy0), (int)(1), info.userdata); + } + } + + CRuntime.free(vertices); + return gbm.pixels; + } + + public static byte* stbtt_GetGlyphBitmap(stbtt_fontinfo info, float scale_x, float scale_y, int glyph, int* width, int* height, int* xoff, int* yoff) + { + return stbtt_GetGlyphBitmapSubpixel(info, (float)(scale_x), (float)(scale_y), (float)(0.0f), (float)(0.0f), (int)(glyph), width, height, xoff, yoff); + } + + public static void stbtt_MakeGlyphBitmapSubpixel(stbtt_fontinfo info, byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) + { + int ix0; + int iy0; + stbtt_vertex* vertices; + int num_verts = (int)(stbtt_GetGlyphShape(info, (int)(glyph), &vertices)); + stbtt__bitmap gbm = new stbtt__bitmap(); + stbtt_GetGlyphBitmapBoxSubpixel(info, (int)(glyph), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), &ix0, &iy0, null, null); + gbm.pixels = output; + gbm.w = (int)(out_w); + gbm.h = (int)(out_h); + gbm.stride = (int)(out_stride); + if (((gbm.w) != 0) && ((gbm.h) != 0)) + stbtt_Rasterize(&gbm, (float)(0.35f), vertices, (int)(num_verts), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(ix0), (int)(iy0), (int)(1), info.userdata); + CRuntime.free(vertices); + } + + public static void stbtt_MakeGlyphBitmap(stbtt_fontinfo info, byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) + { + stbtt_MakeGlyphBitmapSubpixel(info, output, (int)(out_w), (int)(out_h), (int)(out_stride), (float)(scale_x), (float)(scale_y), (float)(0.0f), (float)(0.0f), (int)(glyph)); + } + + public static byte* stbtt_GetCodepointBitmapSubpixel(stbtt_fontinfo info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int* width, int* height, int* xoff, int* yoff) + { + return stbtt_GetGlyphBitmapSubpixel(info, (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(stbtt_FindGlyphIndex(info, (int)(codepoint))), width, height, xoff, yoff); + } + + public static void stbtt_MakeCodepointBitmapSubpixelPrefilter(stbtt_fontinfo info, byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float* sub_x, float* sub_y, int codepoint) + { + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, (int)(out_w), (int)(out_h), (int)(out_stride), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(oversample_x), (int)(oversample_y), sub_x, sub_y, (int)(stbtt_FindGlyphIndex(info, (int)(codepoint)))); + } + + public static void stbtt_MakeCodepointBitmapSubpixel(stbtt_fontinfo info, byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) + { + stbtt_MakeGlyphBitmapSubpixel(info, output, (int)(out_w), (int)(out_h), (int)(out_stride), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(stbtt_FindGlyphIndex(info, (int)(codepoint)))); + } + + public static byte* stbtt_GetCodepointBitmap(stbtt_fontinfo info, float scale_x, float scale_y, int codepoint, int* width, int* height, int* xoff, int* yoff) + { + return stbtt_GetCodepointBitmapSubpixel(info, (float)(scale_x), (float)(scale_y), (float)(0.0f), (float)(0.0f), (int)(codepoint), width, height, xoff, yoff); + } + + public static void stbtt_MakeCodepointBitmap(stbtt_fontinfo info, byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) + { + stbtt_MakeCodepointBitmapSubpixel(info, output, (int)(out_w), (int)(out_h), (int)(out_stride), (float)(scale_x), (float)(scale_y), (float)(0.0f), (float)(0.0f), (int)(codepoint)); + } + + public static int stbtt_BakeFontBitmap_internal(byte* data, int offset, float pixel_height, byte* pixels, int pw, int ph, int first_char, int num_chars, stbtt_bakedchar* chardata) + { + float scale; + int x; + int y; + int bottom_y; + int i; + stbtt_fontinfo f = new stbtt_fontinfo(); + f.userdata = (null); + if (stbtt_InitFont(f, data, (int)(offset)) == 0) + return (int)(-1); + CRuntime.memset(pixels, (int)(0), (ulong)(pw * ph)); + x = (int)(y = (int)(1)); + bottom_y = (int)(1); + scale = (float)(stbtt_ScaleForPixelHeight(f, (float)(pixel_height))); + for (i = (int)(0); (i) < (num_chars); ++i) + { + int advance; + int lsb; + int x0; + int y0; + int x1; + int y1; + int gw; + int gh; + int g = (int)(stbtt_FindGlyphIndex(f, (int)(first_char + i))); + stbtt_GetGlyphHMetrics(f, (int)(g), &advance, &lsb); + stbtt_GetGlyphBitmapBox(f, (int)(g), (float)(scale), (float)(scale), &x0, &y0, &x1, &y1); + gw = (int)(x1 - x0); + gh = (int)(y1 - y0); + if ((x + gw + 1) >= (pw)) + { + y = (int)(bottom_y); + x = (int)(1); + } + if ((y + gh + 1) >= (ph)) + return (int)(-i); + stbtt_MakeGlyphBitmap(f, pixels + x + y * pw, (int)(gw), (int)(gh), (int)(pw), (float)(scale), (float)(scale), (int)(g)); + chardata[i].x0 = (ushort)((short)(x)); + chardata[i].y0 = (ushort)((short)(y)); + chardata[i].x1 = (ushort)((short)(x + gw)); + chardata[i].y1 = (ushort)((short)(y + gh)); + chardata[i].xadvance = (float)(scale * advance); + chardata[i].xoff = ((float)(x0)); + chardata[i].yoff = ((float)(y0)); + x = (int)(x + gw + 1); + if ((y + gh + 1) > (bottom_y)) + bottom_y = (int)(y + gh + 1); + } + return (int)(bottom_y); + } + + public static void stbtt_GetBakedQuad(stbtt_bakedchar* chardata, int pw, int ph, int char_index, float* xpos, float* ypos, stbtt_aligned_quad* q, int opengl_fillrule) + { + float d3d_bias = (float)((opengl_fillrule) != 0 ? 0 : -0.5f); + float ipw = (float)(1.0f / pw); + float iph = (float)(1.0f / ph); + stbtt_bakedchar* b = chardata + char_index; + int round_x = ((int)(CRuntime.floor((double)((*xpos + b->xoff) + 0.5f)))); + int round_y = ((int)(CRuntime.floor((double)((*ypos + b->yoff) + 0.5f)))); + q->x0 = (float)(round_x + d3d_bias); + q->y0 = (float)(round_y + d3d_bias); + q->x1 = (float)(round_x + b->x1 - b->x0 + d3d_bias); + q->y1 = (float)(round_y + b->y1 - b->y0 + d3d_bias); + q->s0 = (float)(b->x0 * ipw); + q->t0 = (float)(b->y0 * iph); + q->s1 = (float)(b->x1 * ipw); + q->t1 = (float)(b->y1 * iph); + *xpos += (float)(b->xadvance); + } + + public static void stbrp_init_target(stbrp_context* con, int pw, int ph, stbrp_node* nodes, int num_nodes) + { + con->width = (int)(pw); + con->height = (int)(ph); + con->x = (int)(0); + con->y = (int)(0); + con->bottom_y = (int)(0); + } + + public static void stbrp_pack_rects(stbrp_context* con, stbrp_rect* rects, int num_rects) + { + int i; + for (i = (int)(0); (i) < (num_rects); ++i) + { + if ((con->x + rects[i].w) > (con->width)) + { + con->x = (int)(0); + con->y = (int)(con->bottom_y); + } + if ((con->y + rects[i].h) > (con->height)) + break; + rects[i].x = (int)(con->x); + rects[i].y = (int)(con->y); + rects[i].was_packed = (int)(1); + con->x += (int)(rects[i].w); + if ((con->y + rects[i].h) > (con->bottom_y)) + con->bottom_y = (int)(con->y + rects[i].h); + } + for (; (i) < (num_rects); ++i) + { + rects[i].was_packed = (int)(0); + } + } + + public static int stbtt_PackBegin(stbtt_pack_context spc, byte* pixels, int pw, int ph, int stride_in_bytes, int padding, object alloc_context) + { + stbrp_context* context = (stbrp_context*)(CRuntime.malloc((ulong)(sizeof(stbrp_context)))); + int num_nodes = (int)(pw - padding); + stbrp_node* nodes = (stbrp_node*)(CRuntime.malloc((ulong)(sizeof(stbrp_node) * num_nodes))); + if (((context) == (null)) || ((nodes) == (null))) + { + if (context != (null)) + CRuntime.free(context); + if (nodes != (null)) + CRuntime.free(nodes); + return (int)(0); + } + + spc.user_allocator_context = alloc_context; + spc.width = (int)(pw); + spc.height = (int)(ph); + spc.pixels = pixels; + spc.pack_info = context; + spc.nodes = nodes; + spc.padding = (int)(padding); + spc.stride_in_bytes = (int)(stride_in_bytes != 0 ? stride_in_bytes : pw); + spc.h_oversample = (uint)(1); + spc.v_oversample = (uint)(1); + stbrp_init_target(context, (int)(pw - padding), (int)(ph - padding), nodes, (int)(num_nodes)); + if ((pixels) != null) + CRuntime.memset(pixels, (int)(0), (ulong)(pw * ph)); + return (int)(1); + } + + public static void stbtt_PackEnd(stbtt_pack_context spc) + { + CRuntime.free(spc.nodes); + CRuntime.free(spc.pack_info); + } + + public static void stbtt_PackSetOversampling(stbtt_pack_context spc, uint h_oversample, uint v_oversample) + { + if (h_oversample <= 8) + spc.h_oversample = (uint)(h_oversample); + if (v_oversample <= 8) + spc.v_oversample = (uint)(v_oversample); + } + + public static void stbtt__h_prefilter(byte* pixels, int w, int h, int stride_in_bytes, uint kernel_width) + { + byte* buffer = stackalloc byte[8]; + int safe_w = (int)(w - kernel_width); + int j; + CRuntime.memset(buffer, (int)(0), (ulong)(8)); + for (j = (int)(0); (j) < (h); ++j) + { + int i; + uint total; + CRuntime.memset(buffer, (int)(0), (ulong)(kernel_width)); + total = (uint)(0); + switch (kernel_width) + { + case 2: + for (i = (int)(0); i <= safe_w; ++i) + { + total += (uint)(pixels[i] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i]); + pixels[i] = ((byte)(total / 2)); + } + break; + case 3: + for (i = (int)(0); i <= safe_w; ++i) + { + total += (uint)(pixels[i] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i]); + pixels[i] = ((byte)(total / 3)); + } + break; + case 4: + for (i = (int)(0); i <= safe_w; ++i) + { + total += (uint)(pixels[i] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i]); + pixels[i] = ((byte)(total / 4)); + } + break; + case 5: + for (i = (int)(0); i <= safe_w; ++i) + { + total += (uint)(pixels[i] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i]); + pixels[i] = ((byte)(total / 5)); + } + break; + default: + for (i = (int)(0); i <= safe_w; ++i) + { + total += (uint)(pixels[i] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i]); + pixels[i] = ((byte)(total / kernel_width)); + } + break; + } + for (; (i) < (w); ++i) + { + total -= (uint)(buffer[i & (8 - 1)]); + pixels[i] = ((byte)(total / kernel_width)); + } + pixels += stride_in_bytes; + } + } + + public static void stbtt__v_prefilter(byte* pixels, int w, int h, int stride_in_bytes, uint kernel_width) + { + byte* buffer = stackalloc byte[8]; + int safe_h = (int)(h - kernel_width); + int j; + CRuntime.memset(buffer, (int)(0), (ulong)(8)); + for (j = (int)(0); (j) < (w); ++j) + { + int i; + uint total; + CRuntime.memset(buffer, (int)(0), (ulong)(kernel_width)); + total = (uint)(0); + switch (kernel_width) + { + case 2: + for (i = (int)(0); i <= safe_h; ++i) + { + total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i * stride_in_bytes]); + pixels[i * stride_in_bytes] = ((byte)(total / 2)); + } + break; + case 3: + for (i = (int)(0); i <= safe_h; ++i) + { + total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i * stride_in_bytes]); + pixels[i * stride_in_bytes] = ((byte)(total / 3)); + } + break; + case 4: + for (i = (int)(0); i <= safe_h; ++i) + { + total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i * stride_in_bytes]); + pixels[i * stride_in_bytes] = ((byte)(total / 4)); + } + break; + case 5: + for (i = (int)(0); i <= safe_h; ++i) + { + total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i * stride_in_bytes]); + pixels[i * stride_in_bytes] = ((byte)(total / 5)); + } + break; + default: + for (i = (int)(0); i <= safe_h; ++i) + { + total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i * stride_in_bytes]); + pixels[i * stride_in_bytes] = ((byte)(total / kernel_width)); + } + break; + } + for (; (i) < (h); ++i) + { + total -= (uint)(buffer[i & (8 - 1)]); + pixels[i * stride_in_bytes] = ((byte)(total / kernel_width)); + } + pixels += 1; + } + } + + public static float stbtt__oversample_shift(int oversample) + { + if (oversample == 0) + return (float)(0.0f); + return (float)((float)(-(oversample - 1)) / (2.0f * (float)(oversample))); + } + + public static int stbtt_PackFontRangesGatherRects(stbtt_pack_context spc, stbtt_fontinfo info, stbtt_pack_range* ranges, int num_ranges, stbrp_rect* rects) + { + int i; + int j; + int k; + k = (int)(0); + for (i = (int)(0); (i) < (num_ranges); ++i) + { + float fh = (float)(ranges[i].font_size); + float scale = (float)((fh) > (0) ? stbtt_ScaleForPixelHeight(info, (float)(fh)) : stbtt_ScaleForMappingEmToPixels(info, (float)(-fh))); + ranges[i].h_oversample = ((byte)(spc.h_oversample)); + ranges[i].v_oversample = ((byte)(spc.v_oversample)); + for (j = (int)(0); (j) < (ranges[i].num_chars); ++j) + { + int x0; + int y0; + int x1; + int y1; + int codepoint = (int)((ranges[i].array_of_unicode_codepoints) == (null) ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]); + int glyph = (int)(stbtt_FindGlyphIndex(info, (int)(codepoint))); + stbtt_GetGlyphBitmapBoxSubpixel(info, (int)(glyph), (float)(scale * spc.h_oversample), (float)(scale * spc.v_oversample), (float)(0), (float)(0), &x0, &y0, &x1, &y1); + rects[k].w = ((int)(x1 - x0 + spc.padding + spc.h_oversample - 1)); + rects[k].h = ((int)(y1 - y0 + spc.padding + spc.v_oversample - 1)); + ++k; + } + } + return (int)(k); + } + + public static void stbtt_MakeGlyphBitmapSubpixelPrefilter(stbtt_fontinfo info, byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float* sub_x, float* sub_y, int glyph) + { + stbtt_MakeGlyphBitmapSubpixel(info, output, (int)(out_w - (prefilter_x - 1)), (int)(out_h - (prefilter_y - 1)), (int)(out_stride), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(glyph)); + if ((prefilter_x) > (1)) + stbtt__h_prefilter(output, (int)(out_w), (int)(out_h), (int)(out_stride), (uint)(prefilter_x)); + if ((prefilter_y) > (1)) + stbtt__v_prefilter(output, (int)(out_w), (int)(out_h), (int)(out_stride), (uint)(prefilter_y)); + *sub_x = (float)(stbtt__oversample_shift((int)(prefilter_x))); + *sub_y = (float)(stbtt__oversample_shift((int)(prefilter_y))); + } + + public static int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context spc, stbtt_fontinfo info, stbtt_pack_range* ranges, int num_ranges, stbrp_rect* rects) + { + int i; + int j; + int k; + int return_value = (int)(1); + int old_h_over = (int)(spc.h_oversample); + int old_v_over = (int)(spc.v_oversample); + k = (int)(0); + for (i = (int)(0); (i) < (num_ranges); ++i) + { + float fh = (float)(ranges[i].font_size); + float scale = (float)((fh) > (0) ? stbtt_ScaleForPixelHeight(info, (float)(fh)) : stbtt_ScaleForMappingEmToPixels(info, (float)(-fh))); + float recip_h; + float recip_v; + float sub_x; + float sub_y; + spc.h_oversample = (uint)(ranges[i].h_oversample); + spc.v_oversample = (uint)(ranges[i].v_oversample); + recip_h = (float)(1.0f / spc.h_oversample); + recip_v = (float)(1.0f / spc.v_oversample); + sub_x = (float)(stbtt__oversample_shift((int)(spc.h_oversample))); + sub_y = (float)(stbtt__oversample_shift((int)(spc.v_oversample))); + for (j = (int)(0); (j) < (ranges[i].num_chars); ++j) + { + stbrp_rect* r = &rects[k]; + if ((r->was_packed) != 0) + { + stbtt_packedchar* bc = &ranges[i].chardata_for_range[j]; + int advance; + int lsb; + int x0; + int y0; + int x1; + int y1; + int codepoint = (int)((ranges[i].array_of_unicode_codepoints) == (null) ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]); + int glyph = (int)(stbtt_FindGlyphIndex(info, (int)(codepoint))); + int pad = (int)(spc.padding); + r->x += (int)(pad); + r->y += (int)(pad); + r->w -= (int)(pad); + r->h -= (int)(pad); + stbtt_GetGlyphHMetrics(info, (int)(glyph), &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, (int)(glyph), (float)(scale * spc.h_oversample), (float)(scale * spc.v_oversample), &x0, &y0, &x1, &y1); + stbtt_MakeGlyphBitmapSubpixel(info, spc.pixels + r->x + r->y * spc.stride_in_bytes, (int)(r->w - spc.h_oversample + 1), (int)(r->h - spc.v_oversample + 1), (int)(spc.stride_in_bytes), (float)(scale * spc.h_oversample), (float)(scale * spc.v_oversample), (float)(0), (float)(0), (int)(glyph)); + if ((spc.h_oversample) > (1)) + stbtt__h_prefilter(spc.pixels + r->x + r->y * spc.stride_in_bytes, (int)(r->w), (int)(r->h), (int)(spc.stride_in_bytes), (uint)(spc.h_oversample)); + if ((spc.v_oversample) > (1)) + stbtt__v_prefilter(spc.pixels + r->x + r->y * spc.stride_in_bytes, (int)(r->w), (int)(r->h), (int)(spc.stride_in_bytes), (uint)(spc.v_oversample)); + bc->x0 = (ushort)((short)(r->x)); + bc->y0 = (ushort)((short)(r->y)); + bc->x1 = (ushort)((short)(r->x + r->w)); + bc->y1 = (ushort)((short)(r->y + r->h)); + bc->xadvance = (float)(scale * advance); + bc->xoff = (float)((float)(x0) * recip_h + sub_x); + bc->yoff = (float)((float)(y0) * recip_v + sub_y); + bc->xoff2 = (float)((x0 + r->w) * recip_h + sub_x); + bc->yoff2 = (float)((y0 + r->h) * recip_v + sub_y); + } + else + { + return_value = (int)(0); + } + ++k; + } + } + spc.h_oversample = (uint)(old_h_over); + spc.v_oversample = (uint)(old_v_over); + return (int)(return_value); + } + + public static void stbtt_PackFontRangesPackRects(stbtt_pack_context spc, stbrp_rect* rects, int num_rects) + { + stbrp_pack_rects((stbrp_context*)(spc.pack_info), rects, (int)(num_rects)); + } + + public static int stbtt_PackFontRanges(stbtt_pack_context spc, byte* fontdata, int font_index, stbtt_pack_range* ranges, int num_ranges) + { + stbtt_fontinfo info = new stbtt_fontinfo(); + int i; + int j; + int n; + int return_value = (int)(1); + stbrp_rect* rects; + for (i = (int)(0); (i) < (num_ranges); ++i) + { + for (j = (int)(0); (j) < (ranges[i].num_chars); ++j) + { + ranges[i].chardata_for_range[j].x0 = (ushort)(ranges[i].chardata_for_range[j].y0 = (ushort)(ranges[i].chardata_for_range[j].x1 = (ushort)(ranges[i].chardata_for_range[j].y1 = (ushort)(0)))); + } + } + n = (int)(0); + for (i = (int)(0); (i) < (num_ranges); ++i) + { + n += (int)(ranges[i].num_chars); + } + rects = (stbrp_rect*)(CRuntime.malloc((ulong)(sizeof(stbrp_rect) * n))); + if ((rects) == (null)) + return (int)(0); + info.userdata = spc.user_allocator_context; + stbtt_InitFont(info, fontdata, (int)(stbtt_GetFontOffsetForIndex(fontdata, (int)(font_index)))); + n = (int)(stbtt_PackFontRangesGatherRects(spc, info, ranges, (int)(num_ranges), rects)); + stbtt_PackFontRangesPackRects(spc, rects, (int)(n)); + return_value = (int)(stbtt_PackFontRangesRenderIntoRects(spc, info, ranges, (int)(num_ranges), rects)); + CRuntime.free(rects); + return (int)(return_value); + } + + public static int stbtt_PackFontRange(stbtt_pack_context spc, byte* fontdata, int font_index, float font_size, int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar* chardata_for_range) + { + stbtt_pack_range range = new stbtt_pack_range(); + range.first_unicode_codepoint_in_range = (int)(first_unicode_codepoint_in_range); + range.array_of_unicode_codepoints = (null); + range.num_chars = (int)(num_chars_in_range); + range.chardata_for_range = chardata_for_range; + range.font_size = (float)(font_size); + return (int)(stbtt_PackFontRanges(spc, fontdata, (int)(font_index), &range, (int)(1))); + } + + public static void stbtt_GetPackedQuad(stbtt_packedchar* chardata, int pw, int ph, int char_index, float* xpos, float* ypos, stbtt_aligned_quad* q, int align_to_integer) + { + float ipw = (float)(1.0f / pw); + float iph = (float)(1.0f / ph); + stbtt_packedchar* b = chardata + char_index; + if ((align_to_integer) != 0) + { + float x = (float)((int)(CRuntime.floor((double)((*xpos + b->xoff) + 0.5f)))); + float y = (float)((int)(CRuntime.floor((double)((*ypos + b->yoff) + 0.5f)))); + q->x0 = (float)(x); + q->y0 = (float)(y); + q->x1 = (float)(x + b->xoff2 - b->xoff); + q->y1 = (float)(y + b->yoff2 - b->yoff); + } + else + { + q->x0 = (float)(*xpos + b->xoff); + q->y0 = (float)(*ypos + b->yoff); + q->x1 = (float)(*xpos + b->xoff2); + q->y1 = (float)(*ypos + b->yoff2); + } + + q->s0 = (float)(b->x0 * ipw); + q->t0 = (float)(b->y0 * iph); + q->s1 = (float)(b->x1 * ipw); + q->t1 = (float)(b->y1 * iph); + *xpos += (float)(b->xadvance); + } + + public static int stbtt__ray_intersect_bezier(float* orig, float* ray, float* q0, float* q1, float* q2, float* hits) + { + float q0perp = (float)(q0[1] * ray[0] - q0[0] * ray[1]); + float q1perp = (float)(q1[1] * ray[0] - q1[0] * ray[1]); + float q2perp = (float)(q2[1] * ray[0] - q2[0] * ray[1]); + float roperp = (float)(orig[1] * ray[0] - orig[0] * ray[1]); + float a = (float)(q0perp - 2 * q1perp + q2perp); + float b = (float)(q1perp - q0perp); + float c = (float)(q0perp - roperp); + float s0 = (float)(0); + float s1 = (float)(0); + int num_s = (int)(0); + if (a != 0.0) + { + float discr = (float)(b * b - a * c); + if ((discr) > (0.0)) + { + float rcpna = (float)(-1 / a); + float d = (float)(CRuntime.sqrt((double)(discr))); + s0 = (float)((b + d) * rcpna); + s1 = (float)((b - d) * rcpna); + if (((s0) >= (0.0)) && (s0 <= 1.0)) + num_s = (int)(1); + if ((((d) > (0.0)) && ((s1) >= (0.0))) && (s1 <= 1.0)) + { + if ((num_s) == (0)) + s0 = (float)(s1); + ++num_s; + } + } + } + else + { + s0 = (float)(c / (-2 * b)); + if (((s0) >= (0.0)) && (s0 <= 1.0)) + num_s = (int)(1); + } + + if ((num_s) == (0)) + return (int)(0); + else + { + float rcp_len2 = (float)(1 / (ray[0] * ray[0] + ray[1] * ray[1])); + float rayn_x = (float)(ray[0] * rcp_len2); + float rayn_y = (float)(ray[1] * rcp_len2); + float q0d = (float)(q0[0] * rayn_x + q0[1] * rayn_y); + float q1d = (float)(q1[0] * rayn_x + q1[1] * rayn_y); + float q2d = (float)(q2[0] * rayn_x + q2[1] * rayn_y); + float rod = (float)(orig[0] * rayn_x + orig[1] * rayn_y); + float q10d = (float)(q1d - q0d); + float q20d = (float)(q2d - q0d); + float q0rd = (float)(q0d - rod); + hits[0] = (float)(q0rd + s0 * (2.0f - 2.0f * s0) * q10d + s0 * s0 * q20d); + hits[1] = (float)(a * s0 + b); + if ((num_s) > (1)) + { + hits[2] = (float)(q0rd + s1 * (2.0f - 2.0f * s1) * q10d + s1 * s1 * q20d); + hits[3] = (float)(a * s1 + b); + return (int)(2); + } + else + { + return (int)(1); + } + } + + } + + public static int equal(float* a, float* b) + { + return (int)(((a[0] == b[0]) && (a[1] == b[1])) ? 1 : 0); + } + + public static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex* verts) + { + int i; + float* orig = stackalloc float[2]; + float* ray = stackalloc float[2]; + ray[0] = (float)(1); + ray[1] = (float)(0); + + float y_frac; + int winding = (int)(0); + orig[0] = (float)(x); + orig[1] = (float)(y); + y_frac = ((float)(CRuntime.fmod((double)(y), (double)(1.0f)))); + if ((y_frac) < (0.01f)) + y += (float)(0.01f); + else if ((y_frac) > (0.99f)) + y -= (float)(0.01f); + orig[1] = (float)(y); + for (i = (int)(0); (i) < (nverts); ++i) + { + if ((verts[i].type) == (STBTT_vline)) + { + int x0 = (int)(verts[i - 1].x); + int y0 = (int)(verts[i - 1].y); + int x1 = (int)(verts[i].x); + int y1 = (int)(verts[i].y); + if ((((y) > ((y0) < (y1) ? (y0) : (y1))) && ((y) < ((y0) < (y1) ? (y1) : (y0)))) && ((x) > ((x0) < (x1) ? (x0) : (x1)))) + { + float x_inter = (float)((y - y0) / (y1 - y0) * (x1 - x0) + x0); + if ((x_inter) < (x)) + winding += (int)(((y0) < (y1)) ? 1 : -1); + } + } + if ((verts[i].type) == (STBTT_vcurve)) + { + int x0 = (int)(verts[i - 1].x); + int y0 = (int)(verts[i - 1].y); + int x1 = (int)(verts[i].cx); + int y1 = (int)(verts[i].cy); + int x2 = (int)(verts[i].x); + int y2 = (int)(verts[i].y); + int ax = (int)((x0) < ((x1) < (x2) ? (x1) : (x2)) ? (x0) : ((x1) < (x2) ? (x1) : (x2))); + int ay = (int)((y0) < ((y1) < (y2) ? (y1) : (y2)) ? (y0) : ((y1) < (y2) ? (y1) : (y2))); + int by = (int)((y0) < ((y1) < (y2) ? (y2) : (y1)) ? ((y1) < (y2) ? (y2) : (y1)) : (y0)); + if ((((y) > (ay)) && ((y) < (by))) && ((x) > (ax))) + { + float* q0 = stackalloc float[2]; + float* q1 = stackalloc float[2]; + float* q2 = stackalloc float[2]; + float* hits = stackalloc float[4]; + q0[0] = ((float)(x0)); + q0[1] = ((float)(y0)); + q1[0] = ((float)(x1)); + q1[1] = ((float)(y1)); + q2[0] = ((float)(x2)); + q2[1] = ((float)(y2)); + if (((equal(q0, q1)) != 0) || ((equal(q1, q2)) != 0)) + { + x0 = ((int)(verts[i - 1].x)); + y0 = ((int)(verts[i - 1].y)); + x1 = ((int)(verts[i].x)); + y1 = ((int)(verts[i].y)); + if ((((y) > ((y0) < (y1) ? (y0) : (y1))) && ((y) < ((y0) < (y1) ? (y1) : (y0)))) && ((x) > ((x0) < (x1) ? (x0) : (x1)))) + { + float x_inter = (float)((y - y0) / (y1 - y0) * (x1 - x0) + x0); + if ((x_inter) < (x)) + winding += (int)(((y0) < (y1)) ? 1 : -1); + } + } + else + { + int num_hits = (int)(stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits)); + if ((num_hits) >= (1)) + if ((hits[0]) < (0)) + winding += (int)((hits[1]) < (0) ? -1 : 1); + if ((num_hits) >= (2)) + if ((hits[2]) < (0)) + winding += (int)((hits[3]) < (0) ? -1 : 1); + } + } + } + } + return (int)(winding); + } + + public static float stbtt__cuberoot(float x) + { + if ((x) < (0)) + return (float)(-(float)(CRuntime.pow((double)(-x), (double)(1.0f / 3.0f)))); + else + return (float)(CRuntime.pow((double)(x), (double)(1.0f / 3.0f))); + } + + public static int stbtt__solve_cubic(float a, float b, float c, float* r) + { + float s = (float)(-a / 3); + float p = (float)(b - a * a / 3); + float q = (float)(a * (2 * a * a - 9 * b) / 27 + c); + float p3 = (float)(p * p * p); + float d = (float)(q * q + 4 * p3 / 27); + if ((d) >= (0)) + { + float z = (float)(CRuntime.sqrt((double)(d))); + float u = (float)((-q + z) / 2); + float v = (float)((-q - z) / 2); + u = (float)(stbtt__cuberoot((float)(u))); + v = (float)(stbtt__cuberoot((float)(v))); + r[0] = (float)(s + u + v); + return (int)(1); + } + else + { + float u = (float)(CRuntime.sqrt((double)(-p / 3))); + float v = (float)((float)(CRuntime.acos((double)(-CRuntime.sqrt((double)(-27 / p3)) * q / 2))) / 3); + float m = (float)(CRuntime.cos((double)(v))); + float n = (float)((float)(CRuntime.cos((double)(v - 3.141592 / 2))) * 1.732050808f); + r[0] = (float)(s + u * 2 * m); + r[1] = (float)(s - u * (m + n)); + r[2] = (float)(s - u * (m - n)); + return (int)(3); + } + + } + + public static byte* stbtt_GetGlyphSDF(stbtt_fontinfo info, float scale, int glyph, int padding, byte onedge_value, float pixel_dist_scale, int* width, int* height, int* xoff, int* yoff) + { + float scale_x = (float)(scale); + float scale_y = (float)(scale); + int ix0; + int iy0; + int ix1; + int iy1; + int w; + int h; + byte* data; + if ((scale_x) == (0)) + scale_x = (float)(scale_y); + if ((scale_y) == (0)) + { + if ((scale_x) == (0)) + return (null); + scale_y = (float)(scale_x); + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, (int)(glyph), (float)(scale), (float)(scale), (float)(0.0f), (float)(0.0f), &ix0, &iy0, &ix1, &iy1); + if (((ix0) == (ix1)) || ((iy0) == (iy1))) + return (null); + ix0 -= (int)(padding); + iy0 -= (int)(padding); + ix1 += (int)(padding); + iy1 += (int)(padding); + w = (int)(ix1 - ix0); + h = (int)(iy1 - iy0); + if ((width) != null) + *width = (int)(w); + if ((height) != null) + *height = (int)(h); + if ((xoff) != null) + *xoff = (int)(ix0); + if ((yoff) != null) + *yoff = (int)(iy0); + scale_y = (float)(-scale_y); + { + int x; + int y; + int i; + int j; + float* precompute; + stbtt_vertex* verts; + int num_verts = (int)(stbtt_GetGlyphShape(info, (int)(glyph), &verts)); + data = (byte*)(CRuntime.malloc((ulong)(w * h))); + precompute = (float*)(CRuntime.malloc((ulong)(num_verts * sizeof(float)))); + for (i = (int)(0), j = (int)(num_verts - 1); (i) < (num_verts); j = (int)(i++)) + { + if ((verts[i].type) == (STBTT_vline)) + { + float x0 = (float)(verts[i].x * scale_x); + float y0 = (float)(verts[i].y * scale_y); + float x1 = (float)(verts[j].x * scale_x); + float y1 = (float)(verts[j].y * scale_y); + float dist = (float)(CRuntime.sqrt((double)((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)))); + precompute[i] = (float)(((dist) == (0)) ? 0.0f : 1.0f / dist); + } + else if ((verts[i].type) == (STBTT_vcurve)) + { + float x2 = (float)(verts[j].x * scale_x); + float y2 = (float)(verts[j].y * scale_y); + float x1 = (float)(verts[i].cx * scale_x); + float y1 = (float)(verts[i].cy * scale_y); + float x0 = (float)(verts[i].x * scale_x); + float y0 = (float)(verts[i].y * scale_y); + float bx = (float)(x0 - 2 * x1 + x2); + float by = (float)(y0 - 2 * y1 + y2); + float len2 = (float)(bx * bx + by * by); + if (len2 != 0.0f) + precompute[i] = (float)(1.0f / (bx * bx + by * by)); + else + precompute[i] = (float)(0.0f); + } + else + precompute[i] = (float)(0.0f); + } + for (y = (int)(iy0); (y) < (iy1); ++y) + { + for (x = (int)(ix0); (x) < (ix1); ++x) + { + float val; + float min_dist = (float)(999999.0f); + float sx = (float)((float)(x) + 0.5f); + float sy = (float)((float)(y) + 0.5f); + float x_gspace = (float)(sx / scale_x); + float y_gspace = (float)(sy / scale_y); + int winding = (int)(stbtt__compute_crossings_x((float)(x_gspace), (float)(y_gspace), (int)(num_verts), verts)); + for (i = (int)(0); (i) < (num_verts); ++i) + { + float x0 = (float)(verts[i].x * scale_x); + float y0 = (float)(verts[i].y * scale_y); + float dist2 = (float)((x0 - sx) * (x0 - sx) + (y0 - sy) * (y0 - sy)); + if ((dist2) < (min_dist * min_dist)) + min_dist = ((float)(CRuntime.sqrt((double)(dist2)))); + if ((verts[i].type) == (STBTT_vline)) + { + float x1 = (float)(verts[i - 1].x * scale_x); + float y1 = (float)(verts[i - 1].y * scale_y); + float dist = (float)((float)(CRuntime.fabs((double)((x1 - x0) * (y0 - sy) - (y1 - y0) * (x0 - sx)))) * precompute[i]); + if ((dist) < (min_dist)) + { + float dx = (float)(x1 - x0); + float dy = (float)(y1 - y0); + float px = (float)(x0 - sx); + float py = (float)(y0 - sy); + float t = (float)(-(px * dx + py * dy) / (dx * dx + dy * dy)); + if (((t) >= (0.0f)) && (t <= 1.0f)) + min_dist = (float)(dist); + } + } + else if ((verts[i].type) == (STBTT_vcurve)) + { + float x2 = (float)(verts[i - 1].x * scale_x); + float y2 = (float)(verts[i - 1].y * scale_y); + float x1 = (float)(verts[i].cx * scale_x); + float y1 = (float)(verts[i].cy * scale_y); + float box_x0 = (float)(((x0) < (x1) ? (x0) : (x1)) < (x2) ? ((x0) < (x1) ? (x0) : (x1)) : (x2)); + float box_y0 = (float)(((y0) < (y1) ? (y0) : (y1)) < (y2) ? ((y0) < (y1) ? (y0) : (y1)) : (y2)); + float box_x1 = (float)(((x0) < (x1) ? (x1) : (x0)) < (x2) ? (x2) : ((x0) < (x1) ? (x1) : (x0))); + float box_y1 = (float)(((y0) < (y1) ? (y1) : (y0)) < (y2) ? (y2) : ((y0) < (y1) ? (y1) : (y0))); + if (((((sx) > (box_x0 - min_dist)) && ((sx) < (box_x1 + min_dist))) && ((sy) > (box_y0 - min_dist))) && ((sy) < (box_y1 + min_dist))) + { + int num = (int)(0); + float ax = (float)(x1 - x0); + float ay = (float)(y1 - y0); + float bx = (float)(x0 - 2 * x1 + x2); + float by = (float)(y0 - 2 * y1 + y2); + float mx = (float)(x0 - sx); + float my = (float)(y0 - sy); + float* res = stackalloc float[3]; + float px; + float py; + float t; + float it; + float a_inv = (float)(precompute[i]); + if ((a_inv) == (0.0)) + { + float a = (float)(3 * (ax * bx + ay * by)); + float b = (float)(2 * (ax * ax + ay * ay) + (mx * bx + my * by)); + float c = (float)(mx * ax + my * ay); + if ((a) == (0.0)) + { + if (b != 0.0) + { + res[num++] = (float)(-c / b); + } + } + else + { + float discriminant = (float)(b * b - 4 * a * c); + if ((discriminant) < (0)) + num = (int)(0); + else + { + float root = (float)(CRuntime.sqrt((double)(discriminant))); + res[0] = (float)((-b - root) / (2 * a)); + res[1] = (float)((-b + root) / (2 * a)); + num = (int)(2); + } + } + } + else + { + float b = (float)(3 * (ax * bx + ay * by) * a_inv); + float c = (float)((2 * (ax * ax + ay * ay) + (mx * bx + my * by)) * a_inv); + float d = (float)((mx * ax + my * ay) * a_inv); + num = (int)(stbtt__solve_cubic((float)(b), (float)(c), (float)(d), res)); + } + if ((((num) >= (1)) && ((res[0]) >= (0.0f))) && (res[0] <= 1.0f)) + { + t = (float)(res[0]); + it = (float)(1.0f - t); + px = (float)(it * it * x0 + 2 * t * it * x1 + t * t * x2); + py = (float)(it * it * y0 + 2 * t * it * y1 + t * t * y2); + dist2 = (float)((px - sx) * (px - sx) + (py - sy) * (py - sy)); + if ((dist2) < (min_dist * min_dist)) + min_dist = ((float)(CRuntime.sqrt((double)(dist2)))); + } + if ((((num) >= (2)) && ((res[1]) >= (0.0f))) && (res[1] <= 1.0f)) + { + t = (float)(res[1]); + it = (float)(1.0f - t); + px = (float)(it * it * x0 + 2 * t * it * x1 + t * t * x2); + py = (float)(it * it * y0 + 2 * t * it * y1 + t * t * y2); + dist2 = (float)((px - sx) * (px - sx) + (py - sy) * (py - sy)); + if ((dist2) < (min_dist * min_dist)) + min_dist = ((float)(CRuntime.sqrt((double)(dist2)))); + } + if ((((num) >= (3)) && ((res[2]) >= (0.0f))) && (res[2] <= 1.0f)) + { + t = (float)(res[2]); + it = (float)(1.0f - t); + px = (float)(it * it * x0 + 2 * t * it * x1 + t * t * x2); + py = (float)(it * it * y0 + 2 * t * it * y1 + t * t * y2); + dist2 = (float)((px - sx) * (px - sx) + (py - sy) * (py - sy)); + if ((dist2) < (min_dist * min_dist)) + min_dist = ((float)(CRuntime.sqrt((double)(dist2)))); + } + } + } + } + if ((winding) == (0)) + min_dist = (float)(-min_dist); + val = (float)(onedge_value + pixel_dist_scale * min_dist); + if ((val) < (0)) + val = (float)(0); + else if ((val) > (255)) + val = (float)(255); + data[(y - iy0) * w + (x - ix0)] = ((byte)(val)); + } + } + CRuntime.free(precompute); + CRuntime.free(verts); + } + + return data; + } + + public static byte* stbtt_GetCodepointSDF(stbtt_fontinfo info, float scale, int codepoint, int padding, byte onedge_value, float pixel_dist_scale, int* width, int* height, int* xoff, int* yoff) + { + return stbtt_GetGlyphSDF(info, (float)(scale), (int)(stbtt_FindGlyphIndex(info, (int)(codepoint))), (int)(padding), (byte)(onedge_value), (float)(pixel_dist_scale), width, height, xoff, yoff); + } + + public static void stbtt_FreeSDF(byte* bitmap, object userdata) + { + CRuntime.free(bitmap); + } + + public static int stbtt__CompareUTF8toUTF16_bigendian_prefix(byte* s1, int len1, byte* s2, int len2) + { + int i = (int)(0); + while ((len2) != 0) + { + ushort ch = (ushort)(s2[0] * 256 + s2[1]); + if ((ch) < (0x80)) + { + if ((i) >= (len1)) + return (int)(-1); + if (s1[i++] != ch) + return (int)(-1); + } + else if ((ch) < (0x800)) + { + if ((i + 1) >= (len1)) + return (int)(-1); + if (s1[i++] != 0xc0 + (ch >> 6)) + return (int)(-1); + if (s1[i++] != 0x80 + (ch & 0x3f)) + return (int)(-1); + } + else if (((ch) >= (0xd800)) && ((ch) < (0xdc00))) + { + uint c; + ushort ch2 = (ushort)(s2[2] * 256 + s2[3]); + if ((i + 3) >= (len1)) + return (int)(-1); + c = (uint)(((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000); + if (s1[i++] != 0xf0 + (c >> 18)) + return (int)(-1); + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) + return (int)(-1); + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) + return (int)(-1); + if (s1[i++] != 0x80 + ((c) & 0x3f)) + return (int)(-1); + s2 += 2; + len2 -= (int)(2); + } + else if (((ch) >= (0xdc00)) && ((ch) < (0xe000))) + { + return (int)(-1); + } + else + { + if ((i + 2) >= (len1)) + return (int)(-1); + if (s1[i++] != 0xe0 + (ch >> 12)) + return (int)(-1); + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) + return (int)(-1); + if (s1[i++] != 0x80 + ((ch) & 0x3f)) + return (int)(-1); + } + s2 += 2; + len2 -= (int)(2); + } + return (int)(i); + } + + public static int stbtt_CompareUTF8toUTF16_bigendian_internal(sbyte* s1, int len1, sbyte* s2, int len2) + { + return (int)((len1) == (stbtt__CompareUTF8toUTF16_bigendian_prefix((byte*)(s1), (int)(len1), (byte*)(s2), (int)(len2))) ? 1 : 0); + } + + public static sbyte* stbtt_GetFontNameString(stbtt_fontinfo font, int* length, int platformID, int encodingID, int languageID, int nameID) + { + int i; + int count; + int stringOffset; + byte* fc = font.data; + uint offset = (uint)(font.fontstart); + uint nm = (uint)(stbtt__find_table(fc, (uint)(offset), "name")); + if (nm == 0) + return (null); + count = (int)(ttUSHORT(fc + nm + 2)); + stringOffset = (int)(nm + ttUSHORT(fc + nm + 4)); + for (i = (int)(0); (i) < (count); ++i) + { + uint loc = (uint)(nm + 6 + 12 * i); + if (((((platformID) == (ttUSHORT(fc + loc + 0))) && ((encodingID) == (ttUSHORT(fc + loc + 2)))) && ((languageID) == (ttUSHORT(fc + loc + 4)))) && ((nameID) == (ttUSHORT(fc + loc + 6)))) + { + *length = (int)(ttUSHORT(fc + loc + 8)); + return (sbyte*)(fc + stringOffset + ttUSHORT(fc + loc + 10)); + } + } + return (null); + } + + public static int stbtt__matchpair(byte* fc, uint nm, byte* name, int nlen, int target_id, int next_id) + { + int i; + int count = (int)(ttUSHORT(fc + nm + 2)); + int stringOffset = (int)(nm + ttUSHORT(fc + nm + 4)); + for (i = (int)(0); (i) < (count); ++i) + { + uint loc = (uint)(nm + 6 + 12 * i); + int id = (int)(ttUSHORT(fc + loc + 6)); + if ((id) == (target_id)) + { + int platform = (int)(ttUSHORT(fc + loc + 0)); + int encoding = (int)(ttUSHORT(fc + loc + 2)); + int language = (int)(ttUSHORT(fc + loc + 4)); + if ((((platform) == (0)) || (((platform) == (3)) && ((encoding) == (1)))) || (((platform) == (3)) && ((encoding) == (10)))) + { + int slen = (int)(ttUSHORT(fc + loc + 8)); + int off = (int)(ttUSHORT(fc + loc + 10)); + int matchlen = (int)(stbtt__CompareUTF8toUTF16_bigendian_prefix(name, (int)(nlen), fc + stringOffset + off, (int)(slen))); + if ((matchlen) >= (0)) + { + if ((((((i + 1) < (count)) && ((ttUSHORT(fc + loc + 12 + 6)) == (next_id))) && ((ttUSHORT(fc + loc + 12)) == (platform))) && ((ttUSHORT(fc + loc + 12 + 2)) == (encoding))) && ((ttUSHORT(fc + loc + 12 + 4)) == (language))) + { + slen = (int)(ttUSHORT(fc + loc + 12 + 8)); + off = (int)(ttUSHORT(fc + loc + 12 + 10)); + if ((slen) == (0)) + { + if ((matchlen) == (nlen)) + return (int)(1); + } + else if (((matchlen) < (nlen)) && ((name[matchlen]) == (' '))) + { + ++matchlen; + if ((stbtt_CompareUTF8toUTF16_bigendian_internal((sbyte*)(name + matchlen), (int)(nlen - matchlen), (sbyte*)(fc + stringOffset + off), (int)(slen))) != 0) + return (int)(1); + } + } + else + { + if ((matchlen) == (nlen)) + return (int)(1); + } + } + } + } + } + return (int)(0); + } + + public static int stbtt__matches(byte* fc, uint offset, byte* name, int flags) + { + int nlen = (int)(CRuntime.strlen((sbyte*)(name))); + uint nm; + uint hd; + if (stbtt__isfont(fc + offset) == 0) + return (int)(0); + if ((flags) != 0) + { + hd = (uint)(stbtt__find_table(fc, (uint)(offset), "head")); + if ((ttUSHORT(fc + hd + 44) & 7) != (flags & 7)) + return (int)(0); + } + + nm = (uint)(stbtt__find_table(fc, (uint)(offset), "name")); + if (nm == 0) + return (int)(0); + if ((flags) != 0) + { + if ((stbtt__matchpair(fc, (uint)(nm), name, (int)(nlen), (int)(16), (int)(-1))) != 0) + return (int)(1); + if ((stbtt__matchpair(fc, (uint)(nm), name, (int)(nlen), (int)(1), (int)(-1))) != 0) + return (int)(1); + if ((stbtt__matchpair(fc, (uint)(nm), name, (int)(nlen), (int)(3), (int)(-1))) != 0) + return (int)(1); + } + else + { + if ((stbtt__matchpair(fc, (uint)(nm), name, (int)(nlen), (int)(16), (int)(17))) != 0) + return (int)(1); + if ((stbtt__matchpair(fc, (uint)(nm), name, (int)(nlen), (int)(1), (int)(2))) != 0) + return (int)(1); + if ((stbtt__matchpair(fc, (uint)(nm), name, (int)(nlen), (int)(3), (int)(-1))) != 0) + return (int)(1); + } + + return (int)(0); + } + + public static int stbtt_FindMatchingFont_internal(byte* font_collection, sbyte* name_utf8, int flags) + { + int i; + for (i = (int)(0); ; ++i) + { + int off = (int)(stbtt_GetFontOffsetForIndex(font_collection, (int)(i))); + if ((off) < (0)) + return (int)(off); + if ((stbtt__matches(font_collection, (uint)(off), (byte*)(name_utf8), (int)(flags))) != 0) + return (int)(off); + } + } + + public static int stbtt_BakeFontBitmap(byte* data, int offset, float pixel_height, byte* pixels, int pw, int ph, int first_char, int num_chars, stbtt_bakedchar* chardata) + { + return (int)(stbtt_BakeFontBitmap_internal(data, (int)(offset), (float)(pixel_height), pixels, (int)(pw), (int)(ph), (int)(first_char), (int)(num_chars), chardata)); + } + + public static int stbtt_GetFontOffsetForIndex(byte* data, int index) + { + return (int)(stbtt_GetFontOffsetForIndex_internal(data, (int)(index))); + } + + public static int stbtt_GetNumberOfFonts(byte* data) + { + return (int)(stbtt_GetNumberOfFonts_internal(data)); + } + + public static int stbtt_InitFont(stbtt_fontinfo info, byte* data, int offset) + { + return (int)(stbtt_InitFont_internal(info, data, (int)(offset))); + } + + public static int stbtt_FindMatchingFont(byte* fontdata, sbyte* name, int flags) + { + return (int)(stbtt_FindMatchingFont_internal(fontdata, name, (int)(flags))); + } + + public static int stbtt_CompareUTF8toUTF16_bigendian(sbyte* s1, int len1, sbyte* s2, int len2) + { + return (int)(stbtt_CompareUTF8toUTF16_bigendian_internal(s1, (int)(len1), s2, (int)(len2))); + } + } +} \ No newline at end of file diff --git a/SpriteFontPlus/StbTrueType/StbTrueType.cs b/SpriteFontPlus/StbTrueType/StbTrueType.cs new file mode 100644 index 000000000..edb42e7ef --- /dev/null +++ b/SpriteFontPlus/StbTrueType/StbTrueType.cs @@ -0,0 +1,38 @@ +namespace StbSharp +{ + internal static unsafe partial class StbTrueType + { + public static uint stbtt__find_table(byte* data, uint fontstart, string tag) + { + int num_tables = ttUSHORT(data + fontstart + 4); + var tabledir = fontstart + 12; + int i; + for (i = 0; (i) < num_tables; ++i) + { + var loc = (uint) (tabledir + 16*i); + if (((data + loc + 0)[0] == (tag[0])) && ((data + loc + 0)[1] == tag[1]) && + ((data + loc + 0)[2] == tag[2]) && ((data + loc + 0)[3] == (tag[3]))) + return ttULONG(data + loc + 8); + } + return 0; + } + + public static bool stbtt_BakeFontBitmap(byte[] ttf, int offset, float pixel_height, byte[] pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar[] chardata) + { + fixed (byte* ttfPtr = ttf) + { + fixed (byte* pixelsPtr = pixels) + { + fixed (stbtt_bakedchar* chardataPtr = chardata) + { + var result = stbtt_BakeFontBitmap(ttfPtr, offset, pixel_height, pixelsPtr, pw, ph, first_char, num_chars, + chardataPtr); + + return result != 0; + } + } + } + } + } +} \ No newline at end of file diff --git a/SpriteFontPlus/StbTrueTypeExtensions.cs b/SpriteFontPlus/StbTrueTypeExtensions.cs new file mode 100644 index 000000000..c025fdaae --- /dev/null +++ b/SpriteFontPlus/StbTrueTypeExtensions.cs @@ -0,0 +1,39 @@ +using StbSharp; + +namespace SpriteFontPlus +{ + internal static unsafe class StbTrueTypeExtensions + { + public static void fons__tt_getFontVMetrics(this StbTrueType.stbtt_fontinfo font, int* ascent, int* descent, int* lineGap) + { + StbTrueType.stbtt_GetFontVMetrics(font, ascent, descent, lineGap); + } + + public static float fons__tt_getPixelHeightScale(this StbTrueType.stbtt_fontinfo font, float size) + { + return (float)(StbTrueType.stbtt_ScaleForPixelHeight(font, (float)(size))); + } + + public static int fons__tt_getGlyphIndex(this StbTrueType.stbtt_fontinfo font, int codepoint) + { + return (int)(StbTrueType.stbtt_FindGlyphIndex(font, (int)(codepoint))); + } + + public static int fons__tt_buildGlyphBitmap(this StbTrueType.stbtt_fontinfo font, int glyph, float size, float scale, int* advance, int* lsb, int* x0, int* y0, int* x1, int* y1) + { + StbTrueType.stbtt_GetGlyphHMetrics(font, (int)(glyph), advance, lsb); + StbTrueType.stbtt_GetGlyphBitmapBox(font, (int)(glyph), (float)(scale), (float)(scale), x0, y0, x1, y1); + return (int)(1); + } + + public static void fons__tt_renderGlyphBitmap(this StbTrueType.stbtt_fontinfo font, byte* output, int outWidth, int outHeight, int outStride, float scaleX, float scaleY, int glyph) + { + StbTrueType.stbtt_MakeGlyphBitmap(font, output, (int)(outWidth), (int)(outHeight), (int)(outStride), (float)(scaleX), (float)(scaleY), (int)(glyph)); + } + + public static int fons__tt_getGlyphKernAdvance(this StbTrueType.stbtt_fontinfo font, int glyph1, int glyph2) + { + return (int)(StbTrueType.stbtt_GetGlyphKernAdvance(font, (int)(glyph1), (int)(glyph2))); + } + } +} diff --git a/SpriteFontPlus/TtfFontBaker.cs b/SpriteFontPlus/TtfFontBaker.cs new file mode 100644 index 000000000..6d169c817 --- /dev/null +++ b/SpriteFontPlus/TtfFontBaker.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using StbSharp; + +namespace SpriteFontPlus +{ + public static unsafe class TtfFontBaker + { + public static TtfFontBakerResult Bake(byte[] ttf, float fontPixelHeight, + int bitmapWidth, int bitmapHeight, + IEnumerable characterRanges) + { + if (ttf == null || ttf.Length == 0) + { + throw new ArgumentNullException(nameof(ttf)); + } + + if (fontPixelHeight <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fontPixelHeight)); + } + + if (bitmapWidth <= 0) + { + throw new ArgumentOutOfRangeException(nameof(bitmapWidth)); + } + + if (bitmapHeight <= 0) + { + throw new ArgumentOutOfRangeException(nameof(bitmapHeight)); + } + + if (characterRanges == null) + { + throw new ArgumentNullException(nameof(characterRanges)); + } + + if (!characterRanges.Any()) + { + throw new ArgumentException("characterRanges must have a least one value."); + } + + byte[] pixels; + var glyphs = new Dictionary(); + fixed (byte* ttfPtr = ttf) + { + StbTrueType.stbtt_fontinfo fontInfo = new StbTrueType.stbtt_fontinfo(); + if (StbTrueType.stbtt_InitFont(fontInfo, ttfPtr, 0) == 0) + { + throw new Exception("Failed to init font."); + } + + float scaleFactor = StbTrueType.stbtt_ScaleForPixelHeight(fontInfo, fontPixelHeight); + + int ascent, descent, lineGap; + StbTrueType.stbtt_GetFontVMetrics(fontInfo, &ascent, &descent, &lineGap); + + pixels = new byte[bitmapWidth * bitmapHeight]; + StbTrueType.stbtt_pack_context pc = new StbTrueType.stbtt_pack_context(); + fixed (byte* pixelsPtr = pixels) + { + StbTrueType.stbtt_PackBegin(pc, pixelsPtr, bitmapWidth, + bitmapHeight, bitmapWidth, 1, null); + } + + foreach (var range in characterRanges) + { + if (range.Start > range.End) + { + continue; + } + + var cd = new StbTrueType.stbtt_packedchar[range.End - range.Start + 1]; + fixed (StbTrueType.stbtt_packedchar* chardataPtr = cd) + { + StbTrueType.stbtt_PackFontRange(pc, ttfPtr, 0, fontPixelHeight, + range.Start, + range.End - range.Start + 1, + chardataPtr); + } + + for (var i = 0; i < cd.Length; ++i) + { + var yOff = cd[i].yoff; + yOff += ascent * scaleFactor; + + var glyphInfo = new GlyphInfo + { + X = cd[i].x0, + Y = cd[i].y0, + Width = cd[i].x1 - cd[i].x0, + Height = cd[i].y1 - cd[i].y0, + XOffset = (int)cd[i].xoff, + YOffset = (int)Math.Round(yOff), + XAdvance = (int)Math.Round(cd[i].xadvance) + }; + + glyphs[(char) (i + range.Start)] = glyphInfo; + } + } + } + + return new TtfFontBakerResult(glyphs, fontPixelHeight, pixels, bitmapWidth, bitmapHeight); + } + } +} \ No newline at end of file diff --git a/SpriteFontPlus/TtfFontBakerResult.cs b/SpriteFontPlus/TtfFontBakerResult.cs new file mode 100644 index 000000000..be1f0c34b --- /dev/null +++ b/SpriteFontPlus/TtfFontBakerResult.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace SpriteFontPlus +{ + public class TtfFontBakerResult + { + public Dictionary Glyphs + { + get; private set; + } + public float FontFontPixelHeight + { + get; private set; + } + public byte[] Pixels + { + get; private set; + } + public int Width + { + get; private set; + } + public int Height + { + get; private set; + } + + public TtfFontBakerResult(Dictionary glyphs, + float fontPixelHeight, + byte[] pixels, + int width, + int height) + { + if (glyphs == null) + { + throw new ArgumentNullException(nameof(glyphs)); + } + + if (fontPixelHeight <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fontPixelHeight)); + } + + if (pixels == null) + { + throw new ArgumentNullException(nameof(pixels)); + } + + if (width <= 0) + { + throw new ArgumentOutOfRangeException(nameof(width)); + } + + if (height <= 0) + { + throw new ArgumentOutOfRangeException(nameof(height)); + } + + if (pixels.Length < width * height) + { + throw new ArgumentException("pixels.Length should be higher than width * height"); + } + + Glyphs = glyphs; + FontFontPixelHeight = fontPixelHeight; + Pixels = pixels; + Width = width; + Height = height; + } + + public SpriteFont CreateSpriteFont(GraphicsDevice graphicsDevice) + { + var rgb = new Color[Width * Height]; + for (var i = 0; i < Pixels.Length; ++i) + { + var b = Pixels[i]; + rgb[i].R = b; + rgb[i].G = b; + rgb[i].B = b; + + rgb[i].A = b; + } + + var texture = new Texture2D(graphicsDevice, Width, Height); + texture.SetData(rgb); + + var glyphBounds = new List(); + var cropping = new List(); + var chars = new List(); + var kerning = new List(); + + var orderedKeys = Glyphs.Keys.OrderBy(a => a); + foreach (var key in orderedKeys) + { + var character = Glyphs[key]; + + var bounds = new Rectangle(character.X, character.Y, + character.Width, + character.Height); + + glyphBounds.Add(bounds); + cropping.Add(new Rectangle((int)character.XOffset, + character.YOffset, + bounds.Width, bounds.Height)); + + chars.Add((char)key); + + kerning.Add(new Vector3(0, bounds.Width, character.XAdvance - bounds.Width)); + } + + var constructorInfo = typeof(SpriteFont).GetTypeInfo().DeclaredConstructors.First(); + var font = (SpriteFont)constructorInfo.Invoke(new object[] + { + texture, glyphBounds, cropping, + chars, (int)FontFontPixelHeight, 0, kerning, ' ' + }); + + return font; + } + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/GameManager/TextManager.cs b/WorldOfTheThreeKingdoms/GameManager/TextManager.cs index d57fd13f8..b6569d13c 100644 --- a/WorldOfTheThreeKingdoms/GameManager/TextManager.cs +++ b/WorldOfTheThreeKingdoms/GameManager/TextManager.cs @@ -1,4 +1,4 @@ -using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework; using Platforms; using SpriteFontPlus; using System; @@ -52,7 +52,7 @@ public static void DrawTexts(string text, FontPair pair, Microsoft.Xna.Framework for (int i = 0; i < texs.Length; i++) { var te = texs[i]; - font.DrawString(Session.Current.SpriteBatch, te, pos + new Vector2(0, i * pair.Size * scale), color, new Vector2(scale, scale)); + font.DrawString(Session.Current.SpriteBatch, te, pos + new Vector2(0, i * pair.Size * scale), color, new Vector2(scale, scale), depth == null ? 0 : (float)depth); } //Session.Current.SpriteBatch.Draw(font.Texture, pos, null, color, 0f, Vector2.Zero, scale, Microsoft.Xna.Framework.Graphics.SpriteEffects.None, depth == null ? 0 : (float)depth); diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/BMFontLoader.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/BMFontLoader.cs new file mode 100644 index 000000000..99373be7c --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/BMFontLoader.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Cyotek.Drawing.BitmapFont; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace SpriteFontPlus +{ + public class BMFontLoader + { + public static SpriteFont LoadXml(string xml, Texture2D texture) + { + var data = new BitmapFont(); + data.LoadXml(xml); + +#if !XENKO + var glyphBounds = new List(); + var cropping = new List(); + var chars = new List(); + var kerning = new List(); + + var characters = data.Characters.Values.OrderBy(c => c.Char); + foreach (var character in characters) + { + var bounds = character.Bounds; + +// bounds.Offset(texture.Bounds.Location); + + glyphBounds.Add(bounds); + cropping.Add(new Rectangle(character.Offset.X, character.Offset.Y, bounds.Width, bounds.Height)); + + chars.Add(character.Char); + + kerning.Add(new Vector3(0, character.Bounds.Width, character.XAdvance - character.Bounds.Width)); + } + + var constructorInfo = typeof(SpriteFont).GetTypeInfo().DeclaredConstructors.First(); + var result = (SpriteFont) constructorInfo.Invoke(new object[] + { + texture, glyphBounds, cropping, + chars, data.LineHeight, 0, kerning, ' ' + }); + + return result; +#else + var textureRegion = textureRegionLoader(data.Pages[0].FileName); + + var glyphs = new List(); + foreach (var pair in data.Characters) + { + var character = pair.Value; + + var bounds = character.Bounds; + bounds.X += textureRegion.Bounds.X; + bounds.Y += textureRegion.Bounds.Y; + var glyph = new Glyph + { + Character = character.Char, + BitmapIndex = 0, + Offset = new Vector2(character.Offset.X, character.Offset.Y), + Subrect = bounds, + XAdvance = character.XAdvance + }; + + glyphs.Add(glyph); + } + + var textures = new List + { + textureRegion.Texture + }; + + return DefaultAssets.FontSystem.NewStatic(data.LineHeight, glyphs, textures, 0, data.LineHeight); +#endif + } + + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/CharacterRange.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/CharacterRange.cs new file mode 100644 index 000000000..0cbd05d7b --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/CharacterRange.cs @@ -0,0 +1,40 @@ +namespace SpriteFontPlus +{ + public struct CharacterRange + { + public static readonly CharacterRange BasicLatin = new CharacterRange((char) 0x0020, (char) 0x007F); + + public static readonly CharacterRange Latin1Supplement = + new CharacterRange((char) 0x00A0, (char) 0x00FF); + + public static readonly CharacterRange LatinExtendedA = + new CharacterRange((char) 0x0100, (char) 0x017F); + + public static readonly CharacterRange LatinExtendedB = + new CharacterRange((char) 0x0180, (char) 0x024F); + + public static readonly CharacterRange Cyrillic = new CharacterRange((char) 0x0400, (char) 0x04FF); + + public static readonly CharacterRange CyrillicSupplement = + new CharacterRange((char) 0x0500, (char) 0x052F); + + public static readonly CharacterRange Hiragana = + new CharacterRange((char) 0x3040, (char) 0x309F); + + public static readonly CharacterRange Katakana = + new CharacterRange((char) 0x30A0, (char) 0x30FF); + + public char Start { get; private set; } + public char End { get; private set; } + + public CharacterRange(char start, char end) + { + Start = start; + End = end; + } + + public CharacterRange(char single): this(single, single) + { + } + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/BitmapFont.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/BitmapFont.cs new file mode 100644 index 000000000..7ec2e5766 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/BitmapFont.cs @@ -0,0 +1,750 @@ +/* AngelCode bitmap font parsing using C# + * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp + * + * Copyright © 2012-2015 Cyotek Ltd. + * + * Licensed under the MIT License. See license.txt for the full text. + */ + +// Some documentation derived from the BMFont file format specification +// http://www.angelcode.com/products/bmfont/doc/file_format.html + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Text; +using System.Xml; +using Point = Microsoft.Xna.Framework.Point; +using Rectangle = Microsoft.Xna.Framework.Rectangle; + +namespace Cyotek.Drawing.BitmapFont +{ + /// + /// A bitmap font. + /// + /// + internal class BitmapFont : IEnumerable + { + #region Constants + + /// + /// When used with , specifies that no wrapping should occur. + /// + public const int NoMaxWidth = -1; + + #endregion + + #region Properties + + /// + /// Gets or sets the alpha channel. + /// + /// + /// The alpha channel. + /// + /// + /// Set to 0 if the channel holds the glyph data, 1 if it holds the outline, 2 if it holds the glyph and the + /// outline, 3 if its set to zero, and 4 if its set to one. + /// + public int AlphaChannel { get; set; } + + /// + /// Gets or sets the number of pixels from the absolute top of the line to the base of the characters. + /// + /// + /// The number of pixels from the absolute top of the line to the base of the characters. + /// + public int BaseHeight { get; set; } + + /// + /// Gets or sets the blue channel. + /// + /// + /// The blue channel. + /// + /// + /// Set to 0 if the channel holds the glyph data, 1 if it holds the outline, 2 if it holds the glyph and the + /// outline, 3 if its set to zero, and 4 if its set to one. + /// + public int BlueChannel { get; set; } + + /// + /// Gets or sets a value indicating whether the font is bold. + /// + /// + /// true if the font is bold, otherwise false. + /// + public bool Bold { get; set; } + + /// + /// Gets or sets the characters that comprise the font. + /// + /// + /// The characters that comprise the font. + /// + public IDictionary Characters { get; set; } + + /// + /// Gets or sets the name of the OEM charset used. + /// + /// + /// The name of the OEM charset used (when not unicode). + /// + public string Charset { get; set; } + + /// + /// Gets or sets the name of the true type font. + /// + /// + /// The font family name. + /// + public string FamilyName { get; set; } + + /// + /// Gets or sets the size of the font. + /// + /// + /// The size of the font. + /// + public int FontSize { get; set; } + + /// + /// Gets or sets the green channel. + /// + /// + /// The green channel. + /// + /// + /// Set to 0 if the channel holds the glyph data, 1 if it holds the outline, 2 if it holds the glyph and the + /// outline, 3 if its set to zero, and 4 if its set to one. + /// + public int GreenChannel { get; set; } + + /// + /// Gets or sets a value indicating whether the font is italic. + /// + /// + /// true if the font is italic, otherwise false. + /// + public bool Italic { get; set; } + + /// + /// Indexer to get items within this collection using array index syntax. + /// + /// The character. + /// + /// The indexed item. + /// + public Character this[char character] => Characters[character]; + + /// + /// Gets or sets the character kernings for the font. + /// + /// + /// The character kernings for the font. + /// + public IDictionary Kernings { get; set; } + + /// + /// Gets or sets the distance in pixels between each line of text. + /// + /// + /// The distance in pixels between each line of text. + /// + public int LineHeight { get; set; } + + /// + /// Gets or sets the outline thickness for the characters. + /// + /// + /// The outline thickness for the characters. + /// + public int OutlineSize { get; set; } + + /// + /// Gets or sets a value indicating whether the monochrome characters have been packed into each of the texture + /// channels. + /// + /// + /// true if the characters are packed, otherwise false. + /// + /// + /// When packed, the property describes what is stored in each channel. + /// + public bool Packed { get; set; } + + /// + /// Gets or sets the padding for each character. + /// + /// + /// The padding for each character. + /// + public Padding Padding { get; set; } + + /// + /// Gets or sets the texture pages for the font. + /// + /// + /// The pages. + /// + public Page[] Pages { get; set; } + + /// + /// Gets or sets the red channel. + /// + /// + /// The red channel. + /// + /// + /// Set to 0 if the channel holds the glyph data, 1 if it holds the outline, 2 if it holds the glyph and the + /// outline, 3 if its set to zero, and 4 if its set to one. + /// + public int RedChannel { get; set; } + + /// + /// Gets or sets a value indicating whether the font is smoothed. + /// + /// + /// true if the font is smoothed, otherwise false. + /// + public bool Smoothed { get; set; } + + /// + /// Gets or sets the spacing for each character. + /// + /// + /// The spacing for each character. + /// + public Point Spacing { get; set; } + + /// + /// Gets or sets the font height stretch. + /// + /// + /// The font height stretch. + /// + /// 100% means no stretch. + public int StretchedHeight { get; set; } + + /// + /// Gets or sets the level of super sampling used by the font. + /// + /// + /// The super sampling level of the font. + /// + /// A value of 1 indicates no super sampling is in use. + public int SuperSampling { get; set; } + + /// + /// Gets or sets the size of the texture images used by the font. + /// + /// + /// The size of the texture. + /// + public Size TextureSize { get; set; } + + /// + /// Gets or sets a value indicating whether the font is unicode. + /// + /// + /// true if the font is unicode, otherwise false. + /// + public bool Unicode { get; set; } + + #endregion + + #region Methods + + /// + /// Gets the kerning for the specified character combination. + /// + /// The previous character. + /// The current character. + /// + /// The spacing between the specified characters. + /// + public int GetKerning(char previous, char current) + { + Kerning key; + int result; + + key = new Kerning(previous, current, 0); + + if (!Kernings.TryGetValue(key, out result)) result = 0; + + return result; + } + + /// + /// Load font information from the specified . + /// + /// Thrown when one or more required arguments are null. + /// + /// Thrown when one or more arguments have unsupported or + /// illegal values. + /// + /// Thrown when an Invalid Data error condition occurs. + /// The stream to load. + public virtual void Load(Stream stream) + { + byte[] buffer; + string header; + + if (stream == null) throw new ArgumentNullException("stream"); + + if (!stream.CanSeek) + throw new ArgumentException("Stream must be seekable in order to determine file format.", "stream"); + + // read the first five bytes so we can try and work out what the format is + // then reset the position so the format loaders can work + buffer = new byte[5]; + stream.Read(buffer, 0, 5); + stream.Seek(0, SeekOrigin.Begin); + header = Encoding.ASCII.GetString(buffer); + + switch (header) + { + case "info ": + LoadText(stream); + break; + case " + /// Load font information from the specified file. + /// + /// Thrown when one or more required arguments are null. + /// Thrown when the requested file is not present. + /// The file name to load. + public void Load(string fileName) + { + if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName"); + + if (!File.Exists(fileName)) + throw new FileNotFoundException(string.Format("Cannot find file '{0}'.", fileName), fileName); + + using (Stream stream = File.OpenRead(fileName)) + { + Load(stream); + } + + BitmapFontLoader.QualifyResourcePaths(this, Path.GetDirectoryName(fileName)); + } + + /// + /// Loads font information from the specified string. + /// + /// String containing the font to load. + /// The source data must be in BMFont text format. + public void LoadText(string text) + { + using (var reader = new StringReader(text)) + { + LoadText(reader); + } + } + + /// + /// Loads font information from the specified stream. + /// + /// + /// The source data must be in BMFont text format. + /// + /// Thrown when one or more required arguments are null. + /// The stream containing the font to load. + public void LoadText(Stream stream) + { + if (stream == null) throw new ArgumentNullException("stream"); + + using (TextReader reader = new StreamReader(stream)) + { + LoadText(reader); + } + } + + /// + /// Loads font information from the specified . + /// + /// + /// The source data must be in BMFont text format. + /// + /// Thrown when one or more required arguments are null. + /// The TextReader used to feed the data into the font. + public virtual void LoadText(TextReader reader) + { + IDictionary pageData; + IDictionary kerningDictionary; + IDictionary charDictionary; + string line; + + if (reader == null) throw new ArgumentNullException("reader"); + + pageData = new SortedDictionary(); + kerningDictionary = new Dictionary(); + charDictionary = new Dictionary(); + + do + { + line = reader.ReadLine(); + + if (line != null) + { + string[] parts; + + parts = BitmapFontLoader.Split(line, ' '); + + if (parts.Length != 0) + switch (parts[0]) + { + case "info": + FamilyName = BitmapFontLoader.GetNamedString(parts, "face"); + FontSize = BitmapFontLoader.GetNamedInt(parts, "size"); + Bold = BitmapFontLoader.GetNamedBool(parts, "bold"); + Italic = BitmapFontLoader.GetNamedBool(parts, "italic"); + Charset = BitmapFontLoader.GetNamedString(parts, "charset"); + Unicode = BitmapFontLoader.GetNamedBool(parts, "unicode"); + StretchedHeight = BitmapFontLoader.GetNamedInt(parts, "stretchH"); + Smoothed = BitmapFontLoader.GetNamedBool(parts, "smooth"); + SuperSampling = BitmapFontLoader.GetNamedInt(parts, "aa"); + Padding = BitmapFontLoader.ParsePadding( + BitmapFontLoader.GetNamedString(parts, "padding")); + Spacing = BitmapFontLoader.ParsePoint( + BitmapFontLoader.GetNamedString(parts, "spacing")); + OutlineSize = BitmapFontLoader.GetNamedInt(parts, "outline"); + break; + case "common": + LineHeight = BitmapFontLoader.GetNamedInt(parts, "lineHeight"); + BaseHeight = BitmapFontLoader.GetNamedInt(parts, "base"); + TextureSize = new Size(BitmapFontLoader.GetNamedInt(parts, "scaleW"), + BitmapFontLoader.GetNamedInt(parts, "scaleH")); + Packed = BitmapFontLoader.GetNamedBool(parts, "packed"); + AlphaChannel = BitmapFontLoader.GetNamedInt(parts, "alphaChnl"); + RedChannel = BitmapFontLoader.GetNamedInt(parts, "redChnl"); + GreenChannel = BitmapFontLoader.GetNamedInt(parts, "greenChnl"); + BlueChannel = BitmapFontLoader.GetNamedInt(parts, "blueChnl"); + break; + case "page": + int id; + string name; + + id = BitmapFontLoader.GetNamedInt(parts, "id"); + name = BitmapFontLoader.GetNamedString(parts, "file"); + + pageData.Add(id, new Page(id, name)); + break; + case "char": + Character charData; + + charData = new Character + { + Char = (char) BitmapFontLoader.GetNamedInt(parts, "id"), + Bounds = + new Rectangle(BitmapFontLoader.GetNamedInt(parts, "x"), + BitmapFontLoader.GetNamedInt(parts, "y"), + BitmapFontLoader.GetNamedInt(parts, "width"), + BitmapFontLoader.GetNamedInt(parts, "height")), + Offset = + new Point(BitmapFontLoader.GetNamedInt(parts, "xoffset"), + BitmapFontLoader.GetNamedInt(parts, "yoffset")), + XAdvance = BitmapFontLoader.GetNamedInt(parts, "xadvance"), + TexturePage = BitmapFontLoader.GetNamedInt(parts, "page"), + Channel = BitmapFontLoader.GetNamedInt(parts, "chnl") + }; + charDictionary.Add(charData.Char, charData); + break; + case "kerning": + Kerning key; + + key = new Kerning((char) BitmapFontLoader.GetNamedInt(parts, "first"), + (char) BitmapFontLoader.GetNamedInt(parts, "second"), + BitmapFontLoader.GetNamedInt(parts, "amount")); + + if (!kerningDictionary.ContainsKey(key)) kerningDictionary.Add(key, key.Amount); + break; + } + } + } while (line != null); + + Pages = BitmapFontLoader.ToArray(pageData.Values); + Characters = charDictionary; + Kernings = kerningDictionary; + } + + /// + /// Loads font information from the specified string. + /// + /// String containing the font to load. + /// The source data must be in BMFont XML format. + public void LoadXml(string xml) + { + using (var reader = new StringReader(xml)) + { + LoadXml(reader); + } + } + + /// + /// Loads font information from the specified . + /// + /// + /// The source data must be in BMFont XML format. + /// + /// Thrown when one or more required arguments are null. + /// The TextReader used to feed the data into the font. + public virtual void LoadXml(TextReader reader) + { + XmlDocument document; + IDictionary pageData; + IDictionary kerningDictionary; + IDictionary charDictionary; + XmlNode root; + XmlNode properties; + + if (reader == null) throw new ArgumentNullException("reader"); + + document = new XmlDocument(); + pageData = new SortedDictionary(); + kerningDictionary = new Dictionary(); + charDictionary = new Dictionary(); + + document.Load(reader); + root = document.DocumentElement; + + // load the basic attributes + properties = root.SelectSingleNode("info"); + FamilyName = properties.Attributes["face"].Value; + FontSize = Convert.ToInt32(properties.Attributes["size"].Value); + Bold = Convert.ToInt32(properties.Attributes["bold"].Value) != 0; + Italic = Convert.ToInt32(properties.Attributes["italic"].Value) != 0; + Unicode = Convert.ToInt32(properties.Attributes["unicode"].Value) != 0; + StretchedHeight = Convert.ToInt32(properties.Attributes["stretchH"].Value); + Charset = properties.Attributes["charset"].Value; + Smoothed = Convert.ToInt32(properties.Attributes["smooth"].Value) != 0; + SuperSampling = Convert.ToInt32(properties.Attributes["aa"].Value); + Padding = BitmapFontLoader.ParsePadding(properties.Attributes["padding"].Value); + Spacing = BitmapFontLoader.ParsePoint(properties.Attributes["spacing"].Value); + OutlineSize = Convert.ToInt32(properties.Attributes["outline"].Value); + + // common attributes + properties = root.SelectSingleNode("common"); + BaseHeight = Convert.ToInt32(properties.Attributes["base"].Value); + LineHeight = Convert.ToInt32(properties.Attributes["lineHeight"].Value); + TextureSize = new Size(Convert.ToInt32(properties.Attributes["scaleW"].Value), + Convert.ToInt32(properties.Attributes["scaleH"].Value)); + Packed = Convert.ToInt32(properties.Attributes["packed"].Value) != 0; + AlphaChannel = Convert.ToInt32(properties.Attributes["alphaChnl"].Value); + RedChannel = Convert.ToInt32(properties.Attributes["redChnl"].Value); + GreenChannel = Convert.ToInt32(properties.Attributes["greenChnl"].Value); + BlueChannel = Convert.ToInt32(properties.Attributes["blueChnl"].Value); + + // load texture information + foreach (XmlNode node in root.SelectNodes("pages/page")) + { + Page page; + + page = new Page(); + page.Id = Convert.ToInt32(node.Attributes["id"].Value); + page.FileName = node.Attributes["file"].Value; + + pageData.Add(page.Id, page); + } + + Pages = BitmapFontLoader.ToArray(pageData.Values); + + // load character information + foreach (XmlNode node in root.SelectNodes("chars/char")) + { + Character character; + + character = new Character(); + character.Char = (char) Convert.ToInt32(node.Attributes["id"].Value); + character.Bounds = new Rectangle(Convert.ToInt32(node.Attributes["x"].Value), + Convert.ToInt32(node.Attributes["y"].Value), + Convert.ToInt32(node.Attributes["width"].Value), + Convert.ToInt32(node.Attributes["height"].Value)); + character.Offset = new Point(Convert.ToInt32(node.Attributes["xoffset"].Value), + Convert.ToInt32(node.Attributes["yoffset"].Value)); + character.XAdvance = Convert.ToInt32(node.Attributes["xadvance"].Value); + character.TexturePage = Convert.ToInt32(node.Attributes["page"].Value); + character.Channel = Convert.ToInt32(node.Attributes["chnl"].Value); + + charDictionary.Add(character.Char, character); + } + + Characters = charDictionary; + + // loading kerning information + foreach (XmlNode node in root.SelectNodes("kernings/kerning")) + { + Kerning key; + + key = new Kerning((char) Convert.ToInt32(node.Attributes["first"].Value), + (char) Convert.ToInt32(node.Attributes["second"].Value), + Convert.ToInt32(node.Attributes["amount"].Value)); + + if (!kerningDictionary.ContainsKey(key)) kerningDictionary.Add(key, key.Amount); + } + + Kernings = kerningDictionary; + } + + /// + /// Loads font information from the specified stream. + /// + /// + /// The source data must be in BMFont XML format. + /// + /// Thrown when one or more required arguments are null. + /// The stream containing the font to load. + public void LoadXml(Stream stream) + { + if (stream == null) throw new ArgumentNullException("stream"); + + using (TextReader reader = new StreamReader(stream)) + { + LoadXml(reader); + } + } + + /// + /// Provides the size, in pixels, of the specified text when drawn with this font. + /// + /// The text to measure. + /// + /// The , in pixels, of drawn with this font. + /// + public Size MeasureFont(string text) + { + return MeasureFont(text, NoMaxWidth); + } + + /// + /// Provides the size, in pixels, of the specified text when drawn with this font, automatically wrapping to keep + /// within the specified with. + /// + /// The text to measure. + /// The maximum width. + /// + /// The , in pixels, of drawn with this font. + /// + /// + /// The MeasureText method uses the parameter to automatically wrap when determining + /// text size. + /// + public Size MeasureFont(string text, double maxWidth) + { + Size result; + + if (!string.IsNullOrEmpty(text)) + { + char previousCharacter; + int currentLineWidth; + int currentLineHeight; + int blockWidth; + int blockHeight; + int length; + List lineHeights; + + length = text.Length; + previousCharacter = ' '; + currentLineWidth = 0; + currentLineHeight = LineHeight; + blockWidth = 0; + blockHeight = 0; + lineHeights = new List(); + + for (var i = 0; i < length; i++) + { + char character; + + character = text[i]; + + if (character == '\n' || character == '\r') + { + if (character == '\n' || i + 1 == length || text[i + 1] != '\n') + { + lineHeights.Add(currentLineHeight); + blockWidth = Math.Max(blockWidth, currentLineWidth); + currentLineWidth = 0; + currentLineHeight = LineHeight; + } + } + else + { + Character data; + int width; + + data = this[character]; + width = data.XAdvance + GetKerning(previousCharacter, character); + + if (maxWidth != NoMaxWidth && currentLineWidth + width >= maxWidth) + { + lineHeights.Add(currentLineHeight); + blockWidth = Math.Max(blockWidth, currentLineWidth); + currentLineWidth = 0; + currentLineHeight = LineHeight; + } + + currentLineWidth += width; + currentLineHeight = Math.Max(currentLineHeight, data.Bounds.Height + data.Offset.Y); + previousCharacter = character; + } + } + + // finish off the current line if required + if (currentLineHeight != 0) lineHeights.Add(currentLineHeight); + + // reduce any lines other than the last back to the base + for (var i = 0; i < lineHeights.Count - 1; i++) lineHeights[i] = LineHeight; + + // calculate the final block height + foreach (var lineHeight in lineHeights) blockHeight += lineHeight; + + result = new Size(Math.Max(currentLineWidth, blockWidth), blockHeight); + } + else + { + result = Size.Empty; + } + + return result; + } + + #endregion + + #region IEnumerable Interface + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through + /// the collection. + /// + /// + public IEnumerator GetEnumerator() + { + foreach (var pair in Characters) yield return pair.Value; + } + + /// + /// Gets the enumerator. + /// + /// + /// The enumerator. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/BitmapFontLoader.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/BitmapFontLoader.cs new file mode 100644 index 000000000..51ac5df28 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/BitmapFontLoader.cs @@ -0,0 +1,339 @@ +/* AngelCode bitmap font parsing using C# + * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp + * + * Copyright © 2012-2015 Cyotek Ltd. + * + * Licensed under the MIT License. See license.txt for the full text. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Xna.Framework; + +namespace Cyotek.Drawing.BitmapFont +{ + /// + /// Parsing class for bitmap fonts generated by AngelCode BMFont + /// + internal static class BitmapFontLoader + { + #region Static Methods + + /// + /// Loads a bitmap font from a file, attempting to auto detect the file type + /// + /// Thrown when one or more required arguments are null. + /// Thrown when the requested file is not present. + /// Thrown when an Invalid Data error condition occurs. + /// Name of the file to load. + /// + /// A containing the loaded data. + /// + public static BitmapFont LoadFontFromFile(string fileName) + { + BitmapFont result; + + if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName", "File name not specified"); + + if (!File.Exists(fileName)) + throw new FileNotFoundException(string.Format("Cannot find file '{0}'", fileName), fileName); + + using (var file = File.OpenRead(fileName)) + { + using (TextReader reader = new StreamReader(file)) + { + string line; + + line = reader.ReadLine(); + + if (line.StartsWith("info ")) + result = LoadFontFromTextFile(fileName); + else if (line.StartsWith(" + /// Loads a bitmap font from a file containing font data in text format. + /// + /// Thrown when one or more required arguments are null. + /// Thrown when the requested file is not present. + /// Name of the file to load. + /// + /// A containing the loaded data. + /// + public static BitmapFont LoadFontFromTextFile(string fileName) + { + BitmapFont font; + + if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName"); + + if (!File.Exists(fileName)) + throw new FileNotFoundException(string.Format("Cannot find file '{0}'", fileName), fileName); + + font = new BitmapFont(); + + using (Stream stream = File.OpenRead(fileName)) + { + font.LoadText(stream); + } + + QualifyResourcePaths(font, Path.GetDirectoryName(fileName)); + + return font; + } + + /// + /// Loads a bitmap font from a file containing font data in XML format. + /// + /// Thrown when one or more required arguments are null. + /// Thrown when the requested file is not present. + /// Name of the file to load. + /// + /// A containing the loaded data. + /// + public static BitmapFont LoadFontFromXmlFile(string fileName) + { + BitmapFont font; + + if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName"); + + if (!File.Exists(fileName)) + throw new FileNotFoundException(string.Format("Cannot find file '{0}'", fileName), fileName); + + font = new BitmapFont(); + + using (Stream stream = File.OpenRead(fileName)) + { + font.LoadXml(stream); + } + + QualifyResourcePaths(font, Path.GetDirectoryName(fileName)); + + return font; + } + + /// + /// Returns a boolean from an array of name/value pairs. + /// + /// The array of parts. + /// The name of the value to return. + /// Default value(if the key doesnt exist or can't be parsed) + /// + internal static bool GetNamedBool(string[] parts, string name, bool defaultValue = false) + { + var s = GetNamedString(parts, name); + + bool result; + int v; + if (int.TryParse(s, out v)) + result = v > 0; + else + result = defaultValue; + + return result; + } + + /// + /// Returns an integer from an array of name/value pairs. + /// + /// The array of parts. + /// The name of the value to return. + /// Default value(if the key doesnt exist or can't be parsed) + /// + internal static int GetNamedInt(string[] parts, string name, int defaultValue = 0) + { + var s = GetNamedString(parts, name); + + int result; + if (!int.TryParse(s, out result)) result = defaultValue; + + return result; + } + + /// + /// Returns a string from an array of name/value pairs. + /// + /// The array of parts. + /// The name of the value to return. + /// + internal static string GetNamedString(string[] parts, string name) + { + string result; + + result = string.Empty; + + foreach (var part in parts) + { + int nameEndIndex; + + nameEndIndex = part.IndexOf('='); + if (nameEndIndex != -1) + { + string namePart; + string valuePart; + + namePart = part.Substring(0, nameEndIndex); + valuePart = part.Substring(nameEndIndex + 1); + + if (string.Equals(name, namePart, StringComparison.OrdinalIgnoreCase)) + { + int length; + + length = valuePart.Length; + + if (length > 1 && valuePart[0] == '"' && valuePart[length - 1] == '"') + valuePart = valuePart.Substring(1, length - 2); + + result = valuePart; + break; + } + } + } + + return result; + } + + /// + /// Creates a Padding object from a string representation + /// + /// The string. + /// + internal static Padding ParsePadding(string s) + { + string[] parts; + + parts = s.Split(','); + + return new Padding + { + Left = Convert.ToInt32(parts[3].Trim()), + Top = Convert.ToInt32(parts[0].Trim()), + Right = Convert.ToInt32(parts[1].Trim()), + Bottom = Convert.ToInt32(parts[2].Trim()) + }; + } + + /// + /// Creates a Point object from a string representation + /// + /// The string. + /// + internal static Point ParsePoint(string s) + { + string[] parts; + + parts = s.Split(','); + + return new Point + { + X = Convert.ToInt32(parts[0].Trim()), + Y = Convert.ToInt32(parts[1].Trim()) + }; + } + + /// + /// Updates data with a fully qualified path + /// + /// The to update. + /// The path where texture resources are located. + internal static void QualifyResourcePaths(BitmapFont font, string resourcePath) + { + Page[] pages; + + pages = font.Pages; + + for (var i = 0; i < pages.Length; i++) + { + Page page; + + page = pages[i]; + page.FileName = Path.Combine(resourcePath, page.FileName); + pages[i] = page; + } + + font.Pages = pages; + } + + /// + /// Splits the specified string using a given delimiter, ignoring any instances of the delimiter as part of a quoted + /// string. + /// + /// The string to split. + /// The delimiter. + /// + internal static string[] Split(string s, char delimiter) + { + string[] results; + + if (s.IndexOf('"') != -1) + { + List parts; + int partStart; + + partStart = -1; + parts = new List(); + + do + { + int partEnd; + int quoteStart; + int quoteEnd; + bool hasQuotes; + + quoteStart = s.IndexOf('"', partStart + 1); + quoteEnd = s.IndexOf('"', quoteStart + 1); + partEnd = s.IndexOf(delimiter, partStart + 1); + + if (partEnd == -1) partEnd = s.Length; + + hasQuotes = quoteStart != -1 && partEnd > quoteStart && partEnd < quoteEnd; + if (hasQuotes) partEnd = s.IndexOf(delimiter, quoteEnd + 1); + + parts.Add(s.Substring(partStart + 1, partEnd - partStart - 1)); + + if (hasQuotes) partStart = partEnd - 1; + + partStart = s.IndexOf(delimiter, partStart + 1); + } while (partStart != -1); + + results = parts.ToArray(); + } + else + { + results = s.Split(new[] + { + delimiter + }, StringSplitOptions.RemoveEmptyEntries); + } + + return results; + } + + /// + /// Converts the given collection into an array + /// + /// Type of the items in the array + /// The values. + /// + internal static T[] ToArray(ICollection values) + { + T[] result; + + // avoid a forced .NET 3 dependency just for one call to Linq + + result = new T[values.Count]; + values.CopyTo(result, 0); + + return result; + } + + #endregion + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/Character.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/Character.cs new file mode 100644 index 000000000..e2f0c645a --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/Character.cs @@ -0,0 +1,89 @@ +/* AngelCode bitmap font parsing using C# + * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp + * + * Copyright © 2012-2015 Cyotek Ltd. + * + * Licensed under the MIT License. See license.txt for the full text. + */ + +using Microsoft.Xna.Framework; + +namespace Cyotek.Drawing.BitmapFont +{ + /// + /// Represents the definition of a single character in a + /// + internal struct Character + { + #region Properties + + /// + /// Gets or sets the bounds of the character image in the source texture. + /// + /// + /// The bounds of the character image in the source texture. + /// + public Rectangle Bounds { get; set; } + + /// + /// Gets or sets the texture channel where the character image is found. + /// + /// + /// The texture channel where the character image is found. + /// + /// + /// 1 = blue, 2 = green, 4 = red, 8 = alpha, 15 = all channels + /// + public int Channel { get; set; } + + /// + /// Gets or sets the character. + /// + /// + /// The character. + /// + public char Char { get; set; } + + /// + /// Gets or sets the offset when copying the image from the texture to the screen. + /// + /// + /// The offset when copying the image from the texture to the screen. + /// + public Point Offset { get; set; } + + /// + /// Gets or sets the texture page where the character image is found. + /// + /// + /// The texture page where the character image is found. + /// + public int TexturePage { get; set; } + + /// + /// Gets or sets the value used to advance the current position after drawing the character. + /// + /// + /// How much the current position should be advanced after drawing the character. + /// + public int XAdvance { get; set; } + + #endregion + + #region Methods + + /// + /// Returns the fully qualified type name of this instance. + /// + /// + /// A containing a fully qualified type name. + /// + /// + public override string ToString() + { + return Char.ToString(); + } + + #endregion + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/Kerning.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/Kerning.cs new file mode 100644 index 000000000..45e8c91c5 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/Kerning.cs @@ -0,0 +1,122 @@ +/* AngelCode bitmap font parsing using C# + * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp + * + * Copyright © 2012-2015 Cyotek Ltd. + * + * Licensed under the MIT License. See license.txt for the full text. + */ + +using System; + +namespace Cyotek.Drawing.BitmapFont +{ + /// + /// Represents the font kerning between two characters. + /// + internal struct Kerning : IEquatable + { + #region Constructors + + /// + /// Constructor. + /// + /// The first character. + /// The second character. + /// + /// How much the x position should be adjusted when drawing the second + /// character immediately following the first. + /// + public Kerning(char firstCharacter, char secondCharacter, int amount) + : this() + { + FirstCharacter = firstCharacter; + SecondCharacter = secondCharacter; + Amount = amount; + } + + #endregion + + #region Properties + + /// + /// Gets or sets how much the x position should be adjusted when drawing the second character immediately following the + /// first. + /// + /// + /// How much the x position should be adjusted when drawing the second character immediately following the first. + /// + public int Amount { get; set; } + + /// + /// Gets or sets the first character. + /// + /// + /// The first character. + /// + public char FirstCharacter { get; set; } + + /// + /// Gets or sets the second character. + /// + /// + /// The second character. + /// + public char SecondCharacter { get; set; } + + #endregion + + #region Methods + + /// + /// Returns the fully qualified type name of this instance. + /// + /// + /// A containing a fully qualified type name. + /// + /// + public override string ToString() + { + return string.Format("{0} to {1} = {2}", FirstCharacter, SecondCharacter, Amount); + } + + /// + /// Check if the object represents kerning between the same two characters. + /// + /// + /// + /// Whether or not the object represents kerning between the same two characters. + /// + public override bool Equals(object obj) + { + if (obj == null) return false; + if (obj.GetType() != typeof(Kerning)) return false; + + return Equals((Kerning) obj); + } + + /// + /// Check if the other kerning is between the same two characters. + /// + /// + /// + /// Whether or not the other kerning is between the same two characters. + /// + public bool Equals(Kerning other) + { + return FirstCharacter == other.FirstCharacter && SecondCharacter == other.SecondCharacter; + } + + /// + /// Return the hash code of the kerning between the two characters. + /// + /// + /// A unique hash code of the kerning between the two characters. + /// + public override int GetHashCode() + { + return (FirstCharacter << 16) | SecondCharacter; + } + + #endregion + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/Padding.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/Padding.cs new file mode 100644 index 000000000..b4ec375be --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/Padding.cs @@ -0,0 +1,88 @@ +/* AngelCode bitmap font parsing using C# + * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp + * + * Copyright © 2012-2015 Cyotek Ltd. + * + * Licensed under the MIT License. See license.txt for the full text. + */ + +namespace Cyotek.Drawing.BitmapFont +{ + /// + /// Represents padding or margin information associated with an element. + /// + internal struct Padding + { + #region Constructors + + /// + /// Initializes a new instance of the stricture using a separate padding size for each edge. + /// + /// The padding size, in pixels, for the left edge. + /// The padding size, in pixels, for the top edge. + /// The padding size, in pixels, for the right edge. + /// The padding size, in pixels, for the bottom edge. + public Padding(int left, int top, int right, int bottom) + : this() + { + Top = top; + Left = left; + Right = right; + Bottom = bottom; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the padding value for the bottom edge. + /// + /// + /// The padding, in pixels, for the bottom edge. + /// + public int Bottom { get; set; } + + /// + /// Gets or sets the padding value for the left edge. + /// + /// + /// The padding, in pixels, for the left edge. + /// + public int Left { get; set; } + + /// + /// Gets or sets the padding value for the right edge. + /// + /// + /// The padding, in pixels, for the right edge. + /// + public int Right { get; set; } + + /// + /// Gets or sets the padding value for the top edge. + /// + /// + /// The padding, in pixels, for the top edge. + /// + public int Top { get; set; } + + #endregion + + #region Methods + + /// + /// Returns the fully qualified type name of this instance. + /// + /// + /// A containing a fully qualified type name. + /// + /// + public override string ToString() + { + return string.Format("{0}, {1}, {2}, {3}", Left, Top, Right, Bottom); + } + + #endregion + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/Page.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/Page.cs new file mode 100644 index 000000000..401ccc9cc --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/Cyotek.Drawing.Bitmap/Page.cs @@ -0,0 +1,70 @@ +/* AngelCode bitmap font parsing using C# + * http://www.cyotek.com/blog/angelcode-bitmap-font-parsing-using-csharp + * + * Copyright © 2012-2015 Cyotek Ltd. + * + * Licensed under the MIT License. See license.txt for the full text. + */ + +using System.IO; + +namespace Cyotek.Drawing.BitmapFont +{ + /// + /// Represents a texture page. + /// + internal struct Page + { + #region Constructors + + /// + /// Creates a texture page using the specified ID and source file name. + /// + /// The identifier. + /// Filename of the texture image. + public Page(int id, string fileName) + : this() + { + FileName = fileName; + Id = id; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the filename of the source texture image. + /// + /// + /// The name of the file containing the source texture image. + /// + public string FileName { get; set; } + + /// + /// Gets or sets the page identifier. + /// + /// + /// The page identifier. + /// + public int Id { get; set; } + + #endregion + + #region Methods + + /// + /// Returns the fully qualified type name of this instance. + /// + /// + /// A containing a fully qualified type name. + /// + /// + public override string ToString() + { + return string.Format("{0} ({1})", Id, Path.GetFileName(FileName)); + } + + #endregion + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/DynamicSpriteFont.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/DynamicSpriteFont.cs new file mode 100644 index 000000000..4520e37f2 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/DynamicSpriteFont.cs @@ -0,0 +1,126 @@ +using FontStashSharp; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace SpriteFontPlus +{ + public class DynamicSpriteFont + { + private static readonly string DefaultFontName = string.Empty; + + private readonly FontSystem _fontSystem; + private readonly int _defaultFontId; + public Texture2D Texture + { + get { return _fontSystem.Texture; } + } + + public float Size + { + get + { + return _fontSystem.Size; + } + set + { + _fontSystem.Size = value; + } + } + + public float Spacing + { + get + { + return _fontSystem.Spacing; + } + set + { + _fontSystem.Spacing = value; + } + } + + public int FontId + { + get + { + return _fontSystem.FontId; + } + set + { + _fontSystem.FontId = value; + } + } + + public int DefaultFontId + { + get + { + return _defaultFontId; + } + } + + private DynamicSpriteFont(byte[] ttf, float defaultSize, int textureWidth, int textureHeight) + { + var fontParams = new FontSystemParams + { + Width = textureWidth, + Height = textureHeight, + Flags = FontSystem.FONS_ZERO_TOPLEFT + }; + + _fontSystem = new FontSystem(fontParams); + _fontSystem.Alignment = FontSystem.FONS_ALIGN_TOP; + + _defaultFontId = _fontSystem.AddFontMem(DefaultFontName, ttf); + Size = defaultSize; + } + + public float DrawString(SpriteBatch batch, string text, Vector2 pos, Color color) + { + return DrawString(batch, text, pos, color, Vector2.One, 0f); + } + + public float DrawString(SpriteBatch batch, string text, Vector2 pos, Color color, Vector2 scale, float depth) + { + _fontSystem.Color = color; + _fontSystem.Scale = scale; + + var result = _fontSystem.DrawText(batch, pos.X, pos.Y, text, depth); + + _fontSystem.Scale = Vector2.One; + + return result; + } + + public int AddTtf(string name, byte[] ttf) + { + return _fontSystem.AddFontMem(name, ttf); + } + + public int? GetFontIdByName(string name) + { + return _fontSystem.GetFontByName(name); + } + + public Vector2 MeasureString(string text) + { + Bounds bounds = new Bounds(); + _fontSystem.TextBounds(0, 0, text, ref bounds); + + return new Vector2(bounds.X2, bounds.Y2); + } + + public Rectangle GetTextBounds(Vector2 position, string text) + { + Bounds bounds = new Bounds(); + _fontSystem.TextBounds(position.X, position.Y, text, ref bounds); + + return new Rectangle((int)bounds.X, (int)bounds.Y, (int)(bounds.X2 - bounds.X), (int)(bounds.Y2 - bounds.Y)); + } + + public static DynamicSpriteFont FromTtf(byte[] ttf, float defaultSize, int textureWidth = 1024, int textureHeight = 1024) + { + return new DynamicSpriteFont(ttf, defaultSize, textureWidth, textureHeight); + } + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/Bounds.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/Bounds.cs new file mode 100644 index 000000000..84a974a45 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/Bounds.cs @@ -0,0 +1,7 @@ +namespace FontStashSharp +{ + internal struct Bounds + { + public float X, Y, X2, Y2; + } +} diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/Font.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/Font.cs new file mode 100644 index 000000000..d2b18563f --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/Font.cs @@ -0,0 +1,20 @@ +using StbSharp; + +namespace FontStashSharp +{ + internal unsafe class Font + { + public StbTrueType.stbtt_fontinfo _font = new StbTrueType.stbtt_fontinfo(); + public string Name; + public byte[] Data; + public float Ascent; + public float Ascender; + public float Descender; + public float LineHeight; + public FontGlyph[] Glyphs; + public int GlyphsNumber; + public int[] Lut = new int[256]; + public int[] Fallbacks = new int[20]; + public int FallbacksCount; + } +} diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontAtlas.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontAtlas.cs new file mode 100644 index 000000000..2b74153f5 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontAtlas.cs @@ -0,0 +1,162 @@ +using System; +using System.Runtime.InteropServices; +using StbSharp; + +namespace FontStashSharp +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe class FontAtlas + { + public int NodesCount; + public int Height; + public int NodesNumber; + public FontAtlasNode* Nodes; + public int Width; + + public FontAtlas(int w, int h, int count) + { + Width = w; + Height = h; + Nodes = (FontAtlasNode*)CRuntime.malloc((ulong)(sizeof(FontAtlasNode) * count)); + CRuntime.memset(Nodes, 0, (ulong)(sizeof(FontAtlasNode) * count)); + count = 0; + NodesCount = count; + Nodes[0].X = 0; + Nodes[0].Y = 0; + Nodes[0].Width = (short)w; + NodesNumber++; + } + + public int InsertNode(int idx, int x, int y, int w) + { + if (NodesNumber + 1 > NodesCount) + { + NodesCount = NodesCount == 0 ? 8 : NodesCount * 2; + Nodes = (FontAtlasNode*)CRuntime.realloc(Nodes, (ulong)(sizeof(FontAtlasNode) * NodesCount)); + if (Nodes == null) + return 0; + } + + for (var i = NodesNumber; i > idx; i--) Nodes[i] = Nodes[i - 1]; + Nodes[idx].X = (short)x; + Nodes[idx].Y = (short)y; + Nodes[idx].Width = (short)w; + NodesNumber++; + return 1; + } + + public void RemoveNode(int idx) + { + if (NodesNumber == 0) + return; + for (var i = idx; i < NodesNumber - 1; i++) Nodes[i] = Nodes[i + 1]; + NodesNumber--; + } + + public void Expand(int w, int h) + { + if (w > Width) + InsertNode(NodesNumber, Width, 0, w - Width); + Width = w; + Height = h; + } + + public void Reset(int w, int h) + { + Width = w; + Height = h; + NodesNumber = 0; + Nodes[0].X = 0; + Nodes[0].Y = 0; + Nodes[0].Width = (short)w; + NodesNumber++; + } + + public int AddSkylineLevel(int idx, int x, int y, int w, int h) + { + if (InsertNode(idx, x, y + h, w) == 0) + return 0; + for (var i = idx + 1; i < NodesNumber; i++) + if (Nodes[i].X < Nodes[i - 1].X + Nodes[i - 1].Width) + { + var shrink = Nodes[i - 1].X + Nodes[i - 1].Width - Nodes[i].X; + Nodes[i].X += (short)shrink; + Nodes[i].Width -= (short)shrink; + if (Nodes[i].Width <= 0) + { + RemoveNode(i); + i--; + } + else + { + break; + } + } + else + { + break; + } + + for (var i = 0; i < NodesNumber - 1; i++) + if (Nodes[i].Y == Nodes[i + 1].Y) + { + Nodes[i].Width += Nodes[i + 1].Width; + RemoveNode(i + 1); + i--; + } + + return 1; + } + + public int RectFits(int i, int w, int h) + { + var x = (int)Nodes[i].X; + var y = (int)Nodes[i].Y; + if (x + w > Width) + return -1; + var spaceLeft = w; + while (spaceLeft > 0) + { + if (i == NodesNumber) + return -1; + y = Math.Max(y, Nodes[i].Y); + if (y + h > Height) + return -1; + spaceLeft -= Nodes[i].Width; + ++i; + } + + return y; + } + + public int AddRect(int rw, int rh, int* rx, int* ry) + { + var besth = Height; + var bestw = Width; + var besti = -1; + var bestx = -1; + var besty = -1; + for (var i = 0; i < NodesNumber; i++) + { + var y = RectFits(i, rw, rh); + if (y != -1) + if (y + rh < besth || y + rh == besth && Nodes[i].Width < bestw) + { + besti = i; + bestw = Nodes[i].Width; + besth = y + rh; + bestx = Nodes[i].X; + besty = y; + } + } + + if (besti == -1) + return 0; + if (AddSkylineLevel(besti, bestx, besty, rw, rh) == 0) + return 0; + *rx = bestx; + *ry = besty; + return 1; + } + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontAtlasNode.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontAtlasNode.cs new file mode 100644 index 000000000..f4a9f7de2 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontAtlasNode.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace FontStashSharp +{ + [StructLayout(LayoutKind.Sequential)] + internal struct FontAtlasNode + { + public short X; + public short Y; + public short Width; + } +} diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontGlyph.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontGlyph.cs new file mode 100644 index 000000000..706a9f27b --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontGlyph.cs @@ -0,0 +1,21 @@ +using System.Runtime.InteropServices; + +namespace FontStashSharp +{ + [StructLayout(LayoutKind.Sequential)] + internal class FontGlyph + { + public int Codepoint; + public int Index; + public int Next; + public int Size; + public int Blur; + public int X0; + public int Y0; + public int X1; + public int Y1; + public int XAdvance; + public int XOffset; + public int YOffset; + } +} diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontGlyphSquad.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontGlyphSquad.cs new file mode 100644 index 000000000..4f2cea948 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontGlyphSquad.cs @@ -0,0 +1,17 @@ +using System.Runtime.InteropServices; + +namespace FontStashSharp +{ + [StructLayout(LayoutKind.Sequential)] + internal struct FontGlyphSquad + { + public float X0; + public float Y0; + public float S0; + public float T0; + public float X1; + public float Y1; + public float S1; + public float T1; + } +} diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontSystem.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontSystem.cs new file mode 100644 index 000000000..e0cd21b54 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontSystem.cs @@ -0,0 +1,860 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using SpriteFontPlus; +using StbSharp; + +namespace FontStashSharp +{ + internal unsafe class FontSystem + { + public const int FONS_ZERO_TOPLEFT = 1; + public const int FONS_ZERO_BOTTOMLEFT = 2; + public const int FONS_ALIGN_LEFT = 1 << 0; + public const int FONS_ALIGN_CENTER = 1 << 1; + public const int FONS_ALIGN_RIGHT = 1 << 2; + public const int FONS_ALIGN_TOP = 1 << 3; + public const int FONS_ALIGN_MIDDLE = 1 << 4; + public const int FONS_ALIGN_BOTTOM = 1 << 5; + public const int FONS_ALIGN_BASELINE = 1 << 6; + public const int FONS_GLYPH_BITMAP_OPTIONAL = 1; + public const int FONS_GLYPH_BITMAP_REQUIRED = 2; + public const int FONS_ATLAS_FULL = 1; + public const int FONS_SCRATCH_FULL = 2; + public const int FONS_STATES_OVERFLOW = 3; + public const int FONS_STATES_UNDERFLOW = 4; + + private FontSystemParams _params_ = new FontSystemParams(); + private FontAtlas _atlas; + private Color[] _colors = new Color[1024]; + private int[] _dirtyRect = new int[4]; + private Font[] _fonts; + private int _fontsNumber; + private float _ith; + private float _itw; + private int _vertsNumber; + private Rectangle[] _textureCoords = new Rectangle[1024 * 2]; + private byte[] _texData; + private Color[] _colorData; + private Rectangle[] _verts = new Rectangle[1024 * 2]; + + public int FontId; + public int Alignment; + public float Size; + public Color Color; + public float BlurValue; + public float Spacing; + public Vector2 Scale; + + public FontSystem(FontSystemParams p) + { + _params_ = p; + + _atlas = new FontAtlas(_params_.Width, _params_.Height, 256); + _fonts = new Font[4]; + _fontsNumber = 0; + _itw = 1.0f / _params_.Width; + _ith = 1.0f / _params_.Height; + _texData = new byte[_params_.Width * _params_.Height]; + _colorData = new Color[_params_.Width * _params_.Height]; + Array.Clear(_texData, 0, _texData.Length); + _dirtyRect[0] = _params_.Width; + _dirtyRect[1] = _params_.Height; + _dirtyRect[2] = 0; + _dirtyRect[3] = 0; + AddWhiteRect(2, 2); + ClearState(); + } + + public Texture2D Texture { get; private set; } + + public void AddWhiteRect(int w, int h) + { + var x = 0; + var y = 0; + var gx = 0; + var gy = 0; + if (_atlas.AddRect(w, h, &gx, &gy) == 0) + return; + fixed (byte* dst2 = &_texData[gx + gy * _params_.Width]) + { + var dst = dst2; + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) dst[x] = 0xff; + dst += _params_.Width; + } + } + + _dirtyRect[0] = Math.Min(_dirtyRect[0], gx); + _dirtyRect[1] = Math.Min(_dirtyRect[1], gy); + _dirtyRect[2] = Math.Max(_dirtyRect[2], gx + w); + _dirtyRect[3] = Math.Max(_dirtyRect[3], gy + h); + } + + public int AddFallbackFont(int _base_, int fallback) + { + var baseFont = _fonts[_base_]; + if (baseFont.FallbacksCount < 20) + { + baseFont.Fallbacks[baseFont.FallbacksCount++] = fallback; + return 1; + } + + return 0; + } + public void ClearState() + { + Size = 12.0f; + Color = Color.White; + FontId = 0; + BlurValue = 0; + Spacing = 0; + Alignment = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE; + } + + public int AddFontMem(string name, byte[] data) + { + var i = 0; + var ascent = 0; + var descent = 0; + var fh = 0; + var lineGap = 0; + Font font; + var idx = AllocFont(); + if (idx == -1) + return -1; + font = _fonts[idx]; + font.Name = name; + for (i = 0; i < 256; ++i) font.Lut[i] = -1; + font.Data = data; + fixed (byte* ptr = data) + { + if (LoadFont(font._font, ptr, data.Length) == 0) + goto error; + } + + font._font.fons__tt_getFontVMetrics(&ascent, &descent, &lineGap); + fh = ascent - descent; + font.Ascent = ascent; + font.Ascender = ascent / (float)fh; + font.Descender = descent / (float)fh; + font.LineHeight = (fh + lineGap) / (float)fh; + return idx; + error:; + _fontsNumber--; + return -1; + } + + public int? GetFontByName(string name) + { + var i = 0; + for (i = 0; i < _fontsNumber; i++) + if (_fonts[i].Name == name) + return i; + return null; + } + + public float DrawText(SpriteBatch batch, float x, float y, StringSegment str, float depth) + { + if (str.IsNullOrEmpty) return 0.0f; + + FontGlyph glyph = null; + var q = new FontGlyphSquad(); + var prevGlyphIndex = -1; + var isize = (int)(Size * 10.0f); + var iblur = (int)BlurValue; + float scale = 0; + Font font; + float width = 0; + if (FontId < 0 || FontId >= _fontsNumber) + return x; + font = _fonts[FontId]; + if (font.Data == null) + return x; + scale = font._font.fons__tt_getPixelHeightScale(isize / 10.0f); + + if ((Alignment & FONS_ALIGN_LEFT) != 0) + { + } + else if ((Alignment & FONS_ALIGN_RIGHT) != 0) + { + var bounds = new Bounds(); + width = TextBounds(x, y, str, ref bounds); + x -= width; + } + else if ((Alignment & FONS_ALIGN_CENTER) != 0) + { + var bounds = new Bounds(); + width = TextBounds(x, y, str, ref bounds); + x -= width * 0.5f; + } + + float originX = 0.0f; + float originY = 0.0f; + + originY += GetVertAlign(font, Alignment, isize); + for (int i = 0; i < str.Length; i += Char.IsSurrogatePair(str.String, i + str.Location) ? 2 : 1) + { + var codepoint = Char.ConvertToUtf32(str.String, i + str.Location); + glyph = GetGlyph(font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_REQUIRED); + if (glyph != null) + { + GetQuad(font, prevGlyphIndex, glyph, scale, Spacing, ref originX, ref originY, &q); + if (_vertsNumber + 6 > 1024) + { + Flush(batch, depth); + } + + q.X0 = (int)(q.X0 * Scale.X); + q.X1 = (int)(q.X1 * Scale.X); + q.Y0 = (int)(q.Y0 * Scale.Y); + q.Y1 = (int)(q.Y1 * Scale.Y); + + AddVertex(new Rectangle((int)(x + q.X0), + (int)(y + q.Y0), + (int)(q.X1 - q.X0), + (int)(q.Y1 - q.Y0)), + new Rectangle((int)(q.S0 * _params_.Width), + (int)(q.T0 * _params_.Height), + (int)((q.S1 - q.S0) * _params_.Width), + (int)((q.T1 - q.T0) * _params_.Height)), + Color); + } + + prevGlyphIndex = glyph != null ? glyph.Index : -1; + } + + Flush(batch, depth); + return x; + } + + public float TextBounds(float x, float y, StringSegment str, ref Bounds bounds) + { + var q = new FontGlyphSquad(); + FontGlyph glyph = null; + var prevGlyphIndex = -1; + var isize = (int)(Size * 10.0f); + var iblur = (int)BlurValue; + float scale = 0; + Font font; + float startx = 0; + float advance = 0; + float minx = 0; + float miny = 0; + float maxx = 0; + float maxy = 0; + if (FontId < 0 || FontId >= _fontsNumber) + return 0; + font = _fonts[FontId]; + if (font.Data == null) + return 0; + scale = font._font.fons__tt_getPixelHeightScale(isize / 10.0f); + y += GetVertAlign(font, Alignment, isize); + minx = maxx = x; + miny = maxy = y; + startx = x; + for (int i = 0; i < str.Length; i += char.IsSurrogatePair(str.String, i + str.Location) ? 2 : 1) + { + var codepoint = char.ConvertToUtf32(str.String, i + str.Location); + glyph = GetGlyph(font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL); + if (glyph != null) + { + GetQuad(font, prevGlyphIndex, glyph, scale, Spacing, ref x, ref y, &q); + if (q.X0 < minx) + minx = q.X0; + if (x > maxx) + maxx = x; + if ((_params_.Flags & FONS_ZERO_TOPLEFT) != 0) + { + if (q.Y0 < miny) + miny = q.Y0; + if (q.Y1 > maxy) + maxy = q.Y1; + } + else + { + if (q.Y1 < miny) + miny = q.Y1; + if (q.Y0 > maxy) + maxy = q.Y0; + } + } + + prevGlyphIndex = glyph != null ? glyph.Index : -1; + } + + advance = x - startx; + if ((Alignment & FONS_ALIGN_LEFT) != 0) + { + } + else if ((Alignment & FONS_ALIGN_RIGHT) != 0) + { + minx -= advance; + maxx -= advance; + } + else if ((Alignment & FONS_ALIGN_CENTER) != 0) + { + minx -= advance * 0.5f; + maxx -= advance * 0.5f; + } + + bounds.X = minx; + bounds.Y = miny; + bounds.X2 = maxx; + bounds.Y2 = maxy; + + return advance; + } + + public void VertMetrics(out float ascender, out float descender, out float lineh) + { + ascender = descender = lineh = 0; + Font font; + int isize = 0; + if (FontId < 0 || FontId >= _fontsNumber) + return; + font = _fonts[FontId]; + isize = (int)(Size * 10.0f); + if (font.Data == null) + return; + + ascender = font.Ascender * isize / 10.0f; + descender = font.Descender * isize / 10.0f; + lineh = font.LineHeight * isize / 10.0f; + } + + public void LineBounds(float y, ref float miny, ref float maxy) + { + Font font; + int isize = 0; + if (FontId < 0 || FontId >= _fontsNumber) + return; + font = _fonts[FontId]; + isize = (int)(Size * 10.0f); + if (font.Data == null) + return; + y += GetVertAlign(font, Alignment, isize); + if ((_params_.Flags & FONS_ZERO_TOPLEFT) != 0) + { + miny = y - font.Ascender * isize / 10.0f; + maxy = miny + font.LineHeight * isize / 10.0f; + } + else + { + maxy = y + font.Descender * isize / 10.0f; + miny = maxy - font.LineHeight * isize / 10.0f; + } + } + + public byte[] GetTextureData(out int width, out int height) + { + width = _params_.Width; + height = _params_.Height; + return _texData; + } + + public int ValidateTexture(int* dirty) + { + if (_dirtyRect[0] < _dirtyRect[2] && _dirtyRect[1] < _dirtyRect[3]) + { + dirty[0] = _dirtyRect[0]; + dirty[1] = _dirtyRect[1]; + dirty[2] = _dirtyRect[2]; + dirty[3] = _dirtyRect[3]; + _dirtyRect[0] = _params_.Width; + _dirtyRect[1] = _params_.Height; + _dirtyRect[2] = 0; + _dirtyRect[3] = 0; + return 1; + } + + return 0; + } + + public void GetAtlasSize(out int width, out int height) + { + width = _params_.Width; + height = _params_.Height; + } + + public int ExpandAtlas(int width, int height) + { + var i = 0; + var maxy = 0; + width = Math.Max(width, _params_.Width); + height = Math.Max(height, _params_.Height); + if (width == _params_.Width && height == _params_.Height) + return 1; + + var data = new byte[width * height]; + for (i = 0; i < _params_.Height; i++) + fixed (byte* dst = &data[i * width]) + { + fixed (byte* src = &_texData[i * _params_.Width]) + { + CRuntime.memcpy(dst, src, (ulong)_params_.Width); + if (width > _params_.Width) + CRuntime.memset(dst + _params_.Width, 0, (ulong)(width - _params_.Width)); + } + } + + if (height > _params_.Height) + Array.Clear(data, _params_.Height * width, (height - _params_.Height) * width); + + _texData = data; + + _colorData = new Color[width * height]; + for(i = 0; i < width * height; ++i) + { + _colorData[i].R = _texData[i]; + _colorData[i].G = _texData[i]; + _colorData[i].B = _texData[i]; + _colorData[i].A = _texData[i]; + } + + _atlas.Expand(width, height); + for (i = 0; i < _atlas.NodesNumber; i++) maxy = Math.Max(maxy, _atlas.Nodes[i].Y); + _dirtyRect[0] = 0; + _dirtyRect[1] = 0; + _dirtyRect[2] = _params_.Width; + _dirtyRect[3] = maxy; + _params_.Width = width; + _params_.Height = height; + _itw = 1.0f / _params_.Width; + _ith = 1.0f / _params_.Height; + return 1; + } + + public int ResetAtlas(int width, int height) + { + var i = 0; + var j = 0; + + _atlas.Reset(width, height); + _texData = new byte[width * height]; + Array.Clear(_texData, 0, _texData.Length); + _dirtyRect[0] = width; + _dirtyRect[1] = height; + _dirtyRect[2] = 0; + _dirtyRect[3] = 0; + for (i = 0; i < _fontsNumber; i++) + { + var font = _fonts[i]; + font.GlyphsNumber = 0; + for (j = 0; j < 256; j++) font.Lut[j] = -1; + } + + _params_.Width = width; + _params_.Height = height; + _itw = 1.0f / _params_.Width; + _ith = 1.0f / _params_.Height; + AddWhiteRect(2, 2); + return 1; + } + + private int LoadFont(StbTrueType.stbtt_fontinfo font, byte* data, int dataSize) + { + var stbError = 0; + font.userdata = this; + stbError = StbTrueType.stbtt_InitFont(font, data, 0); + return stbError; + } + + private int AllocFont() + { + Font font = null; + if (_fontsNumber + 1 > _fonts.Length) + { + var newFonts = new Font[_fonts.Length * 2]; + + for (var i = 0; i < _fonts.Length; ++i) + { + newFonts[i] = _fonts[i]; + } + + _fonts = newFonts; + } + + font = new Font + { + Glyphs = new FontGlyph[256], + GlyphsNumber = 0 + }; + + _fonts[_fontsNumber++] = font; + return _fontsNumber - 1; + } + + private void Blur(byte* dst, int w, int h, int dstStride, int blur) + { + var alpha = 0; + float sigma = 0; + if (blur < 1) + return; + sigma = blur * 0.57735f; + alpha = (int)((1 << 16) * (1.0f - Math.Exp(-2.3f / (sigma + 1.0f)))); + BlurRows(dst, w, h, dstStride, alpha); + BlurCols(dst, w, h, dstStride, alpha); + BlurRows(dst, w, h, dstStride, alpha); + BlurCols(dst, w, h, dstStride, alpha); + } + + private FontGlyph GetGlyph(Font font, int codepoint, int isize, int iblur, int bitmapOption) + { + var i = 0; + var g = 0; + var advance = 0; + var lsb = 0; + var x0 = 0; + var y0 = 0; + var x1 = 0; + var y1 = 0; + var gw = 0; + var gh = 0; + var gx = 0; + var gy = 0; + var x = 0; + var y = 0; + float scale = 0; + FontGlyph glyph = null; + int h = 0; + var size = isize / 10.0f; + var pad = 0; + var added = 0; + var renderFont = font; + if (isize < 2) + return null; + if (iblur > 20) + iblur = 20; + pad = iblur + 2; + h = HashInt(codepoint) & (256 - 1); + i = font.Lut[h]; + while (i != -1) + { + if (font.Glyphs[i].Codepoint == codepoint && font.Glyphs[i].Size == isize && + font.Glyphs[i].Blur == iblur) + { + glyph = font.Glyphs[i]; + if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL || glyph.X0 >= 0 && glyph.Y0 >= 0) return glyph; + break; + } + + i = font.Glyphs[i].Next; + } + + g = font._font.fons__tt_getGlyphIndex((int)codepoint); + if (g == 0) + for (i = 0; i < font.FallbacksCount; ++i) + { + var fallbackFont = _fonts[font.Fallbacks[i]]; + var fallbackIndex = fallbackFont._font.fons__tt_getGlyphIndex((int)codepoint); + if (fallbackIndex != 0) + { + g = fallbackIndex; + renderFont = fallbackFont; + break; + } + } + + scale = renderFont._font.fons__tt_getPixelHeightScale(size); + renderFont._font.fons__tt_buildGlyphBitmap(g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); + gw = x1 - x0 + pad * 2; + gh = y1 - y0 + pad * 2; + if (bitmapOption == FONS_GLYPH_BITMAP_REQUIRED) + { + added = _atlas.AddRect(gw, gh, &gx, &gy); + if (added == 0) + throw new Exception("FONS_ATLAS_FULL"); + } + else + { + gx = -1; + gy = -1; + } + + if (glyph == null) + { + glyph = AllocGlyph(font); + glyph.Codepoint = codepoint; + glyph.Size = isize; + glyph.Blur = iblur; + glyph.Next = 0; + glyph.Next = font.Lut[h]; + font.Lut[h] = font.GlyphsNumber - 1; + } + + glyph.Index = g; + glyph.X0 = (int)gx; + glyph.Y0 = (int)gy; + glyph.X1 = (int)(glyph.X0 + gw); + glyph.Y1 = (int)(glyph.Y0 + gh); + glyph.XAdvance = (int)(scale * advance * 10.0f); + glyph.XOffset = (int)(x0 - pad); + glyph.YOffset = (int)(y0 - pad); + if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL) return glyph; + + fixed (byte* dst = &_texData[glyph.X0 + pad + (glyph.Y0 + pad) * _params_.Width]) + { + renderFont._font.fons__tt_renderGlyphBitmap(dst, gw - pad * 2, gh - pad * 2, _params_.Width, scale, + scale, g); + } + + fixed (byte* dst = &_texData[glyph.X0 + glyph.Y0 * _params_.Width]) + { + for (y = 0; y < gh; y++) + { + dst[y * _params_.Width] = 0; + dst[gw - 1 + y * _params_.Width] = 0; + } + + for (x = 0; x < gw; x++) + { + dst[x] = 0; + dst[x + (gh - 1) * _params_.Width] = 0; + } + } + + if (iblur > 0) + { + fixed (byte* bdst = &_texData[glyph.X0 + glyph.Y0 * _params_.Width]) + { + Blur(bdst, gw, gh, _params_.Width, iblur); + } + } + + _dirtyRect[0] = Math.Min(_dirtyRect[0], glyph.X0); + _dirtyRect[1] = Math.Min(_dirtyRect[1], glyph.Y0); + _dirtyRect[2] = Math.Max(_dirtyRect[2], glyph.X1); + _dirtyRect[3] = Math.Max(_dirtyRect[3], glyph.Y1); + return glyph; + } + + private void GetQuad(Font font, int prevGlyphIndex, FontGlyph glyph, float scale, + float spacing, ref float x, ref float y, FontGlyphSquad* q) + { + + if (prevGlyphIndex != -1) + { + var adv = font._font.fons__tt_getGlyphKernAdvance(prevGlyphIndex, glyph.Index) * scale; + x += (int)(adv + spacing + 0.5f); + } + + float rx = 0; + float ry = 0; + + if ((_params_.Flags & FONS_ZERO_TOPLEFT) != 0) + { + rx = x + glyph.XOffset; + ry = y + glyph.YOffset; + q->X0 = rx; + q->Y0 = ry; + q->X1 = rx + (glyph.X1 - glyph.X0); + q->Y1 = ry + (glyph.Y1 - glyph.Y0); + q->S0 = glyph.X0 * _itw; + q->T0 = glyph.Y0 * _ith; + q->S1 = glyph.X1 * _itw; + q->T1 = glyph.Y1 * _ith; + } + else + { + rx = x + glyph.XOffset; + ry = y - glyph.YOffset; + q->X0 = rx; + q->Y0 = ry; + q->X1 = rx + (glyph.X1 - glyph.X0); + q->Y1 = ry - (glyph.Y1 + glyph.Y0); + q->S0 = glyph.X0 * _itw; + q->T0 = glyph.Y0 * _ith; + q->S1 = glyph.X1 * _itw; + q->T1 = glyph.Y1 * _ith; + } + + x += (int)(glyph.XAdvance / 10.0f + 0.5f); + } + + //Add Depth Parameter + private void Flush(SpriteBatch batch, float depth) + { + if (Texture == null) Texture = new Texture2D(batch.GraphicsDevice, _params_.Width, _params_.Height); + + if (_dirtyRect[0] < _dirtyRect[2] && _dirtyRect[1] < _dirtyRect[3]) + { + if (_texData != null) + { + var x = _dirtyRect[0]; + var y = _dirtyRect[1]; + var w = _dirtyRect[2] - x; + var h = _dirtyRect[3] - y; + var sz = w * h; + for (var xx = x; xx < x + w; ++xx) + { + for (var yy = y; yy < y + h; ++yy) + { + var destPos = yy * _params_.Width + xx; + + var c = _texData[destPos]; + _colorData[destPos].R = c; + _colorData[destPos].G = c; + _colorData[destPos].B = c; + _colorData[destPos].A = c; + } + } + + Texture.SetData(_colorData); + } + + _dirtyRect[0] = _params_.Width; + _dirtyRect[1] = _params_.Height; + _dirtyRect[2] = 0; + _dirtyRect[3] = 0; + } + + if (_vertsNumber > 0) + { + for (var i = 0; i < _vertsNumber; ++i) + { + //Add Depth Parameter + batch.Draw(Texture, _verts[i], _textureCoords[i], _colors[i], 0f, Vector2.Zero, SpriteEffects.None, depth); + } + + _vertsNumber = 0; + } + } + + private void AddVertex(Rectangle destRect, Rectangle srcRect, Color c) + { + _verts[_vertsNumber] = destRect; + _textureCoords[_vertsNumber] = srcRect; + _colors[_vertsNumber] = c; + _vertsNumber++; + } + + private float GetVertAlign(Font font, int align, int isize) + { + float result = 0.0f; ; + if ((_params_.Flags & FONS_ZERO_TOPLEFT) != 0) + { + if ((align & FONS_ALIGN_TOP) != 0) + { + result = font.Ascender * isize / 10.0f; + } + else if ((align & FONS_ALIGN_MIDDLE) != 0) + { + result = (font.Ascender + font.Descender) / 2.0f * isize / 10.0f; + } + else if ((align & FONS_ALIGN_BASELINE) != 0) + { + } + else if ((align & FONS_ALIGN_BOTTOM) != 0) + { + result = font.Descender * isize / 10.0f; + } + } + else + { + if ((align & FONS_ALIGN_TOP) != 0) + { + result = -font.Ascender * isize / 10.0f; + } + else + if ((align & FONS_ALIGN_MIDDLE) != 0) + { + result = -(font.Ascender + font.Descender) / 2.0f * isize / 10.0f; + } + else + if ((align & FONS_ALIGN_BASELINE) != 0) + { + } + else if ((align & FONS_ALIGN_BOTTOM) != 0) + { + result = -font.Descender * isize / 10.0f; + } + } + + return result; + } + + private static FontGlyph AllocGlyph(Font font) + { + if (font.GlyphsNumber + 1 > font.Glyphs.Length) + { + var oldGlyphs = font.Glyphs; + font.Glyphs = new FontGlyph[font.Glyphs.Length * 2]; + + for (var i = 0; i < oldGlyphs.Length; ++i) + { + font.Glyphs[i] = oldGlyphs[i]; + } + } + + font.Glyphs[font.GlyphsNumber] = new FontGlyph(); + + font.GlyphsNumber++; + return font.Glyphs[font.GlyphsNumber - 1]; + } + + private static void BlurCols(byte* dst, int w, int h, int dstStride, int alpha) + { + var x = 0; + var y = 0; + for (y = 0; y < h; y++) + { + var z = 0; + for (x = 1; x < w; x++) + { + z += (alpha * ((dst[x] << 7) - z)) >> 16; + dst[x] = (byte)(z >> 7); + } + + dst[w - 1] = 0; + z = 0; + for (x = w - 2; x >= 0; x--) + { + z += (alpha * ((dst[x] << 7) - z)) >> 16; + dst[x] = (byte)(z >> 7); + } + + dst[0] = 0; + dst += dstStride; + } + } + + private static void BlurRows(byte* dst, int w, int h, int dstStride, int alpha) + { + var x = 0; + var y = 0; + for (x = 0; x < w; x++) + { + var z = 0; + for (y = dstStride; y < h * dstStride; y += dstStride) + { + z += (alpha * ((dst[y] << 7) - z)) >> 16; + dst[y] = (byte)(z >> 7); + } + + dst[(h - 1) * dstStride] = 0; + z = 0; + for (y = (h - 2) * dstStride; y >= 0; y -= dstStride) + { + z += (alpha * ((dst[y] << 7) - z)) >> 16; + dst[y] = (byte)(z >> 7); + } + + dst[0] = 0; + dst++; + } + } + + private static int HashInt(int a) + { + a += ~(a << 15); + a ^= a >> 10; + a += a << 3; + a ^= a >> 6; + a += ~(a << 11); + a ^= a >> 16; + return a; + } + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontSystemParams.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontSystemParams.cs new file mode 100644 index 000000000..02442236d --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/FontSystemParams.cs @@ -0,0 +1,9 @@ +namespace FontStashSharp +{ + internal class FontSystemParams + { + public int Width; + public int Height; + public byte Flags; + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/GlyphPosition.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/GlyphPosition.cs new file mode 100644 index 000000000..52a046dbc --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/GlyphPosition.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace FontStashSharp +{ + [StructLayout(LayoutKind.Sequential)] + internal struct GlyphPosition + { + public StringSegment Str; + public float X; + public float MinX; + public float MaxX; + } +} diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/StringSegment.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/StringSegment.cs new file mode 100644 index 000000000..26dc31bc0 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/FontStashSharp/StringSegment.cs @@ -0,0 +1,79 @@ +namespace FontStashSharp +{ + internal struct StringSegment + { + public static readonly StringSegment Null; + + public string String; + public int Location; + public int Length; + + public char this[int index] + { + get + { + return String[Location + index]; + } + } + + public bool IsNullOrEmpty + { + get + { + if (String == null) + { + return true; + } + + return Location >= String.Length; + } + } + + public StringSegment(StringSegment s, int location) + { + String = s.String; + Length = s.Length; + Location = location; + } + + public StringSegment(StringSegment s, int location, int length) + { + String = s.String; + Length = length; + Location = location; + } + + public static implicit operator StringSegment(string value) + { + return new StringSegment + { + String = value, + Location = 0, + Length = value != null ? value.Length : 0 + }; + } + + public static bool operator ==(StringSegment a, StringSegment b) + { + return object.ReferenceEquals(a.String, b.String) && + a.Location == b.Location && + a.Length == b.Length; + } + + public static bool operator !=(StringSegment a, StringSegment b) + { + return !(a == b); + } + + public static StringSegment operator +(StringSegment a, int loc) + { + return new StringSegment(a, a.Location + loc); + } + + public void Reset() + { + String = null; + Location = Length = 0; + } + } +} diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/GlyphInfo.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/GlyphInfo.cs new file mode 100644 index 000000000..4d18a7891 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/GlyphInfo.cs @@ -0,0 +1,9 @@ +namespace SpriteFontPlus +{ + public struct GlyphInfo + { + public int X, Y, Width, Height; + public int XOffset, YOffset; + public int XAdvance; + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/SpriteBatchExtensions.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/SpriteBatchExtensions.cs new file mode 100644 index 000000000..7e7413a61 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/SpriteBatchExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace SpriteFontPlus +{ + public static class SpriteBatchExtensions + { + public static float DrawString(this SpriteBatch batch, DynamicSpriteFont font, + string _string_, Vector2 pos, Color color) + { + return font.DrawString(batch, _string_, pos, color); + } + + public static float DrawString(this SpriteBatch batch, DynamicSpriteFont font, + string _string_, Vector2 pos, Color color, Vector2 scale) + { + return font.DrawString(batch, _string_, pos, color, scale, 0f); + } + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/SpriteFontPlus.csproj b/WorldOfTheThreeKingdoms/SpriteFontPlus/SpriteFontPlus.csproj new file mode 100644 index 000000000..30cdcc9b5 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/SpriteFontPlus.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + true + + + + + + + diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/StbTrueType/CRuntime.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/StbTrueType/CRuntime.cs new file mode 100644 index 000000000..f84aaf19b --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/StbTrueType/CRuntime.cs @@ -0,0 +1,316 @@ +using System; +using System.Runtime.InteropServices; + +namespace StbSharp +{ + internal static unsafe class CRuntime + { + public const long DBL_EXP_MASK = 0x7ff0000000000000L; + public const int DBL_MANT_BITS = 52; + public const long DBL_SGN_MASK = -1 - 0x7fffffffffffffffL; + public const long DBL_MANT_MASK = 0x000fffffffffffffL; + public const long DBL_EXP_CLR_MASK = DBL_SGN_MASK | DBL_MANT_MASK; + + public static void* malloc(ulong size) + { + return malloc((long) size); + } + + public static void* malloc(long size) + { + var ptr = Marshal.AllocHGlobal((int) size); + + return ptr.ToPointer(); + } + + public static void memcpy(void* a, void* b, long size) + { + var ap = (byte*) a; + var bp = (byte*) b; + for (long i = 0; i < size; ++i) + { + *ap++ = *bp++; + } + } + + public static void memcpy(void* a, void* b, ulong size) + { + memcpy(a, b, (long) size); + } + + public static void memmove(void* a, void* b, long size) + { + void* temp = null; + + try + { + temp = malloc(size); + memcpy(temp, b, size); + memcpy(a, temp, size); + } + + finally + { + if (temp != null) + { + free(temp); + } + } + } + + public static void memmove(void* a, void* b, ulong size) + { + memmove(a, b, (long) size); + } + + public static int memcmp(void* a, void* b, long size) + { + var result = 0; + var ap = (byte*) a; + var bp = (byte*) b; + for (long i = 0; i < size; ++i) + { + if (*ap != *bp) + { + result += 1; + } + + ap++; + bp++; + } + + return result; + } + + public static int memcmp(void* a, void* b, ulong size) + { + return memcmp(a, b, (long) size); + } + + public static int memcmp(byte* a, byte[] b, ulong size) + { + fixed (void* bptr = b) + { + return memcmp(a, bptr, (long) size); + } + } + + public static void free(void* a) + { + var ptr = new IntPtr(a); + Marshal.FreeHGlobal(ptr); + } + + public static void memset(void* ptr, int value, long size) + { + byte* bptr = (byte*) ptr; + var bval = (byte) value; + for (long i = 0; i < size; ++i) + { + *bptr++ = bval; + } + } + + public static void memset(void* ptr, int value, ulong size) + { + memset(ptr, value, (long) size); + } + + public static uint _lrotl(uint x, int y) + { + return (x << y) | (x >> (32 - y)); + } + + public static void* realloc(void* a, long newSize) + { + if (a == null) + { + return malloc(newSize); + } + + var ptr = new IntPtr(a); + var result = Marshal.ReAllocHGlobal(ptr, new IntPtr(newSize)); + + return result.ToPointer(); + } + + public static void* realloc(void* a, ulong newSize) + { + return realloc(a, (long) newSize); + } + + public static int abs(int v) + { + return Math.Abs(v); + } + + /// + /// This code had been borrowed from here: https://github.com/MachineCognitis/C.math.NET + /// + /// + /// + /// + public static double frexp(double number, int* exponent) + { + var bits = BitConverter.DoubleToInt64Bits(number); + var exp = (int) ((bits & DBL_EXP_MASK) >> DBL_MANT_BITS); + *exponent = 0; + + if (exp == 0x7ff || number == 0D) + number += number; + else + { + // Not zero and finite. + *exponent = exp - 1022; + if (exp == 0) + { + // Subnormal, scale number so that it is in [1, 2). + number *= BitConverter.Int64BitsToDouble(0x4350000000000000L); // 2^54 + bits = BitConverter.DoubleToInt64Bits(number); + exp = (int) ((bits & DBL_EXP_MASK) >> DBL_MANT_BITS); + *exponent = exp - 1022 - 54; + } + + // Set exponent to -1 so that number is in [0.5, 1). + number = BitConverter.Int64BitsToDouble((bits & DBL_EXP_CLR_MASK) | 0x3fe0000000000000L); + } + + return number; + } + + public static double pow(double a, double b) + { + return Math.Pow(a, b); + } + + public static float fabs(double a) + { + return (float) Math.Abs(a); + } + + public static double ceil(double a) + { + return Math.Ceiling(a); + } + + + public static double floor(double a) + { + return Math.Floor(a); + } + + public static double log(double value) + { + return Math.Log(value); + } + + public static double exp(double value) + { + return Math.Exp(value); + } + + public static double cos(double value) + { + return Math.Cos(value); + } + + public static double acos(double value) + { + return Math.Acos(value); + } + + public static double sin(double value) + { + return Math.Sin(value); + } + + public static double ldexp(double number, int exponent) + { + return number * Math.Pow(2, exponent); + } + + public delegate int QSortComparer(void* a, void* b); + + private static void qsortSwap(byte* data, long size, long pos1, long pos2) + { + var a = data + size * pos1; + var b = data + size * pos2; + + for (long k = 0; k < size; ++k) + { + var tmp = *a; + *a = *b; + *b = tmp; + + a++; + b++; + } + } + + private static long qsortPartition(byte* data, long size, QSortComparer comparer, long left, long right) + { + void* pivot = data + size * left; + var i = left - 1; + var j = right + 1; + for (;;) + { + do + { + ++i; + } while (comparer(data + size * i, pivot) < 0); + + do + { + --j; + } while (comparer(data + size * j, pivot) > 0); + + if (i >= j) + { + return j; + } + + qsortSwap(data, size, i, j); + } + } + + + private static void qsortInternal(byte* data, long size, QSortComparer comparer, long left, long right) + { + if (left < right) + { + var p = qsortPartition(data, size, comparer, left, right); + + qsortInternal(data, size, comparer, left, p); + qsortInternal(data, size, comparer, p + 1, right); + } + } + + public static void qsort(void* data, ulong count, ulong size, QSortComparer comparer) + { + qsortInternal((byte*) data, (long) size, comparer, 0, (long) count - 1); + } + + public static double sqrt(double val) + { + return Math.Sqrt(val); + } + + public static double fmod(double x, double y) + { + return x % y; + } + + public static ulong strlen(sbyte* str) + { + ulong res = 0; + var ptr = str; + + while (*ptr != '\0') + { + ptr++; + } + + return ((ulong) ptr - (ulong) str - 1); + } + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/StbTrueType/StbTrueType.Generated.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/StbTrueType/StbTrueType.Generated.cs new file mode 100644 index 000000000..e19df33cb --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/StbTrueType/StbTrueType.Generated.cs @@ -0,0 +1,3852 @@ +using System; +using System.Runtime.InteropServices; + +namespace StbSharp +{ + unsafe partial class StbTrueType + { + public const int STBTT_vmove = 1; + public const int STBTT_vline = 2; + public const int STBTT_vcurve = 3; + public const int STBTT_vcubic = 4; + public const int STBTT_PLATFORM_ID_UNICODE = 0; + public const int STBTT_PLATFORM_ID_MAC = 1; + public const int STBTT_PLATFORM_ID_ISO = 2; + public const int STBTT_PLATFORM_ID_MICROSOFT = 3; + public const int STBTT_UNICODE_EID_UNICODE_1_0 = 0; + public const int STBTT_UNICODE_EID_UNICODE_1_1 = 1; + public const int STBTT_UNICODE_EID_ISO_10646 = 2; + public const int STBTT_UNICODE_EID_UNICODE_2_0_BMP = 3; + public const int STBTT_UNICODE_EID_UNICODE_2_0_FULL = 4; + public const int STBTT_MS_EID_SYMBOL = 0; + public const int STBTT_MS_EID_UNICODE_BMP = 1; + public const int STBTT_MS_EID_SHIFTJIS = 2; + public const int STBTT_MS_EID_UNICODE_FULL = 10; + public const int STBTT_MAC_EID_ROMAN = 0; + public const int STBTT_MAC_EID_ARABIC = 4; + public const int STBTT_MAC_EID_JAPANESE = 1; + public const int STBTT_MAC_EID_HEBREW = 5; + public const int STBTT_MAC_EID_CHINESE_TRAD = 2; + public const int STBTT_MAC_EID_GREEK = 6; + public const int STBTT_MAC_EID_KOREAN = 3; + public const int STBTT_MAC_EID_RUSSIAN = 7; + public const int STBTT_MS_LANG_ENGLISH = 0x0409; + public const int STBTT_MS_LANG_ITALIAN = 0x0410; + public const int STBTT_MS_LANG_CHINESE = 0x0804; + public const int STBTT_MS_LANG_JAPANESE = 0x0411; + public const int STBTT_MS_LANG_DUTCH = 0x0413; + public const int STBTT_MS_LANG_KOREAN = 0x0412; + public const int STBTT_MS_LANG_FRENCH = 0x040c; + public const int STBTT_MS_LANG_RUSSIAN = 0x0419; + public const int STBTT_MS_LANG_GERMAN = 0x0407; + public const int STBTT_MS_LANG_SPANISH = 0x0409; + public const int STBTT_MS_LANG_HEBREW = 0x040d; + public const int STBTT_MS_LANG_SWEDISH = 0x041D; + public const int STBTT_MAC_LANG_ENGLISH = 0; + public const int STBTT_MAC_LANG_JAPANESE = 11; + public const int STBTT_MAC_LANG_ARABIC = 12; + public const int STBTT_MAC_LANG_KOREAN = 23; + public const int STBTT_MAC_LANG_DUTCH = 4; + public const int STBTT_MAC_LANG_RUSSIAN = 32; + public const int STBTT_MAC_LANG_FRENCH = 1; + public const int STBTT_MAC_LANG_SPANISH = 6; + public const int STBTT_MAC_LANG_GERMAN = 2; + public const int STBTT_MAC_LANG_SWEDISH = 5; + public const int STBTT_MAC_LANG_HEBREW = 10; + public const int STBTT_MAC_LANG_CHINESE_SIMPLIFIED = 33; + public const int STBTT_MAC_LANG_ITALIAN = 3; + public const int STBTT_MAC_LANG_CHINESE_TRAD = 19; + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__buf + { + public byte* data; + public int cursor; + public int size; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt_bakedchar + { + public ushort x0; + public ushort y0; + public ushort x1; + public ushort y1; + public float xoff; + public float yoff; + public float xadvance; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt_aligned_quad + { + public float x0; + public float y0; + public float s0; + public float t0; + public float x1; + public float y1; + public float s1; + public float t1; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt_packedchar + { + public ushort x0; + public ushort y0; + public ushort x1; + public ushort y1; + public float xoff; + public float yoff; + public float xadvance; + public float xoff2; + public float yoff2; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt_pack_range + { + public float font_size; + public int first_unicode_codepoint_in_range; + public int* array_of_unicode_codepoints; + public int num_chars; + public stbtt_packedchar* chardata_for_range; + public byte h_oversample; + public byte v_oversample; + } + + [StructLayout(LayoutKind.Sequential)] + public class stbtt_pack_context + { + public object user_allocator_context; + public void* pack_info; + public int width; + public int height; + public int stride_in_bytes; + public int padding; + public uint h_oversample; + public uint v_oversample; + public byte* pixels; + public void* nodes; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe class stbtt_fontinfo + { + public object userdata; + public byte* data; + public int fontstart; + public int numGlyphs; + public int loca; + public int head; + public int glyf; + public int hhea; + public int hmtx; + public int kern; + public int gpos; + public int index_map; + public int indexToLocFormat; + public stbtt__buf cff; + public stbtt__buf charstrings; + public stbtt__buf gsubrs; + public stbtt__buf subrs; + public stbtt__buf fontdicts; + public stbtt__buf fdselect; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt_vertex + { + public short x; + public short y; + public short cx; + public short cy; + public short cx1; + public short cy1; + public byte type; + public byte padding; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__bitmap + { + public int w; + public int h; + public int stride; + public byte* pixels; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__csctx + { + public int bounds; + public int started; + public float first_x; + public float first_y; + public float x; + public float y; + public int min_x; + public int max_x; + public int min_y; + public int max_y; + public stbtt_vertex* pvertices; + public int num_vertices; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__hheap_chunk + { + public stbtt__hheap_chunk* next; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__hheap + { + public stbtt__hheap_chunk* head; + public void* first_free; + public int num_remaining_in_head_chunk; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__edge + { + public float x0; + public float y0; + public float x1; + public float y1; + public int invert; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__active_edge + { + public stbtt__active_edge* next; + public float fx; + public float fdx; + public float fdy; + public float direction; + public float sy; + public float ey; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbtt__point + { + public float x; + public float y; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbrp_context + { + public int width; + public int height; + public int x; + public int y; + public int bottom_y; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbrp_node + { + public byte x; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe partial struct stbrp_rect + { + public int x; + public int y; + public int id; + public int w; + public int h; + public int was_packed; + } + + public static byte stbtt__buf_get8(stbtt__buf* b) + { + if ((b->cursor) >= (b->size)) + return (byte)(0); + return (byte)(b->data[b->cursor++]); + } + + public static byte stbtt__buf_peek8(stbtt__buf* b) + { + if ((b->cursor) >= (b->size)) + return (byte)(0); + return (byte)(b->data[b->cursor]); + } + + public static void stbtt__buf_seek(stbtt__buf* b, int o) + { + b->cursor = (int)((((o) > (b->size)) || ((o) < (0))) ? b->size : o); + } + + public static void stbtt__buf_skip(stbtt__buf* b, int o) + { + stbtt__buf_seek(b, (int)(b->cursor + o)); + } + + public static uint stbtt__buf_get(stbtt__buf* b, int n) + { + uint v = (uint)(0); + int i; + for (i = (int)(0); (i) < (n); i++) + { + v = (uint)((v << 8) | stbtt__buf_get8(b)); + } + return (uint)(v); + } + + public static stbtt__buf stbtt__new_buf(void* p, ulong size) + { + stbtt__buf r = new stbtt__buf(); + r.data = (byte*)(p); + r.size = ((int)(size)); + r.cursor = (int)(0); + return (stbtt__buf)(r); + } + + public static stbtt__buf stbtt__buf_range(stbtt__buf* b, int o, int s) + { + stbtt__buf r = (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + if (((((o) < (0)) || ((s) < (0))) || ((o) > (b->size))) || ((s) > (b->size - o))) + return (stbtt__buf)(r); + r.data = b->data + o; + r.size = (int)(s); + return (stbtt__buf)(r); + } + + public static stbtt__buf stbtt__cff_get_index(stbtt__buf* b) + { + int count; + int start; + int offsize; + start = (int)(b->cursor); + count = (int)(stbtt__buf_get((b), (int)(2))); + if ((count) != 0) + { + offsize = (int)(stbtt__buf_get8(b)); + stbtt__buf_skip(b, (int)(offsize * count)); + stbtt__buf_skip(b, (int)(stbtt__buf_get(b, (int)(offsize)) - 1)); + } + + return (stbtt__buf)(stbtt__buf_range(b, (int)(start), (int)(b->cursor - start))); + } + + public static uint stbtt__cff_int(stbtt__buf* b) + { + int b0 = (int)(stbtt__buf_get8(b)); + if (((b0) >= (32)) && (b0 <= 246)) + return (uint)(b0 - 139); + else if (((b0) >= (247)) && (b0 <= 250)) + return (uint)((b0 - 247) * 256 + stbtt__buf_get8(b) + 108); + else if (((b0) >= (251)) && (b0 <= 254)) + return (uint)(-(b0 - 251) * 256 - stbtt__buf_get8(b) - 108); + else if ((b0) == (28)) + return (uint)(stbtt__buf_get((b), (int)(2))); + else if ((b0) == (29)) + return (uint)(stbtt__buf_get((b), (int)(4))); + return (uint)(0); + } + + public static void stbtt__cff_skip_operand(stbtt__buf* b) + { + int v; + int b0 = (int)(stbtt__buf_peek8(b)); + if ((b0) == (30)) + { + stbtt__buf_skip(b, (int)(1)); + while ((b->cursor) < (b->size)) + { + v = (int)(stbtt__buf_get8(b)); + if (((v & 0xF) == (0xF)) || ((v >> 4) == (0xF))) + break; + } + } + else + { + stbtt__cff_int(b); + } + + } + + public static stbtt__buf stbtt__dict_get(stbtt__buf* b, int key) + { + stbtt__buf_seek(b, (int)(0)); + while ((b->cursor) < (b->size)) + { + int start = (int)(b->cursor); + int end; + int op; + while ((stbtt__buf_peek8(b)) >= (28)) + { + stbtt__cff_skip_operand(b); + } + end = (int)(b->cursor); + op = (int)(stbtt__buf_get8(b)); + if ((op) == (12)) + op = (int)(stbtt__buf_get8(b) | 0x100); + if ((op) == (key)) + return (stbtt__buf)(stbtt__buf_range(b, (int)(start), (int)(end - start))); + } + return (stbtt__buf)(stbtt__buf_range(b, (int)(0), (int)(0))); + } + + public static void stbtt__dict_get_ints(stbtt__buf* b, int key, int outcount, uint* _out_) + { + int i; + stbtt__buf operands = (stbtt__buf)(stbtt__dict_get(b, (int)(key))); + for (i = (int)(0); ((i) < (outcount)) && ((operands.cursor) < (operands.size)); i++) + { + _out_[i] = (uint)(stbtt__cff_int(&operands)); + } + } + + public static int stbtt__cff_index_count(stbtt__buf* b) + { + stbtt__buf_seek(b, (int)(0)); + return (int)(stbtt__buf_get((b), (int)(2))); + } + + public static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) + { + int count; + int offsize; + int start; + int end; + stbtt__buf_seek(&b, (int)(0)); + count = (int)(stbtt__buf_get((&b), (int)(2))); + offsize = (int)(stbtt__buf_get8(&b)); + stbtt__buf_skip(&b, (int)(i * offsize)); + start = (int)(stbtt__buf_get(&b, (int)(offsize))); + end = (int)(stbtt__buf_get(&b, (int)(offsize))); + return (stbtt__buf)(stbtt__buf_range(&b, (int)(2 + (count + 1) * offsize + start), (int)(end - start))); + } + + public static ushort ttUSHORT(byte* p) + { + return (ushort)(p[0] * 256 + p[1]); + } + + public static short ttSHORT(byte* p) + { + return (short)(p[0] * 256 + p[1]); + } + + public static uint ttULONG(byte* p) + { + return (uint)((p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]); + } + + public static int ttLONG(byte* p) + { + return (int)((p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]); + } + + public static int stbtt__isfont(byte* font) + { + if (((((((font)[0]) == ('1')) && (((font)[1]) == (0))) && (((font)[2]) == (0))) && (((font)[3]) == (0)))) + return (int)(1); + if (((((((font)[0]) == ("typ1"[0])) && (((font)[1]) == ("typ1"[1]))) && (((font)[2]) == ("typ1"[2]))) && (((font)[3]) == ("typ1"[3])))) + return (int)(1); + if (((((((font)[0]) == ("OTTO"[0])) && (((font)[1]) == ("OTTO"[1]))) && (((font)[2]) == ("OTTO"[2]))) && (((font)[3]) == ("OTTO"[3])))) + return (int)(1); + if (((((((font)[0]) == (0)) && (((font)[1]) == (1))) && (((font)[2]) == (0))) && (((font)[3]) == (0)))) + return (int)(1); + if (((((((font)[0]) == ("true"[0])) && (((font)[1]) == ("true"[1]))) && (((font)[2]) == ("true"[2]))) && (((font)[3]) == ("true"[3])))) + return (int)(1); + return (int)(0); + } + + public static int stbtt_GetFontOffsetForIndex_internal(byte* font_collection, int index) + { + if ((stbtt__isfont(font_collection)) != 0) + return (int)((index) == (0) ? 0 : -1); + if (((((((font_collection)[0]) == ("ttcf"[0])) && (((font_collection)[1]) == ("ttcf"[1]))) && (((font_collection)[2]) == ("ttcf"[2]))) && (((font_collection)[3]) == ("ttcf"[3])))) + { + if (((ttULONG(font_collection + 4)) == (0x00010000)) || ((ttULONG(font_collection + 4)) == (0x00020000))) + { + int n = (int)(ttLONG(font_collection + 8)); + if ((index) >= (n)) + return (int)(-1); + return (int)(ttULONG(font_collection + 12 + index * 4)); + } + } + + return (int)(-1); + } + + public static int stbtt_GetNumberOfFonts_internal(byte* font_collection) + { + if ((stbtt__isfont(font_collection)) != 0) + return (int)(1); + if (((((((font_collection)[0]) == ("ttcf"[0])) && (((font_collection)[1]) == ("ttcf"[1]))) && (((font_collection)[2]) == ("ttcf"[2]))) && (((font_collection)[3]) == ("ttcf"[3])))) + { + if (((ttULONG(font_collection + 4)) == (0x00010000)) || ((ttULONG(font_collection + 4)) == (0x00020000))) + { + return (int)(ttLONG(font_collection + 8)); + } + } + + return (int)(0); + } + + public static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) + { + uint subrsoff = (uint)(0); + uint* private_loc = stackalloc uint[2]; + private_loc[0] = (uint)(0); + private_loc[1] = (uint)(0); + + stbtt__buf pdict = new stbtt__buf(); + stbtt__dict_get_ints(&fontdict, (int)(18), (int)(2), private_loc); + if ((private_loc[1] == 0) || (private_loc[0] == 0)) + return (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + pdict = (stbtt__buf)(stbtt__buf_range(&cff, (int)(private_loc[1]), (int)(private_loc[0]))); + stbtt__dict_get_ints(&pdict, (int)(19), (int)(1), &subrsoff); + if (subrsoff == 0) + return (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + stbtt__buf_seek(&cff, (int)(private_loc[1] + subrsoff)); + return (stbtt__buf)(stbtt__cff_get_index(&cff)); + } + + public static int stbtt_InitFont_internal(stbtt_fontinfo info, byte* data, int fontstart) + { + uint cmap; + uint t; + int i; + int numTables; + info.data = data; + info.fontstart = (int)(fontstart); + info.cff = (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + cmap = (uint)(stbtt__find_table(data, (uint)(fontstart), "cmap")); + info.loca = (int)(stbtt__find_table(data, (uint)(fontstart), "loca")); + info.head = (int)(stbtt__find_table(data, (uint)(fontstart), "head")); + info.glyf = (int)(stbtt__find_table(data, (uint)(fontstart), "glyf")); + info.hhea = (int)(stbtt__find_table(data, (uint)(fontstart), "hhea")); + info.hmtx = (int)(stbtt__find_table(data, (uint)(fontstart), "hmtx")); + info.kern = (int)(stbtt__find_table(data, (uint)(fontstart), "kern")); + info.gpos = (int)(stbtt__find_table(data, (uint)(fontstart), "GPOS")); + if ((((cmap == 0) || (info.head == 0)) || (info.hhea == 0)) || (info.hmtx == 0)) + return (int)(0); + if ((info.glyf) != 0) + { + if (info.loca == 0) + return (int)(0); + } + else + { + stbtt__buf b = new stbtt__buf(); + stbtt__buf topdict = new stbtt__buf(); + stbtt__buf topdictidx = new stbtt__buf(); + uint cstype = (uint)(2); + uint charstrings = (uint)(0); + uint fdarrayoff = (uint)(0); + uint fdselectoff = (uint)(0); + uint cff; + cff = (uint)(stbtt__find_table(data, (uint)(fontstart), "CFF ")); + if (cff == 0) + return (int)(0); + info.fontdicts = (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + info.fdselect = (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + info.cff = (stbtt__buf)(stbtt__new_buf(data + cff, (ulong)(512 * 1024 * 1024))); + b = (stbtt__buf)(info.cff); + stbtt__buf_skip(&b, (int)(2)); + stbtt__buf_seek(&b, (int)(stbtt__buf_get8(&b))); + stbtt__cff_get_index(&b); + topdictidx = (stbtt__buf)(stbtt__cff_get_index(&b)); + topdict = (stbtt__buf)(stbtt__cff_index_get((stbtt__buf)(topdictidx), (int)(0))); + stbtt__cff_get_index(&b); + info.gsubrs = (stbtt__buf)(stbtt__cff_get_index(&b)); + stbtt__dict_get_ints(&topdict, (int)(17), (int)(1), &charstrings); + stbtt__dict_get_ints(&topdict, (int)(0x100 | 6), (int)(1), &cstype); + stbtt__dict_get_ints(&topdict, (int)(0x100 | 36), (int)(1), &fdarrayoff); + stbtt__dict_get_ints(&topdict, (int)(0x100 | 37), (int)(1), &fdselectoff); + info.subrs = (stbtt__buf)(stbtt__get_subrs((stbtt__buf)(b), (stbtt__buf)(topdict))); + if (cstype != 2) + return (int)(0); + if ((charstrings) == (0)) + return (int)(0); + if ((fdarrayoff) != 0) + { + if (fdselectoff == 0) + return (int)(0); + stbtt__buf_seek(&b, (int)(fdarrayoff)); + info.fontdicts = (stbtt__buf)(stbtt__cff_get_index(&b)); + info.fdselect = (stbtt__buf)(stbtt__buf_range(&b, (int)(fdselectoff), (int)(b.size - fdselectoff))); + } + stbtt__buf_seek(&b, (int)(charstrings)); + info.charstrings = (stbtt__buf)(stbtt__cff_get_index(&b)); + } + + t = (uint)(stbtt__find_table(data, (uint)(fontstart), "maxp")); + if ((t) != 0) + info.numGlyphs = (int)(ttUSHORT(data + t + 4)); + else + info.numGlyphs = (int)(0xffff); + numTables = (int)(ttUSHORT(data + cmap + 2)); + info.index_map = (int)(0); + for (i = (int)(0); (i) < (numTables); ++i) + { + uint encoding_record = (uint)(cmap + 4 + 8 * i); + switch (ttUSHORT(data + encoding_record)) + { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data + encoding_record + 2)) + { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + info.index_map = (int)(cmap + ttULONG(data + encoding_record + 4)); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + info.index_map = (int)(cmap + ttULONG(data + encoding_record + 4)); + break; + } + } + if ((info.index_map) == (0)) + return (int)(0); + info.indexToLocFormat = (int)(ttUSHORT(data + info.head + 50)); + return (int)(1); + } + + public static int stbtt_FindGlyphIndex(stbtt_fontinfo info, int unicode_codepoint) + { + byte* data = info.data; + uint index_map = (uint)(info.index_map); + ushort format = (ushort)(ttUSHORT(data + index_map + 0)); + if ((format) == (0)) + { + int bytes = (int)(ttUSHORT(data + index_map + 2)); + if ((unicode_codepoint) < (bytes - 6)) + return (int)(*(data + index_map + 6 + unicode_codepoint)); + return (int)(0); + } + else if ((format) == (6)) + { + uint first = (uint)(ttUSHORT(data + index_map + 6)); + uint count = (uint)(ttUSHORT(data + index_map + 8)); + if ((((uint)(unicode_codepoint)) >= (first)) && (((uint)(unicode_codepoint)) < (first + count))) + return (int)(ttUSHORT(data + index_map + 10 + (unicode_codepoint - first) * 2)); + return (int)(0); + } + else if ((format) == (2)) + { + return (int)(0); + } + else if ((format) == (4)) + { + ushort segcount = (ushort)(ttUSHORT(data + index_map + 6) >> 1); + ushort searchRange = (ushort)(ttUSHORT(data + index_map + 8) >> 1); + ushort entrySelector = (ushort)(ttUSHORT(data + index_map + 10)); + ushort rangeShift = (ushort)(ttUSHORT(data + index_map + 12) >> 1); + uint endCount = (uint)(index_map + 14); + uint search = (uint)(endCount); + if ((unicode_codepoint) > (0xffff)) + return (int)(0); + if ((unicode_codepoint) >= (ttUSHORT(data + search + rangeShift * 2))) + search += (uint)(rangeShift * 2); + search -= (uint)(2); + while ((entrySelector) != 0) + { + ushort end; + searchRange >>= 1; + end = (ushort)(ttUSHORT(data + search + searchRange * 2)); + if ((unicode_codepoint) > (end)) + search += (uint)(searchRange * 2); + --entrySelector; + } + search += (uint)(2); + { + ushort offset; + ushort start; + ushort item = (ushort)((search - endCount) >> 1); + start = (ushort)(ttUSHORT(data + index_map + 14 + segcount * 2 + 2 + 2 * item)); + if ((unicode_codepoint) < (start)) + return (int)(0); + offset = (ushort)(ttUSHORT(data + index_map + 14 + segcount * 6 + 2 + 2 * item)); + if ((offset) == (0)) + return (int)((ushort)(unicode_codepoint + ttSHORT(data + index_map + 14 + segcount * 4 + 2 + 2 * item))); + return (int)(ttUSHORT(data + offset + (unicode_codepoint - start) * 2 + index_map + 14 + segcount * 6 + 2 + 2 * item)); + } + } + else if (((format) == (12)) || ((format) == (13))) + { + uint ngroups = (uint)(ttULONG(data + index_map + 12)); + int low; + int high; + low = (int)(0); + high = ((int)(ngroups)); + while ((low) < (high)) + { + int mid = (int)(low + ((high - low) >> 1)); + uint start_char = (uint)(ttULONG(data + index_map + 16 + mid * 12)); + uint end_char = (uint)(ttULONG(data + index_map + 16 + mid * 12 + 4)); + if (((uint)(unicode_codepoint)) < (start_char)) + high = (int)(mid); + else if (((uint)(unicode_codepoint)) > (end_char)) + low = (int)(mid + 1); + else + { + uint start_glyph = (uint)(ttULONG(data + index_map + 16 + mid * 12 + 8)); + if ((format) == (12)) + return (int)(start_glyph + unicode_codepoint - start_char); + else + return (int)(start_glyph); + } + } + return (int)(0); + } + + return (int)(0); + } + + public static int stbtt_GetCodepointShape(stbtt_fontinfo info, int unicode_codepoint, stbtt_vertex** vertices) + { + return (int)(stbtt_GetGlyphShape(info, (int)(stbtt_FindGlyphIndex(info, (int)(unicode_codepoint))), vertices)); + } + + public static void stbtt_setvertex(stbtt_vertex* v, byte type, int x, int y, int cx, int cy) + { + v->type = (byte)(type); + v->x = ((short)(x)); + v->y = ((short)(y)); + v->cx = ((short)(cx)); + v->cy = ((short)(cy)); + } + + public static int stbtt__GetGlyfOffset(stbtt_fontinfo info, int glyph_index) + { + int g1; + int g2; + if ((glyph_index) >= (info.numGlyphs)) + return (int)(-1); + if ((info.indexToLocFormat) >= (2)) + return (int)(-1); + if ((info.indexToLocFormat) == (0)) + { + g1 = (int)(info.glyf + ttUSHORT(info.data + info.loca + glyph_index * 2) * 2); + g2 = (int)(info.glyf + ttUSHORT(info.data + info.loca + glyph_index * 2 + 2) * 2); + } + else + { + g1 = (int)(info.glyf + ttULONG(info.data + info.loca + glyph_index * 4)); + g2 = (int)(info.glyf + ttULONG(info.data + info.loca + glyph_index * 4 + 4)); + } + + return (int)((g1) == (g2) ? -1 : g1); + } + + public static int stbtt_GetGlyphBox(stbtt_fontinfo info, int glyph_index, int* x0, int* y0, int* x1, int* y1) + { + if ((info.cff.size) != 0) + { + stbtt__GetGlyphInfoT2(info, (int)(glyph_index), x0, y0, x1, y1); + } + else + { + int g = (int)(stbtt__GetGlyfOffset(info, (int)(glyph_index))); + if ((g) < (0)) + return (int)(0); + if ((x0) != null) + *x0 = (int)(ttSHORT(info.data + g + 2)); + if ((y0) != null) + *y0 = (int)(ttSHORT(info.data + g + 4)); + if ((x1) != null) + *x1 = (int)(ttSHORT(info.data + g + 6)); + if ((y1) != null) + *y1 = (int)(ttSHORT(info.data + g + 8)); + } + + return (int)(1); + } + + public static int stbtt_GetCodepointBox(stbtt_fontinfo info, int codepoint, int* x0, int* y0, int* x1, int* y1) + { + return (int)(stbtt_GetGlyphBox(info, (int)(stbtt_FindGlyphIndex(info, (int)(codepoint))), x0, y0, x1, y1)); + } + + public static int stbtt_IsGlyphEmpty(stbtt_fontinfo info, int glyph_index) + { + short numberOfContours; + int g; + if ((info.cff.size) != 0) + return (int)((stbtt__GetGlyphInfoT2(info, (int)(glyph_index), (null), (null), (null), (null))) == (0) ? 1 : 0); + g = (int)(stbtt__GetGlyfOffset(info, (int)(glyph_index))); + if ((g) < (0)) + return (int)(1); + numberOfContours = (short)(ttSHORT(info.data + g)); + return (int)((numberOfContours) == (0) ? 1 : 0); + } + + public static int stbtt__close_shape(stbtt_vertex* vertices, int num_vertices, int was_off, int start_off, int sx, int sy, int scx, int scy, int cx, int cy) + { + if ((start_off) != 0) + { + if ((was_off) != 0) + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vcurve), (int)((cx + scx) >> 1), (int)((cy + scy) >> 1), (int)(cx), (int)(cy)); + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vcurve), (int)(sx), (int)(sy), (int)(scx), (int)(scy)); + } + else + { + if ((was_off) != 0) + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vcurve), (int)(sx), (int)(sy), (int)(cx), (int)(cy)); + else + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vline), (int)(sx), (int)(sy), (int)(0), (int)(0)); + } + + return (int)(num_vertices); + } + + public static int stbtt__GetGlyphShapeTT(stbtt_fontinfo info, int glyph_index, stbtt_vertex** pvertices) + { + short numberOfContours; + byte* endPtsOfContours; + byte* data = info.data; + stbtt_vertex* vertices = null; + int num_vertices = (int)(0); + int g = (int)(stbtt__GetGlyfOffset(info, (int)(glyph_index))); + *pvertices = (null); + if ((g) < (0)) + return (int)(0); + numberOfContours = (short)(ttSHORT(data + g)); + if ((numberOfContours) > (0)) + { + byte flags = (byte)(0); + byte flagcount; + int ins; + int i; + int j = (int)(0); + int m; + int n; + int next_move; + int was_off = (int)(0); + int off; + int start_off = (int)(0); + int x; + int y; + int cx; + int cy; + int sx; + int sy; + int scx; + int scy; + byte* points; + endPtsOfContours = (data + g + 10); + ins = (int)(ttUSHORT(data + g + 10 + numberOfContours * 2)); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + n = (int)(1 + ttUSHORT(endPtsOfContours + numberOfContours * 2 - 2)); + m = (int)(n + 2 * numberOfContours); + vertices = (stbtt_vertex*)(CRuntime.malloc((ulong)(m * sizeof(stbtt_vertex)))); + if ((vertices) == (null)) + return (int)(0); + next_move = (int)(0); + flagcount = (byte)(0); + off = (int)(m - n); + for (i = (int)(0); (i) < (n); ++i) + { + if ((flagcount) == (0)) + { + flags = (byte)(*points++); + if ((flags & 8) != 0) + flagcount = (byte)(*points++); + } + else + --flagcount; + vertices[off + i].type = (byte)(flags); + } + x = (int)(0); + for (i = (int)(0); (i) < (n); ++i) + { + flags = (byte)(vertices[off + i].type); + if ((flags & 2) != 0) + { + short dx = (short)(*points++); + x += (int)((flags & 16) != 0 ? dx : -dx); + } + else + { + if ((flags & 16) == 0) + { + x = (int)(x + (short)(points[0] * 256 + points[1])); + points += 2; + } + } + vertices[off + i].x = ((short)(x)); + } + y = (int)(0); + for (i = (int)(0); (i) < (n); ++i) + { + flags = (byte)(vertices[off + i].type); + if ((flags & 4) != 0) + { + short dy = (short)(*points++); + y += (int)((flags & 32) != 0 ? dy : -dy); + } + else + { + if ((flags & 32) == 0) + { + y = (int)(y + (short)(points[0] * 256 + points[1])); + points += 2; + } + } + vertices[off + i].y = ((short)(y)); + } + num_vertices = (int)(0); + sx = (int)(sy = (int)(cx = (int)(cy = (int)(scx = (int)(scy = (int)(0)))))); + for (i = (int)(0); (i) < (n); ++i) + { + flags = (byte)(vertices[off + i].type); + x = (int)(vertices[off + i].x); + y = (int)(vertices[off + i].y); + if ((next_move) == (i)) + { + if (i != 0) + num_vertices = (int)(stbtt__close_shape(vertices, (int)(num_vertices), (int)(was_off), (int)(start_off), (int)(sx), (int)(sy), (int)(scx), (int)(scy), (int)(cx), (int)(cy))); + start_off = ((flags & 1) != 0 ? 0 : 1); + if ((start_off) != 0) + { + scx = (int)(x); + scy = (int)(y); + if ((vertices[off + i + 1].type & 1) == 0) + { + sx = (int)((x + (int)(vertices[off + i + 1].x)) >> 1); + sy = (int)((y + (int)(vertices[off + i + 1].y)) >> 1); + } + else + { + sx = ((int)(vertices[off + i + 1].x)); + sy = ((int)(vertices[off + i + 1].y)); + ++i; + } + } + else + { + sx = (int)(x); + sy = (int)(y); + } + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vmove), (int)(sx), (int)(sy), (int)(0), (int)(0)); + was_off = (int)(0); + next_move = (int)(1 + ttUSHORT(endPtsOfContours + j * 2)); + ++j; + } + else + { + if ((flags & 1) == 0) + { + if ((was_off) != 0) + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vcurve), (int)((cx + x) >> 1), (int)((cy + y) >> 1), (int)(cx), (int)(cy)); + cx = (int)(x); + cy = (int)(y); + was_off = (int)(1); + } + else + { + if ((was_off) != 0) + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vcurve), (int)(x), (int)(y), (int)(cx), (int)(cy)); + else + stbtt_setvertex(&vertices[num_vertices++], (byte)(STBTT_vline), (int)(x), (int)(y), (int)(0), (int)(0)); + was_off = (int)(0); + } + } + } + num_vertices = (int)(stbtt__close_shape(vertices, (int)(num_vertices), (int)(was_off), (int)(start_off), (int)(sx), (int)(sy), (int)(scx), (int)(scy), (int)(cx), (int)(cy))); + } + else if ((numberOfContours) == (-1)) + { + int more = (int)(1); + byte* comp = data + g + 10; + num_vertices = (int)(0); + vertices = null; + while ((more) != 0) + { + ushort flags; + ushort gidx; + int comp_num_verts = (int)(0); + int i; + stbtt_vertex* comp_verts = null; + stbtt_vertex* tmp = null; + float* mtx = stackalloc float[6]; + mtx[0] = (float)(1); + mtx[1] = (float)(0); + mtx[2] = (float)(0); + mtx[3] = (float)(1); + mtx[4] = (float)(0); + mtx[5] = (float)(0); + float m; + float n; + flags = (ushort)(ttSHORT(comp)); + comp += 2; + gidx = (ushort)(ttSHORT(comp)); + comp += 2; + if ((flags & 2) != 0) + { + if ((flags & 1) != 0) + { + mtx[4] = (float)(ttSHORT(comp)); + comp += 2; + mtx[5] = (float)(ttSHORT(comp)); + comp += 2; + } + else + { + mtx[4] = (float)(*(sbyte*)(comp)); + comp += 1; + mtx[5] = (float)(*(sbyte*)(comp)); + comp += 1; + } + } + else + { + } + if ((flags & (1 << 3)) != 0) + { + mtx[0] = (float)(mtx[3] = (float)(ttSHORT(comp) / 16384.0f)); + comp += 2; + mtx[1] = (float)(mtx[2] = (float)(0)); + } + else if ((flags & (1 << 6)) != 0) + { + mtx[0] = (float)(ttSHORT(comp) / 16384.0f); + comp += 2; + mtx[1] = (float)(mtx[2] = (float)(0)); + mtx[3] = (float)(ttSHORT(comp) / 16384.0f); + comp += 2; + } + else if ((flags & (1 << 7)) != 0) + { + mtx[0] = (float)(ttSHORT(comp) / 16384.0f); + comp += 2; + mtx[1] = (float)(ttSHORT(comp) / 16384.0f); + comp += 2; + mtx[2] = (float)(ttSHORT(comp) / 16384.0f); + comp += 2; + mtx[3] = (float)(ttSHORT(comp) / 16384.0f); + comp += 2; + } + m = ((float)(CRuntime.sqrt((double)(mtx[0] * mtx[0] + mtx[1] * mtx[1])))); + n = ((float)(CRuntime.sqrt((double)(mtx[2] * mtx[2] + mtx[3] * mtx[3])))); + comp_num_verts = (int)(stbtt_GetGlyphShape(info, (int)(gidx), &comp_verts)); + if ((comp_num_verts) > (0)) + { + for (i = (int)(0); (i) < (comp_num_verts); ++i) + { + stbtt_vertex* v = &comp_verts[i]; + short x; + short y; + x = (short)(v->x); + y = (short)(v->y); + v->x = ((short)(m * (mtx[0] * x + mtx[2] * y + mtx[4]))); + v->y = ((short)(n * (mtx[1] * x + mtx[3] * y + mtx[5]))); + x = (short)(v->cx); + y = (short)(v->cy); + v->cx = ((short)(m * (mtx[0] * x + mtx[2] * y + mtx[4]))); + v->cy = ((short)(n * (mtx[1] * x + mtx[3] * y + mtx[5]))); + } + tmp = (stbtt_vertex*)(CRuntime.malloc((ulong)((num_vertices + comp_num_verts) * sizeof(stbtt_vertex)))); + if (tmp == null) + { + if ((vertices) != null) + CRuntime.free(vertices); + if ((comp_verts) != null) + CRuntime.free(comp_verts); + return (int)(0); + } + if ((num_vertices) > (0)) + CRuntime.memcpy(tmp, vertices, (ulong)(num_vertices * sizeof(stbtt_vertex))); + CRuntime.memcpy(tmp + num_vertices, comp_verts, (ulong)(comp_num_verts * sizeof(stbtt_vertex))); + if ((vertices) != null) + CRuntime.free(vertices); + vertices = tmp; + CRuntime.free(comp_verts); + num_vertices += (int)(comp_num_verts); + } + more = (int)(flags & (1 << 5)); + } + } + else if ((numberOfContours) < (0)) + { + } + else + { + } + + *pvertices = vertices; + return (int)(num_vertices); + } + + public static void stbtt__track_vertex(stbtt__csctx* c, int x, int y) + { + if (((x) > (c->max_x)) || (c->started == 0)) + c->max_x = (int)(x); + if (((y) > (c->max_y)) || (c->started == 0)) + c->max_y = (int)(y); + if (((x) < (c->min_x)) || (c->started == 0)) + c->min_x = (int)(x); + if (((y) < (c->min_y)) || (c->started == 0)) + c->min_y = (int)(y); + c->started = (int)(1); + } + + public static void stbtt__csctx_v(stbtt__csctx* c, byte type, int x, int y, int cx, int cy, int cx1, int cy1) + { + if ((c->bounds) != 0) + { + stbtt__track_vertex(c, (int)(x), (int)(y)); + if ((type) == (STBTT_vcubic)) + { + stbtt__track_vertex(c, (int)(cx), (int)(cy)); + stbtt__track_vertex(c, (int)(cx1), (int)(cy1)); + } + } + else + { + stbtt_setvertex(&c->pvertices[c->num_vertices], (byte)(type), (int)(x), (int)(y), (int)(cx), (int)(cy)); + c->pvertices[c->num_vertices].cx1 = ((short)(cx1)); + c->pvertices[c->num_vertices].cy1 = ((short)(cy1)); + } + + c->num_vertices++; + } + + public static void stbtt__csctx_close_shape(stbtt__csctx* ctx) + { + if ((ctx->first_x != ctx->x) || (ctx->first_y != ctx->y)) + stbtt__csctx_v(ctx, (byte)(STBTT_vline), (int)(ctx->first_x), (int)(ctx->first_y), (int)(0), (int)(0), (int)(0), (int)(0)); + } + + public static void stbtt__csctx_rmove_to(stbtt__csctx* ctx, float dx, float dy) + { + stbtt__csctx_close_shape(ctx); + ctx->first_x = (float)(ctx->x = (float)(ctx->x + dx)); + ctx->first_y = (float)(ctx->y = (float)(ctx->y + dy)); + stbtt__csctx_v(ctx, (byte)(STBTT_vmove), (int)(ctx->x), (int)(ctx->y), (int)(0), (int)(0), (int)(0), (int)(0)); + } + + public static void stbtt__csctx_rline_to(stbtt__csctx* ctx, float dx, float dy) + { + ctx->x += (float)(dx); + ctx->y += (float)(dy); + stbtt__csctx_v(ctx, (byte)(STBTT_vline), (int)(ctx->x), (int)(ctx->y), (int)(0), (int)(0), (int)(0), (int)(0)); + } + + public static void stbtt__csctx_rccurve_to(stbtt__csctx* ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) + { + float cx1 = (float)(ctx->x + dx1); + float cy1 = (float)(ctx->y + dy1); + float cx2 = (float)(cx1 + dx2); + float cy2 = (float)(cy1 + dy2); + ctx->x = (float)(cx2 + dx3); + ctx->y = (float)(cy2 + dy3); + stbtt__csctx_v(ctx, (byte)(STBTT_vcubic), (int)(ctx->x), (int)(ctx->y), (int)(cx1), (int)(cy1), (int)(cx2), (int)(cy2)); + } + + public static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) + { + int count = (int)(stbtt__cff_index_count(&idx)); + int bias = (int)(107); + if ((count) >= (33900)) + bias = (int)(32768); + else if ((count) >= (1240)) + bias = (int)(1131); + n += (int)(bias); + if (((n) < (0)) || ((n) >= (count))) + return (stbtt__buf)(stbtt__new_buf((null), (ulong)(0))); + return (stbtt__buf)(stbtt__cff_index_get((stbtt__buf)(idx), (int)(n))); + } + + public static stbtt__buf stbtt__cid_get_glyph_subrs(stbtt_fontinfo info, int glyph_index) + { + stbtt__buf fdselect = (stbtt__buf)(info.fdselect); + int nranges; + int start; + int end; + int v; + int fmt; + int fdselector = (int)(-1); + int i; + stbtt__buf_seek(&fdselect, (int)(0)); + fmt = (int)(stbtt__buf_get8(&fdselect)); + if ((fmt) == (0)) + { + stbtt__buf_skip(&fdselect, (int)(glyph_index)); + fdselector = (int)(stbtt__buf_get8(&fdselect)); + } + else if ((fmt) == (3)) + { + nranges = (int)(stbtt__buf_get((&fdselect), (int)(2))); + start = (int)(stbtt__buf_get((&fdselect), (int)(2))); + for (i = (int)(0); (i) < (nranges); i++) + { + v = (int)(stbtt__buf_get8(&fdselect)); + end = (int)(stbtt__buf_get((&fdselect), (int)(2))); + if (((glyph_index) >= (start)) && ((glyph_index) < (end))) + { + fdselector = (int)(v); + break; + } + start = (int)(end); + } + } + + if ((fdselector) == (-1)) + stbtt__new_buf((null), (ulong)(0)); + return (stbtt__buf)(stbtt__get_subrs((stbtt__buf)(info.cff), (stbtt__buf)(stbtt__cff_index_get((stbtt__buf)(info.fontdicts), (int)(fdselector))))); + } + + public static int stbtt__run_charstring(stbtt_fontinfo info, int glyph_index, stbtt__csctx* c) + { + int in_header = (int)(1); + int maskbits = (int)(0); + int subr_stack_height = (int)(0); + int sp = (int)(0); + int v; + int i; + int b0; + int has_subrs = (int)(0); + int clear_stack; + float* s = stackalloc float[48]; + stbtt__buf* subr_stack = stackalloc stbtt__buf[10]; + stbtt__buf subrs = (stbtt__buf)(info.subrs); + stbtt__buf b = new stbtt__buf(); + float f; + b = (stbtt__buf)(stbtt__cff_index_get((stbtt__buf)(info.charstrings), (int)(glyph_index))); + while ((b.cursor) < (b.size)) + { + i = (int)(0); + clear_stack = (int)(1); + b0 = (int)(stbtt__buf_get8(&b)); + switch (b0) + { + case 0x13: + case 0x14: + if ((in_header) != 0) + maskbits += (int)(sp / 2); + in_header = (int)(0); + stbtt__buf_skip(&b, (int)((maskbits + 7) / 8)); + break; + case 0x01: + case 0x03: + case 0x12: + case 0x17: + maskbits += (int)(sp / 2); + break; + case 0x15: + in_header = (int)(0); + if ((sp) < (2)) + return (int)(0); + stbtt__csctx_rmove_to(c, (float)(s[sp - 2]), (float)(s[sp - 1])); + break; + case 0x04: + in_header = (int)(0); + if ((sp) < (1)) + return (int)(0); + stbtt__csctx_rmove_to(c, (float)(0), (float)(s[sp - 1])); + break; + case 0x16: + in_header = (int)(0); + if ((sp) < (1)) + return (int)(0); + stbtt__csctx_rmove_to(c, (float)(s[sp - 1]), (float)(0)); + break; + case 0x05: + if ((sp) < (2)) + return (int)(0); + for (; (i + 1) < (sp); i += (int)(2)) + { + stbtt__csctx_rline_to(c, (float)(s[i]), (float)(s[i + 1])); + } + break; + case 0x07: + case 0x06: + { + int gotoVlineTo = (int)(0); + if ((b0) == (0x07)) + { + if ((sp) < (1)) + return (int)(0); + gotoVlineTo = (int)(1); + } + if ((sp) < (1)) + return (int)(0); + for (; ; ) + { + if (gotoVlineTo == 0) + { + if ((i) >= (sp)) + break; + stbtt__csctx_rline_to(c, (float)(s[i]), (float)(0)); + i++; + } + else + { + gotoVlineTo = (int)(0); + } + if ((i) >= (sp)) + break; + stbtt__csctx_rline_to(c, (float)(0), (float)(s[i])); + i++; + } + } + break; + case 0x1F: + case 0x1E: + { + int gotoHcurveTo = (int)(0); + if ((b0) == (0x1F)) + { + if ((sp) < (4)) + return (int)(0); + gotoHcurveTo = (int)(1); + } + for (; ; ) + { + if (gotoHcurveTo == 0) + { + if ((i + 3) >= (sp)) + break; + stbtt__csctx_rccurve_to(c, (float)(0), (float)(s[i]), (float)(s[i + 1]), (float)(s[i + 2]), (float)(s[i + 3]), (float)(((sp - i) == (5)) ? s[i + 4] : 0.0f)); + i += (int)(4); + } + else + { + gotoHcurveTo = (int)(0); + } + if ((i + 3) >= (sp)) + break; + stbtt__csctx_rccurve_to(c, (float)(s[i]), (float)(0), (float)(s[i + 1]), (float)(s[i + 2]), (float)(((sp - i) == (5)) ? s[i + 4] : 0.0f), (float)(s[i + 3])); + i += (int)(4); + } + } + break; + case 0x08: + if ((sp) < (6)) + return (int)(0); + for (; (i + 5) < (sp); i += (int)(6)) + { + stbtt__csctx_rccurve_to(c, (float)(s[i]), (float)(s[i + 1]), (float)(s[i + 2]), (float)(s[i + 3]), (float)(s[i + 4]), (float)(s[i + 5])); + } + break; + case 0x18: + if ((sp) < (8)) + return (int)(0); + for (; (i + 5) < (sp - 2); i += (int)(6)) + { + stbtt__csctx_rccurve_to(c, (float)(s[i]), (float)(s[i + 1]), (float)(s[i + 2]), (float)(s[i + 3]), (float)(s[i + 4]), (float)(s[i + 5])); + } + if ((i + 1) >= (sp)) + return (int)(0); + stbtt__csctx_rline_to(c, (float)(s[i]), (float)(s[i + 1])); + break; + case 0x19: + if ((sp) < (8)) + return (int)(0); + for (; (i + 1) < (sp - 6); i += (int)(2)) + { + stbtt__csctx_rline_to(c, (float)(s[i]), (float)(s[i + 1])); + } + if ((i + 5) >= (sp)) + return (int)(0); + stbtt__csctx_rccurve_to(c, (float)(s[i]), (float)(s[i + 1]), (float)(s[i + 2]), (float)(s[i + 3]), (float)(s[i + 4]), (float)(s[i + 5])); + break; + case 0x1A: + case 0x1B: + if ((sp) < (4)) + return (int)(0); + f = (float)(0.0); + if ((sp & 1) != 0) + { + f = (float)(s[i]); + i++; + } + for (; (i + 3) < (sp); i += (int)(4)) + { + if ((b0) == (0x1B)) + stbtt__csctx_rccurve_to(c, (float)(s[i]), (float)(f), (float)(s[i + 1]), (float)(s[i + 2]), (float)(s[i + 3]), (float)(0.0)); + else + stbtt__csctx_rccurve_to(c, (float)(f), (float)(s[i]), (float)(s[i + 1]), (float)(s[i + 2]), (float)(0.0), (float)(s[i + 3])); + f = (float)(0.0); + } + break; + case 0x0A: + case 0x1D: + if ((b0) == (0x0A)) + { + if (has_subrs == 0) + { + if ((info.fdselect.size) != 0) + subrs = (stbtt__buf)(stbtt__cid_get_glyph_subrs(info, (int)(glyph_index))); + has_subrs = (int)(1); + } + } + if ((sp) < (1)) + return (int)(0); + v = ((int)(s[--sp])); + if ((subr_stack_height) >= (10)) + return (int)(0); + subr_stack[subr_stack_height++] = (stbtt__buf)(b); + b = (stbtt__buf)(stbtt__get_subr((stbtt__buf)((b0) == (0x0A) ? subrs : info.gsubrs), (int)(v))); + if ((b.size) == (0)) + return (int)(0); + b.cursor = (int)(0); + clear_stack = (int)(0); + break; + case 0x0B: + if (subr_stack_height <= 0) + return (int)(0); + b = (stbtt__buf)(subr_stack[--subr_stack_height]); + clear_stack = (int)(0); + break; + case 0x0E: + stbtt__csctx_close_shape(c); + return (int)(1); + case 0x0C: + { + float dx1; + float dx2; + float dx3; + float dx4; + float dx5; + float dx6; + float dy1; + float dy2; + float dy3; + float dy4; + float dy5; + float dy6; + float dx; + float dy; + int b1 = (int)(stbtt__buf_get8(&b)); + switch (b1) + { + case 0x22: + if ((sp) < (7)) + return (int)(0); + dx1 = (float)(s[0]); + dx2 = (float)(s[1]); + dy2 = (float)(s[2]); + dx3 = (float)(s[3]); + dx4 = (float)(s[4]); + dx5 = (float)(s[5]); + dx6 = (float)(s[6]); + stbtt__csctx_rccurve_to(c, (float)(dx1), (float)(0), (float)(dx2), (float)(dy2), (float)(dx3), (float)(0)); + stbtt__csctx_rccurve_to(c, (float)(dx4), (float)(0), (float)(dx5), (float)(-dy2), (float)(dx6), (float)(0)); + break; + case 0x23: + if ((sp) < (13)) + return (int)(0); + dx1 = (float)(s[0]); + dy1 = (float)(s[1]); + dx2 = (float)(s[2]); + dy2 = (float)(s[3]); + dx3 = (float)(s[4]); + dy3 = (float)(s[5]); + dx4 = (float)(s[6]); + dy4 = (float)(s[7]); + dx5 = (float)(s[8]); + dy5 = (float)(s[9]); + dx6 = (float)(s[10]); + dy6 = (float)(s[11]); + stbtt__csctx_rccurve_to(c, (float)(dx1), (float)(dy1), (float)(dx2), (float)(dy2), (float)(dx3), (float)(dy3)); + stbtt__csctx_rccurve_to(c, (float)(dx4), (float)(dy4), (float)(dx5), (float)(dy5), (float)(dx6), (float)(dy6)); + break; + case 0x24: + if ((sp) < (9)) + return (int)(0); + dx1 = (float)(s[0]); + dy1 = (float)(s[1]); + dx2 = (float)(s[2]); + dy2 = (float)(s[3]); + dx3 = (float)(s[4]); + dx4 = (float)(s[5]); + dx5 = (float)(s[6]); + dy5 = (float)(s[7]); + dx6 = (float)(s[8]); + stbtt__csctx_rccurve_to(c, (float)(dx1), (float)(dy1), (float)(dx2), (float)(dy2), (float)(dx3), (float)(0)); + stbtt__csctx_rccurve_to(c, (float)(dx4), (float)(0), (float)(dx5), (float)(dy5), (float)(dx6), (float)(-(dy1 + dy2 + dy5))); + break; + case 0x25: + if ((sp) < (11)) + return (int)(0); + dx1 = (float)(s[0]); + dy1 = (float)(s[1]); + dx2 = (float)(s[2]); + dy2 = (float)(s[3]); + dx3 = (float)(s[4]); + dy3 = (float)(s[5]); + dx4 = (float)(s[6]); + dy4 = (float)(s[7]); + dx5 = (float)(s[8]); + dy5 = (float)(s[9]); + dx6 = (float)(dy6 = (float)(s[10])); + dx = (float)(dx1 + dx2 + dx3 + dx4 + dx5); + dy = (float)(dy1 + dy2 + dy3 + dy4 + dy5); + if ((CRuntime.fabs((double)(dx))) > (CRuntime.fabs((double)(dy)))) + dy6 = (float)(-dy); + else + dx6 = (float)(-dx); + stbtt__csctx_rccurve_to(c, (float)(dx1), (float)(dy1), (float)(dx2), (float)(dy2), (float)(dx3), (float)(dy3)); + stbtt__csctx_rccurve_to(c, (float)(dx4), (float)(dy4), (float)(dx5), (float)(dy5), (float)(dx6), (float)(dy6)); + break; + default: + return (int)(0); + } + } + break; + default: + if (((b0 != 255) && (b0 != 28)) && (((b0) < (32)) || ((b0) > (254)))) + return (int)(0); + if ((b0) == (255)) + { + f = (float)((float)(stbtt__buf_get((&b), (int)(4))) / 0x10000); + } + else + { + stbtt__buf_skip(&b, (int)(-1)); + f = ((float)((short)(stbtt__cff_int(&b)))); + } + if ((sp) >= (48)) + return (int)(0); + s[sp++] = (float)(f); + clear_stack = (int)(0); + break; + } + if ((clear_stack) != 0) + sp = (int)(0); + } + return (int)(0); + } + + public static int stbtt__GetGlyphShapeT2(stbtt_fontinfo info, int glyph_index, stbtt_vertex** pvertices) + { + stbtt__csctx count_ctx = new stbtt__csctx(); + count_ctx.bounds = (int)(1); + stbtt__csctx output_ctx = new stbtt__csctx(); + if ((stbtt__run_charstring(info, (int)(glyph_index), &count_ctx)) != 0) + { + *pvertices = (stbtt_vertex*)(CRuntime.malloc((ulong)(count_ctx.num_vertices * sizeof(stbtt_vertex)))); + output_ctx.pvertices = *pvertices; + if ((stbtt__run_charstring(info, (int)(glyph_index), &output_ctx)) != 0) + { + return (int)(output_ctx.num_vertices); + } + } + + *pvertices = (null); + return (int)(0); + } + + public static int stbtt__GetGlyphInfoT2(stbtt_fontinfo info, int glyph_index, int* x0, int* y0, int* x1, int* y1) + { + stbtt__csctx c = new stbtt__csctx(); + c.bounds = (int)(1); + int r = (int)(stbtt__run_charstring(info, (int)(glyph_index), &c)); + if ((x0) != null) + *x0 = (int)((r) != 0 ? c.min_x : 0); + if ((y0) != null) + *y0 = (int)((r) != 0 ? c.min_y : 0); + if ((x1) != null) + *x1 = (int)((r) != 0 ? c.max_x : 0); + if ((y1) != null) + *y1 = (int)((r) != 0 ? c.max_y : 0); + return (int)((r) != 0 ? c.num_vertices : 0); + } + + public static int stbtt_GetGlyphShape(stbtt_fontinfo info, int glyph_index, stbtt_vertex** pvertices) + { + if (info.cff.size == 0) + return (int)(stbtt__GetGlyphShapeTT(info, (int)(glyph_index), pvertices)); + else + return (int)(stbtt__GetGlyphShapeT2(info, (int)(glyph_index), pvertices)); + } + + public static void stbtt_GetGlyphHMetrics(stbtt_fontinfo info, int glyph_index, int* advanceWidth, int* leftSideBearing) + { + ushort numOfLongHorMetrics = (ushort)(ttUSHORT(info.data + info.hhea + 34)); + if ((glyph_index) < (numOfLongHorMetrics)) + { + if ((advanceWidth) != null) + *advanceWidth = (int)(ttSHORT(info.data + info.hmtx + 4 * glyph_index)); + if ((leftSideBearing) != null) + *leftSideBearing = (int)(ttSHORT(info.data + info.hmtx + 4 * glyph_index + 2)); + } + else + { + if ((advanceWidth) != null) + *advanceWidth = (int)(ttSHORT(info.data + info.hmtx + 4 * (numOfLongHorMetrics - 1))); + if ((leftSideBearing) != null) + *leftSideBearing = (int)(ttSHORT(info.data + info.hmtx + 4 * numOfLongHorMetrics + 2 * (glyph_index - numOfLongHorMetrics))); + } + + } + + public static int stbtt__GetGlyphKernInfoAdvance(stbtt_fontinfo info, int glyph1, int glyph2) + { + byte* data = info.data + info.kern; + uint needle; + uint straw; + int l; + int r; + int m; + if (info.kern == 0) + return (int)(0); + if ((ttUSHORT(data + 2)) < (1)) + return (int)(0); + if (ttUSHORT(data + 8) != 1) + return (int)(0); + l = (int)(0); + r = (int)(ttUSHORT(data + 10) - 1); + needle = (uint)(glyph1 << 16 | glyph2); + while (l <= r) + { + m = (int)((l + r) >> 1); + straw = (uint)(ttULONG(data + 18 + (m * 6))); + if ((needle) < (straw)) + r = (int)(m - 1); + else if ((needle) > (straw)) + l = (int)(m + 1); + else + return (int)(ttSHORT(data + 22 + (m * 6))); + } + return (int)(0); + } + + public static int stbtt__GetCoverageIndex(byte* coverageTable, int glyph) + { + ushort coverageFormat = (ushort)(ttUSHORT(coverageTable)); + switch (coverageFormat) + { + case 1: + { + ushort glyphCount = (ushort)(ttUSHORT(coverageTable + 2)); + int l = (int)(0); + int r = (int)(glyphCount - 1); + int m; + int straw; + int needle = (int)(glyph); + while (l <= r) + { + byte* glyphArray = coverageTable + 4; + ushort glyphID; + m = (int)((l + r) >> 1); + glyphID = (ushort)(ttUSHORT(glyphArray + 2 * m)); + straw = (int)(glyphID); + if ((needle) < (straw)) + r = (int)(m - 1); + else if ((needle) > (straw)) + l = (int)(m + 1); + else + { + return (int)(m); + } + } + } + break; + case 2: + { + ushort rangeCount = (ushort)(ttUSHORT(coverageTable + 2)); + byte* rangeArray = coverageTable + 4; + int l = (int)(0); + int r = (int)(rangeCount - 1); + int m; + int strawStart; + int strawEnd; + int needle = (int)(glyph); + while (l <= r) + { + byte* rangeRecord; + m = (int)((l + r) >> 1); + rangeRecord = rangeArray + 6 * m; + strawStart = (int)(ttUSHORT(rangeRecord)); + strawEnd = (int)(ttUSHORT(rangeRecord + 2)); + if ((needle) < (strawStart)) + r = (int)(m - 1); + else if ((needle) > (strawEnd)) + l = (int)(m + 1); + else + { + ushort startCoverageIndex = (ushort)(ttUSHORT(rangeRecord + 4)); + return (int)(startCoverageIndex + glyph - strawStart); + } + } + } + break; + default: + { + } + break; + } + + return (int)(-1); + } + + public static int stbtt__GetGlyphClass(byte* classDefTable, int glyph) + { + ushort classDefFormat = (ushort)(ttUSHORT(classDefTable)); + switch (classDefFormat) + { + case 1: + { + ushort startGlyphID = (ushort)(ttUSHORT(classDefTable + 2)); + ushort glyphCount = (ushort)(ttUSHORT(classDefTable + 4)); + byte* classDef1ValueArray = classDefTable + 6; + if (((glyph) >= (startGlyphID)) && ((glyph) < (startGlyphID + glyphCount))) + return (int)(ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID))); + classDefTable = classDef1ValueArray + 2 * glyphCount; + } + break; + case 2: + { + ushort classRangeCount = (ushort)(ttUSHORT(classDefTable + 2)); + byte* classRangeRecords = classDefTable + 4; + int l = (int)(0); + int r = (int)(classRangeCount - 1); + int m; + int strawStart; + int strawEnd; + int needle = (int)(glyph); + while (l <= r) + { + byte* classRangeRecord; + m = (int)((l + r) >> 1); + classRangeRecord = classRangeRecords + 6 * m; + strawStart = (int)(ttUSHORT(classRangeRecord)); + strawEnd = (int)(ttUSHORT(classRangeRecord + 2)); + if ((needle) < (strawStart)) + r = (int)(m - 1); + else if ((needle) > (strawEnd)) + l = (int)(m + 1); + else + return (int)(ttUSHORT(classRangeRecord + 4)); + } + classDefTable = classRangeRecords + 6 * classRangeCount; + } + break; + default: + { + } + break; + } + + return (int)(-1); + } + + public static int stbtt__GetGlyphGPOSInfoAdvance(stbtt_fontinfo info, int glyph1, int glyph2) + { + ushort lookupListOffset; + byte* lookupList; + ushort lookupCount; + byte* data; + int i; + if (info.gpos == 0) + return (int)(0); + data = info.data + info.gpos; + if (ttUSHORT(data + 0) != 1) + return (int)(0); + if (ttUSHORT(data + 2) != 0) + return (int)(0); + lookupListOffset = (ushort)(ttUSHORT(data + 8)); + lookupList = data + lookupListOffset; + lookupCount = (ushort)(ttUSHORT(lookupList)); + for (i = (int)(0); (i) < (lookupCount); ++i) + { + ushort lookupOffset = (ushort)(ttUSHORT(lookupList + 2 + 2 * i)); + byte* lookupTable = lookupList + lookupOffset; + ushort lookupType = (ushort)(ttUSHORT(lookupTable)); + ushort subTableCount = (ushort)(ttUSHORT(lookupTable + 4)); + byte* subTableOffsets = lookupTable + 6; + switch (lookupType) + { + case 2: + { + int sti; + for (sti = (int)(0); (sti) < (subTableCount); sti++) + { + ushort subtableOffset = (ushort)(ttUSHORT(subTableOffsets + 2 * sti)); + byte* table = lookupTable + subtableOffset; + ushort posFormat = (ushort)(ttUSHORT(table)); + ushort coverageOffset = (ushort)(ttUSHORT(table + 2)); + int coverageIndex = (int)(stbtt__GetCoverageIndex(table + coverageOffset, (int)(glyph1))); + if ((coverageIndex) == (-1)) + continue; + switch (posFormat) + { + case 1: + { + int l; + int r; + int m; + int straw; + int needle; + ushort valueFormat1 = (ushort)(ttUSHORT(table + 4)); + ushort valueFormat2 = (ushort)(ttUSHORT(table + 6)); + int valueRecordPairSizeInBytes = (int)(2); + ushort pairSetCount = (ushort)(ttUSHORT(table + 8)); + ushort pairPosOffset = (ushort)(ttUSHORT(table + 10 + 2 * coverageIndex)); + byte* pairValueTable = table + pairPosOffset; + ushort pairValueCount = (ushort)(ttUSHORT(pairValueTable)); + byte* pairValueArray = pairValueTable + 2; + if (valueFormat1 != 4) + return (int)(0); + if (valueFormat2 != 0) + return (int)(0); + needle = (int)(glyph2); + r = (int)(pairValueCount - 1); + l = (int)(0); + while (l <= r) + { + ushort secondGlyph; + byte* pairValue; + m = (int)((l + r) >> 1); + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = (ushort)(ttUSHORT(pairValue)); + straw = (int)(secondGlyph); + if ((needle) < (straw)) + r = (int)(m - 1); + else if ((needle) > (straw)) + l = (int)(m + 1); + else + { + short xAdvance = (short)(ttSHORT(pairValue + 2)); + return (int)(xAdvance); + } + } + } + break; + case 2: + { + ushort valueFormat1 = (ushort)(ttUSHORT(table + 4)); + ushort valueFormat2 = (ushort)(ttUSHORT(table + 6)); + ushort classDef1Offset = (ushort)(ttUSHORT(table + 8)); + ushort classDef2Offset = (ushort)(ttUSHORT(table + 10)); + int glyph1class = (int)(stbtt__GetGlyphClass(table + classDef1Offset, (int)(glyph1))); + int glyph2class = (int)(stbtt__GetGlyphClass(table + classDef2Offset, (int)(glyph2))); + ushort class1Count = (ushort)(ttUSHORT(table + 12)); + ushort class2Count = (ushort)(ttUSHORT(table + 14)); + if (valueFormat1 != 4) + return (int)(0); + if (valueFormat2 != 0) + return (int)(0); + if (((((glyph1class) >= (0)) && ((glyph1class) < (class1Count))) && ((glyph2class) >= (0))) && ((glyph2class) < (class2Count))) + { + byte* class1Records = table + 16; + byte* class2Records = class1Records + 2 * (glyph1class * class2Count); + short xAdvance = (short)(ttSHORT(class2Records + 2 * glyph2class)); + return (int)(xAdvance); + } + } + break; + default: + { + break; + } + } + } + break; + } + default: + break; + } + } + return (int)(0); + } + + public static int stbtt_GetGlyphKernAdvance(stbtt_fontinfo info, int g1, int g2) + { + int xAdvance = (int)(0); + if ((info.gpos) != 0) + xAdvance += (int)(stbtt__GetGlyphGPOSInfoAdvance(info, (int)(g1), (int)(g2))); + if ((info.kern) != 0) + xAdvance += (int)(stbtt__GetGlyphKernInfoAdvance(info, (int)(g1), (int)(g2))); + return (int)(xAdvance); + } + + public static int stbtt_GetCodepointKernAdvance(stbtt_fontinfo info, int ch1, int ch2) + { + if ((info.kern == 0) && (info.gpos == 0)) + return (int)(0); + return (int)(stbtt_GetGlyphKernAdvance(info, (int)(stbtt_FindGlyphIndex(info, (int)(ch1))), (int)(stbtt_FindGlyphIndex(info, (int)(ch2))))); + } + + public static void stbtt_GetCodepointHMetrics(stbtt_fontinfo info, int codepoint, int* advanceWidth, int* leftSideBearing) + { + stbtt_GetGlyphHMetrics(info, (int)(stbtt_FindGlyphIndex(info, (int)(codepoint))), advanceWidth, leftSideBearing); + } + + public static void stbtt_GetFontVMetrics(stbtt_fontinfo info, int* ascent, int* descent, int* lineGap) + { + if ((ascent) != null) + *ascent = (int)(ttSHORT(info.data + info.hhea + 4)); + if ((descent) != null) + *descent = (int)(ttSHORT(info.data + info.hhea + 6)); + if ((lineGap) != null) + *lineGap = (int)(ttSHORT(info.data + info.hhea + 8)); + } + + public static int stbtt_GetFontVMetricsOS2(stbtt_fontinfo info, int* typoAscent, int* typoDescent, int* typoLineGap) + { + int tab = (int)(stbtt__find_table(info.data, (uint)(info.fontstart), "OS/2")); + if (tab == 0) + return (int)(0); + if ((typoAscent) != null) + *typoAscent = (int)(ttSHORT(info.data + tab + 68)); + if ((typoDescent) != null) + *typoDescent = (int)(ttSHORT(info.data + tab + 70)); + if ((typoLineGap) != null) + *typoLineGap = (int)(ttSHORT(info.data + tab + 72)); + return (int)(1); + } + + public static void stbtt_GetFontBoundingBox(stbtt_fontinfo info, int* x0, int* y0, int* x1, int* y1) + { + *x0 = (int)(ttSHORT(info.data + info.head + 36)); + *y0 = (int)(ttSHORT(info.data + info.head + 38)); + *x1 = (int)(ttSHORT(info.data + info.head + 40)); + *y1 = (int)(ttSHORT(info.data + info.head + 42)); + } + + public static float stbtt_ScaleForPixelHeight(stbtt_fontinfo info, float height) + { + int fheight = (int)(ttSHORT(info.data + info.hhea + 4) - ttSHORT(info.data + info.hhea + 6)); + return (float)(height / fheight); + } + + public static float stbtt_ScaleForMappingEmToPixels(stbtt_fontinfo info, float pixels) + { + int unitsPerEm = (int)(ttUSHORT(info.data + info.head + 18)); + return (float)(pixels / unitsPerEm); + } + + public static void stbtt_FreeShape(stbtt_fontinfo info, stbtt_vertex* v) + { + CRuntime.free(v); + } + + public static void stbtt_GetGlyphBitmapBoxSubpixel(stbtt_fontinfo font, int glyph, float scale_x, float scale_y, float shift_x, float shift_y, int* ix0, int* iy0, int* ix1, int* iy1) + { + int x0 = (int)(0); + int y0 = (int)(0); + int x1; + int y1; + if (stbtt_GetGlyphBox(font, (int)(glyph), &x0, &y0, &x1, &y1) == 0) + { + if ((ix0) != null) + *ix0 = (int)(0); + if ((iy0) != null) + *iy0 = (int)(0); + if ((ix1) != null) + *ix1 = (int)(0); + if ((iy1) != null) + *iy1 = (int)(0); + } + else + { + if ((ix0) != null) + *ix0 = ((int)(CRuntime.floor((double)(x0 * scale_x + shift_x)))); + if ((iy0) != null) + *iy0 = ((int)(CRuntime.floor((double)(-y1 * scale_y + shift_y)))); + if ((ix1) != null) + *ix1 = ((int)(CRuntime.ceil((double)(x1 * scale_x + shift_x)))); + if ((iy1) != null) + *iy1 = ((int)(CRuntime.ceil((double)(-y0 * scale_y + shift_y)))); + } + + } + + public static void stbtt_GetGlyphBitmapBox(stbtt_fontinfo font, int glyph, float scale_x, float scale_y, int* ix0, int* iy0, int* ix1, int* iy1) + { + stbtt_GetGlyphBitmapBoxSubpixel(font, (int)(glyph), (float)(scale_x), (float)(scale_y), (float)(0.0f), (float)(0.0f), ix0, iy0, ix1, iy1); + } + + public static void stbtt_GetCodepointBitmapBoxSubpixel(stbtt_fontinfo font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int* ix0, int* iy0, int* ix1, int* iy1) + { + stbtt_GetGlyphBitmapBoxSubpixel(font, (int)(stbtt_FindGlyphIndex(font, (int)(codepoint))), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), ix0, iy0, ix1, iy1); + } + + public static void stbtt_GetCodepointBitmapBox(stbtt_fontinfo font, int codepoint, float scale_x, float scale_y, int* ix0, int* iy0, int* ix1, int* iy1) + { + stbtt_GetCodepointBitmapBoxSubpixel(font, (int)(codepoint), (float)(scale_x), (float)(scale_y), (float)(0.0f), (float)(0.0f), ix0, iy0, ix1, iy1); + } + + public static void* stbtt__hheap_alloc(stbtt__hheap* hh, ulong size, object userdata) + { + if ((hh->first_free) != null) + { + void* p = hh->first_free; + hh->first_free = *(void**)(p); + return p; + } + else + { + if ((hh->num_remaining_in_head_chunk) == (0)) + { + int count = (int)((size) < (32) ? 2000 : (size) < (128) ? 800 : 100); + stbtt__hheap_chunk* c = (stbtt__hheap_chunk*)(CRuntime.malloc((ulong)((ulong)sizeof(stbtt__hheap_chunk) + size * (ulong)(count)))); + if ((c) == (null)) + return (null); + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = (int)(count); + } + --hh->num_remaining_in_head_chunk; + return (sbyte*)(hh->head) + sizeof(stbtt__hheap_chunk) + size * (ulong)hh->num_remaining_in_head_chunk; + } + + } + + public static void stbtt__hheap_free(stbtt__hheap* hh, void* p) + { + *(void**)(p) = hh->first_free; + hh->first_free = p; + } + + public static void stbtt__hheap_cleanup(stbtt__hheap* hh, object userdata) + { + stbtt__hheap_chunk* c = hh->head; + while ((c) != null) + { + stbtt__hheap_chunk* n = c->next; + CRuntime.free(c); + c = n; + } + } + + public static stbtt__active_edge* stbtt__new_active(stbtt__hheap* hh, stbtt__edge* e, int off_x, float start_point, object userdata) + { + stbtt__active_edge* z = (stbtt__active_edge*)(stbtt__hheap_alloc(hh, (ulong)(sizeof(stbtt__active_edge)), userdata)); + float dxdy = (float)((e->x1 - e->x0) / (e->y1 - e->y0)); + if (z == null) + return z; + z->fdx = (float)(dxdy); + z->fdy = (float)(dxdy != 0.0f ? (1.0f / dxdy) : 0.0f); + z->fx = (float)(e->x0 + dxdy * (start_point - e->y0)); + z->fx -= (float)(off_x); + z->direction = (float)((e->invert) != 0 ? 1.0f : -1.0f); + z->sy = (float)(e->y0); + z->ey = (float)(e->y1); + z->next = null; + return z; + } + + public static void stbtt__handle_clipped_edge(float* scanline, int x, stbtt__active_edge* e, float x0, float y0, float x1, float y1) + { + if ((y0) == (y1)) + return; + if ((y0) > (e->ey)) + return; + if ((y1) < (e->sy)) + return; + if ((y0) < (e->sy)) + { + x0 += (float)((x1 - x0) * (e->sy - y0) / (y1 - y0)); + y0 = (float)(e->sy); + } + + if ((y1) > (e->ey)) + { + x1 += (float)((x1 - x0) * (e->ey - y1) / (y1 - y0)); + y1 = (float)(e->ey); + } + + if ((x0) == (x)) + { + } + else if ((x0) == (x + 1)) + { + } + else if (x0 <= x) + { + } + else if ((x0) >= (x + 1)) + { + } + else + { + } + + if ((x0 <= x) && (x1 <= x)) + { + scanline[x] += (float)(e->direction * (y1 - y0)); + } + else if (((x0) >= (x + 1)) && ((x1) >= (x + 1))) + { + } + else + { + scanline[x] += (float)(e->direction * (y1 - y0) * (1 - ((x0 - x) + (x1 - x)) / 2)); + } + + } + + public static void stbtt__fill_active_edges_new(float* scanline, float* scanline_fill, int len, stbtt__active_edge* e, float y_top) + { + float y_bottom = (float)(y_top + 1); + while ((e) != null) + { + if ((e->fdx) == (0)) + { + float x0 = (float)(e->fx); + if ((x0) < (len)) + { + if ((x0) >= (0)) + { + stbtt__handle_clipped_edge(scanline, (int)(x0), e, (float)(x0), (float)(y_top), (float)(x0), (float)(y_bottom)); + stbtt__handle_clipped_edge(scanline_fill - 1, (int)((int)(x0) + 1), e, (float)(x0), (float)(y_top), (float)(x0), (float)(y_bottom)); + } + else + { + stbtt__handle_clipped_edge(scanline_fill - 1, (int)(0), e, (float)(x0), (float)(y_top), (float)(x0), (float)(y_bottom)); + } + } + } + else + { + float x0 = (float)(e->fx); + float dx = (float)(e->fdx); + float xb = (float)(x0 + dx); + float x_top; + float x_bottom; + float sy0; + float sy1; + float dy = (float)(e->fdy); + if ((e->sy) > (y_top)) + { + x_top = (float)(x0 + dx * (e->sy - y_top)); + sy0 = (float)(e->sy); + } + else + { + x_top = (float)(x0); + sy0 = (float)(y_top); + } + if ((e->ey) < (y_bottom)) + { + x_bottom = (float)(x0 + dx * (e->ey - y_top)); + sy1 = (float)(e->ey); + } + else + { + x_bottom = (float)(xb); + sy1 = (float)(y_bottom); + } + if (((((x_top) >= (0)) && ((x_bottom) >= (0))) && ((x_top) < (len))) && ((x_bottom) < (len))) + { + if (((int)(x_top)) == ((int)(x_bottom))) + { + float height; + int x = (int)(x_top); + height = (float)(sy1 - sy0); + scanline[x] += (float)(e->direction * (1 - ((x_top - x) + (x_bottom - x)) / 2) * height); + scanline_fill[x] += (float)(e->direction * height); + } + else + { + int x; + int x1; + int x2; + float y_crossing; + float step; + float sign; + float area; + if ((x_top) > (x_bottom)) + { + float t; + sy0 = (float)(y_bottom - (sy0 - y_top)); + sy1 = (float)(y_bottom - (sy1 - y_top)); + t = (float)(sy0); + sy0 = (float)(sy1); + sy1 = (float)(t); + t = (float)(x_bottom); + x_bottom = (float)(x_top); + x_top = (float)(t); + dx = (float)(-dx); + dy = (float)(-dy); + t = (float)(x0); + x0 = (float)(xb); + xb = (float)(t); + } + x1 = ((int)(x_top)); + x2 = ((int)(x_bottom)); + y_crossing = (float)((x1 + 1 - x0) * dy + y_top); + sign = (float)(e->direction); + area = (float)(sign * (y_crossing - sy0)); + scanline[x1] += (float)(area * (1 - ((x_top - x1) + (x1 + 1 - x1)) / 2)); + step = (float)(sign * dy); + for (x = (int)(x1 + 1); (x) < (x2); ++x) + { + scanline[x] += (float)(area + step / 2); + area += (float)(step); + } + y_crossing += (float)(dy * (x2 - (x1 + 1))); + scanline[x2] += (float)(area + sign * (1 - ((x2 - x2) + (x_bottom - x2)) / 2) * (sy1 - y_crossing)); + scanline_fill[x2] += (float)(sign * (sy1 - sy0)); + } + } + else + { + int x; + for (x = (int)(0); (x) < (len); ++x) + { + float y0 = (float)(y_top); + float x1 = (float)(x); + float x2 = (float)(x + 1); + float x3 = (float)(xb); + float y3 = (float)(y_bottom); + float y1 = (float)((x - x0) / dx + y_top); + float y2 = (float)((x + 1 - x0) / dx + y_top); + if (((x0) < (x1)) && ((x3) > (x2))) + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x1), (float)(y1)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x1), (float)(y1), (float)(x2), (float)(y2)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x2), (float)(y2), (float)(x3), (float)(y3)); + } + else if (((x3) < (x1)) && ((x0) > (x2))) + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x2), (float)(y2)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x2), (float)(y2), (float)(x1), (float)(y1)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x1), (float)(y1), (float)(x3), (float)(y3)); + } + else if (((x0) < (x1)) && ((x3) > (x1))) + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x1), (float)(y1)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x1), (float)(y1), (float)(x3), (float)(y3)); + } + else if (((x3) < (x1)) && ((x0) > (x1))) + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x1), (float)(y1)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x1), (float)(y1), (float)(x3), (float)(y3)); + } + else if (((x0) < (x2)) && ((x3) > (x2))) + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x2), (float)(y2)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x2), (float)(y2), (float)(x3), (float)(y3)); + } + else if (((x3) < (x2)) && ((x0) > (x2))) + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x2), (float)(y2)); + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x2), (float)(y2), (float)(x3), (float)(y3)); + } + else + { + stbtt__handle_clipped_edge(scanline, (int)(x), e, (float)(x0), (float)(y0), (float)(x3), (float)(y3)); + } + } + } + } + e = e->next; + } + } + + public static void stbtt__rasterize_sorted_edges(stbtt__bitmap* result, stbtt__edge* e, int n, int vsubsample, int off_x, int off_y, object userdata) + { + stbtt__hheap hh; + stbtt__active_edge* active = (null); + int y; + int j = (int)(0); + int i; + float* scanline_data = stackalloc float[129]; + float* scanline; + float* scanline2; + if ((result->w) > (64)) + scanline = (float*)(CRuntime.malloc((ulong)((result->w * 2 + 1) * sizeof(float)))); + else + scanline = scanline_data; + scanline2 = scanline + result->w; + y = (int)(off_y); + e[n].y0 = (float)((float)(off_y + result->h) + 1); + while ((j) < (result->h)) + { + float scan_y_top = (float)(y + 0.0f); + float scan_y_bottom = (float)(y + 1.0f); + stbtt__active_edge** step = &active; + CRuntime.memset(scanline, (int)(0), (ulong)(result->w * sizeof(float))); + CRuntime.memset(scanline2, (int)(0), (ulong)((result->w + 1) * sizeof(float))); + while ((*step) != null) + { + stbtt__active_edge* z = *step; + if (z->ey <= scan_y_top) + { + *step = z->next; + z->direction = (float)(0); + stbtt__hheap_free(&hh, z); + } + else + { + step = &((*step)->next); + } + } while (e->y0 <= scan_y_bottom) + { + if (e->y0 != e->y1) + { + stbtt__active_edge* z = stbtt__new_active(&hh, e, (int)(off_x), (float)(scan_y_top), userdata); + if (z != (null)) + { + z->next = active; + active = z; + } + } + ++e; + } + if ((active) != null) + stbtt__fill_active_edges_new(scanline, scanline2 + 1, (int)(result->w), active, (float)(scan_y_top)); + { + float sum = (float)(0); + for (i = (int)(0); (i) < (result->w); ++i) + { + float k; + int m; + sum += (float)(scanline2[i]); + k = (float)(scanline[i] + sum); + k = (float)((float)(CRuntime.fabs((double)(k))) * 255 + 0.5f); + m = ((int)(k)); + if ((m) > (255)) + m = (int)(255); + result->pixels[j * result->stride + i] = ((byte)(m)); + } + } + step = &active; + while ((*step) != null) + { + stbtt__active_edge* z = *step; + z->fx += (float)(z->fdx); + step = &((*step)->next); + } + ++y; + ++j; + } + stbtt__hheap_cleanup(&hh, userdata); + if (scanline != scanline_data) + CRuntime.free(scanline); + } + + public static void stbtt__sort_edges_ins_sort(stbtt__edge* p, int n) + { + int i; + int j; + for (i = (int)(1); (i) < (n); ++i) + { + stbtt__edge t = (stbtt__edge)(p[i]); + stbtt__edge* a = &t; + j = (int)(i); + while ((j) > (0)) + { + stbtt__edge* b = &p[j - 1]; + int c = (int)(a->y0 < b->y0 ? 1 : 0); + if (c == 0) + break; + p[j] = (stbtt__edge)(p[j - 1]); + --j; + } + if (i != j) + p[j] = (stbtt__edge)(t); + } + } + + public static void stbtt__sort_edges_quicksort(stbtt__edge* p, int n) + { + while ((n) > (12)) + { + stbtt__edge t = new stbtt__edge(); + int c01; + int c12; + int c; + int m; + int i; + int j; + m = (int)(n >> 1); + c01 = (int)(((&p[0])->y0) < ((&p[m])->y0) ? 1 : 0); + c12 = (int)(((&p[m])->y0) < ((&p[n - 1])->y0) ? 1 : 0); + if (c01 != c12) + { + int z; + c = (int)(((&p[0])->y0) < ((&p[n - 1])->y0) ? 1 : 0); + z = (int)(((c) == (c12)) ? 0 : n - 1); + t = (stbtt__edge)(p[z]); + p[z] = (stbtt__edge)(p[m]); + p[m] = (stbtt__edge)(t); + } + t = (stbtt__edge)(p[0]); + p[0] = (stbtt__edge)(p[m]); + p[m] = (stbtt__edge)(t); + i = (int)(1); + j = (int)(n - 1); + for (; ; ) + { + for (i = (int)(i); ; ++i) + { + if (!(((&p[i])->y0) < ((&p[0])->y0))) + break; + } + for (j = (int)(j); ; --j) + { + if (!(((&p[0])->y0) < ((&p[j])->y0))) + break; + } + if ((i) >= (j)) + break; + t = (stbtt__edge)(p[i]); + p[i] = (stbtt__edge)(p[j]); + p[j] = (stbtt__edge)(t); + ++i; + --j; + } + if ((j) < (n - i)) + { + stbtt__sort_edges_quicksort(p, (int)(j)); + p = p + i; + n = (int)(n - i); + } + else + { + stbtt__sort_edges_quicksort(p + i, (int)(n - i)); + n = (int)(j); + } + } + } + + public static void stbtt__sort_edges(stbtt__edge* p, int n) + { + stbtt__sort_edges_quicksort(p, (int)(n)); + stbtt__sort_edges_ins_sort(p, (int)(n)); + } + + public static void stbtt__rasterize(stbtt__bitmap* result, stbtt__point* pts, int* wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, object userdata) + { + float y_scale_inv = (float)((invert) != 0 ? -scale_y : scale_y); + stbtt__edge* e; + int n; + int i; + int j; + int k; + int m; + int vsubsample = (int)(1); + n = (int)(0); + for (i = (int)(0); (i) < (windings); ++i) + { + n += (int)(wcount[i]); + } + e = (stbtt__edge*)(CRuntime.malloc((ulong)(sizeof(stbtt__edge) * (n + 1)))); + if ((e) == (null)) + return; + n = (int)(0); + m = (int)(0); + for (i = (int)(0); (i) < (windings); ++i) + { + stbtt__point* p = pts + m; + m += (int)(wcount[i]); + j = (int)(wcount[i] - 1); + for (k = (int)(0); (k) < (wcount[i]); j = (int)(k++)) + { + int a = (int)(k); + int b = (int)(j); + if ((p[j].y) == (p[k].y)) + continue; + e[n].invert = (int)(0); + if ((((invert) != 0) && ((p[j].y) > (p[k].y))) || ((invert == 0) && ((p[j].y) < (p[k].y)))) + { + e[n].invert = (int)(1); + a = (int)(j); + b = (int)(k); + } + e[n].x0 = (float)(p[a].x * scale_x + shift_x); + e[n].y0 = (float)((p[a].y * y_scale_inv + shift_y) * vsubsample); + e[n].x1 = (float)(p[b].x * scale_x + shift_x); + e[n].y1 = (float)((p[b].y * y_scale_inv + shift_y) * vsubsample); + ++n; + } + } + stbtt__sort_edges(e, (int)(n)); + stbtt__rasterize_sorted_edges(result, e, (int)(n), (int)(vsubsample), (int)(off_x), (int)(off_y), userdata); + CRuntime.free(e); + } + + public static void stbtt__add_point(stbtt__point* points, int n, float x, float y) + { + if (points == null) + return; + points[n].x = (float)(x); + points[n].y = (float)(y); + } + + public static int stbtt__tesselate_curve(stbtt__point* points, int* num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) + { + float mx = (float)((x0 + 2 * x1 + x2) / 4); + float my = (float)((y0 + 2 * y1 + y2) / 4); + float dx = (float)((x0 + x2) / 2 - mx); + float dy = (float)((y0 + y2) / 2 - my); + if ((n) > (16)) + return (int)(1); + if ((dx * dx + dy * dy) > (objspace_flatness_squared)) + { + stbtt__tesselate_curve(points, num_points, (float)(x0), (float)(y0), (float)((x0 + x1) / 2.0f), (float)((y0 + y1) / 2.0f), (float)(mx), (float)(my), (float)(objspace_flatness_squared), (int)(n + 1)); + stbtt__tesselate_curve(points, num_points, (float)(mx), (float)(my), (float)((x1 + x2) / 2.0f), (float)((y1 + y2) / 2.0f), (float)(x2), (float)(y2), (float)(objspace_flatness_squared), (int)(n + 1)); + } + else + { + stbtt__add_point(points, (int)(*num_points), (float)(x2), (float)(y2)); + *num_points = (int)(*num_points + 1); + } + + return (int)(1); + } + + public static void stbtt__tesselate_cubic(stbtt__point* points, int* num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) + { + float dx0 = (float)(x1 - x0); + float dy0 = (float)(y1 - y0); + float dx1 = (float)(x2 - x1); + float dy1 = (float)(y2 - y1); + float dx2 = (float)(x3 - x2); + float dy2 = (float)(y3 - y2); + float dx = (float)(x3 - x0); + float dy = (float)(y3 - y0); + float longlen = (float)(CRuntime.sqrt((double)(dx0 * dx0 + dy0 * dy0)) + CRuntime.sqrt((double)(dx1 * dx1 + dy1 * dy1)) + CRuntime.sqrt((double)(dx2 * dx2 + dy2 * dy2))); + float shortlen = (float)(CRuntime.sqrt((double)(dx * dx + dy * dy))); + float flatness_squared = (float)(longlen * longlen - shortlen * shortlen); + if ((n) > (16)) + return; + if ((flatness_squared) > (objspace_flatness_squared)) + { + float x01 = (float)((x0 + x1) / 2); + float y01 = (float)((y0 + y1) / 2); + float x12 = (float)((x1 + x2) / 2); + float y12 = (float)((y1 + y2) / 2); + float x23 = (float)((x2 + x3) / 2); + float y23 = (float)((y2 + y3) / 2); + float xa = (float)((x01 + x12) / 2); + float ya = (float)((y01 + y12) / 2); + float xb = (float)((x12 + x23) / 2); + float yb = (float)((y12 + y23) / 2); + float mx = (float)((xa + xb) / 2); + float my = (float)((ya + yb) / 2); + stbtt__tesselate_cubic(points, num_points, (float)(x0), (float)(y0), (float)(x01), (float)(y01), (float)(xa), (float)(ya), (float)(mx), (float)(my), (float)(objspace_flatness_squared), (int)(n + 1)); + stbtt__tesselate_cubic(points, num_points, (float)(mx), (float)(my), (float)(xb), (float)(yb), (float)(x23), (float)(y23), (float)(x3), (float)(y3), (float)(objspace_flatness_squared), (int)(n + 1)); + } + else + { + stbtt__add_point(points, (int)(*num_points), (float)(x3), (float)(y3)); + *num_points = (int)(*num_points + 1); + } + + } + + public static stbtt__point* stbtt_FlattenCurves(stbtt_vertex* vertices, int num_verts, float objspace_flatness, int** contour_lengths, int* num_contours, object userdata) + { + stbtt__point* points = null; + int num_points = (int)(0); + float objspace_flatness_squared = (float)(objspace_flatness * objspace_flatness); + int i; + int n = (int)(0); + int start = (int)(0); + int pass; + for (i = (int)(0); (i) < (num_verts); ++i) + { + if ((vertices[i].type) == (STBTT_vmove)) + ++n; + } + *num_contours = (int)(n); + if ((n) == (0)) + return null; + *contour_lengths = (int*)(CRuntime.malloc((ulong)(sizeof(int) * n))); + if ((*contour_lengths) == (null)) + { + *num_contours = (int)(0); + return null; + } + + for (pass = (int)(0); (pass) < (2); ++pass) + { + float x = (float)(0); + float y = (float)(0); + if ((pass) == (1)) + { + points = (stbtt__point*)(CRuntime.malloc((ulong)(num_points * sizeof(stbtt__point)))); + if ((points) == (null)) + goto error; + } + num_points = (int)(0); + n = (int)(-1); + for (i = (int)(0); (i) < (num_verts); ++i) + { + switch (vertices[i].type) + { + case STBTT_vmove: + if ((n) >= (0)) + (*contour_lengths)[n] = (int)(num_points - start); + ++n; + start = (int)(num_points); + x = (float)(vertices[i].x); + y = (float)(vertices[i].y); + stbtt__add_point(points, (int)(num_points++), (float)(x), (float)(y)); + break; + case STBTT_vline: + x = (float)(vertices[i].x); + y = (float)(vertices[i].y); + stbtt__add_point(points, (int)(num_points++), (float)(x), (float)(y)); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, (float)(x), (float)(y), (float)(vertices[i].cx), (float)(vertices[i].cy), (float)(vertices[i].x), (float)(vertices[i].y), (float)(objspace_flatness_squared), (int)(0)); + x = (float)(vertices[i].x); + y = (float)(vertices[i].y); + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, (float)(x), (float)(y), (float)(vertices[i].cx), (float)(vertices[i].cy), (float)(vertices[i].cx1), (float)(vertices[i].cy1), (float)(vertices[i].x), (float)(vertices[i].y), (float)(objspace_flatness_squared), (int)(0)); + x = (float)(vertices[i].x); + y = (float)(vertices[i].y); + break; + } + } (*contour_lengths)[n] = (int)(num_points - start); + } + return points; + error: + ; + CRuntime.free(points); + CRuntime.free(*contour_lengths); + *contour_lengths = null; + *num_contours = (int)(0); + return (null); + } + + public static void stbtt_Rasterize(stbtt__bitmap* result, float flatness_in_pixels, stbtt_vertex* vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, object userdata) + { + float scale = (float)((scale_x) > (scale_y) ? scale_y : scale_x); + int winding_count = (int)(0); + int* winding_lengths = (null); + stbtt__point* windings = stbtt_FlattenCurves(vertices, (int)(num_verts), (float)(flatness_in_pixels / scale), &winding_lengths, &winding_count, userdata); + if ((windings) != null) + { + stbtt__rasterize(result, windings, winding_lengths, (int)(winding_count), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(x_off), (int)(y_off), (int)(invert), userdata); + CRuntime.free(winding_lengths); + CRuntime.free(windings); + } + + } + + public static void stbtt_FreeBitmap(byte* bitmap, object userdata) + { + CRuntime.free(bitmap); + } + + public static byte* stbtt_GetGlyphBitmapSubpixel(stbtt_fontinfo info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int* width, int* height, int* xoff, int* yoff) + { + int ix0; + int iy0; + int ix1; + int iy1; + stbtt__bitmap gbm = new stbtt__bitmap(); + stbtt_vertex* vertices; + int num_verts = (int)(stbtt_GetGlyphShape(info, (int)(glyph), &vertices)); + if ((scale_x) == (0)) + scale_x = (float)(scale_y); + if ((scale_y) == (0)) + { + if ((scale_x) == (0)) + { + CRuntime.free(vertices); + return (null); + } + scale_y = (float)(scale_x); + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, (int)(glyph), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), &ix0, &iy0, &ix1, &iy1); + gbm.w = (int)(ix1 - ix0); + gbm.h = (int)(iy1 - iy0); + gbm.pixels = (null); + if ((width) != null) + *width = (int)(gbm.w); + if ((height) != null) + *height = (int)(gbm.h); + if ((xoff) != null) + *xoff = (int)(ix0); + if ((yoff) != null) + *yoff = (int)(iy0); + if (((gbm.w) != 0) && ((gbm.h) != 0)) + { + gbm.pixels = (byte*)(CRuntime.malloc((ulong)(gbm.w * gbm.h))); + if ((gbm.pixels) != null) + { + gbm.stride = (int)(gbm.w); + stbtt_Rasterize(&gbm, (float)(0.35f), vertices, (int)(num_verts), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(ix0), (int)(iy0), (int)(1), info.userdata); + } + } + + CRuntime.free(vertices); + return gbm.pixels; + } + + public static byte* stbtt_GetGlyphBitmap(stbtt_fontinfo info, float scale_x, float scale_y, int glyph, int* width, int* height, int* xoff, int* yoff) + { + return stbtt_GetGlyphBitmapSubpixel(info, (float)(scale_x), (float)(scale_y), (float)(0.0f), (float)(0.0f), (int)(glyph), width, height, xoff, yoff); + } + + public static void stbtt_MakeGlyphBitmapSubpixel(stbtt_fontinfo info, byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) + { + int ix0; + int iy0; + stbtt_vertex* vertices; + int num_verts = (int)(stbtt_GetGlyphShape(info, (int)(glyph), &vertices)); + stbtt__bitmap gbm = new stbtt__bitmap(); + stbtt_GetGlyphBitmapBoxSubpixel(info, (int)(glyph), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), &ix0, &iy0, null, null); + gbm.pixels = output; + gbm.w = (int)(out_w); + gbm.h = (int)(out_h); + gbm.stride = (int)(out_stride); + if (((gbm.w) != 0) && ((gbm.h) != 0)) + stbtt_Rasterize(&gbm, (float)(0.35f), vertices, (int)(num_verts), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(ix0), (int)(iy0), (int)(1), info.userdata); + CRuntime.free(vertices); + } + + public static void stbtt_MakeGlyphBitmap(stbtt_fontinfo info, byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) + { + stbtt_MakeGlyphBitmapSubpixel(info, output, (int)(out_w), (int)(out_h), (int)(out_stride), (float)(scale_x), (float)(scale_y), (float)(0.0f), (float)(0.0f), (int)(glyph)); + } + + public static byte* stbtt_GetCodepointBitmapSubpixel(stbtt_fontinfo info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int* width, int* height, int* xoff, int* yoff) + { + return stbtt_GetGlyphBitmapSubpixel(info, (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(stbtt_FindGlyphIndex(info, (int)(codepoint))), width, height, xoff, yoff); + } + + public static void stbtt_MakeCodepointBitmapSubpixelPrefilter(stbtt_fontinfo info, byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float* sub_x, float* sub_y, int codepoint) + { + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, (int)(out_w), (int)(out_h), (int)(out_stride), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(oversample_x), (int)(oversample_y), sub_x, sub_y, (int)(stbtt_FindGlyphIndex(info, (int)(codepoint)))); + } + + public static void stbtt_MakeCodepointBitmapSubpixel(stbtt_fontinfo info, byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) + { + stbtt_MakeGlyphBitmapSubpixel(info, output, (int)(out_w), (int)(out_h), (int)(out_stride), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(stbtt_FindGlyphIndex(info, (int)(codepoint)))); + } + + public static byte* stbtt_GetCodepointBitmap(stbtt_fontinfo info, float scale_x, float scale_y, int codepoint, int* width, int* height, int* xoff, int* yoff) + { + return stbtt_GetCodepointBitmapSubpixel(info, (float)(scale_x), (float)(scale_y), (float)(0.0f), (float)(0.0f), (int)(codepoint), width, height, xoff, yoff); + } + + public static void stbtt_MakeCodepointBitmap(stbtt_fontinfo info, byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) + { + stbtt_MakeCodepointBitmapSubpixel(info, output, (int)(out_w), (int)(out_h), (int)(out_stride), (float)(scale_x), (float)(scale_y), (float)(0.0f), (float)(0.0f), (int)(codepoint)); + } + + public static int stbtt_BakeFontBitmap_internal(byte* data, int offset, float pixel_height, byte* pixels, int pw, int ph, int first_char, int num_chars, stbtt_bakedchar* chardata) + { + float scale; + int x; + int y; + int bottom_y; + int i; + stbtt_fontinfo f = new stbtt_fontinfo(); + f.userdata = (null); + if (stbtt_InitFont(f, data, (int)(offset)) == 0) + return (int)(-1); + CRuntime.memset(pixels, (int)(0), (ulong)(pw * ph)); + x = (int)(y = (int)(1)); + bottom_y = (int)(1); + scale = (float)(stbtt_ScaleForPixelHeight(f, (float)(pixel_height))); + for (i = (int)(0); (i) < (num_chars); ++i) + { + int advance; + int lsb; + int x0; + int y0; + int x1; + int y1; + int gw; + int gh; + int g = (int)(stbtt_FindGlyphIndex(f, (int)(first_char + i))); + stbtt_GetGlyphHMetrics(f, (int)(g), &advance, &lsb); + stbtt_GetGlyphBitmapBox(f, (int)(g), (float)(scale), (float)(scale), &x0, &y0, &x1, &y1); + gw = (int)(x1 - x0); + gh = (int)(y1 - y0); + if ((x + gw + 1) >= (pw)) + { + y = (int)(bottom_y); + x = (int)(1); + } + if ((y + gh + 1) >= (ph)) + return (int)(-i); + stbtt_MakeGlyphBitmap(f, pixels + x + y * pw, (int)(gw), (int)(gh), (int)(pw), (float)(scale), (float)(scale), (int)(g)); + chardata[i].x0 = (ushort)((short)(x)); + chardata[i].y0 = (ushort)((short)(y)); + chardata[i].x1 = (ushort)((short)(x + gw)); + chardata[i].y1 = (ushort)((short)(y + gh)); + chardata[i].xadvance = (float)(scale * advance); + chardata[i].xoff = ((float)(x0)); + chardata[i].yoff = ((float)(y0)); + x = (int)(x + gw + 1); + if ((y + gh + 1) > (bottom_y)) + bottom_y = (int)(y + gh + 1); + } + return (int)(bottom_y); + } + + public static void stbtt_GetBakedQuad(stbtt_bakedchar* chardata, int pw, int ph, int char_index, float* xpos, float* ypos, stbtt_aligned_quad* q, int opengl_fillrule) + { + float d3d_bias = (float)((opengl_fillrule) != 0 ? 0 : -0.5f); + float ipw = (float)(1.0f / pw); + float iph = (float)(1.0f / ph); + stbtt_bakedchar* b = chardata + char_index; + int round_x = ((int)(CRuntime.floor((double)((*xpos + b->xoff) + 0.5f)))); + int round_y = ((int)(CRuntime.floor((double)((*ypos + b->yoff) + 0.5f)))); + q->x0 = (float)(round_x + d3d_bias); + q->y0 = (float)(round_y + d3d_bias); + q->x1 = (float)(round_x + b->x1 - b->x0 + d3d_bias); + q->y1 = (float)(round_y + b->y1 - b->y0 + d3d_bias); + q->s0 = (float)(b->x0 * ipw); + q->t0 = (float)(b->y0 * iph); + q->s1 = (float)(b->x1 * ipw); + q->t1 = (float)(b->y1 * iph); + *xpos += (float)(b->xadvance); + } + + public static void stbrp_init_target(stbrp_context* con, int pw, int ph, stbrp_node* nodes, int num_nodes) + { + con->width = (int)(pw); + con->height = (int)(ph); + con->x = (int)(0); + con->y = (int)(0); + con->bottom_y = (int)(0); + } + + public static void stbrp_pack_rects(stbrp_context* con, stbrp_rect* rects, int num_rects) + { + int i; + for (i = (int)(0); (i) < (num_rects); ++i) + { + if ((con->x + rects[i].w) > (con->width)) + { + con->x = (int)(0); + con->y = (int)(con->bottom_y); + } + if ((con->y + rects[i].h) > (con->height)) + break; + rects[i].x = (int)(con->x); + rects[i].y = (int)(con->y); + rects[i].was_packed = (int)(1); + con->x += (int)(rects[i].w); + if ((con->y + rects[i].h) > (con->bottom_y)) + con->bottom_y = (int)(con->y + rects[i].h); + } + for (; (i) < (num_rects); ++i) + { + rects[i].was_packed = (int)(0); + } + } + + public static int stbtt_PackBegin(stbtt_pack_context spc, byte* pixels, int pw, int ph, int stride_in_bytes, int padding, object alloc_context) + { + stbrp_context* context = (stbrp_context*)(CRuntime.malloc((ulong)(sizeof(stbrp_context)))); + int num_nodes = (int)(pw - padding); + stbrp_node* nodes = (stbrp_node*)(CRuntime.malloc((ulong)(sizeof(stbrp_node) * num_nodes))); + if (((context) == (null)) || ((nodes) == (null))) + { + if (context != (null)) + CRuntime.free(context); + if (nodes != (null)) + CRuntime.free(nodes); + return (int)(0); + } + + spc.user_allocator_context = alloc_context; + spc.width = (int)(pw); + spc.height = (int)(ph); + spc.pixels = pixels; + spc.pack_info = context; + spc.nodes = nodes; + spc.padding = (int)(padding); + spc.stride_in_bytes = (int)(stride_in_bytes != 0 ? stride_in_bytes : pw); + spc.h_oversample = (uint)(1); + spc.v_oversample = (uint)(1); + stbrp_init_target(context, (int)(pw - padding), (int)(ph - padding), nodes, (int)(num_nodes)); + if ((pixels) != null) + CRuntime.memset(pixels, (int)(0), (ulong)(pw * ph)); + return (int)(1); + } + + public static void stbtt_PackEnd(stbtt_pack_context spc) + { + CRuntime.free(spc.nodes); + CRuntime.free(spc.pack_info); + } + + public static void stbtt_PackSetOversampling(stbtt_pack_context spc, uint h_oversample, uint v_oversample) + { + if (h_oversample <= 8) + spc.h_oversample = (uint)(h_oversample); + if (v_oversample <= 8) + spc.v_oversample = (uint)(v_oversample); + } + + public static void stbtt__h_prefilter(byte* pixels, int w, int h, int stride_in_bytes, uint kernel_width) + { + byte* buffer = stackalloc byte[8]; + int safe_w = (int)(w - kernel_width); + int j; + CRuntime.memset(buffer, (int)(0), (ulong)(8)); + for (j = (int)(0); (j) < (h); ++j) + { + int i; + uint total; + CRuntime.memset(buffer, (int)(0), (ulong)(kernel_width)); + total = (uint)(0); + switch (kernel_width) + { + case 2: + for (i = (int)(0); i <= safe_w; ++i) + { + total += (uint)(pixels[i] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i]); + pixels[i] = ((byte)(total / 2)); + } + break; + case 3: + for (i = (int)(0); i <= safe_w; ++i) + { + total += (uint)(pixels[i] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i]); + pixels[i] = ((byte)(total / 3)); + } + break; + case 4: + for (i = (int)(0); i <= safe_w; ++i) + { + total += (uint)(pixels[i] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i]); + pixels[i] = ((byte)(total / 4)); + } + break; + case 5: + for (i = (int)(0); i <= safe_w; ++i) + { + total += (uint)(pixels[i] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i]); + pixels[i] = ((byte)(total / 5)); + } + break; + default: + for (i = (int)(0); i <= safe_w; ++i) + { + total += (uint)(pixels[i] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i]); + pixels[i] = ((byte)(total / kernel_width)); + } + break; + } + for (; (i) < (w); ++i) + { + total -= (uint)(buffer[i & (8 - 1)]); + pixels[i] = ((byte)(total / kernel_width)); + } + pixels += stride_in_bytes; + } + } + + public static void stbtt__v_prefilter(byte* pixels, int w, int h, int stride_in_bytes, uint kernel_width) + { + byte* buffer = stackalloc byte[8]; + int safe_h = (int)(h - kernel_width); + int j; + CRuntime.memset(buffer, (int)(0), (ulong)(8)); + for (j = (int)(0); (j) < (w); ++j) + { + int i; + uint total; + CRuntime.memset(buffer, (int)(0), (ulong)(kernel_width)); + total = (uint)(0); + switch (kernel_width) + { + case 2: + for (i = (int)(0); i <= safe_h; ++i) + { + total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i * stride_in_bytes]); + pixels[i * stride_in_bytes] = ((byte)(total / 2)); + } + break; + case 3: + for (i = (int)(0); i <= safe_h; ++i) + { + total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i * stride_in_bytes]); + pixels[i * stride_in_bytes] = ((byte)(total / 3)); + } + break; + case 4: + for (i = (int)(0); i <= safe_h; ++i) + { + total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i * stride_in_bytes]); + pixels[i * stride_in_bytes] = ((byte)(total / 4)); + } + break; + case 5: + for (i = (int)(0); i <= safe_h; ++i) + { + total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i * stride_in_bytes]); + pixels[i * stride_in_bytes] = ((byte)(total / 5)); + } + break; + default: + for (i = (int)(0); i <= safe_h; ++i) + { + total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); + buffer[(i + kernel_width) & (8 - 1)] = (byte)(pixels[i * stride_in_bytes]); + pixels[i * stride_in_bytes] = ((byte)(total / kernel_width)); + } + break; + } + for (; (i) < (h); ++i) + { + total -= (uint)(buffer[i & (8 - 1)]); + pixels[i * stride_in_bytes] = ((byte)(total / kernel_width)); + } + pixels += 1; + } + } + + public static float stbtt__oversample_shift(int oversample) + { + if (oversample == 0) + return (float)(0.0f); + return (float)((float)(-(oversample - 1)) / (2.0f * (float)(oversample))); + } + + public static int stbtt_PackFontRangesGatherRects(stbtt_pack_context spc, stbtt_fontinfo info, stbtt_pack_range* ranges, int num_ranges, stbrp_rect* rects) + { + int i; + int j; + int k; + k = (int)(0); + for (i = (int)(0); (i) < (num_ranges); ++i) + { + float fh = (float)(ranges[i].font_size); + float scale = (float)((fh) > (0) ? stbtt_ScaleForPixelHeight(info, (float)(fh)) : stbtt_ScaleForMappingEmToPixels(info, (float)(-fh))); + ranges[i].h_oversample = ((byte)(spc.h_oversample)); + ranges[i].v_oversample = ((byte)(spc.v_oversample)); + for (j = (int)(0); (j) < (ranges[i].num_chars); ++j) + { + int x0; + int y0; + int x1; + int y1; + int codepoint = (int)((ranges[i].array_of_unicode_codepoints) == (null) ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]); + int glyph = (int)(stbtt_FindGlyphIndex(info, (int)(codepoint))); + stbtt_GetGlyphBitmapBoxSubpixel(info, (int)(glyph), (float)(scale * spc.h_oversample), (float)(scale * spc.v_oversample), (float)(0), (float)(0), &x0, &y0, &x1, &y1); + rects[k].w = ((int)(x1 - x0 + spc.padding + spc.h_oversample - 1)); + rects[k].h = ((int)(y1 - y0 + spc.padding + spc.v_oversample - 1)); + ++k; + } + } + return (int)(k); + } + + public static void stbtt_MakeGlyphBitmapSubpixelPrefilter(stbtt_fontinfo info, byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float* sub_x, float* sub_y, int glyph) + { + stbtt_MakeGlyphBitmapSubpixel(info, output, (int)(out_w - (prefilter_x - 1)), (int)(out_h - (prefilter_y - 1)), (int)(out_stride), (float)(scale_x), (float)(scale_y), (float)(shift_x), (float)(shift_y), (int)(glyph)); + if ((prefilter_x) > (1)) + stbtt__h_prefilter(output, (int)(out_w), (int)(out_h), (int)(out_stride), (uint)(prefilter_x)); + if ((prefilter_y) > (1)) + stbtt__v_prefilter(output, (int)(out_w), (int)(out_h), (int)(out_stride), (uint)(prefilter_y)); + *sub_x = (float)(stbtt__oversample_shift((int)(prefilter_x))); + *sub_y = (float)(stbtt__oversample_shift((int)(prefilter_y))); + } + + public static int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context spc, stbtt_fontinfo info, stbtt_pack_range* ranges, int num_ranges, stbrp_rect* rects) + { + int i; + int j; + int k; + int return_value = (int)(1); + int old_h_over = (int)(spc.h_oversample); + int old_v_over = (int)(spc.v_oversample); + k = (int)(0); + for (i = (int)(0); (i) < (num_ranges); ++i) + { + float fh = (float)(ranges[i].font_size); + float scale = (float)((fh) > (0) ? stbtt_ScaleForPixelHeight(info, (float)(fh)) : stbtt_ScaleForMappingEmToPixels(info, (float)(-fh))); + float recip_h; + float recip_v; + float sub_x; + float sub_y; + spc.h_oversample = (uint)(ranges[i].h_oversample); + spc.v_oversample = (uint)(ranges[i].v_oversample); + recip_h = (float)(1.0f / spc.h_oversample); + recip_v = (float)(1.0f / spc.v_oversample); + sub_x = (float)(stbtt__oversample_shift((int)(spc.h_oversample))); + sub_y = (float)(stbtt__oversample_shift((int)(spc.v_oversample))); + for (j = (int)(0); (j) < (ranges[i].num_chars); ++j) + { + stbrp_rect* r = &rects[k]; + if ((r->was_packed) != 0) + { + stbtt_packedchar* bc = &ranges[i].chardata_for_range[j]; + int advance; + int lsb; + int x0; + int y0; + int x1; + int y1; + int codepoint = (int)((ranges[i].array_of_unicode_codepoints) == (null) ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]); + int glyph = (int)(stbtt_FindGlyphIndex(info, (int)(codepoint))); + int pad = (int)(spc.padding); + r->x += (int)(pad); + r->y += (int)(pad); + r->w -= (int)(pad); + r->h -= (int)(pad); + stbtt_GetGlyphHMetrics(info, (int)(glyph), &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, (int)(glyph), (float)(scale * spc.h_oversample), (float)(scale * spc.v_oversample), &x0, &y0, &x1, &y1); + stbtt_MakeGlyphBitmapSubpixel(info, spc.pixels + r->x + r->y * spc.stride_in_bytes, (int)(r->w - spc.h_oversample + 1), (int)(r->h - spc.v_oversample + 1), (int)(spc.stride_in_bytes), (float)(scale * spc.h_oversample), (float)(scale * spc.v_oversample), (float)(0), (float)(0), (int)(glyph)); + if ((spc.h_oversample) > (1)) + stbtt__h_prefilter(spc.pixels + r->x + r->y * spc.stride_in_bytes, (int)(r->w), (int)(r->h), (int)(spc.stride_in_bytes), (uint)(spc.h_oversample)); + if ((spc.v_oversample) > (1)) + stbtt__v_prefilter(spc.pixels + r->x + r->y * spc.stride_in_bytes, (int)(r->w), (int)(r->h), (int)(spc.stride_in_bytes), (uint)(spc.v_oversample)); + bc->x0 = (ushort)((short)(r->x)); + bc->y0 = (ushort)((short)(r->y)); + bc->x1 = (ushort)((short)(r->x + r->w)); + bc->y1 = (ushort)((short)(r->y + r->h)); + bc->xadvance = (float)(scale * advance); + bc->xoff = (float)((float)(x0) * recip_h + sub_x); + bc->yoff = (float)((float)(y0) * recip_v + sub_y); + bc->xoff2 = (float)((x0 + r->w) * recip_h + sub_x); + bc->yoff2 = (float)((y0 + r->h) * recip_v + sub_y); + } + else + { + return_value = (int)(0); + } + ++k; + } + } + spc.h_oversample = (uint)(old_h_over); + spc.v_oversample = (uint)(old_v_over); + return (int)(return_value); + } + + public static void stbtt_PackFontRangesPackRects(stbtt_pack_context spc, stbrp_rect* rects, int num_rects) + { + stbrp_pack_rects((stbrp_context*)(spc.pack_info), rects, (int)(num_rects)); + } + + public static int stbtt_PackFontRanges(stbtt_pack_context spc, byte* fontdata, int font_index, stbtt_pack_range* ranges, int num_ranges) + { + stbtt_fontinfo info = new stbtt_fontinfo(); + int i; + int j; + int n; + int return_value = (int)(1); + stbrp_rect* rects; + for (i = (int)(0); (i) < (num_ranges); ++i) + { + for (j = (int)(0); (j) < (ranges[i].num_chars); ++j) + { + ranges[i].chardata_for_range[j].x0 = (ushort)(ranges[i].chardata_for_range[j].y0 = (ushort)(ranges[i].chardata_for_range[j].x1 = (ushort)(ranges[i].chardata_for_range[j].y1 = (ushort)(0)))); + } + } + n = (int)(0); + for (i = (int)(0); (i) < (num_ranges); ++i) + { + n += (int)(ranges[i].num_chars); + } + rects = (stbrp_rect*)(CRuntime.malloc((ulong)(sizeof(stbrp_rect) * n))); + if ((rects) == (null)) + return (int)(0); + info.userdata = spc.user_allocator_context; + stbtt_InitFont(info, fontdata, (int)(stbtt_GetFontOffsetForIndex(fontdata, (int)(font_index)))); + n = (int)(stbtt_PackFontRangesGatherRects(spc, info, ranges, (int)(num_ranges), rects)); + stbtt_PackFontRangesPackRects(spc, rects, (int)(n)); + return_value = (int)(stbtt_PackFontRangesRenderIntoRects(spc, info, ranges, (int)(num_ranges), rects)); + CRuntime.free(rects); + return (int)(return_value); + } + + public static int stbtt_PackFontRange(stbtt_pack_context spc, byte* fontdata, int font_index, float font_size, int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar* chardata_for_range) + { + stbtt_pack_range range = new stbtt_pack_range(); + range.first_unicode_codepoint_in_range = (int)(first_unicode_codepoint_in_range); + range.array_of_unicode_codepoints = (null); + range.num_chars = (int)(num_chars_in_range); + range.chardata_for_range = chardata_for_range; + range.font_size = (float)(font_size); + return (int)(stbtt_PackFontRanges(spc, fontdata, (int)(font_index), &range, (int)(1))); + } + + public static void stbtt_GetPackedQuad(stbtt_packedchar* chardata, int pw, int ph, int char_index, float* xpos, float* ypos, stbtt_aligned_quad* q, int align_to_integer) + { + float ipw = (float)(1.0f / pw); + float iph = (float)(1.0f / ph); + stbtt_packedchar* b = chardata + char_index; + if ((align_to_integer) != 0) + { + float x = (float)((int)(CRuntime.floor((double)((*xpos + b->xoff) + 0.5f)))); + float y = (float)((int)(CRuntime.floor((double)((*ypos + b->yoff) + 0.5f)))); + q->x0 = (float)(x); + q->y0 = (float)(y); + q->x1 = (float)(x + b->xoff2 - b->xoff); + q->y1 = (float)(y + b->yoff2 - b->yoff); + } + else + { + q->x0 = (float)(*xpos + b->xoff); + q->y0 = (float)(*ypos + b->yoff); + q->x1 = (float)(*xpos + b->xoff2); + q->y1 = (float)(*ypos + b->yoff2); + } + + q->s0 = (float)(b->x0 * ipw); + q->t0 = (float)(b->y0 * iph); + q->s1 = (float)(b->x1 * ipw); + q->t1 = (float)(b->y1 * iph); + *xpos += (float)(b->xadvance); + } + + public static int stbtt__ray_intersect_bezier(float* orig, float* ray, float* q0, float* q1, float* q2, float* hits) + { + float q0perp = (float)(q0[1] * ray[0] - q0[0] * ray[1]); + float q1perp = (float)(q1[1] * ray[0] - q1[0] * ray[1]); + float q2perp = (float)(q2[1] * ray[0] - q2[0] * ray[1]); + float roperp = (float)(orig[1] * ray[0] - orig[0] * ray[1]); + float a = (float)(q0perp - 2 * q1perp + q2perp); + float b = (float)(q1perp - q0perp); + float c = (float)(q0perp - roperp); + float s0 = (float)(0); + float s1 = (float)(0); + int num_s = (int)(0); + if (a != 0.0) + { + float discr = (float)(b * b - a * c); + if ((discr) > (0.0)) + { + float rcpna = (float)(-1 / a); + float d = (float)(CRuntime.sqrt((double)(discr))); + s0 = (float)((b + d) * rcpna); + s1 = (float)((b - d) * rcpna); + if (((s0) >= (0.0)) && (s0 <= 1.0)) + num_s = (int)(1); + if ((((d) > (0.0)) && ((s1) >= (0.0))) && (s1 <= 1.0)) + { + if ((num_s) == (0)) + s0 = (float)(s1); + ++num_s; + } + } + } + else + { + s0 = (float)(c / (-2 * b)); + if (((s0) >= (0.0)) && (s0 <= 1.0)) + num_s = (int)(1); + } + + if ((num_s) == (0)) + return (int)(0); + else + { + float rcp_len2 = (float)(1 / (ray[0] * ray[0] + ray[1] * ray[1])); + float rayn_x = (float)(ray[0] * rcp_len2); + float rayn_y = (float)(ray[1] * rcp_len2); + float q0d = (float)(q0[0] * rayn_x + q0[1] * rayn_y); + float q1d = (float)(q1[0] * rayn_x + q1[1] * rayn_y); + float q2d = (float)(q2[0] * rayn_x + q2[1] * rayn_y); + float rod = (float)(orig[0] * rayn_x + orig[1] * rayn_y); + float q10d = (float)(q1d - q0d); + float q20d = (float)(q2d - q0d); + float q0rd = (float)(q0d - rod); + hits[0] = (float)(q0rd + s0 * (2.0f - 2.0f * s0) * q10d + s0 * s0 * q20d); + hits[1] = (float)(a * s0 + b); + if ((num_s) > (1)) + { + hits[2] = (float)(q0rd + s1 * (2.0f - 2.0f * s1) * q10d + s1 * s1 * q20d); + hits[3] = (float)(a * s1 + b); + return (int)(2); + } + else + { + return (int)(1); + } + } + + } + + public static int equal(float* a, float* b) + { + return (int)(((a[0] == b[0]) && (a[1] == b[1])) ? 1 : 0); + } + + public static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex* verts) + { + int i; + float* orig = stackalloc float[2]; + float* ray = stackalloc float[2]; + ray[0] = (float)(1); + ray[1] = (float)(0); + + float y_frac; + int winding = (int)(0); + orig[0] = (float)(x); + orig[1] = (float)(y); + y_frac = ((float)(CRuntime.fmod((double)(y), (double)(1.0f)))); + if ((y_frac) < (0.01f)) + y += (float)(0.01f); + else if ((y_frac) > (0.99f)) + y -= (float)(0.01f); + orig[1] = (float)(y); + for (i = (int)(0); (i) < (nverts); ++i) + { + if ((verts[i].type) == (STBTT_vline)) + { + int x0 = (int)(verts[i - 1].x); + int y0 = (int)(verts[i - 1].y); + int x1 = (int)(verts[i].x); + int y1 = (int)(verts[i].y); + if ((((y) > ((y0) < (y1) ? (y0) : (y1))) && ((y) < ((y0) < (y1) ? (y1) : (y0)))) && ((x) > ((x0) < (x1) ? (x0) : (x1)))) + { + float x_inter = (float)((y - y0) / (y1 - y0) * (x1 - x0) + x0); + if ((x_inter) < (x)) + winding += (int)(((y0) < (y1)) ? 1 : -1); + } + } + if ((verts[i].type) == (STBTT_vcurve)) + { + int x0 = (int)(verts[i - 1].x); + int y0 = (int)(verts[i - 1].y); + int x1 = (int)(verts[i].cx); + int y1 = (int)(verts[i].cy); + int x2 = (int)(verts[i].x); + int y2 = (int)(verts[i].y); + int ax = (int)((x0) < ((x1) < (x2) ? (x1) : (x2)) ? (x0) : ((x1) < (x2) ? (x1) : (x2))); + int ay = (int)((y0) < ((y1) < (y2) ? (y1) : (y2)) ? (y0) : ((y1) < (y2) ? (y1) : (y2))); + int by = (int)((y0) < ((y1) < (y2) ? (y2) : (y1)) ? ((y1) < (y2) ? (y2) : (y1)) : (y0)); + if ((((y) > (ay)) && ((y) < (by))) && ((x) > (ax))) + { + float* q0 = stackalloc float[2]; + float* q1 = stackalloc float[2]; + float* q2 = stackalloc float[2]; + float* hits = stackalloc float[4]; + q0[0] = ((float)(x0)); + q0[1] = ((float)(y0)); + q1[0] = ((float)(x1)); + q1[1] = ((float)(y1)); + q2[0] = ((float)(x2)); + q2[1] = ((float)(y2)); + if (((equal(q0, q1)) != 0) || ((equal(q1, q2)) != 0)) + { + x0 = ((int)(verts[i - 1].x)); + y0 = ((int)(verts[i - 1].y)); + x1 = ((int)(verts[i].x)); + y1 = ((int)(verts[i].y)); + if ((((y) > ((y0) < (y1) ? (y0) : (y1))) && ((y) < ((y0) < (y1) ? (y1) : (y0)))) && ((x) > ((x0) < (x1) ? (x0) : (x1)))) + { + float x_inter = (float)((y - y0) / (y1 - y0) * (x1 - x0) + x0); + if ((x_inter) < (x)) + winding += (int)(((y0) < (y1)) ? 1 : -1); + } + } + else + { + int num_hits = (int)(stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits)); + if ((num_hits) >= (1)) + if ((hits[0]) < (0)) + winding += (int)((hits[1]) < (0) ? -1 : 1); + if ((num_hits) >= (2)) + if ((hits[2]) < (0)) + winding += (int)((hits[3]) < (0) ? -1 : 1); + } + } + } + } + return (int)(winding); + } + + public static float stbtt__cuberoot(float x) + { + if ((x) < (0)) + return (float)(-(float)(CRuntime.pow((double)(-x), (double)(1.0f / 3.0f)))); + else + return (float)(CRuntime.pow((double)(x), (double)(1.0f / 3.0f))); + } + + public static int stbtt__solve_cubic(float a, float b, float c, float* r) + { + float s = (float)(-a / 3); + float p = (float)(b - a * a / 3); + float q = (float)(a * (2 * a * a - 9 * b) / 27 + c); + float p3 = (float)(p * p * p); + float d = (float)(q * q + 4 * p3 / 27); + if ((d) >= (0)) + { + float z = (float)(CRuntime.sqrt((double)(d))); + float u = (float)((-q + z) / 2); + float v = (float)((-q - z) / 2); + u = (float)(stbtt__cuberoot((float)(u))); + v = (float)(stbtt__cuberoot((float)(v))); + r[0] = (float)(s + u + v); + return (int)(1); + } + else + { + float u = (float)(CRuntime.sqrt((double)(-p / 3))); + float v = (float)((float)(CRuntime.acos((double)(-CRuntime.sqrt((double)(-27 / p3)) * q / 2))) / 3); + float m = (float)(CRuntime.cos((double)(v))); + float n = (float)((float)(CRuntime.cos((double)(v - 3.141592 / 2))) * 1.732050808f); + r[0] = (float)(s + u * 2 * m); + r[1] = (float)(s - u * (m + n)); + r[2] = (float)(s - u * (m - n)); + return (int)(3); + } + + } + + public static byte* stbtt_GetGlyphSDF(stbtt_fontinfo info, float scale, int glyph, int padding, byte onedge_value, float pixel_dist_scale, int* width, int* height, int* xoff, int* yoff) + { + float scale_x = (float)(scale); + float scale_y = (float)(scale); + int ix0; + int iy0; + int ix1; + int iy1; + int w; + int h; + byte* data; + if ((scale_x) == (0)) + scale_x = (float)(scale_y); + if ((scale_y) == (0)) + { + if ((scale_x) == (0)) + return (null); + scale_y = (float)(scale_x); + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, (int)(glyph), (float)(scale), (float)(scale), (float)(0.0f), (float)(0.0f), &ix0, &iy0, &ix1, &iy1); + if (((ix0) == (ix1)) || ((iy0) == (iy1))) + return (null); + ix0 -= (int)(padding); + iy0 -= (int)(padding); + ix1 += (int)(padding); + iy1 += (int)(padding); + w = (int)(ix1 - ix0); + h = (int)(iy1 - iy0); + if ((width) != null) + *width = (int)(w); + if ((height) != null) + *height = (int)(h); + if ((xoff) != null) + *xoff = (int)(ix0); + if ((yoff) != null) + *yoff = (int)(iy0); + scale_y = (float)(-scale_y); + { + int x; + int y; + int i; + int j; + float* precompute; + stbtt_vertex* verts; + int num_verts = (int)(stbtt_GetGlyphShape(info, (int)(glyph), &verts)); + data = (byte*)(CRuntime.malloc((ulong)(w * h))); + precompute = (float*)(CRuntime.malloc((ulong)(num_verts * sizeof(float)))); + for (i = (int)(0), j = (int)(num_verts - 1); (i) < (num_verts); j = (int)(i++)) + { + if ((verts[i].type) == (STBTT_vline)) + { + float x0 = (float)(verts[i].x * scale_x); + float y0 = (float)(verts[i].y * scale_y); + float x1 = (float)(verts[j].x * scale_x); + float y1 = (float)(verts[j].y * scale_y); + float dist = (float)(CRuntime.sqrt((double)((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)))); + precompute[i] = (float)(((dist) == (0)) ? 0.0f : 1.0f / dist); + } + else if ((verts[i].type) == (STBTT_vcurve)) + { + float x2 = (float)(verts[j].x * scale_x); + float y2 = (float)(verts[j].y * scale_y); + float x1 = (float)(verts[i].cx * scale_x); + float y1 = (float)(verts[i].cy * scale_y); + float x0 = (float)(verts[i].x * scale_x); + float y0 = (float)(verts[i].y * scale_y); + float bx = (float)(x0 - 2 * x1 + x2); + float by = (float)(y0 - 2 * y1 + y2); + float len2 = (float)(bx * bx + by * by); + if (len2 != 0.0f) + precompute[i] = (float)(1.0f / (bx * bx + by * by)); + else + precompute[i] = (float)(0.0f); + } + else + precompute[i] = (float)(0.0f); + } + for (y = (int)(iy0); (y) < (iy1); ++y) + { + for (x = (int)(ix0); (x) < (ix1); ++x) + { + float val; + float min_dist = (float)(999999.0f); + float sx = (float)((float)(x) + 0.5f); + float sy = (float)((float)(y) + 0.5f); + float x_gspace = (float)(sx / scale_x); + float y_gspace = (float)(sy / scale_y); + int winding = (int)(stbtt__compute_crossings_x((float)(x_gspace), (float)(y_gspace), (int)(num_verts), verts)); + for (i = (int)(0); (i) < (num_verts); ++i) + { + float x0 = (float)(verts[i].x * scale_x); + float y0 = (float)(verts[i].y * scale_y); + float dist2 = (float)((x0 - sx) * (x0 - sx) + (y0 - sy) * (y0 - sy)); + if ((dist2) < (min_dist * min_dist)) + min_dist = ((float)(CRuntime.sqrt((double)(dist2)))); + if ((verts[i].type) == (STBTT_vline)) + { + float x1 = (float)(verts[i - 1].x * scale_x); + float y1 = (float)(verts[i - 1].y * scale_y); + float dist = (float)((float)(CRuntime.fabs((double)((x1 - x0) * (y0 - sy) - (y1 - y0) * (x0 - sx)))) * precompute[i]); + if ((dist) < (min_dist)) + { + float dx = (float)(x1 - x0); + float dy = (float)(y1 - y0); + float px = (float)(x0 - sx); + float py = (float)(y0 - sy); + float t = (float)(-(px * dx + py * dy) / (dx * dx + dy * dy)); + if (((t) >= (0.0f)) && (t <= 1.0f)) + min_dist = (float)(dist); + } + } + else if ((verts[i].type) == (STBTT_vcurve)) + { + float x2 = (float)(verts[i - 1].x * scale_x); + float y2 = (float)(verts[i - 1].y * scale_y); + float x1 = (float)(verts[i].cx * scale_x); + float y1 = (float)(verts[i].cy * scale_y); + float box_x0 = (float)(((x0) < (x1) ? (x0) : (x1)) < (x2) ? ((x0) < (x1) ? (x0) : (x1)) : (x2)); + float box_y0 = (float)(((y0) < (y1) ? (y0) : (y1)) < (y2) ? ((y0) < (y1) ? (y0) : (y1)) : (y2)); + float box_x1 = (float)(((x0) < (x1) ? (x1) : (x0)) < (x2) ? (x2) : ((x0) < (x1) ? (x1) : (x0))); + float box_y1 = (float)(((y0) < (y1) ? (y1) : (y0)) < (y2) ? (y2) : ((y0) < (y1) ? (y1) : (y0))); + if (((((sx) > (box_x0 - min_dist)) && ((sx) < (box_x1 + min_dist))) && ((sy) > (box_y0 - min_dist))) && ((sy) < (box_y1 + min_dist))) + { + int num = (int)(0); + float ax = (float)(x1 - x0); + float ay = (float)(y1 - y0); + float bx = (float)(x0 - 2 * x1 + x2); + float by = (float)(y0 - 2 * y1 + y2); + float mx = (float)(x0 - sx); + float my = (float)(y0 - sy); + float* res = stackalloc float[3]; + float px; + float py; + float t; + float it; + float a_inv = (float)(precompute[i]); + if ((a_inv) == (0.0)) + { + float a = (float)(3 * (ax * bx + ay * by)); + float b = (float)(2 * (ax * ax + ay * ay) + (mx * bx + my * by)); + float c = (float)(mx * ax + my * ay); + if ((a) == (0.0)) + { + if (b != 0.0) + { + res[num++] = (float)(-c / b); + } + } + else + { + float discriminant = (float)(b * b - 4 * a * c); + if ((discriminant) < (0)) + num = (int)(0); + else + { + float root = (float)(CRuntime.sqrt((double)(discriminant))); + res[0] = (float)((-b - root) / (2 * a)); + res[1] = (float)((-b + root) / (2 * a)); + num = (int)(2); + } + } + } + else + { + float b = (float)(3 * (ax * bx + ay * by) * a_inv); + float c = (float)((2 * (ax * ax + ay * ay) + (mx * bx + my * by)) * a_inv); + float d = (float)((mx * ax + my * ay) * a_inv); + num = (int)(stbtt__solve_cubic((float)(b), (float)(c), (float)(d), res)); + } + if ((((num) >= (1)) && ((res[0]) >= (0.0f))) && (res[0] <= 1.0f)) + { + t = (float)(res[0]); + it = (float)(1.0f - t); + px = (float)(it * it * x0 + 2 * t * it * x1 + t * t * x2); + py = (float)(it * it * y0 + 2 * t * it * y1 + t * t * y2); + dist2 = (float)((px - sx) * (px - sx) + (py - sy) * (py - sy)); + if ((dist2) < (min_dist * min_dist)) + min_dist = ((float)(CRuntime.sqrt((double)(dist2)))); + } + if ((((num) >= (2)) && ((res[1]) >= (0.0f))) && (res[1] <= 1.0f)) + { + t = (float)(res[1]); + it = (float)(1.0f - t); + px = (float)(it * it * x0 + 2 * t * it * x1 + t * t * x2); + py = (float)(it * it * y0 + 2 * t * it * y1 + t * t * y2); + dist2 = (float)((px - sx) * (px - sx) + (py - sy) * (py - sy)); + if ((dist2) < (min_dist * min_dist)) + min_dist = ((float)(CRuntime.sqrt((double)(dist2)))); + } + if ((((num) >= (3)) && ((res[2]) >= (0.0f))) && (res[2] <= 1.0f)) + { + t = (float)(res[2]); + it = (float)(1.0f - t); + px = (float)(it * it * x0 + 2 * t * it * x1 + t * t * x2); + py = (float)(it * it * y0 + 2 * t * it * y1 + t * t * y2); + dist2 = (float)((px - sx) * (px - sx) + (py - sy) * (py - sy)); + if ((dist2) < (min_dist * min_dist)) + min_dist = ((float)(CRuntime.sqrt((double)(dist2)))); + } + } + } + } + if ((winding) == (0)) + min_dist = (float)(-min_dist); + val = (float)(onedge_value + pixel_dist_scale * min_dist); + if ((val) < (0)) + val = (float)(0); + else if ((val) > (255)) + val = (float)(255); + data[(y - iy0) * w + (x - ix0)] = ((byte)(val)); + } + } + CRuntime.free(precompute); + CRuntime.free(verts); + } + + return data; + } + + public static byte* stbtt_GetCodepointSDF(stbtt_fontinfo info, float scale, int codepoint, int padding, byte onedge_value, float pixel_dist_scale, int* width, int* height, int* xoff, int* yoff) + { + return stbtt_GetGlyphSDF(info, (float)(scale), (int)(stbtt_FindGlyphIndex(info, (int)(codepoint))), (int)(padding), (byte)(onedge_value), (float)(pixel_dist_scale), width, height, xoff, yoff); + } + + public static void stbtt_FreeSDF(byte* bitmap, object userdata) + { + CRuntime.free(bitmap); + } + + public static int stbtt__CompareUTF8toUTF16_bigendian_prefix(byte* s1, int len1, byte* s2, int len2) + { + int i = (int)(0); + while ((len2) != 0) + { + ushort ch = (ushort)(s2[0] * 256 + s2[1]); + if ((ch) < (0x80)) + { + if ((i) >= (len1)) + return (int)(-1); + if (s1[i++] != ch) + return (int)(-1); + } + else if ((ch) < (0x800)) + { + if ((i + 1) >= (len1)) + return (int)(-1); + if (s1[i++] != 0xc0 + (ch >> 6)) + return (int)(-1); + if (s1[i++] != 0x80 + (ch & 0x3f)) + return (int)(-1); + } + else if (((ch) >= (0xd800)) && ((ch) < (0xdc00))) + { + uint c; + ushort ch2 = (ushort)(s2[2] * 256 + s2[3]); + if ((i + 3) >= (len1)) + return (int)(-1); + c = (uint)(((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000); + if (s1[i++] != 0xf0 + (c >> 18)) + return (int)(-1); + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) + return (int)(-1); + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) + return (int)(-1); + if (s1[i++] != 0x80 + ((c) & 0x3f)) + return (int)(-1); + s2 += 2; + len2 -= (int)(2); + } + else if (((ch) >= (0xdc00)) && ((ch) < (0xe000))) + { + return (int)(-1); + } + else + { + if ((i + 2) >= (len1)) + return (int)(-1); + if (s1[i++] != 0xe0 + (ch >> 12)) + return (int)(-1); + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) + return (int)(-1); + if (s1[i++] != 0x80 + ((ch) & 0x3f)) + return (int)(-1); + } + s2 += 2; + len2 -= (int)(2); + } + return (int)(i); + } + + public static int stbtt_CompareUTF8toUTF16_bigendian_internal(sbyte* s1, int len1, sbyte* s2, int len2) + { + return (int)((len1) == (stbtt__CompareUTF8toUTF16_bigendian_prefix((byte*)(s1), (int)(len1), (byte*)(s2), (int)(len2))) ? 1 : 0); + } + + public static sbyte* stbtt_GetFontNameString(stbtt_fontinfo font, int* length, int platformID, int encodingID, int languageID, int nameID) + { + int i; + int count; + int stringOffset; + byte* fc = font.data; + uint offset = (uint)(font.fontstart); + uint nm = (uint)(stbtt__find_table(fc, (uint)(offset), "name")); + if (nm == 0) + return (null); + count = (int)(ttUSHORT(fc + nm + 2)); + stringOffset = (int)(nm + ttUSHORT(fc + nm + 4)); + for (i = (int)(0); (i) < (count); ++i) + { + uint loc = (uint)(nm + 6 + 12 * i); + if (((((platformID) == (ttUSHORT(fc + loc + 0))) && ((encodingID) == (ttUSHORT(fc + loc + 2)))) && ((languageID) == (ttUSHORT(fc + loc + 4)))) && ((nameID) == (ttUSHORT(fc + loc + 6)))) + { + *length = (int)(ttUSHORT(fc + loc + 8)); + return (sbyte*)(fc + stringOffset + ttUSHORT(fc + loc + 10)); + } + } + return (null); + } + + public static int stbtt__matchpair(byte* fc, uint nm, byte* name, int nlen, int target_id, int next_id) + { + int i; + int count = (int)(ttUSHORT(fc + nm + 2)); + int stringOffset = (int)(nm + ttUSHORT(fc + nm + 4)); + for (i = (int)(0); (i) < (count); ++i) + { + uint loc = (uint)(nm + 6 + 12 * i); + int id = (int)(ttUSHORT(fc + loc + 6)); + if ((id) == (target_id)) + { + int platform = (int)(ttUSHORT(fc + loc + 0)); + int encoding = (int)(ttUSHORT(fc + loc + 2)); + int language = (int)(ttUSHORT(fc + loc + 4)); + if ((((platform) == (0)) || (((platform) == (3)) && ((encoding) == (1)))) || (((platform) == (3)) && ((encoding) == (10)))) + { + int slen = (int)(ttUSHORT(fc + loc + 8)); + int off = (int)(ttUSHORT(fc + loc + 10)); + int matchlen = (int)(stbtt__CompareUTF8toUTF16_bigendian_prefix(name, (int)(nlen), fc + stringOffset + off, (int)(slen))); + if ((matchlen) >= (0)) + { + if ((((((i + 1) < (count)) && ((ttUSHORT(fc + loc + 12 + 6)) == (next_id))) && ((ttUSHORT(fc + loc + 12)) == (platform))) && ((ttUSHORT(fc + loc + 12 + 2)) == (encoding))) && ((ttUSHORT(fc + loc + 12 + 4)) == (language))) + { + slen = (int)(ttUSHORT(fc + loc + 12 + 8)); + off = (int)(ttUSHORT(fc + loc + 12 + 10)); + if ((slen) == (0)) + { + if ((matchlen) == (nlen)) + return (int)(1); + } + else if (((matchlen) < (nlen)) && ((name[matchlen]) == (' '))) + { + ++matchlen; + if ((stbtt_CompareUTF8toUTF16_bigendian_internal((sbyte*)(name + matchlen), (int)(nlen - matchlen), (sbyte*)(fc + stringOffset + off), (int)(slen))) != 0) + return (int)(1); + } + } + else + { + if ((matchlen) == (nlen)) + return (int)(1); + } + } + } + } + } + return (int)(0); + } + + public static int stbtt__matches(byte* fc, uint offset, byte* name, int flags) + { + int nlen = (int)(CRuntime.strlen((sbyte*)(name))); + uint nm; + uint hd; + if (stbtt__isfont(fc + offset) == 0) + return (int)(0); + if ((flags) != 0) + { + hd = (uint)(stbtt__find_table(fc, (uint)(offset), "head")); + if ((ttUSHORT(fc + hd + 44) & 7) != (flags & 7)) + return (int)(0); + } + + nm = (uint)(stbtt__find_table(fc, (uint)(offset), "name")); + if (nm == 0) + return (int)(0); + if ((flags) != 0) + { + if ((stbtt__matchpair(fc, (uint)(nm), name, (int)(nlen), (int)(16), (int)(-1))) != 0) + return (int)(1); + if ((stbtt__matchpair(fc, (uint)(nm), name, (int)(nlen), (int)(1), (int)(-1))) != 0) + return (int)(1); + if ((stbtt__matchpair(fc, (uint)(nm), name, (int)(nlen), (int)(3), (int)(-1))) != 0) + return (int)(1); + } + else + { + if ((stbtt__matchpair(fc, (uint)(nm), name, (int)(nlen), (int)(16), (int)(17))) != 0) + return (int)(1); + if ((stbtt__matchpair(fc, (uint)(nm), name, (int)(nlen), (int)(1), (int)(2))) != 0) + return (int)(1); + if ((stbtt__matchpair(fc, (uint)(nm), name, (int)(nlen), (int)(3), (int)(-1))) != 0) + return (int)(1); + } + + return (int)(0); + } + + public static int stbtt_FindMatchingFont_internal(byte* font_collection, sbyte* name_utf8, int flags) + { + int i; + for (i = (int)(0); ; ++i) + { + int off = (int)(stbtt_GetFontOffsetForIndex(font_collection, (int)(i))); + if ((off) < (0)) + return (int)(off); + if ((stbtt__matches(font_collection, (uint)(off), (byte*)(name_utf8), (int)(flags))) != 0) + return (int)(off); + } + } + + public static int stbtt_BakeFontBitmap(byte* data, int offset, float pixel_height, byte* pixels, int pw, int ph, int first_char, int num_chars, stbtt_bakedchar* chardata) + { + return (int)(stbtt_BakeFontBitmap_internal(data, (int)(offset), (float)(pixel_height), pixels, (int)(pw), (int)(ph), (int)(first_char), (int)(num_chars), chardata)); + } + + public static int stbtt_GetFontOffsetForIndex(byte* data, int index) + { + return (int)(stbtt_GetFontOffsetForIndex_internal(data, (int)(index))); + } + + public static int stbtt_GetNumberOfFonts(byte* data) + { + return (int)(stbtt_GetNumberOfFonts_internal(data)); + } + + public static int stbtt_InitFont(stbtt_fontinfo info, byte* data, int offset) + { + return (int)(stbtt_InitFont_internal(info, data, (int)(offset))); + } + + public static int stbtt_FindMatchingFont(byte* fontdata, sbyte* name, int flags) + { + return (int)(stbtt_FindMatchingFont_internal(fontdata, name, (int)(flags))); + } + + public static int stbtt_CompareUTF8toUTF16_bigendian(sbyte* s1, int len1, sbyte* s2, int len2) + { + return (int)(stbtt_CompareUTF8toUTF16_bigendian_internal(s1, (int)(len1), s2, (int)(len2))); + } + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/StbTrueType/StbTrueType.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/StbTrueType/StbTrueType.cs new file mode 100644 index 000000000..edb42e7ef --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/StbTrueType/StbTrueType.cs @@ -0,0 +1,38 @@ +namespace StbSharp +{ + internal static unsafe partial class StbTrueType + { + public static uint stbtt__find_table(byte* data, uint fontstart, string tag) + { + int num_tables = ttUSHORT(data + fontstart + 4); + var tabledir = fontstart + 12; + int i; + for (i = 0; (i) < num_tables; ++i) + { + var loc = (uint) (tabledir + 16*i); + if (((data + loc + 0)[0] == (tag[0])) && ((data + loc + 0)[1] == tag[1]) && + ((data + loc + 0)[2] == tag[2]) && ((data + loc + 0)[3] == (tag[3]))) + return ttULONG(data + loc + 8); + } + return 0; + } + + public static bool stbtt_BakeFontBitmap(byte[] ttf, int offset, float pixel_height, byte[] pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar[] chardata) + { + fixed (byte* ttfPtr = ttf) + { + fixed (byte* pixelsPtr = pixels) + { + fixed (stbtt_bakedchar* chardataPtr = chardata) + { + var result = stbtt_BakeFontBitmap(ttfPtr, offset, pixel_height, pixelsPtr, pw, ph, first_char, num_chars, + chardataPtr); + + return result != 0; + } + } + } + } + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/StbTrueTypeExtensions.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/StbTrueTypeExtensions.cs new file mode 100644 index 000000000..c025fdaae --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/StbTrueTypeExtensions.cs @@ -0,0 +1,39 @@ +using StbSharp; + +namespace SpriteFontPlus +{ + internal static unsafe class StbTrueTypeExtensions + { + public static void fons__tt_getFontVMetrics(this StbTrueType.stbtt_fontinfo font, int* ascent, int* descent, int* lineGap) + { + StbTrueType.stbtt_GetFontVMetrics(font, ascent, descent, lineGap); + } + + public static float fons__tt_getPixelHeightScale(this StbTrueType.stbtt_fontinfo font, float size) + { + return (float)(StbTrueType.stbtt_ScaleForPixelHeight(font, (float)(size))); + } + + public static int fons__tt_getGlyphIndex(this StbTrueType.stbtt_fontinfo font, int codepoint) + { + return (int)(StbTrueType.stbtt_FindGlyphIndex(font, (int)(codepoint))); + } + + public static int fons__tt_buildGlyphBitmap(this StbTrueType.stbtt_fontinfo font, int glyph, float size, float scale, int* advance, int* lsb, int* x0, int* y0, int* x1, int* y1) + { + StbTrueType.stbtt_GetGlyphHMetrics(font, (int)(glyph), advance, lsb); + StbTrueType.stbtt_GetGlyphBitmapBox(font, (int)(glyph), (float)(scale), (float)(scale), x0, y0, x1, y1); + return (int)(1); + } + + public static void fons__tt_renderGlyphBitmap(this StbTrueType.stbtt_fontinfo font, byte* output, int outWidth, int outHeight, int outStride, float scaleX, float scaleY, int glyph) + { + StbTrueType.stbtt_MakeGlyphBitmap(font, output, (int)(outWidth), (int)(outHeight), (int)(outStride), (float)(scaleX), (float)(scaleY), (int)(glyph)); + } + + public static int fons__tt_getGlyphKernAdvance(this StbTrueType.stbtt_fontinfo font, int glyph1, int glyph2) + { + return (int)(StbTrueType.stbtt_GetGlyphKernAdvance(font, (int)(glyph1), (int)(glyph2))); + } + } +} diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/TtfFontBaker.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/TtfFontBaker.cs new file mode 100644 index 000000000..6d169c817 --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/TtfFontBaker.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using StbSharp; + +namespace SpriteFontPlus +{ + public static unsafe class TtfFontBaker + { + public static TtfFontBakerResult Bake(byte[] ttf, float fontPixelHeight, + int bitmapWidth, int bitmapHeight, + IEnumerable characterRanges) + { + if (ttf == null || ttf.Length == 0) + { + throw new ArgumentNullException(nameof(ttf)); + } + + if (fontPixelHeight <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fontPixelHeight)); + } + + if (bitmapWidth <= 0) + { + throw new ArgumentOutOfRangeException(nameof(bitmapWidth)); + } + + if (bitmapHeight <= 0) + { + throw new ArgumentOutOfRangeException(nameof(bitmapHeight)); + } + + if (characterRanges == null) + { + throw new ArgumentNullException(nameof(characterRanges)); + } + + if (!characterRanges.Any()) + { + throw new ArgumentException("characterRanges must have a least one value."); + } + + byte[] pixels; + var glyphs = new Dictionary(); + fixed (byte* ttfPtr = ttf) + { + StbTrueType.stbtt_fontinfo fontInfo = new StbTrueType.stbtt_fontinfo(); + if (StbTrueType.stbtt_InitFont(fontInfo, ttfPtr, 0) == 0) + { + throw new Exception("Failed to init font."); + } + + float scaleFactor = StbTrueType.stbtt_ScaleForPixelHeight(fontInfo, fontPixelHeight); + + int ascent, descent, lineGap; + StbTrueType.stbtt_GetFontVMetrics(fontInfo, &ascent, &descent, &lineGap); + + pixels = new byte[bitmapWidth * bitmapHeight]; + StbTrueType.stbtt_pack_context pc = new StbTrueType.stbtt_pack_context(); + fixed (byte* pixelsPtr = pixels) + { + StbTrueType.stbtt_PackBegin(pc, pixelsPtr, bitmapWidth, + bitmapHeight, bitmapWidth, 1, null); + } + + foreach (var range in characterRanges) + { + if (range.Start > range.End) + { + continue; + } + + var cd = new StbTrueType.stbtt_packedchar[range.End - range.Start + 1]; + fixed (StbTrueType.stbtt_packedchar* chardataPtr = cd) + { + StbTrueType.stbtt_PackFontRange(pc, ttfPtr, 0, fontPixelHeight, + range.Start, + range.End - range.Start + 1, + chardataPtr); + } + + for (var i = 0; i < cd.Length; ++i) + { + var yOff = cd[i].yoff; + yOff += ascent * scaleFactor; + + var glyphInfo = new GlyphInfo + { + X = cd[i].x0, + Y = cd[i].y0, + Width = cd[i].x1 - cd[i].x0, + Height = cd[i].y1 - cd[i].y0, + XOffset = (int)cd[i].xoff, + YOffset = (int)Math.Round(yOff), + XAdvance = (int)Math.Round(cd[i].xadvance) + }; + + glyphs[(char) (i + range.Start)] = glyphInfo; + } + } + } + + return new TtfFontBakerResult(glyphs, fontPixelHeight, pixels, bitmapWidth, bitmapHeight); + } + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/SpriteFontPlus/TtfFontBakerResult.cs b/WorldOfTheThreeKingdoms/SpriteFontPlus/TtfFontBakerResult.cs new file mode 100644 index 000000000..be1f0c34b --- /dev/null +++ b/WorldOfTheThreeKingdoms/SpriteFontPlus/TtfFontBakerResult.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace SpriteFontPlus +{ + public class TtfFontBakerResult + { + public Dictionary Glyphs + { + get; private set; + } + public float FontFontPixelHeight + { + get; private set; + } + public byte[] Pixels + { + get; private set; + } + public int Width + { + get; private set; + } + public int Height + { + get; private set; + } + + public TtfFontBakerResult(Dictionary glyphs, + float fontPixelHeight, + byte[] pixels, + int width, + int height) + { + if (glyphs == null) + { + throw new ArgumentNullException(nameof(glyphs)); + } + + if (fontPixelHeight <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fontPixelHeight)); + } + + if (pixels == null) + { + throw new ArgumentNullException(nameof(pixels)); + } + + if (width <= 0) + { + throw new ArgumentOutOfRangeException(nameof(width)); + } + + if (height <= 0) + { + throw new ArgumentOutOfRangeException(nameof(height)); + } + + if (pixels.Length < width * height) + { + throw new ArgumentException("pixels.Length should be higher than width * height"); + } + + Glyphs = glyphs; + FontFontPixelHeight = fontPixelHeight; + Pixels = pixels; + Width = width; + Height = height; + } + + public SpriteFont CreateSpriteFont(GraphicsDevice graphicsDevice) + { + var rgb = new Color[Width * Height]; + for (var i = 0; i < Pixels.Length; ++i) + { + var b = Pixels[i]; + rgb[i].R = b; + rgb[i].G = b; + rgb[i].B = b; + + rgb[i].A = b; + } + + var texture = new Texture2D(graphicsDevice, Width, Height); + texture.SetData(rgb); + + var glyphBounds = new List(); + var cropping = new List(); + var chars = new List(); + var kerning = new List(); + + var orderedKeys = Glyphs.Keys.OrderBy(a => a); + foreach (var key in orderedKeys) + { + var character = Glyphs[key]; + + var bounds = new Rectangle(character.X, character.Y, + character.Width, + character.Height); + + glyphBounds.Add(bounds); + cropping.Add(new Rectangle((int)character.XOffset, + character.YOffset, + bounds.Width, bounds.Height)); + + chars.Add((char)key); + + kerning.Add(new Vector3(0, bounds.Width, character.XAdvance - bounds.Width)); + } + + var constructorInfo = typeof(SpriteFont).GetTypeInfo().DeclaredConstructors.First(); + var font = (SpriteFont)constructorInfo.Invoke(new object[] + { + texture, glyphBounds, cropping, + chars, (int)FontFontPixelHeight, 0, kerning, ' ' + }); + + return font; + } + } +} \ No newline at end of file diff --git a/WorldOfTheThreeKingdoms/WorldOfTheThreeKingdoms.csproj b/WorldOfTheThreeKingdoms/WorldOfTheThreeKingdoms.csproj index 9795c8b57..3e7ce8446 100644 --- a/WorldOfTheThreeKingdoms/WorldOfTheThreeKingdoms.csproj +++ b/WorldOfTheThreeKingdoms/WorldOfTheThreeKingdoms.csproj @@ -1616,6 +1616,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1665,9 +1692,6 @@ ..\References\SixLabors.Shapes.dll - - ..\packages\SpriteFontPlus.MonoGame.0.6.3.15\lib\net45\SpriteFontPlus.dll - False ..\References\Win\Steamworks.NET.dll @@ -20409,6 +20433,7 @@ +