浏览代码

Fix line break issue.

/main
Yuncong Zhang 5 年前
当前提交
9960d480
共有 7 个文件被更改,包括 124 次插入114 次删除
  1. 2
      Runtime/painting/text_span.cs
  2. 178
      Runtime/ui/painting/canvas_impl.cs
  3. 8
      Runtime/ui/painting/txt/mesh_generator.cs
  4. 38
      Runtime/ui/txt/emoji.cs
  5. 6
      Runtime/ui/txt/layout.cs
  6. 4
      Runtime/ui/txt/layout_utils.cs
  7. 2
      Tests/Editor/Paragraph.cs

2
Runtime/painting/text_span.cs


builder.pushStyle(this.style, textScaleFactor);
}
if (this.splitedText != null) {
if (this.splitedText.Count == 1 && !EmojiUtils.isSurrogatePairStart(this.splitedText[0][0])) {
if (this.splitedText.Count == 1 && !char.IsHighSurrogate(this.splitedText[0][0])) {
builder.addText(this.splitedText[0]);
}
else {

178
Runtime/ui/painting/canvas_impl.cs


readonly float _fringeWidth;
readonly float _devicePixelRatio;
readonly MeshPool _meshPool;
public PictureFlusher(RenderTexture renderTexture, float devicePixelRatio, MeshPool meshPool) {
D.assert(renderTexture);
D.assert(devicePixelRatio > 0);

this._devicePixelRatio = devicePixelRatio;
this._meshPool = meshPool;
}
RenderLayer firstLayer;
if (this._layers.Count == 0) {
var width = this._renderTexture.width;

height = height,
layerBounds = bounds,
};
} else {
}
else {
D.assert(this._layers.Count > 0);
firstLayer = this._layers[0];
firstLayer = new RenderLayer {

var blurMesh = ImageMeshGenerator.imageMesh(null, Rect.one, bounds);
layer.draws.Add(CanvasShader.texRT(layer, paint, blurMesh, blurLayer));
}
} else if (paint.backdrop is _MatrixImageFilter) {
}
else if (paint.backdrop is _MatrixImageFilter) {
var points = new[] {bounds.topLeft, bounds.bottomLeft, bounds.bottomRight, bounds.topRight};
state.matrix.mapPoints(points);

var matrix = Matrix3.makeTrans(-bounds.left, -bounds.top);
matrix.postConcat(filter.transform);
matrix.postTranslate(bounds.left, bounds.top);
var mesh = ImageMeshGenerator.imageMesh(
matrix,
points[0], points[1], points[2], points[3],

}
}
}
void _restore() {
var layer = this._currentLayer;
D.assert(layer.states.Count > 0);

var renderDraw = CanvasShader.texRT(currentLayer, layer.layerPaint, mesh, layer);
currentLayer.draws.Add(renderDraw);
}
void _translate(float dx, float dy) {
var state = this._currentLayer.currentState;
var matrix = Matrix3.makeTrans(dx, dy);

var matrix = Matrix3.makeRotate(radians);
matrix.postConcat(state.matrix);
state.matrix = matrix;
} else {
}
else {
var matrix = Matrix3.makeRotate(radians, offset.dx, offset.dy);
matrix.postConcat(state.matrix);
state.matrix = matrix;

void _skew(float sx, float sy) {
var state = this._currentLayer.currentState;
var matrix = Matrix3.makeSkew( sx, sy);
var matrix = Matrix3.makeSkew(sx, sy);
matrix.postConcat(state.matrix);
state.matrix = matrix;
}

var state = layer.currentState;
layer.clipStack.clipPath(path, state.matrix, state.scale * this._devicePixelRatio);
}
void _tryAddScissor(RenderLayer layer, Rect scissor) {
if (scissor == this._lastScissor) {
return;

});
this._lastScissor = scissor;
}
bool _applyClip(Rect queryBounds) {
if (queryBounds == null || queryBounds.isEmpty) {
return false;

if (scissor == layerBounds) {
this._tryAddScissor(layer, null);
} else {
}
else {
var deviceScissor = Rect.fromLTRB(
scissor.left - layerBounds.left, layerBounds.bottom - scissor.bottom,
scissor.right - layerBounds.left, layerBounds.bottom - scissor.top

if (deviceScissor.isEmpty) {
return false;
}
this._tryAddScissor(layer, deviceScissor);
}

layer.ignoreClip = true;
} else {
}
else {
layer.ignoreClip = false;
// need to inflate a bit to make sure all area is cleared.

return true;
}
void _setLastClipGenId(uint clipGenId, Rect clipBounds) {
var layer = this._currentLayer;
layer.lastClipGenId = clipGenId;

return layer.lastClipGenId != clipGenId || layer.lastClipBounds != clipBounds;
}
RenderLayer _createMaskLayer(RenderLayer parentLayer, Rect maskBounds, Action<Paint> drawCallback, Paint paint) {
RenderLayer _createMaskLayer(RenderLayer parentLayer, Rect maskBounds, Action<Paint> drawCallback,
Paint paint) {
var textureWidth = Mathf.CeilToInt(maskBounds.width * this._devicePixelRatio);
if (textureWidth < 1) {
textureWidth = 1;

if (textureWidth < 1) {
textureWidth = 1;
}
var textureHeight = Mathf.CeilToInt((float) maskLayer.height / scaleFactorY);
if (textureHeight < 1) {
textureHeight = 1;

void _drawPath(Path path, Paint paint) {
D.assert(path != null);
D.assert(paint != null);
if (paint.style == PaintingStyle.fill) {
var state = this._currentLayer.currentState;
var cache = path.flatten(state.scale * this._devicePixelRatio);

Action<Paint> drawMesh = p => {
if (!this._applyClip(mesh.bounds)) {
return;

this._drawWithMaskFilter(mesh.bounds, drawMesh, paint, paint.maskFilter);
return;
}
drawMesh(paint);
}
else {

}
var layer = this._currentLayer;
drawMesh(paint);
}
}

if (src == null) {
src = Rect.one;
} else {
}
else {
src = src.scale(1f / image.width, 1f / image.height);
}

var scaleY = 1f / image.height;
if (src == null) {
src = Rect.one;
} else {
src = src.scale(scaleX, scaleY);
}
else {
src = src.scale(scaleX, scaleY);
}
center = center.scale(scaleX, scaleY);

var mesh = ImageMeshGenerator.imageNineMesh(state.matrix, src, center, image.width, image.height, dst);
var mesh = ImageMeshGenerator.imageNineMesh(state.matrix, src, center, image.width, image.height, dst);
if (!this._applyClip(mesh.bounds)) {
return;
}

this._saveLayer(cmd.rect, cmd.paint);
break;
}
case DrawRestore _: {
saveCount--;
if (saveCount < 0) {

this._restore();
break;
}
case DrawResetMatrix _:
this._resetMatrix();
break;

}
default:
throw new Exception("unknown drawCmd: " + drawCmd);
}

var state = this._currentLayer.currentState;
var scale = state.scale * this._devicePixelRatio;
// request font texture so text mesh could be generated correctly
var style = textBlob.style;
var font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;

Texture tex = null;
bool alpha = !EmojiUtils.isSurrogatePairStart(subText[0]);
if(alpha) {
bool alpha = !char.IsHighSurrogate(subText[0]);
if (alpha) {
font.RequestCharactersInTextureSafe(subText, fontSizeToLoad, style.UnityFontStyle);
tex = font.material.mainTexture;
}

}
var layer = this._currentLayer;
if(alpha) layer.draws.Add(CanvasShader.texAlpha(layer, p, mesh, tex));
if (alpha) {
layer.draws.Add(CanvasShader.texAlpha(layer, p, mesh, tex));
}
Paint paintWithWhite= new Paint(p);
Paint paintWithWhite = new Paint(p);
layer.draws.Add(CanvasShader.tex(layer, paintWithWhite, mesh.resovleMesh(), EmojiUtils.image));
layer.draws.Add(CanvasShader.tex(layer, paintWithWhite, mesh.resolveMesh(), EmojiUtils.image));
}
};

}
this._lastRtID = rtID;
} else {
}
else {
void _drawLayer(RenderLayer layer, CommandBuffer cmdBuf) {
bool toClear = true;

break;
case CmdDraw cmd:
this._setRenderTarget(cmdBuf, layer.rtID, ref toClear);
} else {
}
else {
cmdBuf.SetGlobalTexture(CmdDraw.texId, cmd.layer.rtID);
}
}

MeshMesh mesh = cmd.mesh;
if (cmd.textMesh != null) {
mesh = cmd.textMesh.resovleMesh();
mesh = cmd.textMesh.resolveMesh();
}
if (mesh == null) {

if (mesh.matrix == null) {
cmd.properties.SetFloatArray(CmdDraw.matId, CmdDraw.idMat3.fMat);
} else {
}
else {
cmd.properties.SetFloatArray(CmdDraw.matId, mesh.matrix.fMat);
}

}
} else {
}
else {
D.assert(!toClear);
foreach (var subLayer in layer.layers) {

cmd.meshObj = null;
cmd.meshObjCreated = false;
}
break;
}
}

this.layerBounds.width,
this.layerBounds.height);
}
return this._viewport.Value;
}
}

internal class State {
static readonly Matrix3 _id = Matrix3.I();
Matrix3 _matrix;
float? _scale;
Matrix3 _invMatrix;

this._scale = scale;
this._invMatrix = invMatrix;
}
public Matrix3 matrix {
get { return this._matrix; }
set {

if (this._scale == null) {
this._scale = XformUtils.getScale(this._matrix);
}
public Matrix3 invMatrix {
get {
if (this._invMatrix == null) {

return this._invMatrix;
}
}

}
}
internal class CmdDraw {
public MeshMesh mesh;
public TextBlobMesh textMesh;

public class CommandBufferCanvas : RecorderCanvas {
readonly PictureFlusher _flusher;
public CommandBufferCanvas(RenderTexture renderTexture, float devicePixelRatio, MeshPool meshPool)
public CommandBufferCanvas(RenderTexture renderTexture, float devicePixelRatio, MeshPool meshPool)
: base(new PictureRecorder()) {
this._flusher = new PictureFlusher(renderTexture, devicePixelRatio, meshPool);
}

public override void flush() {
var picture = this._recorder.endRecording();
this._recorder.reset();
this._recorder.reset();
}
}
static class XformUtils {
public static float getAverageScale(Matrix3 matrix) {
return (getScaleX(matrix) + getScaleY(matrix)) * 0.5f;

if (matrix.getSkewY() == 0) {
return matrix.getScaleX();
}
return Mathf.Sqrt(x * x + y * y);
}

// geometric mean of len0 and len1.
return Mathf.Sqrt(scaleX * scaleY);
}
public static float mapRadius(Matrix3 matrix, float radius) {
return getScale(matrix) * radius;
}

return new MeshMesh(matrix, vertices, _imageTriangles, uv);
}
public static MeshMesh imageMesh(Matrix3 matrix, Rect src, Rect dst) {
var vertices = new List<Vector3>(4);
var uv = new List<Vector2>(4);

return new MeshMesh(matrix, vertices, _imageTriangles, uv);
}
public static MeshMesh imageNineMesh(Matrix3 matrix, Rect src, Rect center, int srcWidth, int srcHeight, Rect dst) {
public static MeshMesh imageNineMesh(Matrix3 matrix, Rect src, Rect center, int srcWidth, int srcHeight,
Rect dst) {
float x0 = dst.left;
float x3 = dst.right;
float x1 = x0 + ((center.left - src.left) * srcWidth);

uv.Add(new Vector2(tx2, ty3));
vertices.Add(new Vector2(x3, y3));
uv.Add(new Vector2(tx3, ty3));
}
}
}
}

8
Runtime/ui/painting/txt/mesh_generator.cs


}
}
public MeshMesh resovleMesh() {
public MeshMesh resolveMesh() {
if (this._resolved) {
return this._mesh;
}

var text = this.textBlob.text;
var key = new MeshKey(this.textBlob.instanceId, this.scale);
if (EmojiUtils.isSurrogatePairStart(text[this.textBlob.textOffset])) {
if (char.IsHighSurrogate(text[this.textBlob.textOffset])) {
D.assert(EmojiUtils.isSurrogatePairEnd(b));
D.assert(char.IsLowSurrogate(b));
var pos = this.textBlob.positions[0];
var vert = new List<Vector3> {

var tri = new List<int> {
0, 1, 2, 0, 2, 3,
};
var code = EmojiUtils.decodeSurrogatePair(a, b);
var code = char.ConvertToUtf32(a, b);
var uvRect = EmojiUtils.getUVRect(code);
var uvCoord = new List<Vector2> {
uvRect.bottomLeft.toVector(),

38
Runtime/ui/txt/emoji.cs


using System;
using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.foundation;
using UnityEngine.Windows;
static Image _image;
public static Image image {

}
}
public static readonly Dictionary<uint, int> emojiLookupTable = new Dictionary<uint, int> {
public static readonly Dictionary<int, int> emojiLookupTable = new Dictionary<int, int> {
{0x1F60A, 0},
{0x1F60B, 1},
{0x1F60D, 2},

public const int rowCount = 4;
public const int colCount = 4;
public static Rect getUVRect(uint code) {
public static Rect getUVRect(int code) {
(index % colCount) * (1.0f / colCount),
(index % colCount) * (1.0f / colCount),
(rowCount - 1 - (index / colCount)) * (1.0f / rowCount),
1.0f / colCount, 1.0f / rowCount);
}

}
public static void encodeSurrogatePair(uint character, out char a, out char b) {
uint code;
D.assert(0x10000 <= character && character <= 0x10FFFF);
code = (character - 0x10000);
a = (char) (0xD800 | (code >> 10));
b = (char) (0xDC00 | (code & 0x3FF));
}
public static uint decodeSurrogatePair(char a, char b) {
uint code;
D.assert(0xD800 <= a && a <= 0xDBFF);
D.assert(0xDC00 <= b && b <= 0xDFFF);
code = 0x10000;
code += (uint) ((a & 0x03FF) << 10);
code += (uint) (b & 0x03FF);
return code;
}
public static bool isSurrogatePairStart(uint c) {
return 0xD800 <= c && c <= 0xDBFF;
}
public static bool isSurrogatePairEnd(uint c) {
return 0xDC00 <= c && c <= 0xDFFF;
}
if (i < text.Length - 1 && isSurrogatePairStart(text[i]) && isSurrogatePairEnd(text[i + 1])) {
if (i < text.Length - 1 && char.IsHighSurrogate(text[i]) && char.IsLowSurrogate(text[i + 1])) {
if (i > start) {
list.Add(text.Substring(start, i - start));
}

6
Runtime/ui/txt/layout.cs


Font font;
if (EmojiUtils.isSurrogatePairStart(buff.text[start])) {
if (char.IsHighSurrogate(buff.text[buff.offset + start])) {
D.assert(EmojiUtils.isSurrogatePairEnd(buff.text[start+1]));
D.assert(char.IsLowSurrogate(buff.text[buff.offset + start+1]));
this.layoutEmoji(style);
}

x += letterSpaceHalfLeft;
this._advances[0] += letterSpaceHalfLeft;
var minX = 0 + x;
var minX = x;
var maxX = style.fontSize + x;
var minY = -style.fontSize;
var maxY = 0;

4
Runtime/ui/txt/layout_utils.cs


}
public static bool isWordBreakAfter(ushort c) {
if (isWordSpace(c) || (c >= 0x2000 && c <= 0x200a) || c == 0x3000 || EmojiUtils.isSurrogatePairEnd(c)) {
if (isWordSpace(c) || (c >= 0x2000 && c <= 0x200a) || c == 0x3000) {
// spaces
return true;
}

public static bool isWordBreakBefore(ushort c) {
return isWordBreakAfter(c) || (c >= 0x3400 && c <= 0x9fff) || EmojiUtils.isSurrogatePairStart(c);
return isWordBreakAfter(c) || (c >= 0x3400 && c <= 0x9fff);
}
}

2
Tests/Editor/Paragraph.cs


"This is FontStyle.italic And 发撒放豆腐sad 发生的 Bold Text This is FontStyle.italic And Bold Text\n\n"),
new TextSpan(style: new TextStyle(fontSize: 18),
text: "FontSize 18: Get a named matrix value from the shader."),
new TextSpan(style: new TextStyle(fontSize: 24),
text: "Emoji \ud83d\ude0a\ud83d\ude0b\ud83d\ude0d\ud83d\ude0e\ud83d\ude00"),
new TextSpan(style: new TextStyle(fontSize: 14),
text: "Emoji \ud83d\ude0a\ud83d\ude0b\ud83d\ude0d\ud83d\ude0e\ud83d\ude00"),
new TextSpan(style: new TextStyle(fontSize: 18),

正在加载...
取消
保存