public async Task <string> ListCharacters(CharacterSource source) { List <Person> people; if (source == CharacterSource.File) { string filePath = @"Adapter/People.json"; var characterSource = new CharacterFileSource(); people = await characterSource.GetCharactersFromFile(filePath); } else if (source == CharacterSource.Api) { var swapiSource = new StarWarsApi(); people = await swapiSource.GetCharacters(); } else { throw new Exception("Invalid character source"); } var sb = new StringBuilder(); int nameWidth = 30; sb.AppendLine($"{"NAME".PadRight(nameWidth)} {"HAIR"}"); foreach (Person person in people) { sb.AppendLine($"{person.Name.PadRight(nameWidth)} {person.HairColor}"); } return(sb.ToString()); }
public async Task <string> ListCharacters(CharacterSource source) { List <Person> people; if (source == CharacterSource.File) { string filePath = @"Adapter/People.json"; people = JsonConvert.DeserializeObject <List <Person> >(await File.ReadAllTextAsync(filePath)); } else if (source == CharacterSource.Api) { using (var client = new HttpClient()) { string url = "https://swapi.co/api/people"; string result = await client.GetStringAsync(url); people = JsonConvert.DeserializeObject <ApiResult <Person> >(result).Results; } } else { throw new Exception("Invalid character source"); } var sb = new StringBuilder(); int nameWidth = 30; sb.AppendLine($"{"NAME".PadRight(nameWidth)} {"HAIR"}"); foreach (Person person in people) { sb.AppendLine($"{person.Name.PadRight(nameWidth)} {person.HairColor}"); } return(sb.ToString()); }
void measureString(ref CharacterSource text, out Vector2 size) { if (text.Length == 0) { size = Vector2.Zero; return; } var width = 0.0f; var finalLineHeight = (float)lineHeight; var fullLineCount = 0; BitmapFontRegion currentFontRegion = null; var offset = Vector2.Zero; for (var i = 0; i < text.Length; i++) { var c = text[i]; if (c == '\r') { continue; } if (c == '\n') { fullLineCount++; finalLineHeight = lineHeight; offset.X = 0; offset.Y = lineHeight * fullLineCount; currentFontRegion = null; continue; } if (currentFontRegion != null) { offset.X += spacing + currentFontRegion.xAdvance; } if (!_characterMap.TryGetValue(c, out currentFontRegion)) { currentFontRegion = _defaultCharacterRegion; } var proposedWidth = offset.X + currentFontRegion.xAdvance + spacing; if (proposedWidth > width) { width = proposedWidth; } if (currentFontRegion.height + currentFontRegion.yOffset > finalLineHeight) { finalLineHeight = currentFontRegion.height + currentFontRegion.yOffset; } } size.X = width; size.Y = fullLineCount * lineHeight + finalLineHeight; }
internal void DrawInto( SpriteBatch spriteBatch, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effect, float depth) { var source = new CharacterSource(text); DrawInto(spriteBatch, ref source, position, color, rotation, origin, scale, effect, depth); }
/// <summary> /// Returns the size of the contents of a StringBuilder when /// rendered in this font. /// </summary> /// <param name="text">The text to measure.</param> /// <returns>The size, in pixels, of 'text' when rendered in /// this font.</returns> public Vector2 MeasureString(StringBuilder text) { CharacterSource source = new CharacterSource(text); Vector2 size; MeasureString(ref source, out size); return(size); }
/// <summary> /// Returns the size of a string when rendered in this font. /// </summary> /// <param name="text">The text to measure.</param> /// <returns>The size, in pixels, of 'text' when rendered in /// this font.</returns> public Vector2 MeasureString(string text) { var source = new CharacterSource(text); Vector2 size; MeasureString(ref source, out size); return(size); }
private void MeasureString(ref CharacterSource text, out Vector2 size) { if (text.Length == 0) { size = Vector2.Zero; return; } PerThreadTypesetter.Reset(this); for (int i = 0; i < text.Length; ++i) { PerThreadTypesetter.Input(text [i]); } PerThreadTypesetter.GetSize(out size); }
private void DrawInto( SpriteBatch spriteBatch, ref CharacterSource text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effect, float depth) { Matrix transformation; CreateTransformationFromDrawParameters( ref text, position, rotation, origin, scale, effect, out transformation); var flippedVert = (effect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically; var flippedHorz = (effect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally; PerThreadTypesetter.Reset(this); for (int i = 0; i < text.Length; ++i) { PerThreadTypesetter.Input(text [i]); if (!PerThreadTypesetter.HasCurrentGlyph) { continue; } var p = PerThreadTypesetter.Offset; if (flippedHorz) { p.X += PerThreadTypesetter.CurrentGlyph.BoundsInTexture.Width; } p.X += PerThreadTypesetter.CurrentGlyph.LeftSideBearing; if (flippedVert) { p.Y += PerThreadTypesetter.CurrentGlyph.BoundsInTexture.Height - LineSpacing; } p.Y += PerThreadTypesetter.CurrentGlyph.Cropping.Y; Vector2.Transform(ref p, ref transformation, out p); spriteBatch.Draw( _texture, p, PerThreadTypesetter.CurrentGlyph.BoundsInTexture, color, rotation, Vector2.Zero, scale, effect, depth); } }
private void CreateTransformationFromDrawParameters( ref CharacterSource text, Vector2 position, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effect, out Matrix transformation) { Vector2 flipAdjustment = Vector2.Zero; if ((effect & (SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically)) != 0) { Vector2 size; MeasureString(ref text, out size); if ((effect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X *= -1; scale.X *= -1; flipAdjustment.X = -size.X; } if ((effect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { origin.Y *= -1; scale.Y *= -1; flipAdjustment.Y = LineSpacing - size.Y; } } Matrix temp; Matrix.CreateTranslation(-origin.X, -origin.Y, 0f, out transformation); Matrix.CreateScale(scale.X, scale.Y, 1f, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); Matrix.CreateTranslation(flipAdjustment.X, flipAdjustment.Y, 0, out temp); Matrix.Multiply(ref temp, ref transformation, out transformation); Matrix.CreateRotationZ(rotation, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); Matrix.CreateTranslation(position.X, position.Y, 0f, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); }
public async Task <ActionResult> Get(CharacterSource source) { try { List <Character> people; if (source == CharacterSource.File) { string filePath = @"../DesignPatterns/Structural/Adapter/Data/People.json"; people = JsonConvert.DeserializeObject <List <Character> >(await System.IO.File.ReadAllTextAsync(filePath)); } else if (source == CharacterSource.Api) { using (var client = new HttpClient()) { string url = "https://someurl.com/api/characters"; string result = await client.GetStringAsync(url); people = JsonConvert.DeserializeObject <ApiResult <Character> >(result).Results; } } else { throw new Exception("Invalid character source"); } var sb = new StringBuilder(); int nameWidth = 30; sb.AppendLine($"{"NAME".PadRight(nameWidth)} {"BirthYear"}"); foreach (Character character in people) { sb.AppendLine($"{character.Name.PadRight(nameWidth)} {character.BirthYear}"); } return(Ok(sb.ToString())); } catch (Exception ex) { _logger.LogError(ex.ToString()); return(StatusCode(StatusCodes.Status500InternalServerError)); } }
public async Task <string> ListCharacters(CharacterSource source) { List <Person> people; if (source == CharacterSource.File) { string filePath = @"Adapter/People.json"; people = JsonConvert.DeserializeObject <List <Person> >(await File.ReadAllTextAsync(filePath)); } else if (source == CharacterSource.Api) { var handler = new HttpClientHandler() { AllowAutoRedirect = true }; using (var client = new HttpClient(handler)) { string url = ApiConstants.SWAPI_PEOPLE_ENDPOINT; string result = await client.GetStringAsync(url); people = JsonConvert.DeserializeObject <ApiResult <Person> >(result).Results; } } else { throw new Exception("Invalid character source"); } var sb = new StringBuilder(); int nameWidth = 30; sb.AppendLine($"{"NAME".PadRight(nameWidth)} {"HAIR"}"); foreach (Person person in people) { sb.AppendLine($"{person.Name.PadRight(nameWidth)} {person.HairColor}"); } return(sb.ToString()); }
internal void DrawInto(SpriteBatch spriteBatch, ref CharacterSource text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effect, float depth) { var flipAdjustment = Vector2.Zero; var flippedVert = (effect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically; var flippedHorz = (effect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally; if (flippedVert || flippedHorz) { Vector2 size; MeasureString(ref text, out size); if (flippedHorz) { origin.X *= -1; scale.X *= -1; flipAdjustment.X = -size.X; } if (flippedVert) { origin.Y *= -1; scale.Y *= -1; flipAdjustment.Y = LineSpacing - size.Y; } } // TODO: This looks excessive... i suspect we could do most // of this with simple vector math and avoid this much matrix work. Matrix transformation, temp; Matrix.CreateTranslation(-origin.X, -origin.Y, 0f, out transformation); Matrix.CreateScale(scale.X, scale.Y, 1f, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); Matrix.CreateTranslation(flipAdjustment.X, flipAdjustment.Y, 0, out temp); Matrix.Multiply(ref temp, ref transformation, out transformation); Matrix.CreateRotationZ(rotation, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); Matrix.CreateTranslation(position.X, position.Y, 0f, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); // Get the default glyph here once. Glyph?defaultGlyph = null; if (DefaultCharacter.HasValue) { defaultGlyph = _glyphs[DefaultCharacter.Value]; } var currentGlyph = Glyph.Empty; var offset = Vector2.Zero; var hasCurrentGlyph = false; var firstGlyphOfLine = true; for (var i = 0; i < text.Length; ++i) { var c = text[i]; if (c == '\r') { hasCurrentGlyph = false; continue; } if (c == '\n') { offset.X = 0; offset.Y += LineSpacing; hasCurrentGlyph = false; firstGlyphOfLine = true; continue; } if (hasCurrentGlyph) { offset.X += Spacing + currentGlyph.Width + currentGlyph.RightSideBearing; } hasCurrentGlyph = _glyphs.TryGetValue(c, out currentGlyph); if (!hasCurrentGlyph) { if (!defaultGlyph.HasValue) { throw new ArgumentException(Errors.TextContainsUnresolvableCharacters, "text"); } currentGlyph = defaultGlyph.Value; hasCurrentGlyph = true; } if (hasCurrentGlyph) { // The first character on a line might have a negative left side bearing. // In this scenario, SpriteBatch/SpriteFont normally offset the text to the right, // so that text does not hang off the left side of its rectangle. if (firstGlyphOfLine) { offset.X = Math.Max(offset.X, 0); firstGlyphOfLine = false; } else { offset.X += currentGlyph.LeftSideBearing; } } var p = offset; if (flippedHorz) { p.X += currentGlyph.BoundsInTexture.Width; } p.X += currentGlyph.Cropping.X; if (flippedVert) { p.Y += currentGlyph.BoundsInTexture.Height - LineSpacing; } p.Y += currentGlyph.Cropping.Y; Vector2.Transform(ref p, ref transformation, out p); var destRect = new Vector4(p.X, p.Y, currentGlyph.BoundsInTexture.Width * scale.X, currentGlyph.BoundsInTexture.Height * scale.Y); // TODO: We're passing SpriteEffects thru here unchanged, but // it seems we're applyting the flips ourselves above. // // This just might be a bug! spriteBatch.DrawInternal( _texture, destRect, currentGlyph.BoundsInTexture, color, rotation, Vector2.Zero, effect, depth); } }
internal void DrawInto( SpriteBatch spriteBatch, ref CharacterSource text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effect, float depth) { var flipAdjustment = Vector2.Zero; var flippedVert = (effect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically; var flippedHorz = (effect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally; if (flippedVert || flippedHorz) { Vector2 size; MeasureString(ref text, out size); if (flippedHorz) { origin.X *= -1; flipAdjustment.X = -size.X; } if (flippedVert) { origin.Y *= -1; flipAdjustment.Y = LineSpacing - size.Y; } } // TODO: This looks excessive... i suspect we could do most // of this with simple vector math and avoid this much matrix work. Matrix transformation, temp; Matrix.CreateTranslation(-origin.X, -origin.Y, 0f, out transformation); Matrix.CreateScale((flippedHorz ? -scale.X : scale.X), (flippedVert ? -scale.Y : scale.Y), 1f, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); Matrix.CreateTranslation(flipAdjustment.X, flipAdjustment.Y, 0, out temp); Matrix.Multiply(ref temp, ref transformation, out transformation); Matrix.CreateRotationZ(rotation, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); Matrix.CreateTranslation(position.X, position.Y, 0f, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); // Get the default glyph here once. Glyph? defaultGlyph = null; if (DefaultCharacter.HasValue) defaultGlyph = _glyphs[DefaultCharacter.Value]; var currentGlyph = Glyph.Empty; var offset = Vector2.Zero; var hasCurrentGlyph = false; var firstGlyphOfLine = true; for (var i = 0; i < text.Length; ++i) { var c = text[i]; if (c == '\r') { hasCurrentGlyph = false; continue; } if (c == '\n') { offset.X = 0; offset.Y += LineSpacing; hasCurrentGlyph = false; firstGlyphOfLine = true; continue; } if (hasCurrentGlyph) { offset.X += Spacing + currentGlyph.Width + currentGlyph.RightSideBearing; } hasCurrentGlyph = _glyphs.TryGetValue(c, out currentGlyph); if (!hasCurrentGlyph) { if (!defaultGlyph.HasValue) throw new ArgumentException(Errors.TextContainsUnresolvableCharacters, "text"); currentGlyph = defaultGlyph.Value; hasCurrentGlyph = true; } if (hasCurrentGlyph) { // The first character on a line might have a negative left side bearing. // In this scenario, SpriteBatch/SpriteFont normally offset the text to the right, // so that text does not hang off the left side of its rectangle. if (firstGlyphOfLine) { offset.X = Math.Max(offset.X, 0); firstGlyphOfLine = false; } else { offset.X += currentGlyph.LeftSideBearing; } } var p = offset; if (flippedHorz) p.X += currentGlyph.BoundsInTexture.Width; p.X += currentGlyph.Cropping.X; if (flippedVert) p.Y += currentGlyph.BoundsInTexture.Height - LineSpacing; p.Y += currentGlyph.Cropping.Y; Vector2.Transform(ref p, ref transformation, out p); var destRect = new Vector4( p.X, p.Y, currentGlyph.BoundsInTexture.Width * scale.X, currentGlyph.BoundsInTexture.Height * scale.Y); spriteBatch.DrawInternal( _texture, destRect, currentGlyph.BoundsInTexture, color, rotation, Vector2.Zero, effect, depth, false); } // We need to flush if we're using Immediate sort mode. spriteBatch.FlushIfNeeded(); }
private void MeasureString(ref CharacterSource text, out Vector2 size) { if (text.Length == 0) { size = Vector2.Zero; return; } // Get the default glyph here once. Glyph? defaultGlyph = null; if ( DefaultCharacter.HasValue ) defaultGlyph = _glyphs[DefaultCharacter.Value]; var width = 0.0f; var finalLineHeight = (float)LineSpacing; var fullLineCount = 0; var currentGlyph = Glyph.Empty; var offset = Vector2.Zero; var hasCurrentGlyph = false; var firstGlyphOfLine = true; for (var i = 0; i < text.Length; ++i) { var c = text[i]; if (c == '\r') { hasCurrentGlyph = false; continue; } if (c == '\n') { fullLineCount++; finalLineHeight = LineSpacing; offset.X = 0; offset.Y = LineSpacing * fullLineCount; hasCurrentGlyph = false; firstGlyphOfLine = true; continue; } if (hasCurrentGlyph) { offset.X += Spacing; // The first character on a line might have a negative left side bearing. // In this scenario, SpriteBatch/SpriteFont normally offset the text to the right, // so that text does not hang off the left side of its rectangle. if (firstGlyphOfLine) { offset.X = Math.Max(offset.X + Math.Abs(currentGlyph.LeftSideBearing), 0); firstGlyphOfLine = false; } else { offset.X += currentGlyph.LeftSideBearing; } offset.X += currentGlyph.Width + currentGlyph.RightSideBearing; } hasCurrentGlyph = _glyphs.TryGetValue(c, out currentGlyph); if (!hasCurrentGlyph) { if (!defaultGlyph.HasValue) throw new ArgumentException(Errors.TextContainsUnresolvableCharacters, "text"); currentGlyph = defaultGlyph.Value; hasCurrentGlyph = true; } var proposedWidth = offset.X + currentGlyph.WidthIncludingBearings; if (proposedWidth > width) width = proposedWidth; if (currentGlyph.Cropping.Height > finalLineHeight) finalLineHeight = currentGlyph.Cropping.Height; } size.X = width; size.Y = fullLineCount * LineSpacing + finalLineHeight; }
private void MeasureString(ref CharacterSource text, out Vector2 size) { if (text.Length == 0) { size = Vector2.Zero; return; } PerThreadTypesetter.Reset (this); for (int i = 0; i < text.Length; ++i) PerThreadTypesetter.Input (text [i]); PerThreadTypesetter.GetSize (out size); }
private void MeasureString(ref CharacterSource text, out Vector2 size) { if (text.Length == 0) { size = Vector2.Zero; return; } // Get the default glyph here once. Glyph? defaultGlyph = null; if ( DefaultCharacter.HasValue ) defaultGlyph = _glyphs[DefaultCharacter.Value]; var width = 0.0f; var finalLineHeight = (float)LineSpacing; var fullLineCount = 0; var currentGlyph = Glyph.Empty; var offset = Vector2.Zero; var hasCurrentGlyph = false; for (var i = 0; i < text.Length; ++i) { var c = text[i]; if (c == '\r') { hasCurrentGlyph = false; continue; } if (c == '\n') { fullLineCount++; finalLineHeight = LineSpacing; offset.X = 0; offset.Y = LineSpacing * fullLineCount; hasCurrentGlyph = false; continue; } if (hasCurrentGlyph) offset.X += Spacing + currentGlyph.WidthIncludingBearings; hasCurrentGlyph = _glyphs.TryGetValue(c, out currentGlyph); if (!hasCurrentGlyph) { if (!defaultGlyph.HasValue) throw new ArgumentException(Errors.TextContainsUnresolvableCharacters, "text"); currentGlyph = defaultGlyph.Value; hasCurrentGlyph = true; } var proposedWidth = offset.X + currentGlyph.WidthIncludingBearings; if (proposedWidth > width) width = proposedWidth; if (currentGlyph.Cropping.Height > finalLineHeight) finalLineHeight = currentGlyph.Cropping.Height; } size.X = width; size.Y = fullLineCount * LineSpacing + finalLineHeight; }
internal void MeasureString(ref CharacterSource text, out Vector2 size) { if (text.Length == 0) { size = Vector2.Zero; return; } // Get the default glyph here once. Glyph?defaultGlyph = null; if (DefaultCharacter.HasValue) { defaultGlyph = _glyphs[DefaultCharacter.Value]; } var width = 0.0f; var finalLineHeight = (float)LineSpacing; var currentGlyph = Glyph.Empty; var offset = Vector2.Zero; var firstGlyphOfLine = true; for (var i = 0; i < text.Length; ++i) { var c = text[i]; if (c == '\r') { continue; } if (c == '\n') { finalLineHeight = LineSpacing; offset.X = 0; offset.Y += LineSpacing; firstGlyphOfLine = true; continue; } if (!_glyphs.TryGetValue(c, out currentGlyph)) { if (!defaultGlyph.HasValue) { throw new ArgumentException(Errors.TextContainsUnresolvableCharacters, "text"); } currentGlyph = defaultGlyph.Value; } // The first character on a line might have a negative left side bearing. // In this scenario, SpriteBatch/SpriteFont normally offset the text to the right, // so that text does not hang off the left side of its rectangle. if (firstGlyphOfLine) { offset.X = Math.Max(currentGlyph.LeftSideBearing, 0); firstGlyphOfLine = false; } else { offset.X += Spacing + currentGlyph.LeftSideBearing; } offset.X += currentGlyph.Width; var proposedWidth = offset.X + Math.Max(currentGlyph.RightSideBearing, 0); if (proposedWidth > width) { width = proposedWidth; } offset.X += currentGlyph.RightSideBearing; if (currentGlyph.Cropping.Height > finalLineHeight) { finalLineHeight = currentGlyph.Cropping.Height; } } size.X = width; size.Y = offset.Y + finalLineHeight; }
/// <summary> /// Returns the size of a string when rendered in this font. /// </summary> /// <param name="text">The text to measure.</param> /// <returns>The size, in pixels, of 'text' when rendered in /// this font.</returns> public Vector2 MeasureString(string text) { var source = new CharacterSource(text); Vector2 size; MeasureString(ref source, out size); return size;// * Scaler.Scale; }
private void MeasureString(ref CharacterSource text, out Vector2 size) { if (text.Length == 0) { size = Vector2.Zero; return; } // Get the default glyph here once. Glyph?defaultGlyph = null; if (DefaultCharacter.HasValue) { defaultGlyph = _glyphs[DefaultCharacter.Value]; } var width = 0.0f; var finalLineHeight = (float)LineSpacing; var fullLineCount = 0; var currentGlyph = Glyph.Empty; var offset = Vector2.Zero; var hasCurrentGlyph = false; for (var i = 0; i < text.Length; ++i) { var c = text[i]; if (c == '\r') { hasCurrentGlyph = false; continue; } if (c == '\n') { fullLineCount++; finalLineHeight = LineSpacing; offset.X = 0; offset.Y = LineSpacing * fullLineCount; hasCurrentGlyph = false; continue; } if (hasCurrentGlyph) { offset.X += Spacing + currentGlyph.WidthIncludingBearings; } hasCurrentGlyph = _glyphs.TryGetValue(c, out currentGlyph); if (!hasCurrentGlyph) { if (!defaultGlyph.HasValue) { throw new ArgumentException(Errors.TextContainsUnresolvableCharacters, "text"); } currentGlyph = defaultGlyph.Value; hasCurrentGlyph = true; } var proposedWidth = offset.X + currentGlyph.WidthIncludingBearings; if (proposedWidth > width) { width = proposedWidth; } if (currentGlyph.Cropping.Height > finalLineHeight) { finalLineHeight = currentGlyph.Cropping.Height; } } size.X = width; size.Y = fullLineCount * LineSpacing + finalLineHeight; }
internal void DrawInto( SpriteBatch spriteBatch, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effect, float depth) { var source = new CharacterSource (text); DrawInto (spriteBatch, ref source, position, color, rotation, origin, scale, effect, depth); }
private void CreateTransformationFromDrawParameters( ref CharacterSource text, Vector2 position, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effect, out Matrix transformation) { Vector2 flipAdjustment = Vector2.Zero; if ((effect & (SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically)) != 0) { Vector2 size; MeasureString (ref text, out size); if ((effect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X *= -1; scale.X *= -1; flipAdjustment.X = -size.X; } if ((effect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { origin.Y *= -1; scale.Y *= -1; flipAdjustment.Y = LineSpacing - size.Y; } } Matrix temp; Matrix.CreateTranslation (-origin.X, -origin.Y, 0f, out transformation); Matrix.CreateScale (scale.X, scale.Y, 1f, out temp); Matrix.Multiply (ref transformation, ref temp, out transformation); Matrix.CreateTranslation (flipAdjustment.X, flipAdjustment.Y, 0, out temp); Matrix.Multiply (ref temp, ref transformation, out transformation); Matrix.CreateRotationZ (rotation, out temp); Matrix.Multiply (ref transformation, ref temp, out transformation); Matrix.CreateTranslation (position.X, position.Y, 0f, out temp); Matrix.Multiply (ref transformation, ref temp, out transformation); }
internal void MeasureString(ref CharacterSource text, out Vector2 size) { if (text.Length == 0) { size = Vector2.Zero; return; } var width = 0.0f; var finalLineHeight = (float)LineSpacing; var offset = Vector2.Zero; var firstGlyphOfLine = true; for (var i = 0; i < text.Length; ++i) { var c = text[i]; if (c == '\r') { continue; } if (c == '\n') { finalLineHeight = LineSpacing; offset.X = 0; offset.Y += LineSpacing; firstGlyphOfLine = true; continue; } var currentGlyphIndex = GetGlyphIndexOrDefault(c); Debug.Assert(currentGlyphIndex >= 0 && currentGlyphIndex < Glyphs.Length, "currentGlyphIndex was outside the bounds of the array."); var pCurrentGlyph = Glyphs[currentGlyphIndex]; // The first character on a line might have a negative left side bearing. // In this scenario, SpriteBatch/SpriteFont normally offset the text to the right, // so that text does not hang off the left side of its rectangle. if (firstGlyphOfLine) { offset.X = Math.Max(pCurrentGlyph.LeftSideBearing, 0); firstGlyphOfLine = false; } else { offset.X += Spacing + pCurrentGlyph.LeftSideBearing; } offset.X += pCurrentGlyph.Width; var proposedWidth = offset.X + Math.Max(pCurrentGlyph.RightSideBearing, 0); if (proposedWidth > width) { width = proposedWidth; } offset.X += pCurrentGlyph.RightSideBearing; if (pCurrentGlyph.Cropping.Height > finalLineHeight) { finalLineHeight = pCurrentGlyph.Cropping.Height; } } size.X = width; size.Y = offset.Y + finalLineHeight; }
internal void drawInto(SpriteBatch spriteBatch, ref CharacterSource text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effect, float depth) { var flipAdjustment = Vector2.Zero; var flippedVert = (effect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically; var flippedHorz = (effect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally; if (flippedVert || flippedHorz) { Vector2 size; measureString(ref text, out size); if (flippedHorz) { origin.X *= -1; flipAdjustment.X = -size.X; } if (flippedVert) { origin.Y *= -1; flipAdjustment.Y = lineHeight - size.Y; } } var requiresTransformation = flippedHorz || flippedVert || rotation != 0f || scale != Vector2.One; if (requiresTransformation) { Matrix temp; Matrix.CreateTranslation(-origin.X, -origin.Y, 0f, out _transformationMatrix); Matrix.CreateScale((flippedHorz ? -scale.X : scale.X), (flippedVert ? -scale.Y : scale.Y), 1f, out temp); Matrix.Multiply(ref _transformationMatrix, ref temp, out _transformationMatrix); Matrix.CreateTranslation(flipAdjustment.X, flipAdjustment.Y, 0, out temp); Matrix.Multiply(ref temp, ref _transformationMatrix, out _transformationMatrix); Matrix.CreateRotationZ(rotation, out temp); Matrix.Multiply(ref _transformationMatrix, ref temp, out _transformationMatrix); Matrix.CreateTranslation(position.X, position.Y, 0f, out temp); Matrix.Multiply(ref _transformationMatrix, ref temp, out _transformationMatrix); } BitmapFontRegion currentFontRegion = null; var offset = requiresTransformation ? Vector2.Zero : position - origin; for (var i = 0; i < text.Length; ++i) { var c = text[i]; if (c == '\r') { continue; } if (c == '\n') { offset.X = requiresTransformation ? 0f : position.X - origin.X; offset.Y += lineHeight; currentFontRegion = null; continue; } if (currentFontRegion != null) { offset.X += spacing + currentFontRegion.xAdvance; } if (!_characterMap.TryGetValue(c, out currentFontRegion)) { currentFontRegion = _defaultCharacterRegion; } var p = offset; if (flippedHorz) { p.X += currentFontRegion.width; } p.X += currentFontRegion.xOffset; if (flippedVert) { p.Y += currentFontRegion.height - lineHeight; } p.Y += currentFontRegion.yOffset; // transform our point if we need to if (requiresTransformation) { Vector2.Transform(ref p, ref _transformationMatrix, out p); } var destRect = RectangleExt.fromFloats ( p.X, p.Y, currentFontRegion.width * scale.X, currentFontRegion.height * scale.Y ); spriteBatch.Draw(currentFontRegion.subtexture, destRect, currentFontRegion.subtexture.sourceRect, color, rotation, Vector2.Zero, effect, depth); } }
internal void DrawInto( SpriteBatch spriteBatch, ref CharacterSource text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effect, float depth ) { Vector2 flipAdjustment = Vector2.Zero; bool flippedVert = (effect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically; bool flippedHorz = (effect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally; if (flippedVert || flippedHorz) { Vector2 size; MeasureString(ref text, out size); if (flippedHorz) { origin.X *= -1; flipAdjustment.X = -size.X; } if (flippedVert) { origin.Y *= -1; flipAdjustment.Y = LineSpacing - size.Y; } } /* TODO: This looks excessive... i suspect we could do most * of this with simple vector math and avoid this much matrix work. */ Matrix transformation, temp; Matrix.CreateTranslation(-origin.X, -origin.Y, 0f, out transformation); Matrix.CreateScale((flippedHorz ? -scale.X : scale.X), (flippedVert ? -scale.Y : scale.Y), 1f, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); Matrix.CreateTranslation(flipAdjustment.X, flipAdjustment.Y, 0, out temp); Matrix.Multiply(ref temp, ref transformation, out transformation); Matrix.CreateRotationZ(rotation, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); Matrix.CreateTranslation(position.X, position.Y, 0f, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); // Get the default glyph here once. Glyph?defaultGlyph = null; if (DefaultCharacter.HasValue) { defaultGlyph = _glyphs[DefaultCharacter.Value]; } Glyph currentGlyph = Glyph.Empty; Vector2 offset = Vector2.Zero; bool hasCurrentGlyph = false; bool firstGlyphOfLine = true; for (int i = 0; i < text.Length; i += 1) { char c = text[i]; if (c == '\r') { hasCurrentGlyph = false; continue; } if (c == '\n') { offset.X = 0; offset.Y += LineSpacing; hasCurrentGlyph = false; firstGlyphOfLine = true; continue; } if (hasCurrentGlyph) { offset.X += Spacing + currentGlyph.Width + currentGlyph.RightSideBearing; } hasCurrentGlyph = _glyphs.TryGetValue(c, out currentGlyph); if (!hasCurrentGlyph) { if (!defaultGlyph.HasValue) { throw new ArgumentException(Errors.TextContainsUnresolvableCharacters, "text"); } currentGlyph = defaultGlyph.Value; hasCurrentGlyph = true; } if (hasCurrentGlyph) { /* The first character on a line might have a negative left side bearing. * In this scenario, SpriteBatch/SpriteFont normally offset the text to the right, * so that text does not hang off the left side of its rectangle. */ if (firstGlyphOfLine) { offset.X = Math.Max(offset.X, 0); firstGlyphOfLine = false; } else { offset.X += currentGlyph.LeftSideBearing; } } Vector2 p = offset; if (flippedHorz) { p.X += currentGlyph.BoundsInTexture.Width; } p.X += currentGlyph.Cropping.X; if (flippedVert) { p.Y += currentGlyph.BoundsInTexture.Height - LineSpacing; } p.Y += currentGlyph.Cropping.Y; Vector2.Transform(ref p, ref transformation, out p); Vector4 destRect = new Vector4( p.X, p.Y, currentGlyph.BoundsInTexture.Width * scale.X, currentGlyph.BoundsInTexture.Height * scale.Y ); spriteBatch.DrawInternal( _texture, destRect, currentGlyph.BoundsInTexture, color, rotation, Vector2.Zero, effect, depth, false ); } // We need to flush if we're using Immediate sort mode. spriteBatch.FlushIfNeeded(); }
/// <summary> /// Returns the size of the contents of a StringBuilder when /// rendered in this font. /// </summary> /// <param name="text">The text to measure.</param> /// <returns>The size, in pixels, of 'text' when rendered in /// this font.</returns> public Vector2 MeasureString(StringBuilder text) { var source = new CharacterSource(text); Vector2 size; MeasureString(ref source, out size); return size; }
private void MeasureString(ref CharacterSource text, out Vector2 size) { if (text.Length == 0) { size = Vector2.Zero; return; } // Get the default glyph here once. Glyph?defaultGlyph = null; if (DefaultCharacter.HasValue) { defaultGlyph = _glyphs[DefaultCharacter.Value]; } float width = 0.0f; float finalLineHeight = (float)LineSpacing; int fullLineCount = 0; Glyph currentGlyph = Glyph.Empty; Vector2 offset = Vector2.Zero; bool hasCurrentGlyph = false; bool firstGlyphOfLine = true; for (int i = 0; i < text.Length; i += 1) { char c = text[i]; if (c == '\r') { hasCurrentGlyph = false; continue; } if (c == '\n') { fullLineCount += 1; finalLineHeight = LineSpacing; offset.X = 0; offset.Y = LineSpacing * fullLineCount; hasCurrentGlyph = false; firstGlyphOfLine = true; continue; } if (hasCurrentGlyph) { offset.X += Spacing; /* The first character on a line might have a negative left side bearing. * In this scenario, SpriteBatch/SpriteFont normally offset the text to the right, * so that text does not hang off the left side of its rectangle. */ if (firstGlyphOfLine) { offset.X = Math.Max(offset.X + Math.Abs(currentGlyph.LeftSideBearing), 0); firstGlyphOfLine = false; } else { offset.X += currentGlyph.LeftSideBearing; } offset.X += currentGlyph.Width + currentGlyph.RightSideBearing; } hasCurrentGlyph = _glyphs.TryGetValue(c, out currentGlyph); if (!hasCurrentGlyph) { if (!defaultGlyph.HasValue) { throw new ArgumentException(Errors.TextContainsUnresolvableCharacters, "text"); } currentGlyph = defaultGlyph.Value; hasCurrentGlyph = true; } float proposedWidth = offset.X + currentGlyph.WidthIncludingBearings + Spacing; if (proposedWidth > width) { width = proposedWidth; } if (currentGlyph.Cropping.Height > finalLineHeight) { finalLineHeight = currentGlyph.Cropping.Height; } } size.X = width; size.Y = fullLineCount * LineSpacing + finalLineHeight; }
internal void DrawInto( SpriteBatch spriteBatch, ref CharacterSource text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effect, float depth) { var flipAdjustment = Vector2.Zero; var flippedVert = (effect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically; var flippedHorz = (effect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally; if (flippedVert || flippedHorz) { Vector2 size; MeasureString(ref text, out size); if (flippedHorz) { origin.X *= -1; scale.X *= -1; flipAdjustment.X = -size.X; } if (flippedVert) { origin.Y *= -1; scale.Y *= -1; flipAdjustment.Y = LineSpacing - size.Y; } } // TODO: This looks excessive... i suspect we could do most // of this with simple vector math and avoid this much matrix work. Matrix transformation, temp; Matrix.CreateTranslation(-origin.X, -origin.Y, 0f, out transformation); Matrix.CreateScale(scale.X, scale.Y, 1f, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); Matrix.CreateTranslation(flipAdjustment.X, flipAdjustment.Y, 0, out temp); Matrix.Multiply(ref temp, ref transformation, out transformation); Matrix.CreateRotationZ(rotation, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); Matrix.CreateTranslation(position.X, position.Y, 0f, out temp); Matrix.Multiply(ref transformation, ref temp, out transformation); // Get the default glyph here once. Glyph? defaultGlyph = null; if (DefaultCharacter.HasValue) defaultGlyph = _glyphs[DefaultCharacter.Value]; var currentGlyph = Glyph.Empty; var offset = Vector2.Zero; var hasCurrentGlyph = false; for (var i = 0; i < text.Length; ++i) { var c = text[i]; if (c == '\r') { hasCurrentGlyph = false; continue; } if (c == '\n') { offset.X = 0; offset.Y += LineSpacing; hasCurrentGlyph = false; continue; } if (hasCurrentGlyph) offset.X += Spacing + currentGlyph.Width + currentGlyph.RightSideBearing; hasCurrentGlyph = _glyphs.TryGetValue(c, out currentGlyph); if (!hasCurrentGlyph) { if (!defaultGlyph.HasValue) throw new ArgumentException(Errors.TextContainsUnresolvableCharacters, "text"); currentGlyph = defaultGlyph.Value; hasCurrentGlyph = true; } offset.X += currentGlyph.LeftSideBearing; var p = offset; if (flippedHorz) p.X += currentGlyph.BoundsInTexture.Width; p.X += currentGlyph.Cropping.X; if (flippedVert) p.Y += currentGlyph.BoundsInTexture.Height - LineSpacing; p.Y += currentGlyph.Cropping.Y; Vector2.Transform(ref p, ref transformation, out p); var destRect = new Vector4( p.X, p.Y, currentGlyph.BoundsInTexture.Width * scale.X, currentGlyph.BoundsInTexture.Height * scale.Y); // TODO: We're passing SpriteEffects thru here unchanged, but // it seems we're applyting the flips ourselves above. // // This just might be a bug! spriteBatch.DrawInternal( _texture, destRect, currentGlyph.BoundsInTexture, color, rotation, Vector2.Zero, effect, depth); } }
private void DrawInto( SpriteBatch spriteBatch, ref CharacterSource text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effect, float depth) { Matrix transformation; CreateTransformationFromDrawParameters ( ref text, position, rotation, origin, scale, effect, out transformation); var flippedVert = (effect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically; var flippedHorz = (effect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally; PerThreadTypesetter.Reset (this); for (int i = 0; i < text.Length; ++i) { PerThreadTypesetter.Input (text [i]); if (!PerThreadTypesetter.HasCurrentGlyph) continue; var p = PerThreadTypesetter.Offset; if (flippedHorz) p.X += PerThreadTypesetter.CurrentGlyph.BoundsInTexture.Width; p.X += PerThreadTypesetter.CurrentGlyph.LeftSideBearing; if (flippedVert) p.Y += PerThreadTypesetter.CurrentGlyph.BoundsInTexture.Height - LineSpacing; p.Y += PerThreadTypesetter.CurrentGlyph.Cropping.Y; Vector2.Transform (ref p, ref transformation, out p); spriteBatch.Draw ( _texture, p, PerThreadTypesetter.CurrentGlyph.BoundsInTexture, color, rotation, Vector2.Zero, scale, effect, depth); } }