예제 #1
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);
        }
예제 #2
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);
        }
예제 #3
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);
        }