public static BitmapFont LoadFromFnt(string name, string text, Func <string, TextureRegion2D> textureLoader) { var data = new Cyotek.Drawing.BitmapFont.BitmapFont(); data.LoadText(text); // Resolve pages var pageRegions = new TextureRegion2D[data.Pages.Length]; for (var i = 0; i < data.Pages.Length; ++i) { var fn = data.Pages[i].FileName; var region = textureLoader(fn); if (region == null) { throw new Exception(string.Format("Unable to resolve texture {0}", fn)); } pageRegions[i] = region; } var glyphs = new List <BitmapFontRegion>(); foreach (var pair in data.Characters) { var character = pair.Value; var bounds = character.Bounds; var pageRegion = pageRegions[pair.Value.TexturePage]; bounds.Offset(pageRegion.X, pageRegion.Y); var region = new TextureRegion2D(pageRegion.Texture, bounds); var glyph = new BitmapFontRegion(region, character.Char, character.Offset.X, character.Offset.Y, character.XAdvance); glyphs.Add(glyph); } /* var characterMap = glyphs.ToDictionary(a => a.Character); * * // Process kernings * foreach (var pair in data.Kernings) * { * var kerning = pair.Key; * * BitmapFontRegion glyph; * if (!characterMap.TryGetValue(kerning.FirstCharacter, out glyph)) * { * continue; * } * * glyph.Kernings[kerning.SecondCharacter] = kerning.Amount; * }*/ var result = new BitmapFont(name, glyphs, data.LineHeight); return(result); }
protected override BitmapFont Read(ContentReader input, BitmapFont existingInstance) { var textureAssetCount = input.ReadInt32(); var assets = new List <string>(); for (var i = 0; i < textureAssetCount; i++) { var assetName = input.ReadString(); assets.Add(assetName); } var textures = assets .Select(textureName => input.ContentManager.Load <Texture2D>(textureName)) .ToArray(); var lineHeight = input.ReadInt32(); var regionCount = input.ReadInt32(); var regions = new BitmapFontRegion[regionCount]; for (var r = 0; r < regionCount; r++) { var character = input.ReadInt32(); var textureIndex = input.ReadInt32(); var x = input.ReadInt32(); var y = input.ReadInt32(); var width = input.ReadInt32(); var height = input.ReadInt32(); var xOffset = input.ReadInt32(); var yOffset = input.ReadInt32(); var xAdvance = input.ReadInt32(); var textureRegion = new TextureRegion2D(textures[textureIndex], x, y, width, height); regions[r] = new BitmapFontRegion(textureRegion, character, xOffset, yOffset, xAdvance); } var characterMap = regions.ToDictionary(r => r.Character); var kerningsCount = input.ReadInt32(); for (var k = 0; k < kerningsCount; k++) { var first = input.ReadInt32(); var second = input.ReadInt32(); var amount = input.ReadInt32(); // Find region if (!characterMap.TryGetValue(first, out var region)) { continue; } region.Kernings[second] = amount; } return(new BitmapFont(input.AssetName, regions, lineHeight)); }
private BitmapFont LoadCustomMonospacedFont() { // this is a way to create a font in pure code without a font file. const string characters = @" !""#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; var monospacedTexture = Content.Load <Texture2D>("Fonts/monospaced"); var atlas = TextureAtlas.Create("monospaced-atlas", monospacedTexture, 16, 16); var fontRegions = new BitmapFontRegion[characters.Length]; var index = 0; for (var y = 0; y < monospacedTexture.Height; y += 16) { for (var x = 0; x < monospacedTexture.Width; x += 16) { if (index < characters.Length) { fontRegions[index] = new BitmapFontRegion(atlas[index], characters[index], 0, 0, 16); index++; } } } return(new BitmapFont("monospaced", fontRegions, 16)); }
/// <summary> /// Draws unicode (UTF-16) characters as sprites using the specified <see cref="BitmapFont" />, text /// <see cref="StringBuilder" />, transform <see cref="Matrix2D" /> and optional <see cref="Color" />, origin /// <see cref="Vector2" />, <see cref="FlipFlags" />, and depth <see cref="float" />. /// </summary> /// <param name="bitmapFont">The <see cref="BitmapFont" />.</param> /// <param name="text">The text <see cref="StringBuilder" />.</param> /// <param name="transformMatrix">The transform <see cref="Matrix2D" />.</param> /// <param name="color"> /// The <see cref="Color" />. Use <code>null</code> to use the default /// <see cref="Color.White" />. /// </param> /// <param name="flags">The <see cref="FlipFlags" />. The default value is <see cref="FlipFlags.None" />.</param> /// <param name="depth">The depth <see cref="float" />. The default value is <code>0f</code>.</param> /// <exception cref="InvalidOperationException">The <see cref="Batcher{TDrawCallInfo}.Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> method has not been called.</exception> /// <exception cref="ArgumentNullException"><paramref name="bitmapFont" /> is null or <paramref name="text" /> is null.</exception> public void DrawString(BitmapFont bitmapFont, StringBuilder text, ref Matrix2D transformMatrix, Color?color = null, FlipFlags flags = FlipFlags.None, float depth = 0f) { EnsureHasBegun(); if (bitmapFont == null) { throw new ArgumentNullException(nameof(bitmapFont)); } if (text == null) { throw new ArgumentNullException(nameof(text)); } var lineSpacing = bitmapFont.LineHeight; var offset = new Vector2(0, 0); BitmapFontRegion lastGlyph = null; for (var i = 0; i < text.Length;) { int character; if (char.IsLowSurrogate(text[i])) { character = char.ConvertToUtf32(text[i - 1], text[i]); i += 2; } else if (char.IsHighSurrogate(text[i])) { character = char.ConvertToUtf32(text[i], text[i - 1]); i += 2; } else { character = text[i]; i += 1; } // ReSharper disable once SwitchStatementMissingSomeCases switch (character) { case '\r': continue; case '\n': offset.X = 0; offset.Y += lineSpacing; lastGlyph = null; continue; } var fontRegion = bitmapFont.GetCharacterRegion(character); if (fontRegion == null) { continue; } var transform1Matrix = transformMatrix; transform1Matrix.M31 += offset.X + fontRegion.XOffset; transform1Matrix.M32 += offset.Y + fontRegion.YOffset; var textureRegion = fontRegion.TextureRegion; var bounds = textureRegion.Bounds; DrawSprite(textureRegion.Texture, ref transform1Matrix, ref bounds, color, flags, depth); var advance = fontRegion.XAdvance + bitmapFont.LetterSpacing; if (BitmapFont.UseKernings && lastGlyph != null) { int amount; if (lastGlyph.Kernings.TryGetValue(character, out amount)) { advance += amount; } } offset.X += i != text.Length - 1 ? advance : fontRegion.XOffset + fontRegion.Width; lastGlyph = fontRegion; } }
/// <summary> /// compiles the text into raw verts/texture coordinates. This method must be called anytime text or any other properties are /// changed. /// </summary> public void compile() { _charDetails = new CharDetails[_text.Length]; BitmapFontRegion currentFontRegion = null; var effects = (byte)SpriteEffects.None; var _transformationMatrix = Matrix2D.Identity; var requiresTransformation = rotation != 0f || _scale != Vector2.One; if (requiresTransformation) { Matrix2D temp; Matrix2D.CreateTranslation(-_origin.X, -_origin.Y, out _transformationMatrix); Matrix2D.CreateScale(_scale.X, _scale.Y, out temp); Matrix2D.Multiply(ref _transformationMatrix, ref temp, out _transformationMatrix); Matrix2D.CreateRotationZ(rotation, out temp); Matrix2D.Multiply(ref _transformationMatrix, ref temp, out _transformationMatrix); Matrix2D.CreateTranslation(position.X, position.Y, out temp); Matrix2D.Multiply(ref _transformationMatrix, ref temp, out _transformationMatrix); } var offset = requiresTransformation ? Vector2.Zero : position - _origin; for (var i = 0; i < _text.Length; ++i) { _charDetails[i].initialize(); _charDetails[i].color = _color; var c = _text[i]; if (c == '\n') { offset.X = requiresTransformation ? 0f : position.X - _origin.X; offset.Y += _font.lineHeight; currentFontRegion = null; continue; } if (currentFontRegion != null) { offset.X += _font.spacing + currentFontRegion.xAdvance; } currentFontRegion = _font.fontRegionForChar(c, true); var p = offset; p.X += currentFontRegion.xOffset; p.Y += currentFontRegion.yOffset; // transform our point if we need to if (requiresTransformation) { Vector2Ext.Transform(ref p, ref _transformationMatrix, out p); } var destination = new Vector4(p.X, p.Y, currentFontRegion.width * _scale.X, currentFontRegion.height * _scale.Y); _charDetails[i].texture = currentFontRegion.subtexture.texture2D; // Batcher calculations var sourceRectangle = currentFontRegion.subtexture.sourceRect; float sourceX, sourceY, sourceW, sourceH; var destW = destination.Z; var destH = destination.W; // calculate uvs var inverseTexW = 1.0f / (float)currentFontRegion.subtexture.texture2D.Width; var inverseTexH = 1.0f / (float)currentFontRegion.subtexture.texture2D.Height; sourceX = sourceRectangle.X * inverseTexW; sourceY = sourceRectangle.Y * inverseTexH; sourceW = Math.Max(sourceRectangle.Width, float.Epsilon) * inverseTexW; sourceH = Math.Max(sourceRectangle.Height, float.Epsilon) * inverseTexH; // Rotation Calculations float rotationMatrix1X; float rotationMatrix1Y; float rotationMatrix2X; float rotationMatrix2Y; if (!Mathf.withinEpsilon(rotation, 0.0f)) { var sin = Mathf.sin(rotation); var cos = Mathf.cos(rotation); rotationMatrix1X = cos; rotationMatrix1Y = sin; rotationMatrix2X = -sin; rotationMatrix2Y = cos; } else { rotationMatrix1X = 1.0f; rotationMatrix1Y = 0.0f; rotationMatrix2X = 0.0f; rotationMatrix2Y = 1.0f; } // Calculate vertices, finally. // top-left _charDetails[i].verts[0].X = rotationMatrix2X + rotationMatrix1X + destination.X - 1; _charDetails[i].verts[0].Y = rotationMatrix2Y + rotationMatrix1Y + destination.Y - 1; // top-right var cornerX = _cornerOffsetX[1] * destW; var cornerY = _cornerOffsetY[1] * destH; _charDetails[i].verts[1].X = ( (rotationMatrix2X * cornerY) + (rotationMatrix1X * cornerX) + destination.X ); _charDetails[i].verts[1].Y = ( (rotationMatrix2Y * cornerY) + (rotationMatrix1Y * cornerX) + destination.Y ); // bottom-left cornerX = _cornerOffsetX[2] * destW; cornerY = _cornerOffsetY[2] * destH; _charDetails[i].verts[2].X = ( (rotationMatrix2X * cornerY) + (rotationMatrix1X * cornerX) + destination.X ); _charDetails[i].verts[2].Y = ( (rotationMatrix2Y * cornerY) + (rotationMatrix1Y * cornerX) + destination.Y ); // bottom-right cornerX = _cornerOffsetX[3] * destW; cornerY = _cornerOffsetY[3] * destH; _charDetails[i].verts[3].X = ( (rotationMatrix2X * cornerY) + (rotationMatrix1X * cornerX) + destination.X ); _charDetails[i].verts[3].Y = ( (rotationMatrix2Y * cornerY) + (rotationMatrix1Y * cornerX) + destination.Y ); // texture coordintes _charDetails[i].texCoords[0].X = (_cornerOffsetX[0 ^ effects] * sourceW) + sourceX; _charDetails[i].texCoords[0].Y = (_cornerOffsetY[0 ^ effects] * sourceH) + sourceY; _charDetails[i].texCoords[1].X = (_cornerOffsetX[1 ^ effects] * sourceW) + sourceX; _charDetails[i].texCoords[1].Y = (_cornerOffsetY[1 ^ effects] * sourceH) + sourceY; _charDetails[i].texCoords[2].X = (_cornerOffsetX[2 ^ effects] * sourceW) + sourceX; _charDetails[i].texCoords[2].Y = (_cornerOffsetY[2 ^ effects] * sourceH) + sourceY; _charDetails[i].texCoords[3].X = (_cornerOffsetX[3 ^ effects] * sourceW) + sourceX; _charDetails[i].texCoords[3].Y = (_cornerOffsetY[3 ^ effects] * sourceH) + sourceY; } }