// 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; }
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); }
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); }
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; } }