示例#1
0
        /// <summary>
        /// Measures the specified text string. Parameter bounds contains the bounds of the text.<br/>
        /// Measured values are returned in local coordinate space.
        /// </summary>
        /// <param name="bounds">Contains the bounds of the text when returned.</param>
        /// <returns>The horizontal advance of the measured text (i.e. where the next character should be drawn).</returns>
        public static float TextBounds(this Nvg nvg, Vector2D <float> pos, string @string, string end, out Rectangle <float> bounds)
        {
            bounds = default;

            Fontstash fons     = nvg.fontManager.Fontstash;
            State     state    = nvg.stateStack.CurrentState;
            float     scale    = nvg.fontManager.GetFontScale() * nvg.pixelRatio.DevicePxRatio;
            float     invscale = 1.0f / scale;

            if (state.FontId == Fontstash.INVALID)
            {
                return(0);
            }

            fons.SetSize(state.FontSize * scale);
            fons.SetSpacing(state.LetterSpacing * scale);
            fons.SetBlur(state.FontBlur * scale);
            fons.SetAlign((int)state.TextAlign);
            fons.SetFont(state.FontId);

            float width = fons.TextBounds(pos.X * scale, pos.Y * scale, @string, end, out float[] bs);

            if (bs != null)
            {
                fons.LineBounds(pos.Y * scale, out bs[1], out bs[3]);
                bounds = new Rectangle <float>()
                {
                    Origin = new Vector2D <float>(bs[0] * invscale, bs[1] * invscale),
                    Size   = new Vector2D <float>((bs[2] - bs[0]) * invscale, (bs[3] - bs[1]) * invscale)
                };
            }

            return(width * invscale);
        }
示例#2
0
        public void Render(GL gl)
        {
            gl.Begin(PrimitiveType.Lines);
            if (_selected)
            {
                gl.Color4(0.0f, 1.0f, 0.0f, 1.0f);
            }
            else
            {
                gl.Color4(0.0f, 0.0f, 0.0f, 1.0f);
            }
            gl.Vertex2(_x, _y);
            gl.Vertex2(_x + _width, _y);
            gl.Vertex2(_x, _y);
            gl.Vertex2(_x, _y + _height);
            gl.Vertex2(_x + _width, _y);
            gl.Vertex2(_x + _width, _y + _height);
            gl.Vertex2(_x, _y + _height);
            gl.Vertex2(_x + _width, _y + _height);
            gl.End();

            _fons.SetFont(_fons.GetFontByName(_font));
            _fons.SetSize((float)_fontSize);
            _fons.SetColour(0xFF000000);
            _fons.DrawText(_x + 1.0f, _y + _height - 3.0f, _name + ": " + _text);
        }
示例#3
0
        public void Render(LevelSelectorStateRenderer renderer, Fontstash fons)
        {
            renderer.RenderQuad(_button.X, _button.Y, _button.Width, _button.Height, new Vector4(_colour, Hovering ? 1.0f : 0.75f));

            float size = 32;
            float x    = _button.X + size / 1.5f;
            float y    = _button.Y + size * 1.5f;

            for (int i = 0; i < _data.bestRating; i++)
            {
                renderer.RenderTexturedQuad(_state.Star, x + i * size, y, size, -size);
            }

            renderer.PrepareLegacy();
            fons.SetColour(0xFFFFFFFF);
            fons.SetFont(fons.GetFontByName("stdfont"));
            fons.SetSize(24.0f);
            fons.SetAlign((int)FonsAlign.Center | (int)FonsAlign.Middle);
            fons.DrawText(_button.X + _button.Width / 2.0f, _button.Y + _button.Height / 2.0f, _data.name);
            fons.SetSize(16.0f);
            fons.DrawText(_button.X + _button.Width / 2.0f, _button.Y + _button.Height / 2.0f + 35.0f, "Last Time: " + _data.bestTime.ToString());
            renderer.EndLegacy();
        }
示例#4
0
        public void Render(Fontstash fons)
        {
            if (_active)
            {
                fons.SetColour(0xFF00FFFF);
            }
            else
            {
                fons.SetColour(0xFF000000);
            }

            fons.SetFont(fons.GetFontByName("stdfont"));
            fons.SetSize(18.0f);
            fons.DrawText(_x + 2.0f, _y + _height, _active ? _activeText : _text);
        }
示例#5
0
        /// <summary>
        /// Calculates the glyph x positions of the specified text. Only the sub-string will be used.<br/>
        /// Measures values are returned in local coordinate space.
        /// </summary>
        public static int TextGlyphPositions(this Nvg nvg, Vector2D <float> pos, string @string, string end, out GlyphPosition[] positions, int maxRows)
        {
            positions = new GlyphPosition[maxRows];

            Fontstash fons     = nvg.fontManager.Fontstash;
            State     state    = nvg.stateStack.CurrentState;
            float     scale    = nvg.fontManager.GetFontScale() * nvg.pixelRatio.DevicePxRatio;
            float     invscale = 1.0f / scale;
            FonsQuad  q        = new();
            int       npos     = 0;

            if (state.FontId == Fontstash.INVALID)
            {
                return(0);
            }

            if (@string == end)
            {
                return(0);
            }

            fons.SetSize(state.FontSize * scale);
            fons.SetSpacing(state.LetterSpacing * scale);
            fons.SetBlur(state.FontBlur * scale);
            fons.SetAlign((int)state.TextAlign);
            fons.SetFont(state.FontId);

            fons.TextIterInit(out FonsTextIter iter, pos.X * scale, pos.Y * scale, @string, end, FonsGlyphBitmap.Optional);
            FonsTextIter prevIter = iter;

            while (fons.TextIterNext(ref iter, ref q))
            {
                if (iter.prevGlyphIndex < 0 && nvg.fontManager.AllocTextAtlas())
                {
                    iter = prevIter;
                    fons.TextIterNext(ref iter, ref q);
                }
                prevIter          = iter;
                positions[npos++] = new GlyphPosition(iter.str, iter.x * invscale, MathF.Min(iter.x, q.x0) * invscale, MathF.Max(iter.nextx, q.x1) * invscale);
                if (npos >= maxRows)
                {
                    return(npos);
                }
            }

            return(npos);
        }
示例#6
0
        /// <summary>
        /// Returns the vertical metrics based on the current text style.<br/>
        /// Measured values are returned in local coordinate space.
        /// </summary>
        public static void TextMetrics(this Nvg nvg, out float ascender, out float descender, out float lineh)
        {
            Fontstash fons     = nvg.fontManager.Fontstash;
            State     state    = nvg.stateStack.CurrentState;
            float     scale    = nvg.fontManager.GetFontScale() * nvg.pixelRatio.DevicePxRatio;
            float     invscale = 1.0f / scale;

            if (state.FontId == Fontstash.INVALID)
            {
                ascender = descender = lineh = -1.0f;
                return;
            }

            fons.SetSize(state.FontSize * scale);
            fons.SetSpacing(state.LetterSpacing * scale);
            fons.SetBlur(state.FontBlur * scale);
            fons.SetAlign((int)state.TextAlign);
            fons.SetFont(state.FontId);

            fons.VertMetrics(out ascender, out descender, out lineh);
            ascender  *= invscale;
            descender *= invscale;
            lineh     *= invscale;
        }
示例#7
0
        /// <summary>
        /// Breaks the specified text into lines. Only the sub-string will be used.<br/>
        /// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.<br/>
        /// Words longer than the max width are slit at nearest character (i.e. no hyphenation).
        /// </summary>
        public static int TextBreakLines(this Nvg nvg, string @string, string end, float breakRowWidth, out TextRow[] rows, int maxRows)
        {
            rows = new TextRow[maxRows];

            Fontstash fons = nvg.fontManager.Fontstash;

            State         state = nvg.stateStack.CurrentState;
            float         scale = nvg.fontManager.GetFontScale() * nvg.pixelRatio.DevicePxRatio;
            float         invscale = 1.0f / scale;
            FonsQuad      q = new();
            int           nrows = 0;
            float         rowStartX = 0.0f;
            float         rowWidth = 0.0f;
            float         rowMinX = 0.0f;
            float         rowMaxX = 0.0f;
            string        rowStart = null;
            string        rowEnd = null;
            string        wordStart = null;
            float         wordStartX = 0.0f;
            float         wordMinX = 0.0f;
            string        breakEnd = null;
            float         breakWidth = 0.0f;
            float         breakMaxX = 0.0f;
            CodepointType type, pType = CodepointType.Space;
            uint          pCodepoint = 0;

            if (maxRows == 0)
            {
                return(0);
            }

            if (state.FontId == Fontstash.INVALID)
            {
                return(0);
            }

            if (@string == end || @string.Length == 0)
            {
                return(0);
            }

            fons.SetSize(state.FontSize * scale);
            fons.SetSpacing(state.LetterSpacing * scale);
            fons.SetBlur(state.FontBlur * scale);
            fons.SetAlign((int)state.TextAlign);
            fons.SetFont(state.FontId);

            breakRowWidth *= scale;

            fons.TextIterInit(out FonsTextIter iter, 0, 0, @string, end, FonsGlyphBitmap.Optional);
            FonsTextIter prevIter = iter;

            while (fons.TextIterNext(ref iter, ref q))
            {
                if (iter.prevGlyphIndex < 0 && nvg.fontManager.AllocTextAtlas())
                {
                    iter = prevIter;
                    fons.TextIterNext(ref iter, ref q);
                }
                prevIter = iter;
                switch (iter.codepoint)
                {
                case 9:      // \t
                case 11:     // \v
                case 12:     // \f
                case 32:     // \space
                case 0x00a0: // NBSP
                    type = CodepointType.Space;
                    break;

                case 10:     // \n
                    type = pCodepoint == 13 ? CodepointType.Space : CodepointType.Newline;
                    break;

                case 13:     // \r
                    type = pCodepoint == 10 ? CodepointType.Space : CodepointType.Newline;
                    break;

                case 0x0085:     // NEL
                    type = CodepointType.Newline;
                    break;

                default:
                    if ((iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) ||
                        (iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) ||
                        (iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) ||
                        (iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) ||
                        (iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) ||
                        (iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF))
                    {
                        type = CodepointType.CJKChar;
                    }
                    else
                    {
                        type = CodepointType.Char;
                    }
                    break;
                }

                if (type == CodepointType.Newline)
                {
                    rows[nrows++] = new TextRow()
                    {
                        Start = rowStart ?? iter.str,
                        End   = rowEnd ?? iter.str,
                        Width = rowWidth * invscale,
                        MinX  = rowMinX * invscale,
                        MaxX  = rowMaxX * invscale,
                        Next  = iter.next
                    };

                    if (nrows >= maxRows)
                    {
                        return(nrows);
                    }

                    breakEnd   = rowStart;
                    breakWidth = 0.0f;
                    breakMaxX  = 0.0f;

                    rowStart = null;
                    rowEnd   = null;
                    rowWidth = 0.0f;
                    rowMinX  = rowMaxX = 0.0f;
                }
                else
                {
                    if (rowStart == null)
                    {
                        if (type == CodepointType.Char || type == CodepointType.CJKChar)
                        {
                            rowStartX  = iter.x;
                            rowStart   = iter.str;
                            rowEnd     = iter.next;
                            rowWidth   = iter.nextx - rowStartX;
                            rowMinX    = q.x0 - rowStartX;
                            rowMaxX    = q.x1 - rowStartX;
                            wordStart  = iter.str;
                            wordStartX = iter.x;
                            wordMinX   = q.x0 - rowStartX;

                            breakEnd   = rowStart;
                            breakWidth = 0.0f;
                            breakMaxX  = 0.0f;
                        }
                    }
                    else
                    {
                        float nextWidth = iter.nextx - rowStartX;

                        if (type == CodepointType.Char || type == CodepointType.CJKChar)
                        {
                            rowEnd   = iter.next;
                            rowWidth = iter.nextx - rowStartX;
                            rowMaxX  = q.x1 - rowStartX;
                        }

                        if (((pType == CodepointType.Char || pType == CodepointType.CJKChar) && type == CodepointType.Space) || type == CodepointType.CJKChar)
                        {
                            breakEnd   = iter.str;
                            breakWidth = rowWidth;
                            breakMaxX  = rowMaxX;
                        }

                        if ((pType == CodepointType.Space && (type == CodepointType.Char || type == CodepointType.CJKChar)) || type == CodepointType.CJKChar)
                        {
                            wordStart  = iter.str;
                            wordStartX = iter.x;
                            wordMinX   = q.x0;
                        }

                        if ((type == CodepointType.Char || type == CodepointType.CJKChar) && nextWidth > breakRowWidth)
                        {
                            if (breakEnd == rowStart)
                            {
                                rows[nrows++] = new TextRow()
                                {
                                    Start = rowStart,
                                    End   = iter.str,
                                    Width = rowWidth * invscale,
                                    MinX  = rowMinX * invscale,
                                    MaxX  = rowMaxX * invscale,
                                    Next  = iter.str
                                };

                                if (nrows >= maxRows)
                                {
                                    return(nrows);
                                }

                                rowStartX  = iter.x;
                                rowStart   = iter.str;
                                rowEnd     = iter.next;
                                rowWidth   = iter.nextx - rowStartX;
                                rowMinX    = q.x0 - rowStartX;
                                rowMaxX    = q.x1 - rowStartX;
                                wordStart  = iter.str;
                                wordStartX = iter.x;
                                wordMinX   = q.x0 - rowStartX;
                            }
                            else
                            {
                                rows[nrows++] = new TextRow()
                                {
                                    Start = rowStart,
                                    End   = breakEnd,
                                    Width = breakWidth * invscale,
                                    MinX  = rowMinX * invscale,
                                    MaxX  = breakMaxX * invscale,
                                    Next  = wordStart
                                };

                                if (nrows >= maxRows)
                                {
                                    return(nrows);
                                }

                                rowStartX = wordStartX;
                                rowStart  = wordStart;
                                rowEnd    = iter.next;
                                rowWidth  = iter.nextx - rowStartX;
                                rowMinX   = wordMinX - rowStartX;
                                rowMaxX   = q.x1 - rowStartX;
                            }

                            breakEnd   = rowStart;
                            breakWidth = 0.0f;
                            breakMaxX  = 0.0f;
                        }
                    }
                }

                pCodepoint = iter.codepoint;
                pType      = type;
            }

            if (rowStart != null)
            {
                rows[nrows++] = new TextRow()
                {
                    Start = rowStart,
                    End   = rowEnd,
                    Width = rowWidth * invscale,
                    MinX  = rowMinX * invscale,
                    MaxX  = rowMaxX * invscale,
                    Next  = end
                };
            }

            return(nrows);
        }
示例#8
0
        /// <summary>
        /// Measures the specified multi-text string.<br/>
        /// Measured values are returned in local space.
        /// </summary>
        /// <param name="bounds">Contains the bounds box of the multi-text when returned.</param>
        public static void TextBoxBounds(this Nvg nvg, Vector2D <float> pos, float breakRowWidth, string @string, string end, out Rectangle <float> bounds)
        {
            Fontstash fons     = nvg.fontManager.Fontstash;
            State     state    = nvg.stateStack.CurrentState;
            float     scale    = nvg.fontManager.GetFontScale() * nvg.pixelRatio.DevicePxRatio;
            float     invscale = 1.0f / scale;
            int       nrows;
            Align     oldAlign = state.TextAlign;
            Align     hAlign   = state.TextAlign & (Align.Left | Align.Centre | Align.Right);
            Align     vAlign   = state.TextAlign & (Align.Top | Align.Middle | Align.Bottom | Align.Baseline);

            if (state.FontId == Fontstash.INVALID)
            {
                bounds = default;
                return;
            }

            nvg.TextMetrics(out _, out _, out float lineh);

            state.TextAlign = Align.Left | vAlign;

            float minX = pos.X, maxX = pos.X;
            float minY = pos.Y, maxY = pos.Y;

            fons.SetSize(state.FontSize * scale);
            fons.SetSpacing(state.LetterSpacing * scale);
            fons.SetBlur(state.FontBlur * scale);
            fons.SetAlign((int)state.TextAlign);
            fons.SetFont(state.FontId);
            fons.LineBounds(0, out float rMinY, out float rMaxY);
            rMinY *= invscale;
            rMaxY *= invscale;

            while ((nrows = TextBreakLines(nvg, @string, end, breakRowWidth, out TextRow[] rows, 2)) != 0)
            {
                for (uint i = 0; i < nrows; i++)
                {
                    float rMinX, rMaxX;
                    float dx = 0.0f;
                    if (hAlign.HasFlag(Align.Left))
                    {
                        dx = 0.0f;
                    }
                    else if (hAlign.HasFlag(Align.Centre))
                    {
                        dx = breakRowWidth * 0.5f - rows[i].Width * 0.5f;
                    }
                    else if (hAlign.HasFlag(Align.Right))
                    {
                        dx = breakRowWidth - rows[i].Width;
                    }
                    rMinX = pos.X + rows[i].MinX + dx;
                    rMaxX = pos.X + rows[i].MaxX + dx;
                    minX  = MathF.Min(minX, rMinX);
                    maxX  = MathF.Max(maxX, rMaxX);

                    minY = MathF.Min(minY, pos.Y + rMinY);
                    maxY = MathF.Max(maxY, pos.Y + rMaxY);

                    pos.Y += lineh * state.LineHeight;
                }
                @string = rows[nrows - 1].Next;
            }

            state.TextAlign = oldAlign;

            bounds = new Rectangle <float>(new Vector2D <float>(minX, minY), new Vector2D <float>(maxX, maxY) - new Vector2D <float>(minX, minY));
        }
示例#9
0
        /// <summary>
        /// Draws text string at specified location. Only the sub-string up to the end is drawn.
        /// </summary>
        public static float Text(this Nvg nvg, Vector2D <float> pos, string @string, string end)
        {
            Fontstash fons  = nvg.fontManager.Fontstash;
            State     state = nvg.stateStack.CurrentState;

            FonsQuad q = new();

            float scale     = nvg.fontManager.GetFontScale() * nvg.pixelRatio.DevicePxRatio;
            float invscale  = 1.0f / scale;
            bool  isFlipped = Maths.IsTransformFlipped(state.Transform);

            if (state.FontId == Fontstash.INVALID)
            {
                return(pos.X);
            }

            fons.SetSize(state.FontSize * scale);
            fons.SetSpacing(state.LetterSpacing * scale);
            fons.SetBlur(state.FontBlur * scale);
            fons.SetAlign((int)state.TextAlign);
            fons.SetFont(state.FontId);

            List <Vertex> vertices = new();

            fons.TextIterInit(out FonsTextIter iter, pos.X * scale, pos.Y * scale, @string, end, FonsGlyphBitmap.Requiered);
            FonsTextIter prevIter = iter;

            while (fons.TextIterNext(ref iter, ref q))
            {
                Vector2D <float>[] c = new Vector2D <float> [4];

                if (iter.prevGlyphIndex == -1)
                {
                    if (vertices.Count != 0)
                    {
                        nvg.fontManager.RenderText(vertices);
                        vertices.Clear();
                    }
                    if (!nvg.fontManager.AllocTextAtlas()) // no memory
                    {
                        break;
                    }
                    iter = prevIter;
                    _    = fons.TextIterNext(ref iter, ref q);
                    if (iter.prevGlyphIndex == -1)
                    {
                        break;
                    }
                }
                prevIter = iter;
                if (isFlipped)
                {
                    float tmp = q.y0;
                    q.y0 = q.y1;
                    q.y1 = tmp;

                    tmp  = q.t0;
                    q.t0 = q.t1;
                    q.t1 = tmp;
                }

                c[0] = nvg.TransformPoint(state.Transform, q.x0 * invscale, q.y0 * invscale);
                c[1] = nvg.TransformPoint(state.Transform, q.x1 * invscale, q.y0 * invscale);
                c[2] = nvg.TransformPoint(state.Transform, q.x1 * invscale, q.y1 * invscale);
                c[3] = nvg.TransformPoint(state.Transform, q.x0 * invscale, q.y1 * invscale);

                vertices.Add(new Vertex(c[0], q.s0, q.t0));
                vertices.Add(new Vertex(c[2], q.s1, q.t1));
                vertices.Add(new Vertex(c[1], q.s1, q.t0));
                vertices.Add(new Vertex(c[0], q.s0, q.t0));
                vertices.Add(new Vertex(c[3], q.s0, q.t1));
                vertices.Add(new Vertex(c[2], q.s1, q.t1));
            }

            nvg.fontManager.FlushTextTexture();
            nvg.fontManager.RenderText(vertices);

            return(iter.nextx / scale);
        }