xingwei.zhu 5 年前
当前提交
0b055504
共有 54 个文件被更改,包括 3123 次插入2283 次删除
  1. 2
      Runtime/Plugins/platform/android/editing/TextInputPlugin.java
  2. 9
      Runtime/Resources/UIWidgets_canvas.cginc
  3. 19
      Runtime/painting/text_painter.cs
  4. 14
      Runtime/rendering/editable.cs
  5. 2
      Runtime/ui/painting/draw_cmd.cs
  6. 5
      Runtime/ui/painting/painting.cs
  7. 695
      Runtime/ui/painting/path.cs
  8. 17
      Runtime/ui/painting/picture.cs
  9. 50
      Runtime/ui/painting/shadow_utils.cs
  10. 99
      Runtime/ui/painting/txt/mesh_generator.cs
  11. 164
      Runtime/ui/painting/txt/text_blob.cs
  12. 3
      Runtime/ui/renderer/cmdbufferCanvas/rendering/canvas_clip.cs
  13. 150
      Runtime/ui/renderer/cmdbufferCanvas/rendering/canvas_impl.cs
  14. 122
      Runtime/ui/renderer/cmdbufferCanvas/rendering/canvas_shader.cs
  15. 2
      Runtime/ui/renderer/common/draw_cmd.cs
  16. 68
      Runtime/ui/renderer/common/geometry/path/path.cs
  17. 432
      Runtime/ui/renderer/common/geometry/path/path_cache.cs
  18. 91
      Runtime/ui/renderer/common/geometry/path/path_utils.cs
  19. 13
      Runtime/ui/renderer/common/picture.cs
  20. 1
      Runtime/ui/renderer/compositeCanvas/flow/instrumentation.cs
  21. 1
      Runtime/ui/renderer/compositeCanvas/flow/performance_overlay_layer.cs
  22. 2
      Runtime/ui/renderer/compositeCanvas/flow/physical_shape_layer.cs
  23. 48
      Runtime/ui/text.cs
  24. 33
      Runtime/ui/txt/emoji.cs
  25. 500
      Runtime/ui/txt/layout.cs
  26. 50
      Runtime/ui/txt/layout_utils.cs
  27. 623
      Runtime/ui/txt/linebreaker.cs
  28. 32
      Runtime/ui/txt/paint_record.cs
  29. 1001
      Runtime/ui/txt/paragraph.cs
  30. 2
      Runtime/ui/txt/paragraph_builder.cs
  31. 6
      Runtime/ui/txt/styled_runs.cs
  32. 5
      Runtime/ui/txt/text_buff.cs
  33. 19
      Runtime/ui/txt/word_separate.cs
  34. 59
      Runtime/ui/txt/wordbreaker.cs
  35. 6
      Samples/UIWidgetSample/MaterialSample.cs
  36. 1
      Samples/UIWidgetSample/MaterialThemeSample.cs
  37. 3
      Samples/UIWidgetsGallery/demo/animation/home.cs
  38. 28
      Tests/Editor/CanvasAndLayers.cs
  39. 85
      Runtime/Resources/UIWidgets_canvas_shadowBox.shader
  40. 9
      Runtime/Resources/UIWidgets_canvas_shadowBox.shader.meta
  41. 117
      Runtime/Resources/UIWidgets_canvas_shadowRBox.shader
  42. 9
      Runtime/Resources/UIWidgets_canvas_shadowRBox.shader.meta
  43. 65
      Runtime/Resources/UIWidgets_canvas_strokeAlpha.shader
  44. 9
      Runtime/Resources/UIWidgets_canvas_strokeAlpha.shader.meta
  45. 74
      Runtime/ui/renderer/cmdbufferCanvas/rendering/canvas_shadow_utils.cs
  46. 11
      Runtime/ui/renderer/cmdbufferCanvas/rendering/canvas_shadow_utils.cs.meta
  47. 19
      Runtime/ui/renderer/common/geometry/path/path_extension.cs
  48. 11
      Runtime/ui/renderer/common/geometry/path/path_extension.cs.meta
  49. 146
      Samples/UIWidgetSample/AntialiasSVGSample.cs
  50. 11
      Samples/UIWidgetSample/AntialiasSVGSample.cs.meta
  51. 149
      Samples/UIWidgetSample/BenchMarkLayout.cs
  52. 11
      Samples/UIWidgetSample/BenchMarkLayout.cs.meta
  53. 296
      Samples/UIWidgetSample/TextLayoutSample.unity
  54. 7
      Samples/UIWidgetSample/TextLayoutSample.unity.meta

2
Runtime/Plugins/platform/android/editing/TextInputPlugin.java


ViewGroup contentView = (ViewGroup)UnityPlayer.currentActivity.findViewById(android.R.id.content);
mView = new TextInputView(UnityPlayer.currentActivity);
mView.requestFocus();
contentView.addView(mView, 0, 0);
contentView.addView(mView, 1, 1);
mImm = (InputMethodManager) mView.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
}

9
Runtime/Resources/UIWidgets_canvas.cginc


half4 _color;
fixed _alpha;
fixed _strokeMult;
half4x4 _shaderMat;
sampler2D _shaderTex;
half4 _leftColor;

return color;
}
float strokeMask(float u, float v) {
return min(1.0, (1.0 - abs(u * 2.0 - 1.0)) * 1.0) * min(1.0, v);
}
fixed4 frag_stroke_alpha(v2f i) : SV_Target {
return shader_color(i) * strokeMask(i.ftcoord.x, i.ftcoord.y);
}

19
Runtime/painting/text_painter.cs


this._ellipsis = ellipsis;
}
public float textScaleFactor {
get { return this._textScaleFactor; }
set {

this._textScaleFactor = value;
this._paragraph = null;
Paragraph.release(ref this._paragraph);
}
}

}
this._ellipsis = value;
this._paragraph = null;
Paragraph.release(ref this._paragraph);
this._needsLayout = true;
}
}

}
this._text = value;
this._paragraph = null;
Paragraph.release(ref this._paragraph);
this._needsLayout = true;
}
}

}
this._textDirection = value;
this._paragraph = null;
Paragraph.release(ref this._paragraph);
this._layoutTemplate = null;
this._needsLayout = true;
}

}
this._textAlign = value;
this._paragraph = null;
Paragraph.release(ref this._paragraph);
this._needsLayout = true;
}
}

}
this._maxLines = value;
this._paragraph = null;
Paragraph.release(ref this._paragraph);
this._needsLayout = true;
}
}

var prevCodeUnit = this._text.codeUnitAt(offset);
if (prevCodeUnit == null) // out of upper bounds
{
var rectNextLine = this._paragraph.getNextLineStartRect();
if (rectNextLine != null) {
return new Offset(rectNextLine.start, rectNextLine.top);
var rectNextLineTop = this._paragraph.getNextLineStartRectTop();
if (rectNextLineTop != null) {
return new Offset(0, rectNextLineTop.Value);
}
}
}

14
Runtime/rendering/editable.cs


public TextPosition getParagraphForward(TextPosition position, TextAffinity? affinity = null) {
var lineCount = this._textPainter.getLineCount();
Paragraph.LineRange line = null;
Paragraph.LineRange? line = null;
if (!line.hardBreak) {
if (!line.Value.hardBreak) {
if (line.end > position.offset) {
if (line.Value.end > position.offset) {
break;
}
}

}
return new TextPosition(line.end, affinity ?? position.affinity);
return new TextPosition(line.Value.end, affinity ?? position.affinity);
}

Paragraph.LineRange line = null;
Paragraph.LineRange? line = null;
for (int i = lineCount - 1; i >= 0; --i) {
line = this._textPainter.getLineRange(i);
if (i != 0 && !this._textPainter.getLineRange(i - 1).hardBreak) {

if (line.start < position.offset) {
if (line.Value.start < position.offset) {
break;
}
}

}
return new TextPosition(line.start, affinity ?? position.affinity);
return new TextPosition(line.Value.start, affinity ?? position.affinity);
}
protected override float computeMinIntrinsicWidth(float height) {

2
Runtime/ui/painting/draw_cmd.cs


}
public class DrawTextBlob : DrawCmd {
public TextBlob textBlob;
public TextBlob? textBlob;
public Offset offset;
public Paint paint;
}

5
Runtime/ui/painting/painting.cs


solid,
outer,
inner,
fast_shadow
}
public class MaskFilter : IEquatable<MaskFilter> {

public static MaskFilter blur(BlurStyle style, float sigma) {
return new MaskFilter(style, sigma);
}
public static MaskFilter fastShadow(float sigma) {
return new MaskFilter(BlurStyle.fast_shadow, sigma);
}
public readonly BlurStyle style;

695
Runtime/ui/painting/path.cs
文件差异内容过多而无法显示
查看文件

17
Runtime/ui/painting/picture.cs


var state = this._getState();
var scale = XformUtils.getScale(state.xform);
var rect = cmd.path.flatten(
var cache = cmd.path.flatten(
).getFillMesh(out _).transform(state.xform).bounds;
);
cache.computeFillMesh(0.0f, out _);
var rect = cache.fillMesh.transform(state.xform).bounds;
state.scissor = state.scissor == null ? rect : state.scissor.intersect(rect);
break;
}

MeshMesh mesh;
if (paint.style == PaintingStyle.fill) {
var cache = path.flatten(scale * devicePixelRatio);
mesh = cache.getFillMesh(out _).transform(state.xform);
cache.computeFillMesh(0.0f, out _);
mesh = cache.fillMesh.transform(state.xform);
}
else {
float strokeWidth = (paint.strokeWidth * scale).clamp(0, 200.0f);

}
var cache = path.flatten(scale * devicePixelRatio);
mesh = cache.getStrokeMesh(
cache.computeStrokeMesh(
0.0f,
paint.strokeMiterLimit).transform(state.xform);
paint.strokeMiterLimit);
mesh = cache.strokeMesh.transform(state.xform);
}
if (paint.maskFilter != null && paint.maskFilter.sigma != 0) {

case DrawTextBlob cmd: {
var state = this._getState();
var scale = XformUtils.getScale(state.xform);
var rect = cmd.textBlob.boundsInText.shift(cmd.offset);
var rect = cmd.textBlob.Value.shiftedBoundsInText(cmd.offset.dx, cmd.offset.dy);
rect = state.xform.mapRect(rect);
var paint = cmd.paint;

50
Runtime/ui/painting/shadow_utils.cs


using Unity.UIWidgets.material;
public const bool kUseFastShadow = false;
public const bool kUseFastShadow = true;
const float kAmbientHeightFactor = 1.0f / 128.0f;
const float kAmbientGeomFactor = 64.0f;

const float kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor;
const bool debugShadow = false;
static float divideAndPin(float numer, float denom, float min, float max) {
return (numer / denom).clamp(min, max);

_shadowPaint.maskFilter = null;
}
//debug shadow
if (debugShadow) {
var isRRect = path.isNaiveRRect;
if (isRRect) {
ambientColor = uiColor.fromColor(Colors.red);
spotColor = uiColor.fromColor(Colors.red);
}
else {
ambientColor = uiColor.fromColor(Colors.green);
spotColor = uiColor.fromColor(Colors.green);
}
}
//ambient light
float devSpaceOutset = ambientBlurRadius(zPlaneParams.z);
float oneOverA = ambientRecipAlpha(zPlaneParams.z);

//Paint paint = new Paint {color = ambientColor, strokeWidth = strokeWidth, style = PaintingStyle.fill};
_shadowPaint.color = new Color(ambientColor.value);
_shadowPaint.strokeWidth = strokeWidth;
_shadowPaint.style = PaintingStyle.fill;

float radius = 0.0f;
if (!getSpotShadowTransform(devLightPos, lightRadius, viewMatrix, zPlaneParams, path.getBounds(),
_shadowMatrix, ref radius)) {
return;

canvas.setMatrix(_shadowMatrix);
//Paint paint2 = new Paint {color = spotColor};
float sigma2 = convertRadiusToSigma(radius);
_shadowPaint.maskFilter = path.isNaiveRRect ? MaskFilter.fastShadow(sigma2) : MaskFilter.blur(BlurStyle.normal, sigma2);
_shadowPaint.maskFilter = null;
}
/*
* Check whether the RRect is a naive Round-Rect, of which
* (1) all the corner radius are the same
* (2) the corner radius is not bigger than either half the width or the height of the Round Rect's bounding box
*
* Usage: The shadow of a naive Round-Rect can be easily drawn using a ShadowRBox shader, so we can use it to
* find all the situations that a fast shadow can be drawn to tackle the performance issue
*/
public static bool isNaiveRRect(this RRect rrect) {
var radius = rrect.tlRadiusX;
return rrect.tlRadiusY == radius &&
rrect.trRadiusX == radius &&
rrect.trRadiusY == radius &&
rrect.blRadiusX == radius &&
rrect.blRadiusY == radius &&
rrect.brRadiusX == radius &&
rrect.brRadiusY == radius &&
radius <= rrect.width / 2 &&
radius <= rrect.height / 2;
}
}
}

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


public float scale;
public MeshKey() {
}
public static MeshKey create(long textBlobId, float scale) {

long _timeToLive;
public MeshInfo() {
}
public static MeshInfo create(MeshKey key, uiMeshMesh mesh, long textureVersion, int timeToLive = 5) {

}
}
public TextBlob textBlob;
public TextBlob? textBlob;
uiMeshMesh _mesh;
bool _resolved;

public override void clear() {
this.textBlob = null;
this.textBlob = null;
}
public static TextBlobMesh create(TextBlob textBlob, float scale, uiMatrix3 matrix) {

newMesh.matrix = matrix;
return newMesh;
}
public static long frameCount {
get { return _frameCount; }
}

}
static List<MeshKey> _keysToRemove = new List<MeshKey>();
public static void tickNextFrame() {
_frameCount++;
D.assert(_keysToRemove.Count == 0);

}
}
foreach (var key in _keysToRemove)
{
foreach (var key in _keysToRemove) {
_keysToRemove.Clear();
}

}
this._resolved = true;
var style = this.textBlob.style;
var text = this.textBlob.text;
var key = MeshKey.create(this.textBlob.instanceId, this.scale);
var style = this.textBlob.Value.style;
var text = this.textBlob.Value.text;
var key = MeshKey.create(this.textBlob.Value.instanceId, this.scale);
_meshes.TryGetValue(key, out var meshInfo);
if (meshInfo != null && meshInfo.textureVersion == fontInfo.textureVersion) {
ObjectPool<MeshKey>.release(key);

}
char startingChar = text[this.textBlob.textOffset];
char startingChar = text[this.textBlob.Value.textOffset];
var metrics = FontMetrics.fromFont(font, style.UnityFontSize);
var minMaxRect = EmojiUtils.getMinMaxRect(style.fontSize, metrics.ascent, metrics.descent);
var minX = minMaxRect.left;

for (int i = 0; i < this.textBlob.textSize; i++) {
char a = text[this.textBlob.textOffset + i];
for (int i = 0; i < this.textBlob.Value.textSize; i++) {
char a = text[this.textBlob.Value.textOffset + i];
D.assert(i+1 < this.textBlob.textSize);
D.assert(this.textBlob.textOffset+i+1 < this.textBlob.text.Length);
char b = text[this.textBlob.textOffset+i+1];
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];
} else if (char.IsLowSurrogate(a) || EmojiUtils.isEmptyEmoji(a)) {
}
else if (char.IsLowSurrogate(a) || EmojiUtils.isEmptyEmoji(a)) {
var pos = this.textBlob.positions[i];
var positionX = this.textBlob.Value.getPositionX(i);
vert.Add(new Vector3(pos.x + minX, pos.y + minY, 0));
vert.Add(new Vector3(pos.x + maxX, pos.y + minY, 0));
vert.Add(new Vector3(pos.x + maxX, pos.y + maxY, 0));
vert.Add(new Vector3(pos.x + minX, pos.y + maxY, 0));
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);

uvCoord.Add(uvRect.topRight.toVector());
uvCoord.Add(uvRect.topLeft.toVector());
if(char.IsHighSurrogate(a)) i++;
if (char.IsHighSurrogate(a)) {
i++;
}
var length = this.textBlob.textSize;
var length = this.textBlob.Value.textSize;
var ch = text[charIndex + this.textBlob.textOffset];
var ch = text[charIndex + this.textBlob.Value.textOffset];
var position = this.textBlob.positions[charIndex];
var positionX = this.textBlob.Value.getPositionX(charIndex);
if (LayoutUtils.isWordSpace(ch) || LayoutUtils.isLineEndSpace(ch) || ch == '\t') {
continue;
}

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

vertices.Add(new Vector3((position.x + minX), (position.y + minY), 0));
vertices.Add(new Vector3((position.x + maxX), (position.y + minY), 0));
vertices.Add(new Vector3((position.x + maxX), (position.y + maxY), 0));
vertices.Add(new Vector3((position.x + minX), (position.y + maxY), 0));
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);

}
uiMeshMesh mesh = vertices.Count > 0 ? uiMeshMesh.create(null, vertices, triangles, uv) : null;
_meshes[key] = MeshInfo.create(key, mesh, fontInfo.textureVersion);
this._mesh = mesh.transform(this.matrix);

164
Runtime/ui/painting/txt/text_blob.cs


namespace Unity.UIWidgets.ui {
public class TextBlob {
internal TextBlob(string text, int textOffset, int textSize, Vector2d[] positions, Rect bounds, TextStyle style) {
this.instanceId = ++_nextInstanceId;
this.positions = positions;
this.text = text;
this.textOffset = textOffset;
this.textSize = textSize;
this.style = style;
this.bounds = bounds;
}
public Rect boundsInText {
get {
if (this._boundsInText == null) {
this._boundsInText = this.bounds.shift(new Offset(this.positions[0].x, this.positions[0].y));
}
return this._boundsInText;
}
}
static long _nextInstanceId = 0;
internal readonly long instanceId;
internal readonly string text;
internal readonly int textOffset;
internal readonly int textSize;
internal readonly Vector2d[] positions;
internal readonly TextStyle style;
internal readonly Rect bounds; // bounds with positions[start] as origin
Rect _boundsInText;
}
public class TextBlobBuilder {
TextStyle _style;
public Vector2d[] positions;
string _text;
int _textOffset;
int _size;
Rect _bounds;
public void allocRunPos(painting.TextStyle style, string text, int offset, int size, float textScaleFactor = 1.0f) {
this.allocRunPos(TextStyle.applyStyle(null, style, textScaleFactor), text, offset, size);
}
internal void allocRunPos(TextStyle style, string text, int offset, int size) {
this._style = style;
this._text = text;
this._textOffset = offset;
this._size = size;
if (this.positions == null || this.positions.Length < size) {
this.positions = new Vector2d[size];
}
}
public void setBounds(Rect bounds) {
this._bounds = bounds;
}
public TextBlob make() {
var result = new TextBlob(this._text, this._textOffset,
this._size, this.positions, this._bounds, this._style);
this.positions = null;
return result;
}
}
namespace Unity.UIWidgets.ui {
public struct TextBlob {
internal TextBlob(string text, int textOffset, int textSize, float[] positionXs,
UnityEngine.Rect bounds, TextStyle style) {
this.instanceId = ++_nextInstanceId;
this._positionXs = positionXs;
this.text = text;
this.textOffset = textOffset;
this.textSize = textSize;
this.style = style;
this._bounds = bounds;
this._boundsInText = null;
}
public Rect boundsInText {
get {
if (this._boundsInText == null) {
var pos = this.getPositionX(0);
this._boundsInText = Rect.fromLTWH(this._bounds.xMin + pos, this._bounds.yMin,
this._bounds.width, this._bounds.height);
}
return this._boundsInText;
}
}
public Rect shiftedBoundsInText(float dx, float dy) {
var pos = this.getPositionX(0);
return Rect.fromLTWH(this._bounds.xMin + pos + dx, this._bounds.yMin + dy,
this._bounds.width, this._bounds.height);
}
public float getPositionX(int i) {
return this._positionXs[this.textOffset + i];
}
static long _nextInstanceId;
internal readonly long instanceId;
internal readonly string text;
internal readonly int textOffset;
internal readonly int textSize;
internal readonly TextStyle style;
readonly UnityEngine.Rect _bounds; // bounds with positions[start] as origin
readonly float[] _positionXs;
Rect _boundsInText;
}
public struct TextBlobBuilder {
TextStyle _style;
float[] _positionXs;
string _text;
int _textOffset;
int _size;
UnityEngine.Rect _bounds;
public void allocRunPos(painting.TextStyle style, string text, int offset, int size,
float textScaleFactor = 1.0f) {
this.allocRunPos(TextStyle.applyStyle(null, style, textScaleFactor), text, offset, size);
}
internal void allocRunPos(TextStyle style, string text, int offset, int size) {
this._style = style;
this._text = text;
this._textOffset = offset;
this._size = size;
// Allocate a single buffer for all text blobs that share this text, to save memory and GC.
// It is assumed that all of `text` is being used. This may cause great waste if a long text is passed
// but only a small part of it is to be rendered, which is not the case for now.
this.allocPos(text.Length);
}
internal void allocPos(int size) {
if (this._positionXs == null || this._positionXs.Length < size) {
this._positionXs = new float[size];
}
}
public void setPositionX(int i, float positionX) {
this._positionXs[this._textOffset + i] = positionX;
}
public void setPositionXs(float[] positionXs) {
this._positionXs = positionXs;
}
public void setBounds(UnityEngine.Rect bounds) {
this._bounds = bounds;
}
public TextBlob make() {
var result = new TextBlob(this._text, this._textOffset,
this._size, this._positionXs, this._bounds, this._style);
return result;
}
}
}

3
Runtime/ui/renderer/cmdbufferCanvas/rendering/canvas_clip.cs


newElement.saveCount = saveCount;
var pathCache = uiPath.flatten(scale);
var fillMesh = pathCache.getFillMesh(out newElement.convex);
pathCache.computeFillMesh(0.0f, out newElement.convex);
var fillMesh = pathCache.fillMesh;
newElement.mesh = fillMesh.transform(matrix);
var vertices = newElement.mesh.vertices;

150
Runtime/ui/renderer/cmdbufferCanvas/rendering/canvas_impl.cs


RenderLayer _createMaskLayer(RenderLayer parentLayer, uiRect maskBounds,
_drawPathDrawMeshCallbackDelegate drawCallback,
uiPaint paint, bool convex, float alpha, Texture tex, uiRect texBound, TextBlobMesh textMesh,
uiMeshMesh mesh, bool notEmoji) {
uiPaint paint, bool convex, float alpha, float strokeMult, Texture tex, uiRect texBound,
TextBlobMesh textMesh,
uiMeshMesh fillMesh, uiMeshMesh strokeMesh, bool notEmoji) {
var textureWidth = Mathf.CeilToInt(maskBounds.width * this._devicePixelRatio);
if (textureWidth < 1) {
textureWidth = 1;

var maskState = maskLayer.states[maskLayer.states.Count - 1];
maskState.matrix = parentState.matrix;
drawCallback.Invoke(uiPaint.shapeOnly(paint), mesh, convex, alpha, tex, texBound, textMesh, notEmoji);
drawCallback.Invoke(uiPaint.shapeOnly(paint), fillMesh, strokeMesh, convex, alpha, strokeMult, tex,
texBound, textMesh, notEmoji);
var removed = this._layers.removeLast();
D.assert(removed == maskLayer);

}
void _drawWithMaskFilter(uiRect meshBounds, uiPaint paint, uiMaskFilter maskFilter,
uiMeshMesh mesh, bool convex, float alpha, Texture tex, uiRect texBound, TextBlobMesh textMesh, bool notEmoji,
uiMeshMesh fillMesh, uiMeshMesh strokeMesh, bool convex, float alpha, float strokeMult, Texture tex,
uiRect texBound, TextBlobMesh textMesh, bool notEmoji,
_drawPathDrawMeshCallbackDelegate drawCallback) {
var layer = this._currentLayer;
var clipBounds = layer.layerBounds;

}
if (clipBounds.isEmpty) {
this._drawPathDrawMeshQuit(mesh, textMesh);
this._drawPathDrawMeshQuit(fillMesh, strokeMesh, textMesh);
return;
}

this._drawPathDrawMeshQuit(mesh, textMesh);
this._drawPathDrawMeshQuit(fillMesh, strokeMesh, textMesh);
return;
}

if (maskBounds.isEmpty) {
this._drawPathDrawMeshQuit(mesh, textMesh);
this._drawPathDrawMeshQuit(fillMesh, strokeMesh, textMesh);
var maskLayer = this._createMaskLayer(layer, maskBounds, drawCallback, paint, convex, alpha, tex, texBound,
textMesh, mesh, notEmoji);
var maskLayer = this._createMaskLayer(layer, maskBounds, drawCallback, paint, convex, alpha, strokeMult,
tex, texBound,
textMesh, fillMesh, strokeMesh, notEmoji);
var blurLayer = this._createBlurLayer(maskLayer, sigma, sigma, layer);

layer.draws.Add(CanvasShader.texRT(layer, paint, blurMesh, blurLayer));
}
delegate void _drawPathDrawMeshCallbackDelegate(uiPaint p, uiMeshMesh mesh, bool convex, float alpha,
delegate void _drawPathDrawMeshCallbackDelegate(uiPaint p, uiMeshMesh fillMesh, uiMeshMesh strokeMesh,
bool convex, float alpha, float strokeMult,
void _drawPathDrawMeshCallback(uiPaint p, uiMeshMesh mesh, bool convex, float alpha, Texture tex,
void _drawPathDrawMeshCallback(uiPaint p, uiMeshMesh fillMesh, uiMeshMesh strokeMesh, bool convex, float alpha,
float strokeMult, Texture tex,
if (!this._applyClip(mesh.bounds)) {
ObjectPool<uiMeshMesh>.release(mesh);
if (!this._applyClip(fillMesh.bounds)) {
ObjectPool<uiMeshMesh>.release(fillMesh);
ObjectPool<uiMeshMesh>.release(strokeMesh);
layer.draws.Add(CanvasShader.convexFill(layer, p, mesh));
layer.draws.Add(CanvasShader.convexFill(layer, p, fillMesh));
layer.draws.Add(CanvasShader.fill0(layer, mesh));
layer.draws.Add(CanvasShader.fill1(layer, p, mesh.boundsMesh));
layer.draws.Add(CanvasShader.fill0(layer, fillMesh));
layer.draws.Add(CanvasShader.fill1(layer, p, fillMesh.boundsMesh));
}
if (strokeMesh != null) {
layer.draws.Add(CanvasShader.strokeAlpha(layer, p, alpha, strokeMult, strokeMesh));
layer.draws.Add(CanvasShader.stroke1(layer, strokeMesh.duplicate()));
void _drawPathDrawMeshCallback2(uiPaint p, uiMeshMesh mesh, bool convex, float alpha, Texture tex,
void _drawPathDrawMeshCallback2(uiPaint p, uiMeshMesh fillMesh, uiMeshMesh strokeMesh, bool convex, float alpha,
float strokeMult, Texture tex,
if (!this._applyClip(mesh.bounds)) {
ObjectPool<uiMeshMesh>.release(mesh);
if (!this._applyClip(strokeMesh.bounds)) {
ObjectPool<uiMeshMesh>.release(strokeMesh);
layer.draws.Add(CanvasShader.stroke0(layer, p, alpha, mesh));
layer.draws.Add(CanvasShader.stroke1(layer, mesh.duplicate()));
layer.draws.Add(CanvasShader.strokeAlpha(layer, p, alpha, strokeMult, strokeMesh));
layer.draws.Add(CanvasShader.stroke1(layer, strokeMesh.duplicate()));
void _drawTextDrawMeshCallback(uiPaint p, uiMeshMesh mesh, bool convex, float alpha, Texture tex,
void _drawTextDrawMeshCallback(uiPaint p, uiMeshMesh fillMesh, uiMeshMesh strokeMesh, bool convex, float alpha,
float strokeMult, Texture tex,
uiRect textBlobBounds, TextBlobMesh textMesh, bool notEmoji) {
if (!this._applyClip(textBlobBounds)) {
ObjectPool<TextBlobMesh>.release(textMesh);

}
}
void _drawPathDrawMeshQuit(uiMeshMesh mesh, TextBlobMesh textMesh) {
ObjectPool<uiMeshMesh>.release(mesh);
void _drawPathDrawMeshQuit(uiMeshMesh fillMesh, uiMeshMesh strokeMesh, TextBlobMesh textMesh) {
ObjectPool<uiMeshMesh>.release(fillMesh);
ObjectPool<uiMeshMesh>.release(strokeMesh);
//draw fast shadow
if (paint.maskFilter != null && paint.maskFilter.Value.style == BlurStyle.fast_shadow) {
this._drawRRectShadow(path, paint);
return;
}
if (paint.style == PaintingStyle.fill) {
var state = this._currentLayer.currentState;

var fillMesh = cache.getFillMesh(out convex);
var mesh = fillMesh.transform(state.matrix);
cache.computeFillMesh(this._fringeWidth, out convex);
var fillMesh = cache.fillMesh;
var strokeMesh = cache.strokeMesh;
var fmesh = fillMesh.transform(state.matrix);
var smesh = strokeMesh?.transform(state.matrix);
float strokeMult = 1.0f;
this._drawWithMaskFilter(mesh.bounds, paint, paint.maskFilter.Value, mesh, convex, 0, null,
this._drawWithMaskFilter(fmesh.bounds, paint, paint.maskFilter.Value, fmesh, smesh, convex, 0,
strokeMult, null,
this._drawPathDrawMeshCallback(paint, mesh, convex, 0, null, uiRectHelper.zero, null, false);
this._drawPathDrawMeshCallback(paint, fmesh, smesh, convex, 1.0f, strokeMult, null, uiRectHelper.zero,
null, false);
}
else {
var state = this._currentLayer.currentState;

strokeWidth = this._fringeWidth;
}
strokeWidth = strokeWidth / state.scale * 0.5f;
float strokeMult = (this._fringeWidth * 0.5f + strokeWidth * 0.5f) / this._fringeWidth;
var strokenMesh = cache.getStrokeMesh(
strokeWidth / state.scale * 0.5f,
cache.computeStrokeMesh(
strokeWidth,
this._fringeWidth,
var strokeMesh = cache.strokeMesh;
var mesh = strokenMesh.transform(state.matrix);
var mesh = strokeMesh.transform(state.matrix);
this._drawWithMaskFilter(mesh.bounds, paint, paint.maskFilter.Value, mesh, false, alpha, null,
this._drawWithMaskFilter(mesh.bounds, paint, paint.maskFilter.Value, null, mesh, false, alpha,
strokeMult, null,
this._drawPathDrawMeshCallback2(paint, mesh, false, alpha, null, uiRectHelper.zero, null, false);
this._drawPathDrawMeshCallback2(paint, null, mesh, false, alpha, strokeMult, null, uiRectHelper.zero,
null, false);
}
}

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

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

}
case DrawClipPath cmd: {
var uipath = uiPath.fromPath(cmd.path);
this._clipPath(uipath);

case DrawPath cmd: {
var uipath = uiPath.fromPath(cmd.path);
this._drawPath(uipath, uiPaint.fromPaint(cmd.paint));

case DrawImageNine cmd: {
this._drawImageNine(cmd.image, uiRectHelper.fromRect(cmd.src),
uiRectHelper.fromRect(cmd.center), uiRectHelper.fromRect(cmd.dst),

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

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

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

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

}
}
void _drawTextBlob(TextBlob textBlob, uiOffset offset, uiPaint paint) {
void _drawTextBlob(TextBlob? textBlob, uiOffset offset, uiPaint paint) {
D.assert(textBlob != null);
var state = this._currentLayer.currentState;

matrix.preTranslate(offset.dx, offset.dy);
var mesh = TextBlobMesh.create(textBlob, scale, matrix);
var textBlobBounds = matrix.mapRect(uiRectHelper.fromRect(textBlob.boundsInText));
var mesh = TextBlobMesh.create(textBlob.Value, scale, matrix);
var textBlobBounds = matrix.mapRect(uiRectHelper.fromRect(textBlob.Value.boundsInText));
var style = textBlob.style;
var style = textBlob.Value.style;
var subText = textBlob.text.Substring(textBlob.textOffset, textBlob.textSize);
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) {

if (paint.maskFilter != null && paint.maskFilter.Value.sigma != 0) {
this._drawWithMaskFilter(textBlobBounds, paint, paint.maskFilter.Value, null, false, 0, tex,
this._drawWithMaskFilter(textBlobBounds, paint, paint.maskFilter.Value, null, null, false, 0, 0, tex,
this._drawTextDrawMeshCallback(paint, null, false, 0, tex, textBlobBounds, mesh, notEmoji);
this._drawTextDrawMeshCallback(paint, null, null, false, 0, 0, tex, textBlobBounds, mesh, notEmoji);
}
public void flush(uiPicture picture) {

this._lastScissor = null;
this._layers.Clear();
}
AllocDebugger.onFrameEnd();
}
int _lastRtID;

122
Runtime/ui/renderer/cmdbufferCanvas/rendering/canvas_shader.cs


static readonly MaterialByBlendModeStencilComp _texMat;
static readonly Material _stencilMat;
static readonly Material _filterMat;
static readonly MaterialByBlendModeStencilComp _strokeAlphaMat;
static readonly Material _shadowBox;
static readonly Material _shadowRBox;
static CanvasShader() {
var convexFillShader = Shader.Find("UIWidgets/canvas_convexFill");
if (convexFillShader == null) {
throw new Exception("UIWidgets/canvas_convexFill not found");
static Shader GetShader(string shaderName) {
var shader = Shader.Find(shaderName);
if (shader == null) {
throw new Exception(shaderName + " not found");
var fill0Shader = Shader.Find("UIWidgets/canvas_fill0");
if (fill0Shader == null) {
throw new Exception("UIWidgets/canvas_fill0 not found");
}
return shader;
}
var fill1Shader = Shader.Find("UIWidgets/canvas_fill1");
if (fill1Shader == null) {
throw new Exception("UIWidgets/canvas_fill1 not found");
}
var stroke0Shader = Shader.Find("UIWidgets/canvas_stroke0");
if (stroke0Shader == null) {
throw new Exception("UIWidgets/canvas_stroke0 not found");
}
var stroke1Shader = Shader.Find("UIWidgets/canvas_stroke1");
if (stroke1Shader == null) {
throw new Exception("UIWidgets/canvas_stroke1 not found");
}
var texShader = Shader.Find("UIWidgets/canvas_tex");
if (texShader == null) {
throw new Exception("UIWidgets/canvas_tex not found");
}
var stencilShader = Shader.Find("UIWidgets/canvas_stencil");
if (stencilShader == null) {
throw new Exception("UIWidgets/canvas_stencil not found");
}
var filterShader = Shader.Find("UIWidgets/canvas_filter");
if (filterShader == null) {
throw new Exception("UIWidgets/canvas_filter not found");
}
static CanvasShader() {
var convexFillShader = GetShader("UIWidgets/canvas_convexFill");
var fill0Shader = GetShader("UIWidgets/canvas_fill0");
var fill1Shader = GetShader("UIWidgets/canvas_fill1");
var stroke0Shader = GetShader("UIWidgets/canvas_stroke0");
var stroke1Shader = GetShader("UIWidgets/canvas_stroke1");
var texShader = GetShader("UIWidgets/canvas_tex");
var stencilShader = GetShader("UIWidgets/canvas_stencil");
var filterShader = GetShader("UIWidgets/canvas_filter");
var shadowBoxShader = GetShader("UIWidgets/ShadowBox");
var shadowRBoxShader = GetShader("UIWidgets/ShadowRBox");
var strokeAlphaShader = GetShader("UIWidgets/canvas_strokeAlpha");
_convexFillMat = new MaterialByBlendModeStencilComp(convexFillShader);
_fill0Mat = new MaterialByStencilComp(fill0Shader);

_strokeAlphaMat = new MaterialByBlendModeStencilComp(strokeAlphaShader);
_shadowBox = new Material(shadowBoxShader) {hideFlags = HideFlags.HideAndDontSave};
_shadowRBox = new Material(shadowRBoxShader) {hideFlags = HideFlags.HideAndDontSave};
public static Material shadowBox => _shadowBox;
static readonly int _strokeMultId = Shader.PropertyToID("_strokeMult");
static readonly int _colorId = Shader.PropertyToID("_color");
static readonly int _shaderMatId = Shader.PropertyToID("_shaderMat");
static readonly int _shaderTexId = Shader.PropertyToID("_shaderTex");

static readonly int _mfImgIncId = Shader.PropertyToID("_mf_imgInc");
static readonly int _mfKernelId = Shader.PropertyToID("_mf_kernel");
static readonly int _shadowBoxId = Shader.PropertyToID("_sb_box");
static readonly int _shadowSigmaId = Shader.PropertyToID("_sb_sigma");
static readonly int _shadowColorId = Shader.PropertyToID("_sb_color");
static readonly int _shadowCornerId = Shader.PropertyToID("_sb_corner");
static Vector4 _colorToVector4(uiColor c) {
return new Vector4(
c.red / 255f,

}
static void _getShaderPassAndProps(
PictureFlusher.RenderLayer layer, uiPaint paint, uiMatrix3? meshMatrix, float alpha,
PictureFlusher.RenderLayer layer, uiPaint paint, uiMatrix3? meshMatrix, float alpha, float strokeMult,
out int pass, out MaterialPropertyBlockWrapper props) {
Vector4 viewport = layer.viewport;

props.SetFloat(_strokeMultId, strokeMult);
switch (paint.shader) {
case null:

public static PictureFlusher.CmdDraw convexFill(PictureFlusher.RenderLayer layer, uiPaint paint,
uiMeshMesh mesh) {
var mat = _convexFillMat.getMaterial(paint.blendMode, layer.ignoreClip);
_getShaderPassAndProps(layer, paint, mesh.matrix, 1.0f, out var pass, out var props);
_getShaderPassAndProps(layer, paint, mesh.matrix, 1.0f, 0.0f, out var pass, out var props);
return PictureFlusher.CmdDraw.create(
mesh: mesh,

public static PictureFlusher.CmdDraw fill1(PictureFlusher.RenderLayer layer, uiPaint paint,
uiMeshMesh mesh) {
var mat = _fill1Mat.getMaterial(paint.blendMode);
_getShaderPassAndProps(layer, paint, mesh.matrix, 1.0f, out var pass, out var props);
_getShaderPassAndProps(layer, paint, mesh.matrix, 1.0f, 0.0f, out var pass, out var props);
var ret = PictureFlusher.CmdDraw.create(
mesh: mesh.boundsMesh,

public static PictureFlusher.CmdDraw stroke0(PictureFlusher.RenderLayer layer, uiPaint paint,
float alpha, uiMeshMesh mesh) {
var mat = _stroke0Mat.getMaterial(paint.blendMode, layer.ignoreClip);
_getShaderPassAndProps(layer, paint, mesh.matrix, alpha, out var pass, out var props);
_getShaderPassAndProps(layer, paint, mesh.matrix, alpha, 0.0f, out var pass, out var props);
return PictureFlusher.CmdDraw.create(
mesh: mesh,

var pass = 0;
var props = ObjectPool<MaterialPropertyBlockWrapper>.alloc();
props.SetVector(_viewportId, viewport);
return PictureFlusher.CmdDraw.create(
mesh: mesh,
pass: pass,
material: mat,
properties: props
);
}
public static PictureFlusher.CmdDraw strokeAlpha(PictureFlusher.RenderLayer layer, uiPaint paint, float alpha, float strokeMult, uiMeshMesh mesh) {
var mat = _strokeAlphaMat.getMaterial(paint.blendMode, layer.ignoreClip);
_getShaderPassAndProps(layer, paint, mesh.matrix, alpha, strokeMult, out var pass, out var props);
return PictureFlusher.CmdDraw.create(
mesh: mesh,

public static PictureFlusher.CmdDraw tex(PictureFlusher.RenderLayer layer, uiPaint paint,
uiMeshMesh mesh, Image image) {
var mat = _texMat.getMaterial(paint.blendMode, layer.ignoreClip);
_getShaderPassAndProps(layer, paint, mesh.matrix, 1.0f, out var pass, out var props);
_getShaderPassAndProps(layer, paint, mesh.matrix, 1.0f, 0.0f, out var pass, out var props);
image.texture.filterMode = paint.filterMode;
props.SetTexture(_texId, image.texture);

public static PictureFlusher.CmdDraw texRT(PictureFlusher.RenderLayer layer, uiPaint paint,
uiMeshMesh mesh, PictureFlusher.RenderLayer renderLayer) {
var mat = _texMat.getMaterial(paint.blendMode, layer.ignoreClip);
_getShaderPassAndProps(layer, paint, mesh.matrix, 1.0f, out var pass, out var props);
_getShaderPassAndProps(layer, paint, mesh.matrix, 1.0f, 0.0f, out var pass, out var props);
props.SetInt(_texModeId, 1); // pre alpha
return PictureFlusher.CmdDraw.create(

uiMeshMesh mesh, TextBlobMesh textMesh, Texture tex) {
var mat = _texMat.getMaterial(paint.blendMode, layer.ignoreClip);
var meshMatrix = mesh != null ? mesh.matrix : textMesh.matrix;
_getShaderPassAndProps(layer, paint, meshMatrix, 1.0f, out var pass, out var props);
_getShaderPassAndProps(layer, paint, meshMatrix, 1.0f, 0.0f, out var pass, out var props);
tex.filterMode = paint.filterMode;
props.SetTexture(_texId, tex);
props.SetInt(_texModeId, 2); // alpha only

material: mat,
properties: props,
layerId: renderLayer.rtID
);
}
public static PictureFlusher.CmdDraw fastShadow(PictureFlusher.RenderLayer layer, uiMeshMesh mesh, float sigma,
bool isRect, bool isCircle, float corner, Vector4 bound, uiColor color) {
Vector4 viewport = layer.viewport;
var mat = _shadowBox;
if (!isRect) {
mat = _shadowRBox;
}
var props = ObjectPool<MaterialPropertyBlockWrapper>.alloc();
props.SetVector(_viewportId, viewport);
props.SetFloat(_shadowSigmaId, sigma);
props.SetVector(_shadowBoxId, bound);
props.SetVector(_shadowColorId, _colorToVector4(color));
if (!isRect) {
props.SetFloat(_shadowCornerId, corner);
}
return PictureFlusher.CmdDraw.create(
mesh: mesh,
pass: 0,
material: mat,
properties: props
);
}
}

2
Runtime/ui/renderer/common/draw_cmd.cs


this.offset = null;
}
public TextBlob textBlob;
public TextBlob? textBlob;
public uiOffset? offset;
public uiPaint paint;
}

68
Runtime/ui/renderer/common/geometry/path/path.cs


using UnityEngine;
namespace Unity.UIWidgets.ui {
public class uiPath : PoolObject {
public partial class uiPath : PoolObject {
const float _KAPPA90 = 0.5522847493f;
uiList<float> _commands;

public uint pathKey = 0;
public bool needCache = false;
bool _isNaiveRRect = false;
public bool isNaiveRRect => this._isNaiveRRect;
uiPathShapeHint _shapeHint = uiPathShapeHint.Other;
public uiPathShapeHint shapeHint => this._shapeHint;
float _rRectCorner;
public float rRectCorner => this._rRectCorner;
void _updateRRectFlag(bool isNaiveRRect, uiPathShapeHint shapeHint = uiPathShapeHint.Other, float corner = 0) {
if (this._commands.Count > 0 && !this._isNaiveRRect) {
return;
}
this._isNaiveRRect = isNaiveRRect && this._hasOnlyMoveTos();
if (this._isNaiveRRect) {
this._shapeHint = shapeHint;
this._rRectCorner = corner;
}
}
bool _hasOnlyMoveTos() {
var i = 0;
while (i < this._commands.Count) {
var cmd = (PathCommand) this._commands[i];
switch (cmd) {
case PathCommand.moveTo:
i += 3;
break;
case PathCommand.lineTo:
return false;
case PathCommand.bezierTo:
return false;
case PathCommand.close:
i++;
break;
case PathCommand.winding:
i += 2;
break;
default:
return false;
}
}
return true;
}
public static uiPath create(int capacity = 128) {
uiPath newPath = ObjectPool<uiPath>.alloc();

this.needCache = false;
this.pathKey = 0;
this._isNaiveRRect = false;
}
void _reset() {

this._maxY = float.MinValue;
ObjectPool<uiPathCache>.release(this._cache);
this._cache = null;
this._isNaiveRRect = false;
}
internal uiPathCache flatten(float scale) {

this._maxY = y;
}
}
public uiRect getBounds() {
if (this._minX >= this._maxX || this._minY >= this._maxY) {
return uiRectHelper.zero;
}
return uiRectHelper.fromLTRB(this._minX, this._minY, this._maxX, this._maxY);
}
void _appendMoveTo(float x, float y) {
this._commands.Add((float) uiPathCommand.moveTo);

}
public void addRect(uiRect rect) {
this._updateRRectFlag(true, uiPathShapeHint.Rect);
this._appendMoveTo(rect.left, rect.top);
this._appendLineTo(rect.left, rect.bottom);
this._appendLineTo(rect.right, rect.bottom);

public void addRect(Rect rect) {
this._updateRRectFlag(true, uiPathShapeHint.Rect);
this._appendMoveTo(rect.left, rect.top);
this._appendLineTo(rect.left, rect.bottom);
this._appendLineTo(rect.right, rect.bottom);

public void addRRect(RRect rrect) {
this._updateRRectFlag(rrect.isNaiveRRect(), uiPathShapeHint.NaiveRRect, rrect.blRadiusX);
float w = rrect.width;
float h = rrect.height;
float halfw = Mathf.Abs(w) * 0.5f;

}
public void lineTo(float x, float y) {
this._updateRRectFlag(false);
this._appendLineTo(x, y);
}

public void addEllipse(float cx, float cy, float rx, float ry) {
this._updateRRectFlag(rx == ry, uiPathShapeHint.Circle, rx);
this._appendMoveTo(cx - rx, cy);
this._appendBezierTo(cx - rx, cy + ry * _KAPPA90,
cx - rx * _KAPPA90, cy + ry, cx, cy + ry);

}
public void arcTo(Rect rect, float startAngle, float sweepAngle, bool forceMoveTo = true) {
this._updateRRectFlag(false);
var mat = Matrix3.makeScale(rect.width / 2, rect.height / 2);
var center = rect.center;
mat.postTranslate(center.dx, center.dy);

if (exists) {
return uipath;
}
uipath._updateRRectFlag(path.isNaiveRRect, (uiPathShapeHint)path.shapeHint, path.rRectCorner);
var i = 0;
var _commands = path.commands;
while (i < _commands.Count) {

432
Runtime/ui/renderer/common/geometry/path/path_cache.cs


float _scale;
bool _fillConvex;
bool _fillConvex;
public uiMeshMesh fillMesh {
get { return this._fillMesh; }
}
public uiMeshMesh strokeMesh {
get { return this._strokeMesh; }
}
float _fringe;
public static uiPathCache create(float scale) {
uiPathCache newPathCache = ObjectPool<uiPathCache>.alloc();

}
}
uiList<Vector3> _expandFill() {
var points = this._points;
var paths = this._paths;
for (var j = 0; j < paths.Count; j++) {
var path = paths[j];
if (path.count <= 2) {
continue;
}
var ip0 = path.first + path.count - 1;
var ip1 = path.first;
for (var i = 0; i < path.count; i++) {
var p0 = points[ip0];
var p1 = points[ip1];
p0.dx = p1.x - p0.x; // no need to normalize
p0.dy = p1.y - p0.y;
points[ip0] = p0;
ip0 = ip1++;
}
path.convex = true;
ip0 = path.first + path.count - 1;
ip1 = path.first;
for (var i = 0; i < path.count; i++) {
var p0 = points[ip0];
var p1 = points[ip1];
float cross = p1.dx * p0.dy - p0.dx * p1.dy;
if (cross < 0.0f) {
path.convex = false;
}
ip0 = ip1++;
}
paths[j] = path;
}
var cvertices = 0;
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
cvertices += path.count;
}
var _vertices = ObjectPool<uiList<Vector3>>.alloc();
_vertices.SetCapacity(cvertices);
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
path.ifill = _vertices.Count;
for (var j = 0; j < path.count; j++) {
var p = points[path.first + j];
_vertices.Add(new Vector2(p.x, p.y));
}
path.nfill = _vertices.Count - path.ifill;
paths[i] = path;
}
return _vertices;
}
public uiMeshMesh getFillMesh(out bool convex) {
if (this._fillMesh != null) {
convex = this._fillConvex;
return this._fillMesh;
}
var vertices = this._expandFill();
var paths = this._paths;
var cindices = 0;
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
if (path.nfill > 0) {
D.assert(path.nfill >= 2);
cindices += (path.nfill - 2) * 3;
}
}
var indices = ObjectPool<uiList<int>>.alloc();
indices.SetCapacity(cindices);
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
if (path.nfill > 0) {
for (var j = 2; j < path.nfill; j++) {
indices.Add(path.ifill);
indices.Add(path.ifill + j);
indices.Add(path.ifill + j - 1);
}
}
}
D.assert(indices.Count == cindices);
var mesh = uiMeshMesh.create(null, vertices, indices);
this._fillMesh = mesh;
this._fillConvex = false;
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
if (this._fillConvex) {
// if more than two paths, convex is false.
this._fillConvex = false;
break;
}
if (!path.convex) {
// if not convex, convex is false.
break;
}
this._fillConvex = true;
}
convex = this._fillConvex;
return this._fillMesh;
}
void _calculateJoins(float w, StrokeJoin lineJoin, float miterLimit) {
float iw = w > 0.0f ? 1.0f / w : 0.0f;

ip0 = path.first + path.count - 1;
ip1 = path.first;
path.convex = true;
for (var j = 0; j < path.count; j++) {
var p0 = points[ip0];
var p1 = points[ip1];

// Keep track of left turns.
float cross = p1.dx * p0.dy - p0.dx * p1.dy;
} else if (cross < 0.0f) {
path.convex = false;
}
// Calculate if we should use bevel or miter for inner join.

ip0 = ip1++;
}
paths[i] = path;
uiList<Vector3> _expandStroke(float w, StrokeCap lineCap, StrokeJoin lineJoin, float miterLimit) {
this._calculateJoins(w, lineJoin, miterLimit);
uiVertexUV _expandStroke(float w, float fringe, StrokeCap lineCap, StrokeJoin lineJoin, float miterLimit) {
float aa = fringe;
float u0 = 0.0f, u1 = 1.0f;
w += aa * 0.5f;
if (aa == 0.0f) {
u0 = 0.5f;
u1 = 0.5f;
}
this._calculateJoins(w, lineJoin, miterLimit);
var points = this._points;
var paths = this._paths;

}
cvertices += path.count * 2;
cvertices += 4;
cvertices += 8;
var _uv = ObjectPool<uiList<Vector2>>.alloc();
_uv.SetCapacity(cvertices);
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 1) {

if (!path.closed) {
if (lineCap == StrokeCap.butt) {
_vertices.buttCapStart(p0, p0.dx, p0.dy, w, 0.0f);
_vertices.buttCapStart(_uv, p0, p0.dx, p0.dy, w, 0.0f, aa, u0, u1);
_vertices.buttCapStart(p0, p0.dx, p0.dy, w, w);
_vertices.buttCapStart(_uv, p0, p0.dx, p0.dy, w, w, aa, u0, u1);
_vertices.roundCapStart(p0, p0.dx, p0.dy, w, ncap);
_vertices.roundCapStart(_uv, p0, p0.dx, p0.dy, w, ncap, u0, u1);
}
}

if ((p1.flags & (uiPointFlags.bevel | uiPointFlags.innerBevel)) != 0) {
if (lineJoin == StrokeJoin.round) {
_vertices.roundJoin(p0, p1, w, w, ncap);
_vertices.roundJoin(_uv, p0, p1, w, w, ncap, u0, u1, aa);
_vertices.bevelJoin(p0, p1, w, w);
_vertices.bevelJoin(_uv, p0, p1, w, w, u0, u1, aa);
_uv.Add(new Vector2(u0, 1));
_uv.Add(new Vector2(u1, 1));
}
ip0 = ip1++;

p0 = points[ip0];
p1 = points[ip1];
if (lineCap == StrokeCap.butt) {
_vertices.buttCapEnd(p1, p0.dx, p0.dy, w, 0.0f);
_vertices.buttCapEnd(_uv, p1, p0.dx, p0.dy, w, 0.0f, aa, u0, u1);
_vertices.buttCapEnd(p1, p0.dx, p0.dy, w, w);
_vertices.buttCapEnd(_uv, p1, p0.dx, p0.dy, w, w, aa, u0, u1);
_vertices.roundCapEnd(p1, p0.dx, p0.dy, w, ncap);
_vertices.roundCapEnd(_uv, p1, p0.dx, p0.dy, w, ncap, u0, u1);
_uv.Add(new Vector2(u0, 1));
_uv.Add(new Vector2(u1, 1));
D.assert(_uv.Count == _vertices.Count);
return _vertices;
return new uiVertexUV {
strokeVertices = _vertices,
strokeUV = _uv,
};
public uiMeshMesh getStrokeMesh(float strokeWidth, StrokeCap lineCap, StrokeJoin lineJoin, float miterLimit) {
uiVertexUV _expandFill(float fringe) {
float aa = fringe;
float woff = aa * 0.5f;
var points = this._points;
var paths = this._paths;
this._calculateJoins(fringe, StrokeJoin.miter, 4.0f);
var cvertices = 0;
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
cvertices += path.count;
}
this._fillConvex = false;
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
if (this._fillConvex) {
// if more than two paths, convex is false.
this._fillConvex = false;
break;
}
if (!path.convex) {
// if not convex, convex is false.
break;
}
this._fillConvex = true;
}
var _vertices = ObjectPool<uiList<Vector3>>.alloc();
_vertices.SetCapacity(cvertices);
var _uv = ObjectPool<uiList<Vector2>>.alloc();
_uv.SetCapacity(cvertices);
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
path.ifill = _vertices.Count;
for (var j = 0; j < path.count; j++) {
var p = points[path.first + j];
if (aa > 0.0f) {
_vertices.Add(new Vector2(p.x + p.dmx * woff, p.y + p.dmy * woff));
}
else {
_vertices.Add(new Vector2(p.x, p.y));
}
_uv.Add(new Vector2(0.5f, 1.0f));
}
path.nfill = _vertices.Count - path.ifill;
paths[i] = path;
}
uiList<Vector3> _strokeVertices = null;
uiList<Vector2> _strokeUV = null;
if (aa > 0.0f) {
_strokeVertices = ObjectPool<uiList<Vector3>>.alloc();
_strokeUV = ObjectPool<uiList<Vector2>>.alloc();
cvertices = 0;
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
cvertices += path.count * 2;
}
_strokeVertices.SetCapacity(cvertices);
_strokeUV.SetCapacity(cvertices);
float lw = this._fillConvex ? woff : aa + woff;
float rw = aa - woff;
float lu = this._fillConvex ? 0.5f : 0.0f;
float ru = 1.0f;
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
path.istroke = _strokeVertices.Count;
for (var j = 0; j < path.count; j++) {
var p = points[path.first + j];
_strokeVertices.Add(new Vector2(p.x + p.dmx * lw, p.y + p.dmy * lw));
_strokeUV.Add(new Vector2(lu, 1.0f));
_strokeVertices.Add(new Vector2(p.x - p.dmx * rw, p.y - p.dmy * rw));
_strokeUV.Add(new Vector2(ru, 1.0f));
}
path.nstroke = _strokeVertices.Count - path.istroke;
paths[i] = path;
}
}
return new uiVertexUV {
fillVertices = _vertices,
fillUV = _uv,
strokeVertices = _strokeVertices,
strokeUV = _strokeUV,
};
}
public void computeStrokeMesh(float strokeWidth, float fringe, StrokeCap lineCap, StrokeJoin lineJoin, float miterLimit) {
this._fillMesh == null && // Ensure that the cached stroke mesh was not calculated in computeFillMesh
this._fringe == fringe &&
return this._strokeMesh;
return;
var vertices = this._expandStroke(strokeWidth, lineCap, lineJoin, miterLimit);
var verticesUV = this._expandStroke(strokeWidth, fringe, lineCap, lineJoin, miterLimit);
var paths = this._paths;

D.assert(indices.Count == cindices);
ObjectPool<uiMeshMesh>.release(this._strokeMesh);
this._strokeMesh = uiMeshMesh.create(null, vertices, indices);
this._strokeMesh = uiMeshMesh.create(null, verticesUV.strokeVertices, indices, verticesUV.strokeUV);
ObjectPool<uiMeshMesh>.release(this._fillMesh);
this._fillMesh = null;
this._fringe = fringe;
return this._strokeMesh;
return;
}
public void computeFillMesh(float fringe, out bool convex) {
if (this._fillMesh != null && (fringe != 0.0f || this._strokeMesh != null) && this._fringe == fringe) {
convex = this._fillConvex;
return;
}
var verticesUV = this._expandFill(fringe);
convex = this._fillConvex;
var paths = this._paths;
var cindices = 0;
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
if (path.nfill > 0) {
D.assert(path.nfill >= 2);
cindices += (path.nfill - 2) * 3;
}
}
var indices = ObjectPool<uiList<int>>.alloc();
indices.SetCapacity(cindices);
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
if (path.nfill > 0) {
for (var j = 2; j < path.nfill; j++) {
indices.Add(path.ifill);
indices.Add(path.ifill + j);
indices.Add(path.ifill + j - 1);
}
}
}
D.assert(indices.Count == cindices);
if (verticesUV.strokeVertices != null) {
cindices = 0;
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
if (path.nstroke > 0) {
D.assert(path.nstroke >= 6);
cindices += path.nstroke * 3;
}
}
var strokeIndices = ObjectPool<uiList<int>>.alloc();
strokeIndices.SetCapacity(cindices);
for (var i = 0; i < paths.Count; i++) {
var path = paths[i];
if (path.count <= 2) {
continue;
}
if (path.nstroke > 0) {
strokeIndices.Add(path.istroke + path.nstroke - 1);
strokeIndices.Add(path.istroke + path.nstroke - 2);
strokeIndices.Add(path.istroke);
strokeIndices.Add(path.istroke + path.nstroke - 1);
strokeIndices.Add(path.istroke);
strokeIndices.Add(path.istroke + 1);
for (var j = 2; j < path.nstroke; j++) {
if ((j & 1) == 0) {
strokeIndices.Add(path.istroke + j - 1);
strokeIndices.Add(path.istroke + j - 2);
strokeIndices.Add(path.istroke + j);
}
else {
strokeIndices.Add(path.istroke + j - 2);
strokeIndices.Add(path.istroke + j - 1);
strokeIndices.Add(path.istroke + j);
}
}
}
}
D.assert(strokeIndices.Count == cindices);
ObjectPool<uiMeshMesh>.release(this._strokeMesh);
this._strokeMesh = uiMeshMesh.create(null, verticesUV.strokeVertices, strokeIndices, verticesUV.strokeUV);
}
var mesh = uiMeshMesh.create(null, verticesUV.fillVertices, indices, verticesUV.fillUV);
this._fillMesh = mesh;
this._fringe = fringe;
}
}
}

91
Runtime/ui/renderer/common/geometry/path/path_utils.cs


}
}
struct uiVertexUV {
public uiList<Vector3> fillVertices;
public uiList<Vector2> fillUV;
public uiList<Vector3> strokeVertices;
public uiList<Vector2> strokeUV;
}
static class uiPathUtils {
public static bool ptEquals(float x1, float y1, float x2, float y2, float tol) {
float dx = x2 - x1;

return d;
}
public static void buttCapStart(this uiList<Vector3> dst, uiPathPoint p,
float dx, float dy, float w, float d) {
public static void buttCapStart(this uiList<Vector3> dst, uiList<Vector2> uv, uiPathPoint p,
float dx, float dy, float w, float d, float aa, float u0, float u1) {
dst.Add(new Vector2(px + dlx * w - dx * aa, py + dly * w - dy * aa));
dst.Add(new Vector2(px - dlx * w - dx * aa, py - dly * w - dy * aa));
uv.Add(new Vector2(u0, 0));
uv.Add(new Vector2(u1, 0));
uv.Add(new Vector2(u0, 1));
uv.Add(new Vector2(u1, 1));
public static void buttCapEnd(this uiList<Vector3> dst, uiPathPoint p,
float dx, float dy, float w, float d) {
public static void buttCapEnd(this uiList<Vector3> dst, uiList<Vector2> uv, uiPathPoint p,
float dx, float dy, float w, float d, float aa, float u0, float u1) {
float px = p.x + dx * d;
float py = p.y + dy * d;
float dlx = dy;

dst.Add(new Vector2(px - dlx * w, py - dly * w));
dst.Add(new Vector2(px + dlx * w + dx * aa, py + dly * w + dy * aa));
dst.Add(new Vector2(px - dlx * w + dx * aa, py - dly * w + dy * aa));
uv.Add(new Vector2(u0, 1));
uv.Add(new Vector2(u1, 1));
uv.Add(new Vector2(u0, 0));
uv.Add(new Vector2(u1, 0));
public static void roundCapStart(this uiList<Vector3> dst, uiPathPoint p,
float dx, float dy, float w, int ncap) {
public static void roundCapStart(this uiList<Vector3> dst, uiList<Vector2> uv, uiPathPoint p,
float dx, float dy, float w, int ncap, float u0, float u1) {
float px = p.x;
float py = p.y;
float dlx = dy;

float ax = Mathf.Cos(a) * w, ay = Mathf.Sin(a) * w;
dst.Add(new Vector2(px - dlx * ax - dx * ay, py - dly * ax - dy * ay));
dst.Add(new Vector2(px, py));
uv.Add(new Vector2(u0, 1));
uv.Add(new Vector2(0.5f, 1));
uv.Add(new Vector2(u0, 1));
uv.Add(new Vector2(u1, 1));
public static void roundCapEnd(this uiList<Vector3> dst, uiPathPoint p,
float dx, float dy, float w, int ncap) {
public static void roundCapEnd(this uiList<Vector3> dst, uiList<Vector2> uv, uiPathPoint p,
float dx, float dy, float w, int ncap, float u0, float u1) {
float px = p.x;
float py = p.y;
float dlx = dy;

dst.Add(new Vector2(px - dlx * w, py - dly * w));
uv.Add(new Vector2(u0, 1));
uv.Add(new Vector2(u1, 1));
for (var i = 0; i < ncap; i++) {
float a = (float) i / (ncap - 1) * Mathf.PI;

uv.Add(new Vector2(0.5f, 1));
uv.Add(new Vector2(u0, 1));
}
}

return Mathf.Max(2, Mathf.CeilToInt(arc / da));
}
public static void roundJoin(this uiList<Vector3> dst, uiPathPoint p0, uiPathPoint p1,
float lw, float rw, int ncap) {
public static void roundJoin(this uiList<Vector3> dst, uiList<Vector2> uv, uiPathPoint p0, uiPathPoint p1,
float lw, float rw, int ncap, float lu, float ru, float fringe) {
float dlx0 = p0.dy;
float dly0 = -p0.dx;
float dlx1 = p1.dy;

dst.Add(new Vector2(lx0, ly0));
dst.Add(new Vector2(p1.x - dlx0 * rw, p1.y - dly0 * rw));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(ru, 1));
var n = Mathf.CeilToInt((a0 - a1) / Mathf.PI * ncap).clamp(2, ncap);
for (var i = 0; i < n; i++) {

dst.Add(new Vector2(p1.x, p1.y));
dst.Add(new Vector2(rx, ry));
uv.Add(new Vector2(0.5f, 1));
uv.Add(new Vector2(ru, 1));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(ru, 1));
}
else {
float rx0, ry0, rx1, ry1;

dst.Add(new Vector2(p1.x + dlx0 * lw, p1.y + dly0 * lw));
dst.Add(new Vector2(rx0, ry0));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(ru, 1));
var n = Mathf.CeilToInt((a1 - a0) / Mathf.PI * ncap).clamp(2, ncap);
for (var i = 0; i < n; i++) {

dst.Add(new Vector2(lx, ly));
dst.Add(new Vector2(p1.x, p1.y));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(0.5f, 1));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(ru, 1));
public static void bevelJoin(this uiList<Vector3> dst, uiPathPoint p0, uiPathPoint p1,
float lw, float rw) {
public static void bevelJoin(this uiList<Vector3> dst, uiList<Vector2> uv, uiPathPoint p0, uiPathPoint p1,
float lw, float rw, float lu, float ru, float fringe) {
float rx0, ry0, rx1, ry1;
float lx0, ly0, lx1, ly1;

dst.Add(new Vector2 {x = lx0, y = ly0});
dst.Add(new Vector2 {x = p1.x - dlx0 * rw, y = p1.y - dly0 * rw});
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(ru, 1));
if ((p1.flags & uiPointFlags.bevel) != 0) {
dst.Add(new Vector2(lx0, ly0));

uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(ru, 1));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(ru, 1));
}
else {
rx0 = p1.x - p1.dmx * rw;

dst.Add(new Vector2(rx0, ry0));
dst.Add(new Vector2(p1.x, p1.y));
dst.Add(new Vector2(p1.x - dlx1 * rw, p1.y - dly1 * rw));
uv.Add(new Vector2(0.5f, 1));
uv.Add(new Vector2(ru, 1));
uv.Add(new Vector2(ru, 1));
uv.Add(new Vector2(ru, 1));
uv.Add(new Vector2(0.5f, 1));
uv.Add(new Vector2(ru, 1));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(ru, 1));
}
else {
chooseBevel((p1.flags & uiPointFlags.innerBevel) != 0, p0, p1, -rw,

dst.Add(new Vector2(rx0, ry0));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(ru, 1));
if ((p1.flags & uiPointFlags.bevel) != 0) {
dst.Add(new Vector2(p1.x + dlx0 * lw, p1.y + dly0 * lw));

uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(ru, 1));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(ru, 1));
}
else {
lx0 = p1.x + p1.dmx * lw;

dst.Add(new Vector2(lx0, ly0));
dst.Add(new Vector2(p1.x + dlx1 * lw, p1.y + dly1 * lw));
dst.Add(new Vector2(p1.x, p1.y));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(0.5f, 1));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(0.5f, 1));
uv.Add(new Vector2(lu, 1));
uv.Add(new Vector2(ru, 1));
}
}
}

13
Runtime/ui/renderer/common/picture.cs


var rectPathCache = cmd.path.flatten(
scale * Window.instance.devicePixelRatio);
var rectMesh = rectPathCache.getFillMesh(out _);
rectPathCache.computeFillMesh(0.0f, out _);
var rectMesh = rectPathCache.fillMesh;
var transformedMesh = rectMesh.transform(state.xform);
var rect = transformedMesh.bounds;
state.scissor = state.scissor == null ? rect : state.scissor.Value.intersect(rect);

uiMeshMesh mesh;
if (paint.style == PaintingStyle.fill) {
var cache = path.flatten(scale * devicePixelRatio);
var fillMesh = cache.getFillMesh(out _);
cache.computeFillMesh(0.0f, out _);
var fillMesh = cache.fillMesh;
mesh = fillMesh.transform(state.xform);
}
else {

}
var cache = path.flatten(scale * devicePixelRatio);
var strokenMesh = cache.getStrokeMesh(
cache.computeStrokeMesh(
0.0f,
var strokenMesh = cache.strokeMesh;
mesh = strokenMesh.transform(state.xform);
}

case uiDrawTextBlob cmd: {
var state = this._getState();
var scale = uiXformUtils.getScale(state.xform);
var rect = uiRectHelper.fromRect(cmd.textBlob.boundsInText).shift(cmd.offset.Value);
var rect = uiRectHelper.fromRect(
cmd.textBlob.Value.shiftedBoundsInText(cmd.offset.Value.dx, cmd.offset.Value.dy));
rect = state.xform.mapRect(rect);
var paint = cmd.paint;

1
Runtime/ui/renderer/compositeCanvas/flow/instrumentation.cs


paragraph.layout(new ParagraphConstraints(width: 800));
canvas.drawParagraph(paragraph, new Offset(rect.left, rect.top + rect.height - 12));
Paragraph.release(ref paragraph);
}
}
}

1
Runtime/ui/renderer/compositeCanvas/flow/performance_overlay_layer.cs


paragraph.layout(new ParagraphConstraints(width: 300));
canvas.drawParagraph(paragraph, new Offset(x, y));
Paragraph.release(ref paragraph);
}
}
}

2
Runtime/ui/renderer/compositeCanvas/flow/physical_shape_layer.cs


const float kAmbientAlpha = 0.039f;
const float kLightHeight = 600f;
const float kLightRadius = 800f;
const float kSpotAlpha = ShadowUtils.kUseFastShadow ? 0.1f : 0.25f;
const float kSpotAlpha = 0.25f;
public static void drawShadow(Canvas canvas, Path path, Color color, float elevation, bool transparentOccluder,
float dpr) {

48
Runtime/ui/text.cs


}
class TextStyle : IEquatable<TextStyle> {
public readonly Color color = Color.fromARGB(255, 0, 0, 0);
public readonly float fontSize = 14.0f;
public readonly FontWeight fontWeight = FontWeight.w400;
public readonly FontStyle fontStyle = FontStyle.normal;
public readonly float letterSpacing = 0.0f;
public readonly float wordSpacing = 0.0f;
public readonly TextBaseline textBaseline = TextBaseline.alphabetic;
public readonly float height = 1.0f;
public readonly TextDecoration decoration = TextDecoration.none;
public static readonly Color kDefaultColor = Color.fromARGB(255, 0, 0, 0);
public const float kDefaultFontSize = 14.0f;
public static readonly FontWeight kDefaultFontWeight = FontWeight.w400;
public const FontStyle kDefaultfontStyle = FontStyle.normal;
public const float kDefaultLetterSpacing = 0.0f;
public const float kDefaultWordSpacing = 0.0f;
public const TextBaseline kDefaultTextBaseline = TextBaseline.alphabetic;
public const float kDefaultHeight = 1.0f;
public static readonly TextDecoration kDefaultDecoration = TextDecoration.none;
public const TextDecorationStyle kDefaultDecorationStyle = TextDecorationStyle.solid;
public const string kDefaultFontFamily = "Helvetica";
public readonly Color color = kDefaultColor;
public readonly float fontSize = kDefaultFontSize;
public readonly FontWeight fontWeight = kDefaultFontWeight;
public readonly FontStyle fontStyle = kDefaultfontStyle;
public readonly float letterSpacing = kDefaultLetterSpacing;
public readonly float wordSpacing = kDefaultWordSpacing;
public readonly TextBaseline textBaseline = kDefaultTextBaseline;
public readonly float height = kDefaultHeight;
public readonly TextDecoration decoration = kDefaultDecoration;
public readonly TextDecorationStyle decorationStyle = TextDecorationStyle.solid;
public readonly string fontFamily = "Helvetica";
public readonly TextDecorationStyle decorationStyle = kDefaultDecorationStyle;
public readonly string fontFamily = kDefaultFontFamily;
public readonly Paint background;
internal UnityEngine.Color UnityColor {

}
}
public class TextBox : IEquatable<TextBox> {
public struct TextBox : IEquatable<TextBox> {
public readonly float left;
public readonly float top;

}
public bool Equals(TextBox other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return this.left.Equals(other.left) && this.top.Equals(other.top) && this.right.Equals(other.right) &&
this.bottom.Equals(other.bottom) && this.direction == other.direction;
}

return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {

33
Runtime/ui/txt/emoji.cs


{0x2b50, 1230}, {0x2b55, 1231}, {0x3030, 1232}, {0x303d, 1233}, {0x3297, 1234}, {0x3299, 1235},
};
public static readonly HashSet<int> SingleCharEmojiCodePoints = new HashSet<int> {
0x203c, 0x2049, 0x2122, 0x2139, 0x2194,
0x2195, 0x2196, 0x2197, 0x2198, 0x2199, 0x21a9,
0x21aa, 0x231a, 0x231b, 0x2328, 0x23cf, 0x23e9,
0x23ea, 0x23eb, 0x23ec, 0x23ed, 0x23ee, 0x23ef,
0x23f0, 0x23f1, 0x23f2, 0x23f3, 0x23f8, 0x23f9,
0x23fa, 0x24c2, 0x25aa, 0x25ab, 0x25b6, 0x25c0,
0x25fb, 0x25fc, 0x25fd, 0x25fe, 0x2600, 0x2601,
0x2602, 0x2603, 0x2604, 0x260e, 0x2611, 0x2614,
0x2615, 0x2618, 0x261d, 0x2620, 0x2622, 0x2623,
0x2626, 0x262a, 0x262e, 0x262f, 0x2638, 0x2639,
0x263a, 0x2640, 0x2642, 0x2648, 0x2649, 0x264a,
0x264b, 0x264c, 0x264d, 0x264e, 0x264f, 0x2650,
0x2651, 0x2652, 0x2653, 0x265f, 0x2660, 0x2663,
0x2665, 0x2666, 0x2668, 0x267b, 0x267e, 0x267f,
0x2692, 0x2693, 0x2694, 0x2695, 0x2696, 0x2697,
0x2699, 0x269b, 0x269c, 0x26a0, 0x26a1, 0x26aa,
0x26ab, 0x26b0, 0x26b1, 0x26bd, 0x26be, 0x26c4,
0x26c5, 0x26c8, 0x26ce, 0x26cf, 0x26d1, 0x26d3,
0x26d4, 0x26e9, 0x26ea, 0x26f0, 0x26f1, 0x26f2,
0x26f3, 0x26f4, 0x26f5, 0x26f7, 0x26f8, 0x26f9,
0x26fa, 0x26fd, 0x2702, 0x2705, 0x2708, 0x2709,
0x270a, 0x270b, 0x270c, 0x270d, 0x270f, 0x2712,
0x2714, 0x2716, 0x271d, 0x2721, 0x2728, 0x2733,
0x2734, 0x2744, 0x2747, 0x274c, 0x274e, 0x2753,
0x2754, 0x2755, 0x2757, 0x2763, 0x2764, 0x2795,
0x2796, 0x2797, 0x27a1, 0x27b0, 0x27bf, 0x2934,
0x2935, 0x2b05, 0x2b06, 0x2b07, 0x2b1b, 0x2b1c,
0x2b50, 0x2b55, 0x3030, 0x303d, 0x3297, 0x3299,
};
public const int rowCount = 35;
public const int colCount = 36;

}
public static bool isSingleCharNonEmptyEmoji(int c) {
return emojiLookupTable.ContainsKey(c);
return SingleCharEmojiCodePoints.Contains(c);
}
public static bool isEmptyEmoji(int c) {

500
Runtime/ui/txt/layout.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using UnityEngine;
namespace Unity.UIWidgets.ui {
class Layout {
int _start;
int _count;
List<float> _advances = new List<float>();
List<float> _positions = new List<float>();
float _advance;
UnityEngine.Rect _bounds;
TabStops _tabStops;
public static float measureText(float offset, TextBuff buff, int start, int count, TextStyle style,
List<float> advances, int advanceOffset, TabStops tabStops) {
Layout layout = new Layout();
layout.setTabStops(tabStops);
layout.doLayout(offset, buff, start, count, style);
if (advances != null) {
var layoutAdv = layout.getAdvances();
for (int i = 0; i < count; i++) {
advances[i + advanceOffset] = layoutAdv[i];
}
}
return layout.getAdvance();
}
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;
}
void layoutWord(float offset, int layoutOffset,
TextBuff buff, int start, int wordCount, TextStyle style, Font font) {
float wordSpacing =
wordCount == 1 && LayoutUtils.isWordSpace(buff.charAt(start)) ? style.wordSpacing : 0;
float x = this._advance;
float letterSpace = style.letterSpacing;
float letterSpaceHalfLeft = letterSpace * 0.5f;
float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
for (int i = 0; i < wordCount; i++) {
var ch = buff.charAt(start + i);
if (i == 0) {
x += letterSpaceHalfLeft + wordSpacing;
this._advances[i + layoutOffset] += letterSpaceHalfLeft + wordSpacing;
}
else {
this._advances[i - 1 + layoutOffset] += letterSpaceHalfRight;
this._advances[i + layoutOffset] += letterSpaceHalfLeft;
x += letterSpace;
}
if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) {
var minX = glyphInfo.minX + x;
var maxX = glyphInfo.maxX + x;
var minY = -glyphInfo.maxY;
var maxY = -glyphInfo.minY;
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 + layoutOffset] = x;
float advance = glyphInfo.advance;
if (ch == '\t') {
advance = this._tabStops.nextTab((this._advance + offset)) - this._advance;
}
x += advance;
this._advances[i + layoutOffset] += advance;
if (i + 1 == wordCount) {
this._advances[i + layoutOffset] += letterSpaceHalfRight;
x += letterSpaceHalfRight;
}
}
this._advance = x;
}
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;
}
}
public void setTabStops(TabStops tabStops) {
this._tabStops = tabStops;
}
public int nGlyphs() {
return this._count;
}
public List<float> getAdvances() {
return this._advances;
}
public float getAdvance() {
return this._advance;
}
public float getX(int index) {
return this._positions[index];
}
public float getY(int index) {
return 0;
}
public float getCharAdvance(int index) {
return this._advances[index];
}
public Rect getBounds() {
return Rect.fromLTWH(this._bounds.x, this._bounds.y, this._bounds.width, this._bounds.height);
}
}
using UnityEngine;
namespace Unity.UIWidgets.ui {
static class Layout {
// 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 + 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;
}
else {
totalWidth += style.letterSpacing;
}
if (LayoutUtils.isWordSpace(ch)) {
totalWidth += style.wordSpacing;
}
}
}
return totalWidth;
}
public static int computeTruncateCount(float offset, string text, int start, int count, TextStyle style,
float advanceLimit, TabStops tabStops) {
char startingChar = text[start];
float currentAdvance = offset;
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) {
float advance = style.fontSize + style.letterSpacing;
for (int i = 0; i < count; i++) {
char ch = text[start + i];
if (char.IsHighSurrogate(ch) || EmojiUtils.isSingleCharNonEmptyEmoji(ch)) {
currentAdvance += advance;
if (currentAdvance > advanceLimit) {
return count - i;
}
}
}
}
else {
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;
for (int i = 0; i < count; i++) {
char ch = text[start + i];
if (ch == '\t') {
currentAdvance = tabStops.nextTab(currentAdvance);
}
else if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) {
currentAdvance += glyphInfo.advance + style.letterSpacing;
}
else {
currentAdvance += style.letterSpacing;
}
if (LayoutUtils.isWordSpace(ch)) {
currentAdvance += style.wordSpacing;
}
if (currentAdvance > advanceLimit) {
return count - i;
}
}
}
return 0;
}
public static float computeCharWidths(float offset, string text, int start, int count, TextStyle style,
float[] advances, int advanceOffset, TabStops tabStops) {
char startingChar = text[start];
float totalWidths = 0;
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) {
float advance = style.fontSize + style.letterSpacing;
for (int i = 0; i < count; i++) {
char ch = text[start + i];
if (char.IsHighSurrogate(ch) || EmojiUtils.isSingleCharNonEmptyEmoji(ch)) {
advances[i + advanceOffset] = advance;
totalWidths += advance;
}
else {
advances[i + advanceOffset] = 0;
}
}
}
else {
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;
// TODO: it is kind of a waste to require the entire string for this style, but SubString causes alloc
font.RequestCharactersInTextureSafe(text, style.UnityFontSize, style.UnityFontStyle);
for (int i = 0; i < count; i++) {
char ch = text[start + i];
if (ch == '\t') {
advances[i + advanceOffset] = tabStops.nextTab(offset + totalWidths) - (offset + totalWidths);
}
else if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) {
advances[i + advanceOffset] = glyphInfo.advance + style.letterSpacing;
}
else {
advances[i + advanceOffset] = style.letterSpacing;
}
if (LayoutUtils.isWordSpace(ch)) {
advances[i + advanceOffset] += style.wordSpacing;
}
totalWidths += advances[i + advanceOffset];
}
}
return totalWidths;
}
public static float doLayout(float offset, string text, int start, int count, TextStyle style,
float[] advances, float[] positions, TabStops tabStops, out UnityEngine.Rect bounds) {
float advance = 0;
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;
char startingChar = text[start];
bounds = new UnityEngine.Rect();
if (char.IsHighSurrogate(startingChar) || EmojiUtils.isSingleCharEmoji(startingChar)) {
advance = _layoutEmoji(text, start, count, style, font, advances, positions, ref bounds);
}
else {
// According to the logic of Paragraph.layout, it is assured that all the characters are requested
// in the texture before (in computing line breaks), so skip it here for optimization.
// The only exception is the ellipsis, which did not appear in line breaking. It is taken care of
// only when needed.
// 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.getNextWordBreak(text, iter, start + count);
advance = _layoutWord(offset, iter - start, text, iter,
wordend - iter, style, font, advances, positions, advance,
tabStops, ref bounds);
}
}
// bounds relative to first character
bounds.x -= positions[0];
return advance;
}
static float _layoutWord(float offset, int layoutOffset,
string text, int start, int wordCount, TextStyle style, Font font, float[] advances,
float[] positions, float initAdvance, TabStops tabStops, ref UnityEngine.Rect bounds) {
float wordSpacing =
wordCount == 1 && LayoutUtils.isWordSpace(text[start]) ? style.wordSpacing : 0;
float x = initAdvance;
float letterSpace = style.letterSpacing;
float letterSpaceHalfLeft = letterSpace * 0.5f;
float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
for (int i = 0; i < wordCount; i++) {
initAdvance = x;
var ch = text[start + i];
if (i == 0) {
x += letterSpaceHalfLeft + wordSpacing;
advances[i + layoutOffset] = letterSpaceHalfLeft + wordSpacing;
}
else {
advances[i - 1 + layoutOffset] += letterSpaceHalfRight;
advances[i + layoutOffset] = letterSpaceHalfLeft;
x += letterSpace;
}
if (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) {
_updateBounds(glyphInfo, x, ref bounds);
}
positions[i + layoutOffset] = x;
float advance;
if (ch == '\t') {
advance = tabStops.nextTab(initAdvance + offset) - initAdvance - offset;
}
else {
advance = glyphInfo.advance;
}
x += advance;
advances[i + layoutOffset] += advance;
if (i + 1 == wordCount) {
advances[i + layoutOffset] += letterSpaceHalfRight;
x += letterSpaceHalfRight;
}
}
return x;
}
static float _layoutEmoji(string text, int start, int count, TextStyle style, Font font, float[] advances,
float[] positions, ref UnityEngine.Rect bounds) {
var metrics = FontMetrics.fromFont(font, style.UnityFontSize);
float x = 0;
for (int i = 0; i < count; i++) {
char c = text[start + i];
if (EmojiUtils.isSingleCharNonEmptyEmoji(c) || char.IsHighSurrogate(c)) {
float letterSpace = style.letterSpacing;
float letterSpaceHalfLeft = letterSpace * 0.5f;
float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
x += letterSpaceHalfLeft;
advances[i] = letterSpaceHalfLeft;
var minX = x;
var maxX = metrics.descent - metrics.ascent + x;
var minY = metrics.ascent;
var maxY = metrics.descent;
_updateBounds(minX, maxX, minY, maxY, ref bounds);
positions[i] = x;
float advance = style.fontSize;
x += advance;
advances[i] += advance;
advances[i] += letterSpaceHalfRight;
x += letterSpaceHalfRight;
}
else {
advances[i] = 0;
positions[i] = x;
}
}
return x;
}
static void _updateBounds(CharacterInfo glyphInfo, float x, ref UnityEngine.Rect bounds) {
var minX = glyphInfo.minX + x;
var maxX = glyphInfo.maxX + x;
var minY = -glyphInfo.maxY;
var maxY = -glyphInfo.minY;
_updateBounds(minX, maxX, minY, maxY, ref bounds);
}
static void _updateBounds(float minX, float maxX, float minY, float maxY, ref UnityEngine.Rect bounds) {
if (bounds.width <= 0 || bounds.height <= 0) {
bounds.Set(minX, minY, maxX - minX, maxY - minY);
}
else {
if (minX < bounds.x) {
bounds.x = minX;
}
if (minY < bounds.y) {
bounds.y = minY;
}
if (maxX > bounds.xMax) {
bounds.xMax = maxX;
}
if (maxY > bounds.yMax) {
bounds.yMax = maxY;
}
}
}
}
}

50
Runtime/ui/txt/layout_utils.cs


c == 0x205F || c == 0x3000;
}
public static int getPrevWordBreakForCache(TextBuff buff, int offset) {
int len = buff.size;
if (offset == 0) {
return 0;
}
if (offset > len) {
offset = len;
public static int getNextWordBreak(string text, int offset, int maxOffset) {
int len = text.Length;
if (len > maxOffset) {
len = maxOffset + 1;
if (isWordBreakBefore(buff.charAt(offset - 1))) {
return offset - 1;
}
for (int i = offset - 1; i > 0; i--) {
if (isWordBreakBefore(buff.charAt(i)) || isWordBreakAfter(buff.charAt(i - 1))) {
return i;
}
}
return 0;
}
public static int getNextWordBreakForCache(TextBuff buff, int offset) {
int len = buff.size;
if (isWordBreakAfter(buff.charAt(offset))) {
if (isWordBreakAfter(text[offset])) {
if (isWordBreakBefore(buff.charAt(i))) {
if (isWordBreakBefore(text[i])) {
return len;
return maxOffset;
if (isWordSpace(c) || (c >= 0x2000 && c <= 0x200a) || c == 0x3000) {
// spaces
return true;
}
return false;
return isWordSpace(c) || (c >= 0x2000 && c <= 0x200a) || c == 0x3000;
public static int minPowerOfTwo(int i) {
// Assume that int is 32 bit
i--;
i = i | (i >> 1);
i = i | (i >> 2);
i = i | (i >> 4);
i = i | (i >> 8);
i = i | (i >> 16);
return i + 1;
}
}
}

623
Runtime/ui/txt/linebreaker.cs


using System.Collections.Generic;
using Unity.UIWidgets.InternalBridge;
using UnityEngine;
namespace Unity.UIWidgets.ui {
class TabStops {
int _tabWidth = int.MaxValue;
Font _font;
int _fontSize;
const int kTabSpaceCount = 4;
List<int> _stops = new List<int>();
public void set(List<int> stops, int tabWidth) {
this._stops.Clear();
if (stops != null) {
this._stops.AddRange(stops);
}
this._tabWidth = tabWidth;
}
public void setFont(Font font, int size) {
if (this._font != font || this._fontSize != size) {
this._tabWidth = int.MaxValue;
}
this._font = font;
this._fontSize = size;
}
public float nextTab(float widthSoFar) {
for (int i = 0; i < this._stops.Count; i++) {
if (this._stops[i] > widthSoFar) {
return this._stops[i];
}
}
if (this._tabWidth == int.MaxValue) {
if (this._fontSize > 0) {
this._font.RequestCharactersInTextureSafe(" ", this._fontSize);
this._font.getGlyphInfo(' ', out var glyphInfo, this._fontSize, UnityEngine.FontStyle.Normal);
this._tabWidth = glyphInfo.advance * kTabSpaceCount;
}
}
if (this._tabWidth == 0) {
return widthSoFar;
}
return (Mathf.Floor(widthSoFar / this._tabWidth + 1) * this._tabWidth);
}
}
class Candidate {
public int offset;
public int pre;
public float preBreak;
public float penalty;
public float postBreak;
public int preSpaceCount;
public int postSpaceCount;
}
class LineBreaker {
const float ScoreInfty = float.MaxValue;
const float ScoreDesperate = 1e10f;
TextBuff _textBuf;
List<float> _charWidths = new List<float>();
List<int> _breaks = new List<int>();
List<float> _widths = new List<float>();
WordBreaker _wordBreaker = new WordBreaker();
float _width = 0.0f;
float _preBreak;
float _lineWidth;
int _lastBreak;
int _bestBreak;
float _bestScore;
int _spaceCount;
TabStops _tabStops;
int mFirstTabIndex;
List<Candidate> _candidates = new List<Candidate>();
public int computeBreaks() {
int nCand = this._candidates.Count;
if (nCand > 0 && (nCand == 1 || this._lastBreak != nCand - 1)) {
var cand = this._candidates[this._candidates.Count - 1];
this._pushBreak(cand.offset, (cand.postBreak - this._preBreak));
}
return this._breaks.Count;
}
public List<int> getBreaks() {
return this._breaks;
}
public void resize(int size) {
if (this._charWidths.Count < size) {
NoAllocHelpersBridge<float>.ResizeList(this._charWidths, size);
}
}
public void setText(string text, int textOffset, int textLength) {
this._textBuf = new TextBuff(text, textOffset, textLength);
this._wordBreaker.setText(this._textBuf);
this._wordBreaker.next();
this._candidates.Clear();
Candidate can = new Candidate {
offset = 0, postBreak = 0, preBreak = 0, postSpaceCount = 0, preSpaceCount = 0, pre = 0
};
this._candidates.Add(can);
this._lastBreak = 0;
this._bestBreak = 0;
this._bestScore = ScoreInfty;
this._preBreak = 0;
this.mFirstTabIndex = int.MaxValue;
this._spaceCount = 0;
}
public void setLineWidth(float lineWidth) {
this._lineWidth = lineWidth;
}
public float addStyleRun(TextStyle style, int start, int end) {
float width = 0.0f;
if (style != null) {
width = Layout.measureText(this._width - this._preBreak, this._textBuf,
start, end - start, style,
this._charWidths, start, this._tabStops);
}
int current = this._wordBreaker.current();
int afterWord = start;
int lastBreak = start;
float lastBreakWidth = this._width;
float postBreak = this._width;
int postSpaceCount = this._spaceCount;
for (int i = start; i < end; i++) {
char c = this._textBuf.charAt(i);
if (c == '\t') {
this._width = this._preBreak + this._tabStops.nextTab((this._width - this._preBreak));
if (this.mFirstTabIndex == int.MaxValue) {
this.mFirstTabIndex = i;
}
}
else {
if (LayoutUtils.isWordSpace(c)) {
this._spaceCount += 1;
}
this._width += this._charWidths[i];
if (!LayoutUtils.isLineEndSpace(c)) {
postBreak = this._width;
postSpaceCount = this._spaceCount;
afterWord = i + 1;
}
}
if (i + 1 == current) {
int wordStart = this._wordBreaker.wordStart();
int wordEnd = this._wordBreaker.wordEnd();
if (style != null || current == end || this._charWidths[current] > 0) {
this._addWordBreak(current, this._width, postBreak, this._spaceCount, postSpaceCount, 0);
}
lastBreak = current;
lastBreakWidth = this._width;
current = this._wordBreaker.next();
}
}
return width;
}
public void finish() {
this._wordBreaker.finish();
this._width = 0;
this._candidates.Clear();
this._widths.Clear();
this._breaks.Clear();
this._textBuf = default;
}
public List<float> getWidths() {
return this._widths;
}
public void setTabStops(TabStops tabStops) {
this._tabStops = tabStops;
}
void _addWordBreak(int offset, float preBreak, float postBreak, int preSpaceCount, int postSpaceCount,
float penalty) {
float width = this._candidates[this._candidates.Count - 1].preBreak;
if (postBreak - width > this._lineWidth) {
int i = this._candidates[this._candidates.Count - 1].offset;
width += this._charWidths[i++];
for (; i < offset; i++) {
float w = this._charWidths[i];
if (w > 0) {
this._addCandidate(new Candidate {
offset = i,
preBreak = width,
postBreak = width,
preSpaceCount = postSpaceCount,
postSpaceCount = postSpaceCount,
penalty = ScoreDesperate,
});
width += w;
}
}
}
this._addCandidate(new Candidate {
offset = offset,
preBreak = preBreak,
postBreak = postBreak,
preSpaceCount = preSpaceCount,
postSpaceCount = postSpaceCount,
penalty = penalty
});
}
void _addCandidate(Candidate cand) {
int candIndex = this._candidates.Count;
this._candidates.Add(cand);
if (cand.postBreak - this._preBreak > this._lineWidth) {
if (this._bestBreak == this._lastBreak) {
this._bestBreak = candIndex;
}
this._pushGreedyBreak();
}
while (this._lastBreak != candIndex && cand.postBreak - this._preBreak > this._lineWidth) {
for (int i = this._lastBreak + 1; i < candIndex; i++) {
float penalty = this._candidates[i].penalty;
if (penalty <= this._bestScore) {
this._bestBreak = i;
this._bestScore = penalty;
}
}
if (this._bestBreak == this._lastBreak) {
this._bestBreak = candIndex;
}
this._pushGreedyBreak();
}
if (cand.penalty <= this._bestScore) {
this._bestBreak = candIndex;
this._bestScore = cand.penalty;
}
}
void _pushGreedyBreak() {
var bestCandidate = this._candidates[this._bestBreak];
this._pushBreak(bestCandidate.offset, (bestCandidate.postBreak - this._preBreak));
this._bestScore = ScoreInfty;
this._lastBreak = this._bestBreak;
this._preBreak = bestCandidate.preBreak;
}
void _pushBreak(int offset, float width) {
this._breaks.Add(offset);
this._widths.Add(width);
}
}
using System.Collections.Generic;
using UnityEngine;
namespace Unity.UIWidgets.ui {
class TabStops {
int _tabWidth = int.MaxValue;
Font _font;
int _fontSize;
int _spaceAdvance;
const int kTabSpaceCount = 4;
public void setFont(Font font, int size) {
if (this._font != font || this._fontSize != size) {
this._tabWidth = int.MaxValue;
}
this._font = font;
// Recompute the advance of space (' ') if font size changes
if (this._fontSize != size) {
this._fontSize = size;
this._font.RequestCharactersInTextureSafe(" ", this._fontSize);
this._font.getGlyphInfo(' ', out var glyphInfo, this._fontSize, UnityEngine.FontStyle.Normal);
this._spaceAdvance = glyphInfo.advance;
}
}
public float nextTab(float widthSoFar) {
if (this._tabWidth == int.MaxValue) {
if (this._fontSize > 0) {
this._tabWidth = this._spaceAdvance * kTabSpaceCount;
}
}
if (this._tabWidth == 0) {
return widthSoFar;
}
return (Mathf.Floor(widthSoFar / this._tabWidth + 1) * this._tabWidth);
}
}
struct Candidate {
public int offset;
public int pre;
public float preBreak;
public float penalty;
public float postBreak;
public int preSpaceCount;
public int postSpaceCount;
}
class LineBreaker {
const float ScoreInfty = float.MaxValue;
const float ScoreDesperate = 1e10f;
int _lineLimit = 0;
// Limit number of lines, 0 means no limit
public int lineLimit {
get { return this._lineLimit; }
set { this._lineLimit = value; }
}
public static LineBreaker instance {
get {
if (_instance == null) {
_instance = new LineBreaker();
}
return _instance;
}
}
static LineBreaker _instance;
public static int[] newLinePositions(string text, out int count) {
count = 0;
for (var i = 0; i < text.Length; i++) {
if (text[i] == '\n') {
count++;
}
}
count++;
if (_newLinePositions == null || _newLinePositions.Length < count) {
_newLinePositions = new int[count];
}
count = 0;
for (var i = 0; i < text.Length; i++) {
if (text[i] == '\n') {
_newLinePositions[count++] = i;
}
}
_newLinePositions[count++] = text.Length;
return _newLinePositions;
}
static int[] _newLinePositions;
TextBuff _textBuf;
float[] _charWidths;
List<int> _breaks = new List<int>();
int _breaksCount = 0;
List<float> _widths = new List<float>();
int _widthsCount = 0;
WordBreaker _wordBreaker = new WordBreaker();
float _width = 0.0f;
float _preBreak;
float _lineWidth;
int _lastBreak;
int _bestBreak;
float _bestScore;
int _spaceCount;
TabStops _tabStops;
int mFirstTabIndex;
List<Candidate> _candidates = new List<Candidate>();
int _candidatesCount = 0;
public int computeBreaks() {
int nCand = this._candidatesCount;
if (nCand > 0 && (nCand == 1 || this._lastBreak != nCand - 1)) {
var cand = this._candidates[this._candidatesCount - 1];
this._pushBreak(cand.offset, (cand.postBreak - this._preBreak));
}
return this._breaksCount;
}
public int getBreaksCount() {
return this._breaksCount;
}
public int getBreak(int i) {
return this._breaks[i];
}
public float getWidth(int i) {
return this._widths[i];
}
public void resize(int size) {
if (this._charWidths == null || this._charWidths.Length < size) {
this._charWidths = new float[LayoutUtils.minPowerOfTwo(size)];
}
}
public void setText(string text, int textOffset, int textLength) {
this._textBuf = new TextBuff(text, textOffset, textLength);
this._wordBreaker.setText(this._textBuf);
this._wordBreaker.next();
this._candidatesCount = 0;
Candidate can = new Candidate {
offset = 0, postBreak = 0, preBreak = 0, postSpaceCount = 0, preSpaceCount = 0, pre = 0
};
this._addCandidateToList(can);
this._lastBreak = 0;
this._bestBreak = 0;
this._bestScore = ScoreInfty;
this._preBreak = 0;
this.mFirstTabIndex = int.MaxValue;
this._spaceCount = 0;
}
public void setLineWidth(float lineWidth) {
this._lineWidth = lineWidth;
}
public float addStyleRun(TextStyle style, int start, int end) {
float width = 0;
if (style != null) {
// Layout.measureText(this._width - this._preBreak, this._textBuf,
// start, end - start, style,
// this._charWidths, start, this._tabStops);
width = Layout.computeCharWidths(this._width - this._preBreak, this._textBuf.text,
this._textBuf.offset + start, end - start, style,
this._charWidths, start, this._tabStops);
}
int current = this._wordBreaker.current();
float postBreak = this._width;
int postSpaceCount = this._spaceCount;
for (int i = start; i < end; i++) {
char c = this._textBuf.charAt(i);
if (c == '\t') {
this._width = this._preBreak + this._tabStops.nextTab(this._width - this._preBreak);
if (this.mFirstTabIndex == int.MaxValue) {
this.mFirstTabIndex = i;
}
}
else {
if (LayoutUtils.isWordSpace(c)) {
this._spaceCount += 1;
}
this._width += this._charWidths[i];
if (!LayoutUtils.isLineEndSpace(c)) {
postBreak = this._width;
postSpaceCount = this._spaceCount;
}
}
if (i + 1 == current) {
if (style != null || current == end || this._charWidths[current] > 0) {
this._addWordBreak(current, this._width, postBreak, this._spaceCount, postSpaceCount, 0);
}
current = this._wordBreaker.next();
}
}
return width;
}
public void finish() {
this._wordBreaker.finish();
this._width = 0;
this._candidatesCount = 0;
this._breaksCount = 0;
this._widthsCount = 0;
this._textBuf = default;
}
public int getWidthsCount() {
return this._widthsCount;
}
public void setTabStops(TabStops tabStops) {
this._tabStops = tabStops;
}
void _addWordBreak(int offset, float preBreak, float postBreak, int preSpaceCount, int postSpaceCount,
float penalty) {
float width = this._candidates[this._candidatesCount - 1].preBreak;
if (postBreak - width > this._lineWidth) {
this._addCandidatesInsideWord(width, offset, postSpaceCount);
}
this._addCandidate(new Candidate {
offset = offset,
preBreak = preBreak,
postBreak = postBreak,
preSpaceCount = preSpaceCount,
postSpaceCount = postSpaceCount,
penalty = penalty
});
}
void _addCandidatesInsideWord(float width, int offset, int postSpaceCount) {
int i = this._candidates[this._candidatesCount - 1].offset;
width += this._charWidths[i++];
for (; i < offset; i++) {
float w = this._charWidths[i];
if (w > 0) {
this._addCandidate(new Candidate {
offset = i,
preBreak = width,
postBreak = width,
preSpaceCount = postSpaceCount,
postSpaceCount = postSpaceCount,
penalty = ScoreDesperate,
});
width += w;
}
}
}
void _addCandidateToList(Candidate cand) {
if (this._candidates.Count == this._candidatesCount) {
this._candidates.Add(cand);
this._candidatesCount++;
}
else {
this._candidates[this._candidatesCount++] = cand;
}
}
void _addCandidate(Candidate cand) {
int candIndex = this._candidatesCount;
this._addCandidateToList(cand);
if (cand.postBreak - this._preBreak > this._lineWidth) {
if (this._bestBreak == this._lastBreak) {
this._bestBreak = candIndex;
}
this._pushGreedyBreak();
}
while (this._lastBreak != candIndex && cand.postBreak - this._preBreak > this._lineWidth) {
for (int i = this._lastBreak + 1; i < candIndex; i++) {
float penalty = this._candidates[i].penalty;
if (penalty <= this._bestScore) {
this._bestBreak = i;
this._bestScore = penalty;
}
}
if (this._bestBreak == this._lastBreak) {
this._bestBreak = candIndex;
}
this._pushGreedyBreak();
}
if (cand.penalty <= this._bestScore) {
this._bestBreak = candIndex;
this._bestScore = cand.penalty;
}
}
void _pushGreedyBreak() {
var bestCandidate = this._candidates[this._bestBreak];
this._pushBreak(bestCandidate.offset, bestCandidate.postBreak - this._preBreak);
this._bestScore = ScoreInfty;
this._lastBreak = this._bestBreak;
this._preBreak = bestCandidate.preBreak;
}
void _pushBreak(int offset, float width) {
if (this.lineLimit == 0 || this._breaksCount < this.lineLimit) {
if (this._breaks.Count == this._breaksCount) {
this._breaks.Add(offset);
this._breaksCount++;
}
else {
this._breaks[this._breaksCount++] = offset;
}
if (this._widths.Count == this._widthsCount) {
this._widths.Add(width);
this._widthsCount++;
}
else {
this._widths[this._widthsCount++] = width;
}
}
}
}
}

32
Runtime/ui/txt/paint_record.cs


namespace Unity.UIWidgets.ui {
class PaintRecord {
public PaintRecord(TextStyle style, Offset offset, TextBlob text,
FontMetrics metrics,
int line, float runWidth) {
struct PaintRecord {
public PaintRecord(TextStyle style, float dx, float dy, TextBlob text, FontMetrics metrics, float runWidth) {
this._line = line;
this._offset = offset;
this._dx = dx;
this._dy = dy;
}
public TextBlob text {

get { return this._style; }
}
public int line {
get { return this._line; }
}
public Offset offset {
get { return this._offset; }
set { this._offset = value; }
}
public void shift(float x, float y) {
this._dx += x;
this._dy += y;
}
public Offset shiftedOffset(Offset other) {
return new Offset(this._dx + other.dx, this._dy + other.dy);
}
int _line;
Offset _offset;
float _dx;
float _dy;
FontMetrics _metrics;
}
}

1001
Runtime/ui/txt/paragraph.cs
文件差异内容过多而无法显示
查看文件

2
Runtime/ui/txt/paragraph_builder.cs


public Paragraph build() {
this._runs.endRunIfNeeded(this._text.Length);
var paragraph = new Paragraph();
var paragraph = Paragraph.create();
paragraph.setText(this._text.ToString(), this._runs);
paragraph.setParagraphStyle(this._paragraphStyle);
return paragraph;

6
Runtime/ui/txt/styled_runs.cs


readonly List<TextStyle> styles = new List<TextStyle>();
readonly List<IndexedRun> runs = new List<IndexedRun>();
public class RunIterator {
public struct RunIterator {
int _charIndex;
int _runIndex;
StyledRuns _runs;

}
}
internal class Run {
internal struct Run {
public readonly TextStyle style;
public readonly int start;
public readonly int end;

}
internal class IndexedRun {
public readonly int styleIndex = 0;
public readonly int styleIndex;
public readonly int start;
public int end;

5
Runtime/ui/txt/text_buff.cs


using System;
using Unity.UIWidgets.foundation;
namespace Unity.UIWidgets.ui {

public override string ToString() {
return this.text.Substring(this.offset, this.size);
}
public string subString(int shift, int size) {
return this.text.Substring(this.offset + shift, size);
}
}
}

19
Runtime/ui/txt/word_separate.cs


namespace Unity.UIWidgets.ui {
class WordSeparate {
enum Direction {
Forward,
Backward,
}
internal enum characterType {
struct WordSeparate {
internal enum CharacterType {
LetterLike,
Symbol,
WhiteSpace

}
internal static characterType classifyChar(string text, int index) {
internal static CharacterType classifyChar(string text, int index) {
internal static characterType classifyChar(char ch) {
internal static CharacterType classifyChar(char ch) {
return characterType.WhiteSpace;
return CharacterType.WhiteSpace;
return characterType.LetterLike;
return CharacterType.LetterLike;
return characterType.Symbol;
return CharacterType.Symbol;
}
}
}

59
Runtime/ui/txt/wordbreaker.cs


class WordBreaker {
struct WordBreaker {
public const uint U16_SURROGATE_OFFSET = ((0xd800 << 10) + 0xdc00 - 0x10000);
TextBuff _text;
int _current;

if (this._current == this._text.size) {
return -1;
}
WordSeparate.characterType preType = WordSeparate.classifyChar(this._text.charAt(this._current));
bool preBoundaryChar = isBoundaryChar(this._text.charAt(this._current));
char c = this._text.charAt(this._current);
bool preWhiteSpace = char.IsWhiteSpace(c);
bool preBoundaryChar = isBoundaryChar(c);
this._findBoundaryCharOrTypeChange(preWhiteSpace);
return this._current;
}
void _findBoundaryCharOrTypeChange(bool preWhiteSpace) {
for (; this._current < this._text.size; ++this._current) {
// this.nextUntilCodePoint();
if (this._current >= this._text.size) {

if (isBoundaryChar(this._text.charAt(this._current))) {
char c = this._text.charAt(this._current);
if (isBoundaryChar(c)) {
var currentType = WordSeparate.classifyChar(this._text.charAt(this._current));
if ((currentType == WordSeparate.characterType.WhiteSpace)
!= (preType == WordSeparate.characterType.WhiteSpace)) {
bool currentType = char.IsWhiteSpace(c);
if (currentType != preWhiteSpace) {
preType = currentType;
preWhiteSpace = currentType;
return this._current;
void _detectEmailOrUrl() {
}

}
public static bool isLeadSurrogate(uint c) {
return ((c) & 0xfffffc00) == 0xd800;
return (c & 0xfffffc00) == 0xd800;
return ((c) & 0xfffffc00) == 0xdc00;
return (c & 0xfffffc00) == 0xdc00;
return (char) (((uint) (lead) << 10) + (uint) (trail - U16_SURROGATE_OFFSET));
return (char) ((lead << 10) + (trail - U16_SURROGATE_OFFSET));
if (char.IsPunctuation(code)) {
return true;
}
if (code >= 0x4E00 && code <= 0x9FFF) { // cjk https://en.wikipedia.org/wiki/CJK_Unified_Ideographs
return true;
}
// https://social.msdn.microsoft.com/Forums/en-US/0d1888de-9745-4dd1-80fd-d3c29d3e381d/checking-for-japanese-characters-in-a-string?forum=vcmfcatl
if (code >= 0x3040 && code <= 0x30FF) { // Hiragana or Katakana
return true;
}
return (code >= 0x4E00 && code <= 0x9FFF) || (code >= 0x3040 && code <= 0x30FF) || char.IsPunctuation(code);
}
return false;
}
&& (char.IsLowSurrogate(this._text.charAt(this._current))
|| char.IsHighSurrogate(this._text.charAt(this._current)))) {
&& (char.IsLowSurrogate(this._text.charAt(this._current))
|| char.IsHighSurrogate(this._text.charAt(this._current)))) {
this._current++;
}
}

6
Samples/UIWidgetSample/MaterialSample.cs


child: new Center(
child: new Column(
children: new List<Widget> {
new FlatButton(
new Padding(padding: EdgeInsets.only(top: 30f)),
new MaterialButton(
shape: new RoundedRectangleBorder(borderRadius: BorderRadius.all(20.0f)),
color: new Color(0xFF00FF00),
splashColor: new Color(0xFFFF0011),

),
new RaisedButton(
new Padding(padding: EdgeInsets.only(top: 30f)),
new MaterialButton(
shape: new RoundedRectangleBorder(borderRadius: BorderRadius.all(20.0f)),
color: new Color(0xFFFF00FF),
splashColor: new Color(0xFFFF0011),

1
Samples/UIWidgetSample/MaterialThemeSample.cs


}
class _MaterialThemeSampleWidgetState : State<MaterialThemeSampleWidget> {
int _currentIndex = 0;
public override Widget build(BuildContext context) {
return new Theme(
data: new ThemeData(

3
Samples/UIWidgetsGallery/demo/animation/home.cs


float scrollFactor = 5.0f
) : base(key: key) {
D.assert(maxHeight != null && maxHeight >= 0.0f);
D.assert(scrollFactor != null && scrollFactor >= 1.0f);
D.assert(scrollFactor >= 1.0f);
this.maxHeight = maxHeight;
this.scrollFactor = scrollFactor;
}

float? midScrollOffset = null
) : base(parent: parent) {
D.assert(midScrollOffset != null);
this.midScrollOffset = midScrollOffset ?? 0.0f;
}
public readonly float midScrollOffset;

28
Tests/Editor/CanvasAndLayers.cs


canvas.scale(3);
TextBlobBuilder builder = new TextBlobBuilder();
string text = "This is a text blob";
builder.setBounds(new Rect(-10, -20, 200, 50));
builder.setPositionXs(new float[] {
10, 20, 30, 40, 50, 60, 70, 80, 90, 100,
110, 120, 130, 140, 150, 160, 170, 180, 190
});
builder.setBounds(Unity.UIWidgets.ui.Rect.fromLTWH(-10, -20, 200, 50));
builder.positions = new Vector2d[] {
new Vector2d(10, 0),
new Vector2d(20, 0),
new Vector2d(30, 0),
new Vector2d(40, 0),
new Vector2d(50, 0),
new Vector2d(60, 0),
new Vector2d(70, 0),
new Vector2d(80, 0),
new Vector2d(90, 0),
new Vector2d(100, 0),
new Vector2d(110, 0),
new Vector2d(120, 0),
new Vector2d(130, 0),
new Vector2d(140, 0),
new Vector2d(150, 0),
new Vector2d(160, 0),
new Vector2d(170, 0),
new Vector2d(180, 0),
new Vector2d(190, 0),
};
var textBlob = builder.make();
canvas.drawTextBlob(textBlob, new Offset(100, 100), new Paint {

this._meshPool);
canvas.drawParagraph(paragraph, new Offset(10f, 100f));
canvas.flush();
Unity.UIWidgets.ui.Paragraph.release(ref paragraph);
}
void drawImageRect() {

85
Runtime/Resources/UIWidgets_canvas_shadowBox.shader


Shader "UIWidgets/ShadowBox"
{
//originally from http://madebyevan.com/shaders/fast-rounded-rectangle-shadows/
Properties
{
_SrcBlend("_SrcBlend", Int) = 1 // One
_DstBlend("_DstBlend", Int) = 10 // OneMinusSrcAlpha
_StencilComp("_StencilComp", Float) = 8 // - Equal, 8 - Always
}
SubShader
{
ZTest Always
ZWrite Off
Blend [_SrcBlend] [_DstBlend]
Stencil {
Ref 128
Comp [_StencilComp]
}
Pass {
CGPROGRAM
float4 _sb_box;
float4 _viewport;
float _sb_sigma;
float4 _sb_color;
float _mat[9];
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 coord : TEXCOORD0;
};
float4 erf(float4 x)
{
float4 s = sign(x);
float4 a = abs(x);
x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
x = x * x;
return s - s / (x * x);
return s;
}
float boxShadow(float2 lower, float2 upper, float2 pnt, float sigma)
{
float4 query = float4(pnt - lower, pnt - upper);
float4 integral = 0.5 + 0.5 * erf(query * (sqrt(0.5) / sigma));
return (integral.z - integral.x) * (integral.w - integral.y);
}
v2f vert(appdata v){
v2f o;
float padding = 3.0 * _sb_sigma;
o.coord = lerp(_sb_box.xy - padding, _sb_box.zw + padding, v.vertex.xy);
float3x3 mat = float3x3(_mat[0], _mat[1], _mat[2], _mat[3], _mat[4], _mat[5], 0, 0, 1);
float2 p = mul(mat, float3(o.coord.xy, 1.0)).xy - _viewport.xy;
#if UNITY_UV_STARTS_AT_TOP
o.vertex = float4(2.0 * p.x / _viewport.z - 1.0, 2.0 * p.y / _viewport.w - 1.0, 0, 1);
#else
o.vertex = float4(2.0 * p.x / _viewport.z - 1.0, 1.0 - 2.0 * p.y / _viewport.w, 0, 1);
#endif
return o;
}
float4 frag(v2f i) : SV_TARGET {
float4 fragColor = _sb_color;
fragColor.a = fragColor.a * boxShadow(_sb_box.xy, _sb_box.zw, i.coord, _sb_sigma);
return fragColor;
}
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}

9
Runtime/Resources/UIWidgets_canvas_shadowBox.shader.meta


fileFormatVersion: 2
guid: 105f52491741049d88708e718c749e34
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

117
Runtime/Resources/UIWidgets_canvas_shadowRBox.shader


Shader "UIWidgets/ShadowRBox"
{
//originally from http://madebyevan.com/shaders/fast-rounded-rectangle-shadows/
Properties
{
_SrcBlend("_SrcBlend", Int) = 1 // One
_DstBlend("_DstBlend", Int) = 10 // OneMinusSrcAlpha
_StencilComp("_StencilComp", Float) = 8 // - Equal, 8 - Always
}
SubShader
{
ZTest Always
ZWrite Off
Blend [_SrcBlend] [_DstBlend]
Stencil {
Ref 128
Comp [_StencilComp]
}
Pass {
CGPROGRAM
float4 _sb_box;
float4 _viewport;
float _sb_sigma;
float4 _sb_color;
float _sb_corner;
float _mat[9];
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 coord : TEXCOORD0;
};
float gaussian(float x, float sigma)
{
float pi = 3.141592653589793;
return exp(-(x*x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * pi) * sigma);
}
float2 erf(float2 x)
{
float2 s = sign(x);
float2 a = abs(x);
x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
x = x * x;
return s - s / (x * x);
return s;
}
float roundedBoxShadowX(float x, float y, float sigma, float corner, float2 halfSize)
{
float delta = min(halfSize.y - corner - abs(y), 0.0);
float curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta));
float2 integral = 0.5 + 0.5 * erf((x + float2(-curved, curved)) * (sqrt(0.5)/sigma));
return integral.y - integral.x;
}
float roundedBoxShadow(float2 lower, float2 upper, float2 pnt, float sigma, float corner)
{
float2 center = (lower + upper) * 0.5;
float2 halfSize = (upper - lower) * 0.5;
pnt -= center;
float low = pnt.y - halfSize.y;
float high = pnt.y + halfSize.y;
float start = clamp(-3.0 * sigma, low, high);
float end = clamp(3.0 * sigma, low, high);
float step = (end - start) / 4.0;
float y = start + step * 0.5;
float value = 0.0;
for(int i=0; i<4;i++)
{
value += roundedBoxShadowX(pnt.x, pnt.y - y, sigma, corner, halfSize) * gaussian(y, sigma) * step;
y += step;
}
return value;
}
v2f vert(appdata v){
v2f o;
float padding = 3.0 * _sb_sigma;
o.coord = lerp(_sb_box.xy - padding, _sb_box.zw + padding, v.vertex.xy);
float3x3 mat = float3x3(_mat[0], _mat[1], _mat[2], _mat[3], _mat[4], _mat[5], 0, 0, 1);
float2 p = mul(mat, float3(o.coord.xy, 1.0)).xy - _viewport.xy;
#if UNITY_UV_STARTS_AT_TOP
o.vertex = float4(2.0 * p.x / _viewport.z - 1.0, 2.0 * p.y / _viewport.w - 1.0, 0, 1);
#else
o.vertex = float4(2.0 * p.x / _viewport.z - 1.0, 1.0 - 2.0 * p.y / _viewport.w, 0, 1);
#endif
return o;
}
float4 frag(v2f i) : SV_TARGET {
float4 fragColor = _sb_color;
fragColor.a = fragColor.a * roundedBoxShadow(_sb_box.xy, _sb_box.zw, i.coord, _sb_sigma, _sb_corner);
return fragColor;
}
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}

9
Runtime/Resources/UIWidgets_canvas_shadowRBox.shader.meta


fileFormatVersion: 2
guid: 77c8565b9b449434490473ef8f7f0e91
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

65
Runtime/Resources/UIWidgets_canvas_strokeAlpha.shader


Shader "UIWidgets/canvas_strokeAlpha"
{
Properties {
_SrcBlend("_SrcBlend", Int) = 1 // One
_DstBlend("_DstBlend", Int) = 10 // OneMinusSrcAlpha
_StencilComp("_StencilComp", Float) = 3 // - Equal, 8 - Always
}
SubShader {
ZTest Always
ZWrite Off
Blend [_SrcBlend] [_DstBlend]
Stencil {
Ref 128
Comp [_StencilComp]
Pass IncrSat
}
Pass { // 0, color
CGPROGRAM
#define UIWIDGETS_COLOR
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_stroke_alpha
ENDCG
}
Pass { // 1, linear
CGPROGRAM
#define UIWIDGETS_LINEAR
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_stroke_alpha
ENDCG
}
Pass { // 2, radial
CGPROGRAM
#define UIWIDGETS_RADIAL
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_stroke_alpha
ENDCG
}
Pass { // 3, sweep
CGPROGRAM
#define UIWIDGETS_SWEEP
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_stroke_alpha
ENDCG
}
Pass { // 4, image
CGPROGRAM
#define UIWIDGETS_IMAGE
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_stroke_alpha
ENDCG
}
}
}

9
Runtime/Resources/UIWidgets_canvas_strokeAlpha.shader.meta


fileFormatVersion: 2
guid: c57ed8e638931f946a7b5e26a54725d8
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

74
Runtime/ui/renderer/cmdbufferCanvas/rendering/canvas_shadow_utils.cs


using Unity.UIWidgets.foundation;
using UnityEngine;
namespace Unity.UIWidgets.ui {
public partial class PictureFlusher {
void _drawRRectShadow(uiPath path, uiPaint paint) {
D.assert(path.isNaiveRRect, () => "Cannot draw fast Shadow for non-NaiveRRect shapes");
D.assert(paint.style == PaintingStyle.fill, () => "Cannot draw fast Shadow for stroke lines");
var layer = this._currentLayer;
var state = layer.currentState;
var cache = path.flatten(state.scale * this._devicePixelRatio);
bool convex;
cache.computeFillMesh(this._fringeWidth, out convex);
var fillMesh = cache.fillMesh;
var meshBounds = fillMesh.transform(state.matrix);
var clipBounds = layer.layerBounds;
uiRect? stackBounds;
bool iior;
layer.clipStack.getBounds(out stackBounds, out iior);
if (stackBounds != null) {
clipBounds = uiRectHelper.intersect(clipBounds, stackBounds.Value);
}
if (clipBounds.isEmpty) {
ObjectPool<uiMeshMesh>.release(meshBounds);
return;
}
var maskBounds = meshBounds.bounds;
maskBounds = uiRectHelper.intersect(maskBounds, clipBounds);
if (maskBounds.isEmpty) {
ObjectPool<uiMeshMesh>.release(meshBounds);
return;
}
var blurMesh = ImageMeshGenerator.imageMesh(null, uiRectHelper.one, maskBounds);
if (!this._applyClip(blurMesh.bounds)) {
ObjectPool<uiMeshMesh>.release(meshBounds);
ObjectPool<uiMeshMesh>.release(blurMesh);
return;
}
var bound = path.getBounds();
var sigma = state.scale * paint.maskFilter.Value.sigma;
var vertices = ObjectPool<uiList<Vector3>>.alloc();
vertices.SetCapacity(4);
vertices.Add(new Vector2(0, 0));
vertices.Add(new Vector2(1, 0));
vertices.Add(new Vector2(0, 1));
vertices.Add(new Vector2(1, 1));
var _triangles = ObjectPool<uiList<int>>.alloc();
_triangles.SetCapacity(6);
_triangles.Add(0);
_triangles.Add(1);
_triangles.Add(2);
_triangles.Add(2);
_triangles.Add(1);
_triangles.Add(3);
ObjectPool<uiMeshMesh>.release(meshBounds);
ObjectPool<uiMeshMesh>.release(blurMesh);
var mesh = uiMeshMesh.create(state.matrix, vertices, _triangles);
layer.draws.Add(CanvasShader.fastShadow(layer, mesh, sigma, path.isRect, path.isCircle, path.rRectCorner, new Vector4(bound.left, bound.top, bound.right, bound.bottom), paint.color));
}
}
}

11
Runtime/ui/renderer/cmdbufferCanvas/rendering/canvas_shadow_utils.cs.meta


fileFormatVersion: 2
guid: fbf14696e076b4453a580e27ad682959
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

19
Runtime/ui/renderer/common/geometry/path/path_extension.cs


namespace Unity.UIWidgets.ui {
public partial class uiPath {
public enum uiPathShapeHint {
Rect,
Circle,
NaiveRRect,
Other
}
public bool isRect {
get { return this._shapeHint == uiPathShapeHint.Rect; }
}
public bool isCircle {
get { return this._shapeHint == uiPathShapeHint.Circle; }
}
}
}

11
Runtime/ui/renderer/common/geometry/path/path_extension.cs.meta


fileFormatVersion: 2
guid: 41c5cc4750b044c49b1089ec86ffe415
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

146
Samples/UIWidgetSample/AntialiasSVGSample.cs


using System.Collections.Generic;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Canvas = Unity.UIWidgets.ui.Canvas;
using Color = Unity.UIWidgets.ui.Color;
using Gradient = Unity.UIWidgets.ui.Gradient;
using Image = Unity.UIWidgets.ui.Image;
using UIWidgetRect = Unity.UIWidgets.ui.Rect;
public class AntialiasSVGSample : UIWidgetsPanel {
protected override void OnEnable() {
FontManager.instance.addFont(Resources.Load<Font>("MaterialIcons-Regular"), "Material Icons");
FontManager.instance.addFont(Resources.Load<Font>("GalleryIcons"), "GalleryIcons");
base.OnEnable();
}
protected override Widget createWidget() {
Debug.Log("[SVG Testbed Panel Created]");
return new SVGTestbedPanelWidget();
}
}
public class SVGTestbedPanelWidget : StatefulWidget {
public SVGTestbedPanelWidget(Key key = null) : base(key) { }
public override State createState() {
return new SVGTestbedPanelWidgetState();
}
}
public class SVGTestbedPanelWidgetState : State<SVGTestbedPanelWidget> {
static Texture2D texture6;
public class CurvePainter : AbstractCustomPainter {
public override void paint(Canvas canvas, Size size) {
var paint = new Paint()
{
color = new Color(0xFFFF0000),
};
paint.color = Colors.yellow;
paint.style = PaintingStyle.stroke;
paint.strokeWidth = 3;
var startPoint = new Offset(0, size.height / 6);
var controlPoint1 = new Offset(size.width / 4, 0);
var controlPoint2 = new Offset(3 * size.width / 4, 0);
var endPoint = new Offset(size.width, size.height / 6);
var path = new Path();
path.moveTo(startPoint.dx, startPoint.dy);
path.cubicTo(
controlPoint1.dx, controlPoint1.dy,
controlPoint2.dx, controlPoint2.dy,
endPoint.dx, endPoint.dy
);
path.moveTo(10, 10);
path.lineTo(90, 10);
path.lineTo(10, 90);
path.lineTo(90, 90);
path.winding(PathWinding.clockwise);
path.close();
path.moveTo(110, 10);
path.lineTo(190, 10);
path.lineTo(110, 90);
path.lineTo(190, 90);
path.close();
path.addRect(UIWidgetRect.fromLTWH(10, 25, 180, 50));
path.addRect(UIWidgetRect.fromLTWH(200, 0, 100, 100));
path.addRRect(RRect.fromRectAndRadius(UIWidgetRect.fromLTWH(225, 25, 50, 50), 10));
path.winding(PathWinding.clockwise);
path.addOval(UIWidgetRect.fromLTWH(200, 50, 100, 100));
path.winding(PathWinding.clockwise);
canvas.drawPath(path, paint);
paint = new Paint {
color = new Color(0xFFFF0000),
shader = Gradient.linear(
new Offset(0, 0),
new Offset(size.width, 200),
new List<Color>() {
Colors.red, Colors.black, Colors.green
}, null, TileMode.clamp),
};
canvas.translate(0, 200);
canvas.drawPath(path, paint);
canvas.translate(0, 200);
// paint.maskFilter = MaskFilter.blur(BlurStyle.normal, 5);
paint.shader = new ImageShader(new Image(texture6, true), TileMode.mirror);
canvas.drawPath(path, paint);
canvas.translate(0, 200);
paint = new Paint {
color = new Color(0xFF00FF00),
shader = Gradient.sweep(
new Offset(size.width / 2, 100),
new List<Color>() {
Colors.red, Colors.black, Colors.green, Colors.red,
}, null, TileMode.clamp, 0 * Mathf.PI / 180, 360 * Mathf.PI / 180),
};
canvas.drawPath(path, paint);
paint.shader = Gradient.radial(
new Offset(size.width / 2, 100), 200f,
new List<Color>()
{
Colors.red, Colors.black, Colors.green
}, null, TileMode.clamp);
canvas.translate(0, 200);
canvas.drawPath(path, paint);
}
public override bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
public override Widget build(BuildContext context) {
texture6 = Resources.Load<Texture2D>("6");
return new SingleChildScrollView(
child: new Container(
child: new CustomPaint(
painter: new CurvePainter(),
child: new Container()
),
height: 1500
)
);
}
}

11
Samples/UIWidgetSample/AntialiasSVGSample.cs.meta


fileFormatVersion: 2
guid: 41696ca988ae5c44da3f545825faf3ac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

149
Samples/UIWidgetSample/BenchMarkLayout.cs


using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using FontStyle = Unity.UIWidgets.ui.FontStyle;
using Material = Unity.UIWidgets.material.Material;
namespace UIWidgetsSample {
public class BenchMarkLayout : UIWidgetsSamplePanel {
protected override Widget createWidget() {
return new MaterialApp(
showPerformanceOverlay: false,
home: new Material(
child: new BenchMarkLayoutWidget()),
builder: (_, child) => {
return new Builder(builder:
context => {
return new MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0f
),
child: child);
});
});
}
protected override void OnEnable() {
base.OnEnable();
FontManager.instance.addFont(Resources.Load<Font>(path: "MaterialIcons-Regular"), "Material Icons");
}
}
class BenchMarkLayoutWidget : StatefulWidget {
public BenchMarkLayoutWidget(Key key = null) : base(key) {
}
public override State createState() {
return new BenchMarkLayoutWidgetState();
}
}
class BenchMarkLayoutWidgetState : State<BenchMarkLayoutWidget> {
int width = 260;
bool visible = true;
Widget richtext = new Container(
child: new RichText(
text: new TextSpan("", children:
new List<TextSpan>() {
new TextSpan("Real-time 3D revolutioni\t淡粉色的方式地方\tzes the animation pipeline "),
new TextSpan(style: new TextStyle(color: Color.fromARGB(255, 255, 0, 0)),
text: "for Disney Television Animation's\t “Baymax Dreams"),
new TextSpan("\t", style: new TextStyle(color: Colors.black)),
new TextSpan(" Unity Widgets"),
new TextSpan(" Text"),
new TextSpan("Real-time 3D revolutionizes the animation pipeline "),
new TextSpan(style: new TextStyle(color: Color.fromARGB(125, 255, 0, 0)),
text: "Transparent Red Text\n\n"),
new TextSpan("Bold Text Test Bold Textfs Test: FontWeight.w70\n\n"),
new TextSpan(style: new TextStyle(fontStyle: FontStyle.italic),
text: "This is FontStyle.italic Text This is FontStyle.italic Text\n\n"),
new TextSpan(
style: new TextStyle(fontStyle: FontStyle.italic, fontWeight: FontWeight.w700),
text:
"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.\n\n"),
new TextSpan(style: new TextStyle(fontSize: 24),
text: "Emoji \ud83d\ude0a\ud83d\ude0b\t\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 Emoji"),
new TextSpan(style: new TextStyle(fontSize: 18),
text: "Emoji \ud83d\ude01\ud83d\ude02\ud83d\ude03\ud83d\ude04\ud83d\ude05"),
new TextSpan(style: new TextStyle(fontSize: 18),
text: "\ud83d\ude01\ud83d\ude02\ud83d\ude03\ud83d\ude04\ud83d\ude05"),
new TextSpan(style: new TextStyle(fontSize: 18),
text: "\ud83d\ude01\ud83d\ude02\ud83d\ude03\ud83d\ude04\ud83d\ude05"),
new TextSpan(style: new TextStyle(fontSize: 18),
text: "\ud83d\ude01\ud83d\ude02\ud83d\ude03\ud83d\ude04\ud83d\ude05"),
new TextSpan(style: new TextStyle(fontSize: 18),
text: "\ud83d\ude01\ud83d\ude02\ud83d\ude03\ud83d\ude04\ud83d\ude05"),
new TextSpan(style: new TextStyle(fontSize: 24),
text:
"Emoji \ud83d\ude06\ud83d\ude1C\ud83d\ude18\ud83d\ude2D\ud83d\ude0C\ud83d\ude1E\n\n"),
new TextSpan(style: new TextStyle(fontSize: 14),
text: "FontSize 14"),
})
)
);
public override Widget build(BuildContext context) {
Widget buttons = new Column(
mainAxisAlignment: MainAxisAlignment.end,
children: new List<Widget> {
new Text($"Width: {this.width}"),
new RaisedButton(
onPressed: () => { this.setState(() => { this.width += 10; }); },
child: new Text("Add Width")
),
new Divider(),
new RaisedButton(
onPressed: () => { this.setState(() => { this.width -= 10; }); },
child: new Text("Dec Width")
),
new Divider(),
new RaisedButton(
onPressed: () => { this.setState(() => { this.visible = true; }); },
child: new Text("Show")
),
new Divider(),
new RaisedButton(
onPressed: () => { this.setState(() => { this.visible = false; }); },
child: new Text("Hide")
)
}
);
Widget child = new Column(
children: new List<Widget> {
this.visible ? this.richtext : new Text(""),
this.visible
? new Text(
"Very Very Very Very Very Very Very Very Very Very Very Very Very Very\nVery Very Very Very Very Very Very Very Very Very Very Long Text",
maxLines: 3, overflow: TextOverflow.ellipsis, textAlign: TextAlign.justify
)
: new Text("")
});
child = new Stack(
children: new List<Widget> {
child,
buttons
}
);
child = new Container(
width: this.width,
color: Colors.black12,
child: child
);
child = new Center(
child: child
);
return child;
}
}
}

11
Samples/UIWidgetSample/BenchMarkLayout.cs.meta


fileFormatVersion: 2
guid: cba57cf034e23904ab2f929598f4cb45
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

296
Samples/UIWidgetSample/TextLayoutSample.unity


%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 9
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 0
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 11
m_GIWorkflowMode: 1
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 1
m_LightmapEditorSettings:
serializedVersion: 12
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_FinalGather: 0
m_FinalGatherFiltering: 1
m_FinalGatherRayCount: 256
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 512
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 256
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 1
m_PVRDenoiserTypeDirect: 1
m_PVRDenoiserTypeIndirect: 1
m_PVRDenoiserTypeAO: 1
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVREnvironmentMIS: 1
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ShowResolutionOverlay: 1
m_ExportTrainingData: 0
m_LightingDataAsset: {fileID: 0}
m_UseShadowmask: 1
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 2
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &1668830267
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1668830270}
- component: {fileID: 1668830269}
- component: {fileID: 1668830268}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!81 &1668830268
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1668830267}
m_Enabled: 1
--- !u!20 &1668830269
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1668830267}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 1
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_FocalLength: 50
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 0
orthographic size: 5
m_Depth: -1
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &1668830270
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1668830267}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1774196668
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1774196670}
- component: {fileID: 1774196669}
m_Layer: 0
m_Name: Directional Light
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!108 &1774196669
Light:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1774196668}
m_Enabled: 1
serializedVersion: 9
m_Type: 1
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
m_Intensity: 1
m_Range: 10
m_SpotAngle: 30
m_InnerSpotAngle: 21.80208
m_CookieSize: 10
m_Shadows:
m_Type: 2
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
e01: 0
e02: 0
e03: 0
e10: 0
e11: 1
e12: 0
e13: 0
e20: 0
e21: 0
e22: 1
e23: 0
e30: 0
e31: 0
e32: 0
e33: 1
m_UseCullingMatrixOverride: 0
m_Cookie: {fileID: 0}
m_DrawHalo: 0
m_Flare: {fileID: 0}
m_RenderMode: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingLayerMask: 1
m_Lightmapping: 4
m_LightShadowCasterMode: 0
m_AreaSize: {x: 1, y: 1}
m_BounceIntensity: 1
m_ColorTemperature: 6570
m_UseColorTemperature: 0
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
m_UseBoundingSphereOverride: 0
m_ShadowRadius: 0
m_ShadowAngle: 0
--- !u!4 &1774196670
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1774196668}
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
m_LocalPosition: {x: 0, y: 3, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}

7
Samples/UIWidgetSample/TextLayoutSample.unity.meta


fileFormatVersion: 2
guid: 12e27c4b597519147a207be6204dc1f5
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存