/// <summary>
        /// Draws the string in the specified coordinate system.
        /// </summary>
        /// <param name="text">The text to draw</param>
        /// <param name="position">A position in the chosen coordinate system where the top left corner of the first character will be</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>
        protected internal StringMetrics DrawString(string text, STRVector position, float realFontSize, STRColor color, CoordinateType coordinateType)
        {
            StringMetrics sm;

            IterateStringEm(text, position, true, realFontSize, color, coordinateType, out sm);
            return(sm);
        }
 internal StringMetrics ToStringMetrics(STRVector position, float scalX, float scalY)
 {
     return(new StringMetrics
     {
         TopLeft = position,
         Size = new STRVector(CharSize.X * scalX, CharSize.Y * scalY),
         OverhangTop = Math.Abs(scalY) * OverhangTop,
         OverhangBottom = Math.Abs(scalY) * OverhangBottom,
         OverhangLeft = Math.Abs(scalX) * OverhangLeft,
         OverhangRight = Math.Abs(scalX) * OverhangRight,
     });
 }
        private void IterateString(string text, STRVector position, bool draw, float scale, STRColor color, CoordinateType coordinateType, out StringMetrics metrics)
        {
            metrics = new StringMetrics();
            STRVector startPosition = position;
            float     scalY         = coordinateType == SpriteTextRenderer.CoordinateType.SNorm ? -1 : 1;

            string visualText = NBidi.NBidi.LogicalToVisual(text);

            int[] codePoints = Helpers.ConvertToCodePointArray(visualText);

            foreach (int c in codePoints)
            {
                var charDesc    = GetCharDescription(c);
                var charMetrics = charDesc.ToStringMetrics(position, scale, scale * scalY);
                if (draw)
                {
                    if (charMetrics.FullRectSize.X != 0 && charMetrics.FullRectSize.Y != 0)
                    {
                        float posY = position.Y - scalY * charMetrics.OverhangTop;
                        float posX = position.X - charMetrics.OverhangLeft;
                        Sprite.Draw(charDesc.TableDescription.SRV, new STRVector(posX, posY), charMetrics.FullRectSize, STRVector.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;
                }
            }
        }
        private STRVector ConvertCoordinate(STRVector coordinate, CoordinateType coordinateType)
        {
            switch (coordinateType)
            {
            case CoordinateType.SNorm:
                return(coordinate);

            case CoordinateType.UNorm:
                coordinate.X = (coordinate.X - 0.5f) * 2;
                coordinate.Y = -(coordinate.Y - 0.5f) * 2;
                return(coordinate);

            case CoordinateType.Relative:
                coordinate.X = coordinate.X / ScreenSize.X * 2 - 1;
                coordinate.Y = -(coordinate.Y / ScreenSize.Y * 2 - 1);
                return(coordinate);

            case SpriteTextRenderer.CoordinateType.Absolute:
                coordinate.X = coordinate.X / viewport.Width * 2 - 1;
                coordinate.Y = -(coordinate.Y / viewport.Height * 2 - 1);
                return(coordinate);
            }
            return(STRVector.Zero);
        }
        private void IterateStringEm(string text, STRVector position, bool Draw, float realFontSize, STRColor color, CoordinateType coordinateType, out StringMetrics metrics)
        {
            float scale = realFontSize / _FontSize;

            IterateString(text, position, Draw, scale, color, coordinateType, out metrics);
        }
 /// <summary>
 /// Draws the string untransformed in absolute coordinate system.
 /// </summary>
 /// <param name="text">The text to draw</param>
 /// <param name="position">A position in absolute coordinates where the top left corner of the first character will be</param>
 /// <param name="color">The color in which to draw the text</param>
 /// <returns>The StringMetrics for the rendered text</returns>
 protected internal StringMetrics DrawString(string text, STRVector position, STRColor color)
 {
     return(DrawString(text, position, FontSize, color, CoordinateType.Absolute));
 }
        /// <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
            bool xAxisFlipped = Size.X < 0;
            bool yAxisFlipped = Size.Y < 0;

            //Check, if axes of both instances point in the same direction
            if (this.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 (this.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
            float top    = findTopMost(this.TopLeft.Y, second.TopLeft.Y);
            float bottom = findBottomMost(this.TopLeft.Y + this.Size.Y, second.TopLeft.Y + second.Size.Y);
            float left   = findLeftMost(this.TopLeft.X, second.TopLeft.X);
            float right  = findRightMost(this.TopLeft.X + this.Size.X, second.TopLeft.X + second.Size.X);

            //Find new overhangs
            float topOverhangPos    = findTopMost(this.FullRectTopLeft.Y, second.FullRectTopLeft.Y);
            float bottomOverhangPos = findBottomMost(this.FullRectTopLeft.Y + this.FullRectSize.Y, second.FullRectTopLeft.Y + second.FullRectSize.Y);
            float leftOverhangPos   = findLeftMost(this.FullRectTopLeft.X, second.FullRectTopLeft.X);
            float rightOverhangPos  = findRightMost(this.FullRectTopLeft.X + this.FullRectSize.X, second.FullRectTopLeft.X + second.FullRectSize.X);

            TopLeft        = new STRVector(left, top);
            Size           = new STRVector(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 a region of a texture on the screen.
        /// </summary>
        /// <param name="texture">The shader resource view of the texture to draw</param>
        /// <param name="position">Position of the center of the texture in the chosen coordinate system</param>
        /// <param name="size">Size of the texture in the chosen coordinate system. The size is specified in the screen's coordinate system.</param>
        /// <param name="center">Specify the texture's center in the chosen coordinate system. The center is specified in the texture's local coordinate system. E.g. for <paramref name="coordinateType"/>=CoordinateType.SNorm, the texture's center is defined by (0, 0).</param>
        /// <param name="rotationAngle">The angle in radians to rotate the texture. Positive values mean counter-clockwise rotation. Rotations can only be applied for relative or absolute coordinates. Consider using the Degrees or Radians helper structs.</param>
        /// <param name="coordinateType">A custom coordinate system in which to draw the texture</param>
        /// <param name="color">The color with which to multiply the texture</param>
        /// <param name="texCoords">Texture coordinates for the top left corner</param>
        /// <param name="texCoordsSize">Size of the region in texture coordinates</param>
        protected internal void Draw(object texture, STRVector position, STRVector size, STRVector center, double rotationAngle, STRVector texCoords, STRVector texCoordsSize, STRColor color, CoordinateType coordinateType)
        {
            if (texture == null)
            {
                return;
            }

            size.X = Math.Abs(size.X);
            size.Y = Math.Abs(size.Y);

            //Difference vectors from the center to the texture edges (in screen coordinates).
            STRVector left, up, right, down;

            if (coordinateType == CoordinateType.UNorm)
            {
                left  = new STRVector(0 - center.X * size.X, 0);
                up    = new STRVector(0, 0 - center.Y * size.Y);
                right = new STRVector((1 - center.X) * size.X, 0);
                down  = new STRVector(0, (1 - center.Y) * size.Y);
            }
            else if (coordinateType == CoordinateType.SNorm)
            {
                left  = new STRVector((-1 - center.X) * size.X / 2, 0);
                up    = new STRVector(0, (1 - center.Y) * size.Y / 2);
                right = new STRVector((1 - center.X) * size.X / 2, 0);
                down  = new STRVector(0, (-1 - center.Y) * size.Y / 2);
            }
            else
            {
                left  = new STRVector(-center.X, 0);
                up    = new STRVector(0, -center.Y);
                right = new STRVector(size.X - center.X, 0);
                down  = new STRVector(0, size.Y - center.Y);
            }

            if (rotationAngle != 0)
            {
                if (coordinateType != CoordinateType.Absolute && coordinateType != CoordinateType.Relative)
                {
                    //Normalized coordinates tend to be skewed when applying rotation
                    throw new ArgumentException("Rotation is only allowed for relative or absolute coordinates", "rotationAngle");
                }
                float sine   = (float)Math.Sin(rotationAngle);
                float cosine = (float)Math.Cos(rotationAngle);
                left  = Rotate(left, sine, cosine);
                right = Rotate(right, sine, cosine);
                up    = Rotate(up, sine, cosine);
                down  = Rotate(down, sine, cosine);
            }

            var data = new SpriteVertexLayout.Struct();

            data.TexCoord     = texCoords;
            data.TexCoordSize = texCoordsSize;
            data.Color        = color.ToArgb();
            data.TopLeft      = ConvertCoordinate(position + up + left, coordinateType);
            data.TopRight     = ConvertCoordinate(position + up + right, coordinateType);
            data.BottomLeft   = ConvertCoordinate(position + down + left, coordinateType);
            data.BottomRight  = ConvertCoordinate(position + down + right, coordinateType);

            if (AllowReorder)
            {
                //Is there already a sprite for this texture?
                if (textureSprites.ContainsKey(texture))
                {
                    //Add the sprite to the last segment for this texture
                    var Segment = textureSprites[texture].Last();
                    AddIn(Segment, data);
                }
                else
                {
                    //Add a new segment for this texture
                    AddNew(texture, data);
                }
            }
            else
            {
                //Add a new segment for this texture
                AddNew(texture, data);
            }
        }
 private STRVector Rotate(STRVector v, float sine, float cosine)
 {
     return(new STRVector(cosine * v.X + sine * v.Y, -sine * v.X + cosine * v.Y));
 }
 /// <summary>
 /// Draws a complete texture on the screen.
 /// </summary>
 /// <param name="texture">The shader resource view of the texture to draw</param>
 /// <param name="position">Position of the top left corner of the texture in the chosen coordinate system</param>
 /// <param name="size">Size of the texture in the chosen coordinate system. The size is specified in the screen's coordinate system.</param>
 /// <param name="center">Specify the texture's center in the chosen coordinate system. The center is specified in the texture's local coordinate system. E.g. for <paramref name="coordinateType"/>=CoordinateType.SNorm, the texture's center is defined by (0, 0).</param>
 /// <param name="rotationAngle">The angle in radians to rotate the texture. Positive values mean counter-clockwise rotation. Rotations can only be applied for relative or absolute coordinates. Consider using the Degrees or Radians helper structs.</param>
 /// <param name="coordinateType">A custom coordinate system in which to draw the texture</param>
 /// <param name="color">The color with which to multiply the texture</param>
 protected internal void Draw(object texture, STRVector position, STRVector size, STRVector center, double rotationAngle, STRColor color, CoordinateType coordinateType)
 {
     Draw(texture, position, size, center, rotationAngle, STRVector.Zero, new STRVector(1, 1), color, coordinateType);
 }
 /// <summary>
 /// Draws a complete texture on the screen.
 /// </summary>
 /// <param name="texture">The shader resource view of the texture to draw</param>
 /// <param name="position">Position of the top left corner of the texture in the chosen coordinate system</param>
 /// <param name="size">Size of the texture in the chosen coordinate system. The size is specified in the screen's coordinate system.</param>
 /// <param name="coordinateType">A custom coordinate system in which to draw the texture</param>
 protected internal void Draw(object texture, STRVector position, STRVector size, CoordinateType coordinateType)
 {
     Draw(texture, position, size, STRVector.Zero, 0, coordinateType);
 }