// Measure the length of the span of the text. Currently, this is only used to compute the length
        // of ellipsis, assuming that the ellipsis does not contain any tab, tab is not considered for simplicity
        public static float measureText(string text, TextStyle style)
        {
            char  startingChar = text[0];
            float totalWidth   = 0;

            if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar))
            {
                float advance = style.fontSize * EmojiUtils.advanceFactor + style.letterSpacing;
                for (int i = 0; i < text.Length; i++)
                {
                    char ch = text[i];
                    if (char.IsHighSurrogate(ch) || EmojiUtils.isSingleCharNonEmptyEmoji(ch))
                    {
                        totalWidth += advance;
                    }
                }
            }
            else
            {
                Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;
                font.RequestCharactersInTextureSafe(text, style.UnityFontSize, style.UnityFontStyle);
                for (int i = 0; i < text.Length; i++)
                {
                    char ch = text[i];
                    if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle))
                    {
                        totalWidth += glyphInfo.advance + style.letterSpacing;
                    }
Beispiel #2
0
        void _drawTextBlob(TextBlob?textBlob, uiOffset offset, uiPaint paint)
        {
            D.assert(textBlob != null);

            var state = this._currentLayer.currentState;
            var scale = state.scale * this._devicePixelRatio;

            var matrix = new uiMatrix3(state.matrix.Value);

            matrix.preTranslate(offset.dx, offset.dy);

            var mesh           = TextBlobMesh.create(textBlob.Value, scale, matrix);
            var textBlobBounds = matrix.mapRect(uiRectHelper.fromRect(textBlob.Value.boundsInText));

            // request font texture so text mesh could be generated correctly
            var     style          = textBlob.Value.style;
            var     font           = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;
            var     fontSizeToLoad = Mathf.CeilToInt(style.UnityFontSize * scale);
            var     subText        = textBlob.Value.text.Substring(textBlob.Value.textOffset, textBlob.Value.textSize);
            Texture tex            = null;
            bool    notEmoji       = !char.IsHighSurrogate(subText[0]) && !EmojiUtils.isSingleCharEmoji(subText[0]);

            if (notEmoji)
            {
                font.RequestCharactersInTextureSafe(subText, fontSizeToLoad, style.UnityFontStyle);
                tex = font.material.mainTexture;
            }

            if (paint.maskFilter != null && paint.maskFilter.Value.sigma != 0)
            {
                this._drawWithMaskFilter(textBlobBounds, paint, paint.maskFilter.Value, null, null, false, 0, 0, tex,
                                         textBlobBounds, mesh, notEmoji, this.___drawTextDrawMeshCallback);
                return;
            }

            this._drawTextDrawMeshCallback(paint, null, null, false, 0, 0, tex, textBlobBounds, mesh, notEmoji);
        }
Beispiel #3
0
        public void doLayout(float offset, TextBuff buff, int start, int count, TextStyle style)
        {
            this._start = start;
            this._count = count;
            this._advances.reset(count);
            this._positions.reset(count);
            this._advance = 0;
            this._bounds  = default;

            Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;

            char startingChar = buff.text[buff.offset + start];

            if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar))
            {
                this.layoutEmoji(buff.text.Substring(buff.offset + start, count), style, font, start, count);
            }
            else
            {
                font.RequestCharactersInTextureSafe(buff.text, style.UnityFontSize, style.UnityFontStyle);

                int wordstart = start == buff.size
                    ? start
                    : LayoutUtils.getPrevWordBreakForCache(buff, start + 1);
                int wordend;
                for (int iter = start; iter < start + count; iter = wordend)
                {
                    wordend = LayoutUtils.getNextWordBreakForCache(buff, iter);
                    int wordCount = Mathf.Min(start + count, wordend) - iter;
                    this.layoutWord(offset, iter - start, buff.subBuff(wordstart, wordend - wordstart),
                                    iter - wordstart, wordCount, style, font);
                    wordstart = wordend;
                }
            }
            this._count = count;
        }
        public uiMeshMesh resolveMesh()
        {
            if (this._resolved)
            {
                return(this._mesh);
            }

            this._resolved = true;

            var style = this.textBlob.Value.style;

            var text     = this.textBlob.Value.text;
            var key      = MeshKey.create(this.textBlob.Value.instanceId, this.scale);
            var fontInfo = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle);
            var font     = fontInfo.font;

            _meshes.TryGetValue(key, out var meshInfo);
            if (meshInfo != null && meshInfo.textureVersion == fontInfo.textureVersion)
            {
                ObjectPool <MeshKey> .release(key);

                meshInfo.touch();
                this._mesh = meshInfo.mesh.transform(this.matrix);
                return(this._mesh);
            }

            // Handling Emoji
            char startingChar = text[this.textBlob.Value.textOffset];

            if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar))
            {
                var vert = ObjectPool <uiList <Vector3> > .alloc();

                var tri = ObjectPool <uiList <int> > .alloc();

                var uvCoord = ObjectPool <uiList <Vector2> > .alloc();

                var metrics    = FontMetrics.fromFont(font, style.UnityFontSize);
                var minMaxRect = EmojiUtils.getMinMaxRect(style.fontSize, metrics.ascent, metrics.descent);
                var minX       = minMaxRect.left;
                var maxX       = minMaxRect.right;
                var minY       = minMaxRect.top;
                var maxY       = minMaxRect.bottom;

                for (int i = 0; i < this.textBlob.Value.textSize; i++)
                {
                    char a    = text[this.textBlob.Value.textOffset + i];
                    int  code = a;
                    if (char.IsHighSurrogate(a))
                    {
                        D.assert(i + 1 < this.textBlob.Value.textSize);
                        D.assert(this.textBlob.Value.textOffset + i + 1 < this.textBlob.Value.text.Length);
                        char b = text[this.textBlob.Value.textOffset + i + 1];
                        D.assert(char.IsLowSurrogate(b));
                        code = char.ConvertToUtf32(a, b);
                    }
                    else if (char.IsLowSurrogate(a) || EmojiUtils.isEmptyEmoji(a))
                    {
                        continue;
                    }

                    var uvRect = EmojiUtils.getUVRect(code);

                    var positionX = this.textBlob.Value.getPositionX(i);

                    int baseIndex = vert.Count;
                    vert.Add(new Vector3(positionX + minX, minY, 0));
                    vert.Add(new Vector3(positionX + maxX, minY, 0));
                    vert.Add(new Vector3(positionX + maxX, maxY, 0));
                    vert.Add(new Vector3(positionX + minX, maxY, 0));
                    tri.Add(baseIndex);
                    tri.Add(baseIndex + 1);
                    tri.Add(baseIndex + 2);
                    tri.Add(baseIndex);
                    tri.Add(baseIndex + 2);
                    tri.Add(baseIndex + 3);
                    uvCoord.Add(uvRect.bottomLeft.toVector());
                    uvCoord.Add(uvRect.bottomRight.toVector());
                    uvCoord.Add(uvRect.topRight.toVector());
                    uvCoord.Add(uvRect.topLeft.toVector());

                    if (char.IsHighSurrogate(a))
                    {
                        i++;
                    }
                }

                uiMeshMesh meshMesh = uiMeshMesh.create(null, vert, tri, uvCoord);

                if (_meshes.ContainsKey(key))
                {
                    ObjectPool <MeshInfo> .release(_meshes[key]);

                    _meshes.Remove(key);
                }

                _meshes[key] = MeshInfo.create(key, meshMesh, 0);

                this._mesh = meshMesh.transform(this.matrix);
                return(this._mesh);
            }

            var length         = this.textBlob.Value.textSize;
            var fontSizeToLoad = Mathf.CeilToInt(style.UnityFontSize * this.scale);

            var vertices = ObjectPool <uiList <Vector3> > .alloc();

            vertices.SetCapacity(length * 4);

            var triangles = ObjectPool <uiList <int> > .alloc();

            triangles.SetCapacity(length * 6);

            var uv = ObjectPool <uiList <Vector2> > .alloc();

            uv.SetCapacity(length * 4);

            for (int charIndex = 0; charIndex < length; ++charIndex)
            {
                var ch = text[charIndex + this.textBlob.Value.textOffset];
                // first char as origin for mesh position
                var positionX = this.textBlob.Value.getPositionX(charIndex);
                if (LayoutUtils.isWordSpace(ch) || LayoutUtils.isLineEndSpace(ch) || ch == '\t')
                {
                    continue;
                }

                if (fontSizeToLoad == 0)
                {
                    continue;
                }

                font.getGlyphInfo(ch, out var glyphInfo, fontSizeToLoad, style.UnityFontStyle);

                var minX = glyphInfo.minX / this.scale;
                var maxX = glyphInfo.maxX / this.scale;
                var minY = -glyphInfo.maxY / this.scale;
                var maxY = -glyphInfo.minY / this.scale;

                var baseIndex = vertices.Count;

                vertices.Add(new Vector3(positionX + minX, minY, 0));
                vertices.Add(new Vector3(positionX + maxX, minY, 0));
                vertices.Add(new Vector3(positionX + maxX, maxY, 0));
                vertices.Add(new Vector3(positionX + minX, maxY, 0));

                triangles.Add(baseIndex);
                triangles.Add(baseIndex + 1);
                triangles.Add(baseIndex + 2);
                triangles.Add(baseIndex);
                triangles.Add(baseIndex + 2);
                triangles.Add(baseIndex + 3);

                uv.Add(glyphInfo.uvTopLeft);
                uv.Add(glyphInfo.uvTopRight);
                uv.Add(glyphInfo.uvBottomRight);
                uv.Add(glyphInfo.uvBottomLeft);
            }

            if (vertices.Count == 0)
            {
                this._mesh = null;
                ObjectPool <uiList <Vector3> > .release(vertices);

                ObjectPool <uiList <Vector2> > .release(uv);

                ObjectPool <uiList <int> > .release(triangles);

                ObjectPool <MeshKey> .release(key);

                return(null);
            }

            uiMeshMesh mesh = vertices.Count > 0 ? uiMeshMesh.create(null, vertices, triangles, uv) : null;

            if (_meshes.ContainsKey(key))
            {
                ObjectPool <MeshInfo> .release(_meshes[key]);

                _meshes.Remove(key);
            }

            _meshes[key] = MeshInfo.create(key, mesh, fontInfo.textureVersion);

            this._mesh = mesh.transform(this.matrix);
            return(this._mesh);
        }
Beispiel #5
0
        void layoutEmoji(string text, TextStyle style, Font font, int start, int count)
        {
            for (int i = 0; i < count; i++)
            {
                char  c = text[i];
                float x = this._advance;
                if (EmojiUtils.isSingleCharNonEmptyEmoji(c) || char.IsHighSurrogate(c))
                {
                    float letterSpace          = style.letterSpacing;
                    float letterSpaceHalfLeft  = letterSpace * 0.5f;
                    float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;

                    x += letterSpaceHalfLeft;
                    this._advances[i] += letterSpaceHalfLeft;

                    var metrics = FontMetrics.fromFont(font, style.UnityFontSize);

                    var minX = x;
                    var maxX = metrics.descent - metrics.ascent + x;
                    var minY = metrics.ascent;
                    var maxY = metrics.descent;

                    if (this._bounds.width <= 0 || this._bounds.height <= 0)
                    {
                        this._bounds = UnityEngine.Rect.MinMaxRect(
                            minX, minY, maxX, maxY);
                    }
                    else
                    {
                        if (minX < this._bounds.x)
                        {
                            this._bounds.x = minX;
                        }

                        if (minY < this._bounds.y)
                        {
                            this._bounds.y = minY;
                        }

                        if (maxX > this._bounds.xMax)
                        {
                            this._bounds.xMax = maxX;
                        }

                        if (maxY > this._bounds.yMax)
                        {
                            this._bounds.yMax = maxY;
                        }
                    }

                    this._positions[i] = x;
                    float advance = style.fontSize;
                    x += advance;

                    this._advances[i] += advance;
                    this._advances[i] += letterSpaceHalfRight;
                    x += letterSpaceHalfRight;
                }
                else
                {
                    this._advances[i]  = 0;
                    this._positions[i] = x;
                }

                this._advance = x;
            }
        }