public static Vector2 MeasureTextLine(this IFont font, string text, float fontHeight, int start, int length, float letterSpacing) { FontChar prevChar = null; var size = new Vector2(0, fontHeight); float width = 0; for (int i = 0; i < length; i++) { char ch = text[i + start]; if (ch == '\n') { size.Y += fontHeight; width = 0; prevChar = null; continue; } var fontChar = font.CharSource.Get(ch, fontHeight); if (fontChar == FontChar.Null) { continue; } float scale = fontChar.Height != 0.0f ? fontHeight / fontChar.Height : 0.0f; width += scale * (fontChar.ACWidths.X + fontChar.Kerning(prevChar) + fontChar.Width + fontChar.ACWidths.Y + letterSpacing); size.X = Math.Max(size.X, width); prevChar = fontChar; } return(size); }
private FontChar CreateFontChar(char code) { var glyph = fontRenderer.Render(code, fontHeight); if (texture == null) { CreateNewFontTexture(); } if (glyph == null) { return(null); } // We add 1px left and right padding to each char on the texture and also to the UV // so that chars will be blurred correctly after stretching or drawing to float position. // And we compensate this padding by ACWidth, so that the text will take the same space. // See "texture bleeding" const int padding = 1; // Space between characters on the texture const int spacing = 1; var paddedGlyphWidth = glyph.Width + padding * 2; if (position.X + paddedGlyphWidth + spacing >= texture.ImageSize.Width) { position.X = 0; position.Y += fontHeight + spacing + lineAdditionalHeight; lineAdditionalHeight = 0; } if (glyph.VerticalOffset < -lineAdditionalHeight) { lineAdditionalHeight = -glyph.VerticalOffset; } if (position.Y + fontHeight + spacing + lineAdditionalHeight >= texture.ImageSize.Height) { CreateNewFontTexture(); position = IntVector2.Zero; } CopyGlyphToTexture(glyph, texture, position + new IntVector2(padding, 0)); var fontChar = new FontChar { Char = code, UV0 = (Vector2)position / (Vector2)texture.ImageSize, UV1 = ((Vector2)position + new Vector2(paddedGlyphWidth, fontHeight)) / (Vector2)texture.ImageSize, ACWidths = glyph.ACWidths - Vector2.One * padding, Width = paddedGlyphWidth, Height = fontHeight, RgbIntensity = glyph.RgbIntensity, KerningPairs = glyph.KerningPairs, TextureIndex = textureIndex, VerticalOffset = Math.Min(0, glyph.VerticalOffset) }; position.X += paddedGlyphWidth + spacing; return(fontChar); }
private FontChar CreateFontChar(char code) { var glyph = FontRenderer.Render(code, fontHeight); if (texture == null) { CreateNewFontTexture(); } if (glyph == null) { return(null); } // Space between characters on the texture const int spacing = 1; var paddedWidth = glyph.Width + Padding * 2; var paddedHeight = fontHeight + Padding * 2; if (position.X + paddedWidth + spacing >= texture.ImageSize.Width) { position.X = 0; position.Y += paddedHeight + spacing + lineAdditionalHeight; lineAdditionalHeight = 0; } if (glyph.VerticalOffset < -lineAdditionalHeight) { lineAdditionalHeight = -glyph.VerticalOffset; } if (position.Y + paddedHeight + spacing + lineAdditionalHeight >= texture.ImageSize.Height) { CreateNewFontTexture(); position = IntVector2.Zero; } CopyGlyphToTexture(glyph, texture, position + new IntVector2(Padding, Padding)); var fontChar = new FontChar { Char = code, UV0 = (Vector2)position / (Vector2)texture.ImageSize, UV1 = ((Vector2)position + new Vector2(paddedWidth, paddedHeight)) / (Vector2)texture.ImageSize, ACWidths = glyph.ACWidths, Width = glyph.Width, Height = fontHeight, RgbIntensity = glyph.RgbIntensity, KerningPairs = glyph.KerningPairs, TextureIndex = textureIndex, VerticalOffset = Math.Min(0, glyph.VerticalOffset), // Invisible glyphs doesn't have padding Padding = char.IsWhiteSpace(code) ? 0 : Padding, }; position.X += paddedWidth + spacing; return(fontChar); }
public float Kerning(FontChar prevChar) { if (prevChar != null && prevChar.KerningPairs != null) { foreach (var pair in prevChar.KerningPairs) { if (pair.Char == Char) { return(pair.Kerning); } } } return(0); }
private static bool ContainsKerningFor(this FontChar fontChar, char @char) { if (fontChar.KerningPairs != null) { foreach (var pair in fontChar.KerningPairs) { if (pair.Char == @char) { return(true); } } } return(false); }
private static void AddOrReplaceKerningPair(this FontChar fontChar, char @char, float kerning) { var pairs = fontChar.KerningPairs = fontChar.KerningPairs ?? new List <KerningPair>(); for (int i = 0; i < pairs.Count; i++) { var pair = pairs[i]; if (pair.Char == @char) { pair.Kerning = kerning; pairs[i] = pair; return; } } pairs.Add(new KerningPair { Char = @char, Kerning = kerning }); }
public static void DrawTextLine( IFont font, Vector2 position, string text, Color4 color, float fontHeight, int start, int length, float letterSpacing, SpriteList list, Action <int, Vector2, Vector2> onDrawChar = null, int tag = -1) { int j = 0; if (list != null) { for (int i = 0; i < length; i++) { char ch = text[i + start]; if (ch != '\n' && ch != '\r' && font.CharSource.Get(ch, fontHeight) != FontChar.Null) { ++j; } } } // Use array instead of list to reduce memory consumption. var chars = new SpriteList.CharDef[j]; j = 0; FontChar prevChar = null; var halfLetterSpacing = letterSpacing * .5f; float savedX = position.X; for (int i = 0; i < length; i++) { char ch = text[i + start]; if (ch == '\n') { onDrawChar?.Invoke(i, position, Vector2.Down * fontHeight); position.X = savedX; position.Y += fontHeight; prevChar = null; continue; } else if (ch == '\r') { continue; } FontChar fontChar = font.CharSource.Get(ch, fontHeight); if (fontChar == FontChar.Null) { onDrawChar?.Invoke(i, position, Vector2.Down * fontHeight); continue; } var scale = fontChar.Height != 0.0f ? fontHeight / fontChar.Height : 0.0f; // Pen Position + Bearing + Kerning gives a proper letter position // but we have to subtract left padding in order to avoid extra spacing position.X += scale * (fontChar.ACWidths.X + fontChar.Kerning(prevChar) + halfLetterSpacing - fontChar.Padding); var size = new Vector2(scale * fontChar.PaddedWidth, fontHeight + scale * (fontChar.PaddedHeight - fontChar.VerticalOffset)); var charPosition = new Vector2(position.X, position.Y + scale * (fontChar.VerticalOffset - fontChar.Padding)); if (font.RoundCoordinates) { charPosition = new Vector2(charPosition.X.Round(), charPosition.Y.Round()); } onDrawChar?.Invoke(i, charPosition, size); chars[j].FontChar = fontChar; chars[j].Position = charPosition; ++j; // We have to add left padding to get actual letter position // Width + (Horizontal Advance - Bearing) will give a proper pen position for next letter position.X += scale * (fontChar.Width + fontChar.ACWidths.Y + halfLetterSpacing + fontChar.Padding); prevChar = fontChar; } list.Add(font, color, fontHeight, chars, tag); }
public static void DrawTextLine( IFont font, Vector2 position, string text, Color4 color, float fontHeight, int start, int length, float letterSpacing, SpriteList list, Action <int, Vector2, Vector2> onDrawChar = null, int tag = -1) { int j = 0; if (list != null) { for (int i = 0; i < length; i++) { char ch = text[i + start]; if (ch != '\n' && ch != '\r' && font.Chars.Get(ch, fontHeight) != FontChar.Null) { ++j; } } } // Use array instead of list to reduce memory consumption. var chars = new SpriteList.CharDef[j]; j = 0; FontChar prevChar = null; float savedX = position.X; for (int i = 0; i < length; i++) { char ch = text[i + start]; if (ch == '\n') { onDrawChar?.Invoke(i, position, Vector2.Down * fontHeight); position.X = savedX; position.Y += fontHeight; prevChar = null; continue; } else if (ch == '\r') { continue; } FontChar fontChar = font.Chars.Get(ch, fontHeight); if (fontChar == FontChar.Null) { onDrawChar?.Invoke(i, position, Vector2.Down * fontHeight); continue; } float scale = fontChar.Height != 0.0f ? fontHeight / fontChar.Height : 0.0f; var xDelta = scale * (fontChar.ACWidths.X + fontChar.Kerning(prevChar) + letterSpacing); position.X += xDelta; var size = new Vector2(scale * fontChar.Width, fontHeight - fontChar.VerticalOffset); Vector2 roundPos; if (font.RoundCoordinates) { roundPos = new Vector2(position.X.Round(), position.Y.Round() + fontChar.VerticalOffset); onDrawChar?.Invoke(i, new Vector2((position.X - xDelta).Round(), position.Y.Round()), size); } else { roundPos = new Vector2(position.X, position.Y + fontChar.VerticalOffset); onDrawChar?.Invoke(i, new Vector2(position.X - xDelta, position.Y), size); } chars[j].FontChar = fontChar; chars[j].Position = roundPos; ++j; position.X += scale * (fontChar.Width + fontChar.ACWidths.Y); prevChar = fontChar; } list.Add(font, color, fontHeight, chars, tag); }