private void IterateStringEm(string text, Vector2 position, bool draw, float realFontSize, Color4 color, CoordinateType coordinateType, out StringMetrics metrics) { var scale = realFontSize / FontSize; IterateString(text, position, draw, scale, color, coordinateType, out metrics); }
private void IterateString(string text, Vector2 position, bool draw, float scale, Color4 color, CoordinateType coordinateType, out StringMetrics metrics) { metrics = new StringMetrics(); float scalY = coordinateType == CoordinateType.SNorm ? -1 : 1; var visualText = NBidi.NBidi.LogicalToVisual(text); var codePoints = Helpers.ConvertToCodePointArray(visualText); for (var i = 0; i < codePoints.Length; i++) { var c = codePoints[i]; var charDesc = GetCharDescription(c); var charMetrics = charDesc.ToStringMetrics(position, scale, scale * scalY); if (draw) { if (charMetrics.FullRectSize.X != 0 && charMetrics.FullRectSize.Y != 0) { var posY = position.Y - scalY * charMetrics.OverhangTop; var posX = position.X - charMetrics.OverhangLeft; Sprite.Draw(charDesc.TableDescription.Srv, new Vector2(posX, posY), charMetrics.FullRectSize, Vector2.Zero, 0, charDesc.TexCoordsStart, charDesc.TexCoordsSize, color, coordinateType); } } metrics.Merge(charMetrics); position.X += charMetrics.Size.X; // Break newlines if (c == '\r') { position.X = metrics.TopLeft.X; } if (c == '\n') { position.Y = metrics.BottomRight.Y - charMetrics.Size.Y / 2; } } }
/// <summary> /// Merges this instance of StringMetrics with another instance. /// The textblock and overhangs of this instance will be increased to cover both instances. /// </summary> /// <param name="second">The second StringMetrics instance. This object will not be changed.</param> /// <exception cref="System.ArgumentException">Thrown when one instance has flipped axes and the other does not.</exception> public void Merge(StringMetrics second) { // If current instance has no values yet, take the values of the second instance if (Size.X == 0 && Size.Y == 0) { TopLeft = second.TopLeft; Size = second.Size; OverhangLeft = second.OverhangLeft; OverhangRight = second.OverhangRight; OverhangTop = second.OverhangTop; OverhangBottom = second.OverhangBottom; return; } // If second instance is not visible, do nothing if (second.FullRectSize.X == 0 && second.FullRectSize.Y == 0) { return; } // Flipped y axis means that positive y points upwards // Flipped x axis means that positive x points to the right var xAxisFlipped = Size.X < 0; var yAxisFlipped = Size.Y < 0; // Check, if axes of both instances point in the same direction if (Size.X * second.Size.X < 0) { throw new ArgumentException("The x-axis of the current instance is " + (xAxisFlipped ? "" : "not ") + "flipped. The x-axis of the second instance has to point in the same direction"); } if (Size.Y * second.Size.Y < 0) { throw new ArgumentException("The y-axis of the current instance is " + (yAxisFlipped ? "" : "not ") + "flipped. The y-axis of the second instance has to point in the same direction"); } // Update flipped info if it cannot be obtained from the current instance if (Size.X == 0) { xAxisFlipped = second.Size.X < 0; } if (Size.Y == 0) { yAxisFlipped = second.Size.Y < 0; } // Find the functions to determine the topmost of two values and so on Func <float, float, float> findTopMost, findBottomMost; Func <float, float, float> findLeftMost, findRightMost; if (yAxisFlipped) { findTopMost = Math.Max; findBottomMost = Math.Min; } else { findTopMost = Math.Min; findBottomMost = Math.Max; } if (xAxisFlipped) { findLeftMost = Math.Max; findRightMost = Math.Min; } else { findLeftMost = Math.Min; findRightMost = Math.Max; } // Find new textblock var top = findTopMost(TopLeft.Y, second.TopLeft.Y); var bottom = findBottomMost(TopLeft.Y + Size.Y, second.TopLeft.Y + second.Size.Y); var left = findLeftMost(TopLeft.X, second.TopLeft.X); var right = findRightMost(TopLeft.X + Size.X, second.TopLeft.X + second.Size.X); // Find new overhangs var topOverhangPos = findTopMost(FullRectTopLeft.Y, second.FullRectTopLeft.Y); var bottomOverhangPos = findBottomMost(FullRectTopLeft.Y + FullRectSize.Y, second.FullRectTopLeft.Y + second.FullRectSize.Y); var leftOverhangPos = findLeftMost(FullRectTopLeft.X, second.FullRectTopLeft.X); var rightOverhangPos = findRightMost(FullRectTopLeft.X + FullRectSize.X, second.FullRectTopLeft.X + second.FullRectSize.X); TopLeft = new Vector2(left, top); Size = new Vector2(right - left, bottom - top); OverhangLeft = (left - leftOverhangPos) * (xAxisFlipped ? -1 : 1); OverhangRight = (rightOverhangPos - right) * (xAxisFlipped ? -1 : 1); OverhangTop = (top - topOverhangPos) * (yAxisFlipped ? -1 : 1); OverhangBottom = (bottomOverhangPos - bottom) * (yAxisFlipped ? -1 : 1); }
/// <summary> /// Draws the string in the specified coordinate system aligned in the given rectangle. The text is not clipped or wrapped. /// </summary> /// <param name="text">The text to draw</param> /// <param name="rect">The rectangle in which to align the text</param> /// <param name="align">Alignment of text in rectangle</param> /// <param name="realFontSize">The real font size in the chosen coordinate system</param> /// <param name="color">The color in which to draw the text</param> /// <param name="coordinateType">The chosen coordinate system</param> /// <returns>The StringMetrics for the rendered text</returns> public StringMetrics DrawString(string text, RectangleF rect, TextAlignment align, float realFontSize, Color4 color, CoordinateType coordinateType) { // If text is aligned top and left, no adjustment has to be made if (align.HasFlag(TextAlignment.Top) && align.HasFlag(TextAlignment.Left)) { return(DrawString(text, new Vector2(rect.X, rect.Y), realFontSize, color, coordinateType)); } text = text.Replace("\r", ""); var rawTextMetrics = MeasureString(text, realFontSize, coordinateType); var mMetrics = MeasureString("m", realFontSize, coordinateType); float startY; if (align.HasFlag(TextAlignment.Top)) { startY = rect.Top; } else if (align.HasFlag(TextAlignment.VerticalCenter)) { startY = rect.Top + rect.Height / 2 - rawTextMetrics.Size.Y / 2; } else { startY = rect.Bottom - rawTextMetrics.Size.Y; } var totalMetrics = new StringMetrics(); // break text into lines var lines = text.Split('\n'); for (var i = 0; i < lines.Length; i++) { var line = lines[i]; float startX; if (align.HasFlag(TextAlignment.Left)) { startX = rect.X; } else { var lineMetrics = MeasureString(line, realFontSize, coordinateType); startX = align.HasFlag(TextAlignment.HorizontalCenter) ? rect.X + rect.Width / 2 - lineMetrics.Size.X / 2 : rect.Right - lineMetrics.Size.X; } { var lineMetrics = DrawString(line, new Vector2(startX, startY), realFontSize, color, coordinateType); startY += mMetrics.Size.Y < 0 ? Math.Min(lineMetrics.Size.Y, mMetrics.Size.Y) : Math.Max(lineMetrics.Size.Y, mMetrics.Size.Y); totalMetrics.Merge(lineMetrics); } } return(totalMetrics); }
private void IterateString(string text, Vector2 position, float angle, bool draw, float scale, Color4 color, CoordinateType coordinateType, out StringMetrics metrics) { metrics = new StringMetrics(); float scalY = coordinateType == CoordinateType.SNorm ? -1 : 1; var visualText = NBidi.NBidi.LogicalToVisual(text); var codePoints = Helpers.ConvertToCodePointArray(visualText); float sine, cosine; if (angle != 0f) { sine = (float)Math.Sin(angle); cosine = (float)Math.Cos(angle); } else { sine = cosine = 0f; } var x = 0f; var y = Lazier.Create(() => Math.Abs(GetCharDescription('M').CharSize.Y *scale *scalY)); for (var i = 0; i < codePoints.Length; i++) { var c = codePoints[i]; var charDesc = GetCharDescription(c); var charMetrics = charDesc.ToStringMetrics(position, scale, scale * scalY); metrics.Merge(charMetrics); if (c != '\r' && c != '\n') { if (draw) { var h = charMetrics.FullRectSize.Y; if (h != 0 && charMetrics.FullRectSize.X != 0) { var fix = new Vector2(-charMetrics.OverhangLeft, -scalY * charMetrics.OverhangTop); if (angle != 0f) { fix = SpriteRenderer.Rotate(fix, sine, cosine); } Sprite.Draw(charDesc.TableDescription.Srv, position + fix, charMetrics.FullRectSize, Vector2.Zero, sine, cosine, charDesc.TexCoordsStart, charDesc.TexCoordsSize, color, coordinateType); } } } var delta = new Vector2(charMetrics.Size.X + KerningAdjustment, 0f); switch (c) { case '\n': delta.Y += y.Value; goto case '\r'; case '\r': delta.X = -x; x = 0f; break; default: x += delta.X; break; } if (angle != 0f) { delta = SpriteRenderer.Rotate(delta, sine, cosine); } position += delta; } }