/// <summary> /// Creates a <see cref="TrippyFontFile"/> holding information for multiple fonts with the same size. /// </summary> /// <remarks>All the fonts have the same character range.</remarks> public static TrippyFontFile CreateFontFile(ReadOnlySpan <string> fontFiles, float size, char firstChar = ' ', char lastChar = '~', Color?backgroundColor = null) { IGlyphSource[] glyphSources = new IGlyphSource[fontFiles.Length]; for (int i = 0; i < glyphSources.Length; i++) { glyphSources[i] = new FontGlyphSource(FontInstance.LoadFont(fontFiles[i]), size, firstChar, lastChar); } return(FontBuilder.CreateFontFile(glyphSources, backgroundColor)); }
/// <summary> /// Creates a <see cref="TrippyFontFile"/> holding information for multiple fonts. /// </summary> /// <remarks>All the fonts have the same character range.</remarks> public static TrippyFontFile CreateFontFile(ReadOnlySpan <Font> fonts, char firstChar = ' ', char lastChar = '~', Color?backgroundColor = null) { IGlyphSource[] glyphSources = new IGlyphSource[fonts.Length]; for (int i = 0; i < fonts.Length; i++) { glyphSources[i] = new FontGlyphSource(fonts[i], firstChar, lastChar); } return(FontBuilder.CreateFontFile(glyphSources, backgroundColor)); }
protected override void Initialize() { base.Initialize(); Materials = new DefaultMaterialSet(RenderCoordinator); Alignment.Pressed += (s, e) => { Text.Alignment = (HorizontalAlignment)(((int)Text.Alignment + 1) % 7); }; CharacterWrap.Pressed += (s, e) => { Text.CharacterWrap = !Text.CharacterWrap; }; WordWrap.Pressed += (s, e) => { Text.WordWrap = !Text.WordWrap; }; Hinting.Pressed += (s, e) => { var ftf = (FreeTypeFont)LatinFont; ftf.Hinting = !ftf.Hinting; ftf.Invalidate(); ftf = (FreeTypeFont)UniFont; ftf.Hinting = !ftf.Hinting; ftf.Invalidate(); Text.Invalidate(); }; Margin.Pressed += (s, e) => { var ftf = (FreeTypeFont)LatinFont; ftf.GlyphMargin = (ftf.GlyphMargin + 1) % 6; ftf.Invalidate(); Text.Invalidate(); }; Monochrome.Pressed += (s, e) => { var ftf = (FreeTypeFont)LatinFont; ftf.Monochrome = !ftf.Monochrome; ftf.Invalidate(); ftf = (FreeTypeFont)UniFont; ftf.Monochrome = !ftf.Monochrome; ftf.Invalidate(); Text.Invalidate(); }; FreeType.Pressed += (s, e) => { if (ActiveFont == FallbackFont) { ActiveFont = new SpriteFontGlyphSource(DutchAndHarley); TextScale = 1f; } else { ActiveFont = FallbackFont; TextScale = 2f; } Text.GlyphSource = ActiveFont; Text.Scale = TextScale; }; }
protected override void LoadContent() { SpriteFont = new SpriteFontGlyphSource(Content.Load <SpriteFont>("font")); LatinFont = new FreeTypeFont(RenderCoordinator, "FiraSans-Regular.otf") { SizePoints = 40, DPIPercent = 200, GlyphMargin = 4, Gamma = 1.6 }; if (false) { LatinFont = new FreeTypeFont(RenderCoordinator, "cambria.ttc") { SizePoints = 40, DPIPercent = 200, GlyphMargin = 4, Gamma = 1.6 } } ; UniFont = new FreeTypeFont(RenderCoordinator, @"C:\Windows\Fonts\ArialUni.ttf") { SizePoints = 30, DPIPercent = 200, GlyphMargin = 4, Gamma = 1.6 }; FallbackFont = new FallbackGlyphSource(LatinFont, UniFont); ActiveFont = FallbackFont; Text = new DynamicStringLayout(ActiveFont, TestText) { // Alignment = HorizontalAlignment.Right, AlignToPixels = GlyphPixelAlignment.FloorY, CharacterWrap = true, WordWrap = true, Scale = 2f, ReverseOrder = true }; Text2 = new DynamicStringLayout(ActiveFont, TestText2) { AlignToPixels = GlyphPixelAlignment.FloorY, CharacterWrap = true, WordWrap = true, Scale = 2f, ReverseOrder = true }; }
public void DrawString( IGlyphSource glyphSource, string text, Vector2 position, Color?color = null, float scale = 1, DrawCallSortKey?sortKey = null, int characterSkipCount = 0, int characterLimit = int.MaxValue, int?layer = null, bool?worldSpace = null, BlendState blendState = null, SamplerState samplerState = null ) { using (var buffer = BufferPool <BitmapDrawCall> .Allocate(text.Length)) { var layout = glyphSource.LayoutString( text, new ArraySegment <BitmapDrawCall>(buffer.Data), position, color, scale, sortKey.GetValueOrDefault(NextSortKey), characterSkipCount, characterLimit, alignToPixels: true ); DrawMultiple( layout, layer: layer, worldSpace: worldSpace, blendState: blendState, samplerState: samplerState ); buffer.Clear(); } }
// Yuck :( public static StringLayout LayoutString( this IGlyphSource glyphSource, AbstractString text, ArraySegment <BitmapDrawCall>?buffer = null, Vector2?position = null, Color?color = null, float scale = 1, DrawCallSortKey sortKey = default(DrawCallSortKey), int characterSkipCount = 0, int?characterLimit = null, float xOffsetOfFirstLine = 0, float?lineBreakAtX = null, bool alignToPixels = false, Dictionary <char, KerningAdjustment> kerningAdjustments = null, bool wordWrap = false, char wrapCharacter = '\0' ) { var state = new StringLayoutEngine { position = position, color = color, scale = scale, sortKey = sortKey, characterSkipCount = characterSkipCount, characterLimit = characterLimit, xOffsetOfFirstLine = xOffsetOfFirstLine, lineBreakAtX = lineBreakAtX, alignToPixels = alignToPixels, characterWrap = lineBreakAtX.HasValue, wordWrap = wordWrap, wrapCharacter = wrapCharacter, buffer = buffer.GetValueOrDefault(default(ArraySegment <BitmapDrawCall>)) }; state.Initialize(); using (state) { var segment = state.AppendText( glyphSource, text, kerningAdjustments ); return(state.Finish()); } }
protected override void OnLoadContent(bool isReloading) { var margin = 6; LatinFont = new FreeTypeFont(RenderCoordinator, "FiraSans-Regular.otf") { SizePoints = 40, DPIPercent = 200, GlyphMargin = margin, Gamma = 1.6, DefaultGlyphColors = { { (uint)'h', Color.Red } } }; if (false) { LatinFont = new FreeTypeFont(RenderCoordinator, "cambria.ttc") { SizePoints = 40, DPIPercent = 200, GlyphMargin = margin, Gamma = 1.6 } } ; UniFont = new FreeTypeFont(RenderCoordinator, @"C:\Windows\Fonts\msgothic.ttc") { SizePoints = 30, DPIPercent = 200, GlyphMargin = margin, Gamma = 1.6 }; FallbackFont = new FallbackGlyphSource(LatinFont, UniFont); SmallLatinFont = new FreeTypeFont.FontSize((FreeTypeFont)LatinFont, 40 * 0.75f); ActiveFont = FallbackFont; Content.RootDirectory = ""; DutchAndHarley = Content.Load <SpriteFont>("DutchAndHarley"); Text = new DynamicStringLayout(ActiveFont, SelectedString) { AlignToPixels = GlyphPixelAlignment.RoundXY, CharacterWrap = true, WordWrap = true, Scale = TextScale, ReverseOrder = true, RichText = true, HideOverflow = true, RichTextConfiguration = new RichTextConfiguration { MarkedStringProcessor = ProcessMarkedString, Styles = new Dictionary <string, RichStyle> { { "quick", new RichStyle { Color = Color.Yellow } }, { "brown", new RichStyle { Color = Color.Brown, Scale = 2 } } }, GlyphSources = new Dictionary <string, IGlyphSource> { { "large", LatinFont }, { "small", SmallLatinFont } }, ImageProvider = Text_ImageProvider }, WordWrapCharacters = new uint[] { '\\', '/', ':', ',' }, }; for (int i = 0; i < Images.Length; i++) { using (var s = File.OpenRead($"{i + 1}.png")) Images[i] = Texture2D.FromStream(Graphics.GraphicsDevice, s); } }
public DynamicStringLayout(IGlyphSource font = null, string text = "") { _GlyphSource = font; _Text = text; }
public DynamicStringLayout(SpriteFont font, string text = "") { _GlyphSource = new SpriteFontGlyphSource(font); _Text = text; }
/// <summary> /// Creates a <see cref="TrippyFontFile"/> holding information for a single font. /// </summary> /// <param name="glyphSources">The <see cref="IGlyphSource"/> for getting the information of the font.</param> /// <param name="backgroundColor">The background color of the generated image. Null for transparent.</param> public static TrippyFontFile CreateFontFile(IGlyphSource glyphSources, Color?backgroundColor = null) { return(CreateFontFile(new IGlyphSource[] { glyphSources }, backgroundColor)); }
public static Dictionary <char, KerningAdjustment> GetDefaultKerningAdjustments(IGlyphSource font) { // FIXME if (font is SpriteFontGlyphSource) { Dictionary <char, KerningAdjustment> result; _DefaultKerningAdjustments.TryGetValue(((SpriteFontGlyphSource)font).Font, out result); return(result); } else { return(null); } }
public ArraySegment <BitmapDrawCall> AppendText( IGlyphSource font, AbstractString text, Dictionary <char, KerningAdjustment> kerningAdjustments = null ) { if (!IsInitialized) { throw new InvalidOperationException("Call Initialize first"); } if (font == null) { throw new ArgumentNullException("font"); } if (text.IsNull) { throw new ArgumentNullException("text"); } EnsureBufferCapacity(bufferWritePosition + text.Length); if (kerningAdjustments == null) { kerningAdjustments = StringLayout.GetDefaultKerningAdjustments(font); } var effectiveScale = scale / font.DPIScaleFactor; var drawCall = default(BitmapDrawCall); drawCall.MultiplyColor = color.GetValueOrDefault(Color.White); drawCall.ScaleF = effectiveScale; drawCall.SortKey = sortKey; float x = 0; float?defaultLineSpacing = null; for (int i = 0, l = text.Length; i < l; i++) { var ch = text[i]; bool isWhiteSpace = char.IsWhiteSpace(ch), forcedWrap = false, lineBreak = false, deadGlyph = false; Glyph glyph; KerningAdjustment kerningAdjustment; if (ch == '\r') { if (((i + 1) < l) && (text[i + 1] == '\n')) { i += 1; } lineBreak = true; } else if (ch == '\n') { lineBreak = true; } if (isWhiteSpace) { wordStartWritePosition = -1; wordWrapSuppressed = false; } else { if (wordStartWritePosition < 0) { wordStartWritePosition = bufferWritePosition; wordStartOffset = characterOffset; } } deadGlyph = !font.GetGlyph(ch, out glyph); float effectiveLineSpacing = glyph.LineSpacing; if (deadGlyph) { if (currentLineSpacing.HasValue) { effectiveLineSpacing = currentLineSpacing.Value; } else if (defaultLineSpacing.HasValue) { effectiveLineSpacing = defaultLineSpacing.Value; } else { Glyph space; if (font.GetGlyph(' ', out space)) { defaultLineSpacing = effectiveLineSpacing = space.LineSpacing; } } } if ((kerningAdjustments != null) && kerningAdjustments.TryGetValue(ch, out kerningAdjustment)) { glyph.LeftSideBearing += kerningAdjustment.LeftSideBearing; glyph.Width += kerningAdjustment.Width; glyph.RightSideBearing += kerningAdjustment.RightSideBearing; } x = characterOffset.X + glyph.LeftSideBearing + glyph.RightSideBearing + glyph.Width + glyph.CharacterSpacing; if ((x * effectiveScale) >= lineBreakAtX) { if ( !deadGlyph && (colIndex > 0) && !isWhiteSpace ) { forcedWrap = true; } } if (forcedWrap) { var currentWordSize = x - wordStartOffset.X; if (wordWrap && !wordWrapSuppressed && (currentWordSize * effectiveScale <= lineBreakAtX)) { WrapWord(buffer, wordStartOffset, wordStartWritePosition, bufferWritePosition - 1, effectiveScale, effectiveLineSpacing); wordWrapSuppressed = true; lineBreak = true; } else if (characterWrap) { characterOffset.X = xOffsetOfWrappedLine; characterOffset.Y += effectiveLineSpacing; maxX = Math.Max(maxX, currentLineMaxX * effectiveScale); wordStartWritePosition = bufferWritePosition; wordStartOffset = characterOffset; lineBreak = true; } } if (lineBreak) { if (!forcedWrap) { characterOffset.X = xOffsetOfNewLine; characterOffset.Y += effectiveLineSpacing; maxX = Math.Max(maxX, currentLineMaxX * effectiveScale); } initialLineXOffset = characterOffset.X; currentLineMaxX = 0; currentLineWhitespaceMaxX = 0; currentLineWhitespaceMaxXLeft = 0; rowIndex += 1; colIndex = 0; } if (deadGlyph) { characterSkipCount--; if (characterLimit.HasValue) { characterLimit--; } continue; } // HACK: Recompute after wrapping x = characterOffset.X + glyph.LeftSideBearing + glyph.RightSideBearing + glyph.Width + glyph.CharacterSpacing; characterOffset.X += glyph.CharacterSpacing; lastCharacterBounds = Bounds.FromPositionAndSize( characterOffset * effectiveScale, new Vector2( glyph.LeftSideBearing + glyph.Width + glyph.RightSideBearing, glyph.LineSpacing ) * effectiveScale ); if ((rowIndex == 0) && (colIndex == 0)) { firstCharacterBounds = lastCharacterBounds; } characterOffset.X += glyph.LeftSideBearing; if (colIndex == 0) { characterOffset.X = Math.Max(characterOffset.X, 0); } if (characterSkipCount <= 0) { if (characterLimit.HasValue && characterLimit.Value <= 0) { break; } var glyphPosition = new Vector2( actualPosition.X + (glyph.XOffset + characterOffset.X) * effectiveScale, actualPosition.Y + (glyph.YOffset + characterOffset.Y) * effectiveScale ); if (!isWhiteSpace) { if (bufferWritePosition >= buffer.Count) { EnsureBufferCapacity(bufferWritePosition); } drawCall.Texture = glyph.Texture; drawCall.TextureRegion = glyph.Texture.BoundsFromRectangle(ref glyph.BoundsInTexture); Snap(glyphPosition, out drawCall.Position); // HACK so that the alignment pass can detect rows. We strip this later. if (alignment != HorizontalAlignment.Left) { drawCall.SortKey.Order = rowIndex; } buffer.Array[buffer.Offset + bufferWritePosition] = drawCall; currentLineMaxX = Math.Max(currentLineMaxX, x); maxY = Math.Max(maxY, (characterOffset.Y + effectiveLineSpacing) * effectiveScale); bufferWritePosition += 1; drawCallsWritten += 1; } else { currentLineWhitespaceMaxXLeft = Math.Max(currentLineWhitespaceMaxXLeft, characterOffset.X); currentLineWhitespaceMaxX = Math.Max(currentLineWhitespaceMaxX, x); } characterLimit--; } else { characterSkipCount--; } characterOffset.X += (glyph.Width + glyph.RightSideBearing); currentLineSpacing = glyph.LineSpacing; maxLineSpacing = Math.Max(maxLineSpacing, effectiveLineSpacing); colIndex += 1; } var segment = new ArraySegment <BitmapDrawCall>( buffer.Array, buffer.Offset, drawCallsWritten ); maxX = Math.Max(maxX, currentLineMaxX * effectiveScale); return(segment); }