浏览代码

Merge branch 'master' of gitlab.cds.internal.unity3d.com:upm-packages/ui-widgets/com.unity.uiwidgets into material

/main
xingwei.zhu 6 年前
当前提交
fb3be64c
共有 65 个文件被更改,包括 4191 次插入1867 次删除
  1. 2
      Runtime/editor/rasterizer.cs
  2. 39
      Runtime/foundation/basic_types.cs
  3. 9
      Runtime/material/text_selection.cs
  4. 2
      Runtime/painting/box_decoration.cs
  5. 508
      Runtime/painting/gradient.cs
  6. 80
      Runtime/painting/image_resolution.cs
  7. 20
      Runtime/painting/matrix_utils.cs
  8. 6
      Runtime/painting/text_painter.cs
  9. 2
      Runtime/painting/text_style.cs
  10. 15
      Runtime/rendering/editable.cs
  11. 6
      Runtime/rendering/paragraph.cs
  12. 13
      Runtime/ui/geometry.cs
  13. 50
      Runtime/ui/matrix.cs
  14. 33
      Runtime/ui/painting/canvas.cs
  15. 75
      Runtime/ui/painting/canvas_clip.cs
  16. 905
      Runtime/ui/painting/canvas_impl.cs
  17. 112
      Runtime/ui/painting/painting.cs
  18. 136
      Runtime/ui/painting/path.cs
  19. 296
      Runtime/ui/painting/picture.cs
  20. 27
      Runtime/ui/painting/txt/font_manager.cs
  21. 106
      Runtime/ui/painting/txt/mesh_generator.cs
  22. 56
      Runtime/ui/painting/txt/text_blob.cs
  23. 4
      Runtime/ui/text.cs
  24. 314
      Runtime/ui/txt/linebreaker.cs
  25. 721
      Runtime/ui/txt/paragraph.cs
  26. 6
      Runtime/ui/txt/word_separate.cs
  27. 4
      Runtime/widgets/basic.cs
  28. 2
      Runtime/widgets/routes.cs
  29. 105
      Tests/Editor/CanvasAndLayers.cs
  30. 8
      Tests/Editor/EditableTextWiget.cs
  31. 11
      Tests/Editor/Widgets.cs
  32. 178
      Runtime/Resources/UIWidgets_canvas.cginc
  33. 9
      Runtime/Resources/UIWidgets_canvas.cginc.meta
  34. 64
      Runtime/Resources/UIWidgets_canvas_convexFill.shader
  35. 9
      Runtime/Resources/UIWidgets_canvas_convexfill.shader.meta
  36. 31
      Runtime/Resources/UIWidgets_canvas_fill0.shader
  37. 9
      Runtime/Resources/UIWidgets_canvas_fill0.shader.meta
  38. 66
      Runtime/Resources/UIWidgets_canvas_fill1.shader
  39. 9
      Runtime/Resources/UIWidgets_canvas_fill1.shader.meta
  40. 18
      Runtime/Resources/UIWidgets_canvas_filter.shader
  41. 9
      Runtime/Resources/UIWidgets_canvas_filter.shader.meta
  42. 56
      Runtime/Resources/UIWidgets_canvas_stencil.shader
  43. 9
      Runtime/Resources/UIWidgets_canvas_stencil.shader.meta
  44. 65
      Runtime/Resources/UIWidgets_canvas_stroke0.shader
  45. 9
      Runtime/Resources/UIWidgets_canvas_stroke0.shader.meta
  46. 27
      Runtime/Resources/UIWidgets_canvas_stroke1.shader
  47. 9
      Runtime/Resources/UIWidgets_canvas_stroke1.shader.meta
  48. 64
      Runtime/Resources/UIWidgets_canvas_tex.shader
  49. 9
      Runtime/Resources/UIWidgets_canvas_tex.shader.meta
  50. 453
      Runtime/ui/painting/canvas_shader.cs
  51. 11
      Runtime/ui/painting/canvas_shader.cs.meta
  52. 603
      Runtime/ui/painting/shader.cs
  53. 11
      Runtime/ui/painting/shader.cs.meta
  54. 102
      Runtime/ui/txt/layout.cs
  55. 11
      Runtime/ui/txt/layout.cs.meta
  56. 14
      Runtime/ui/txt/layout_utils.cs
  57. 11
      Runtime/ui/txt/layout_utils.cs.meta
  58. 147
      Runtime/ui/txt/wordbreaker.cs
  59. 11
      Runtime/ui/txt/wordbreaker.cs.meta
  60. 8
      Tests/Resources.meta
  61. 3
      Tests/Resources/6.png
  62. 88
      Tests/Resources/6.png.meta
  63. 3
      Runtime/Resources/UIWidgets_canvas.shader.meta
  64. 259
      Runtime/Resources/UIWidgets_canvas.shader

2
Runtime/editor/rasterizer.cs


var canvas = frame.getCanvas();
using (var compositorFrame = this._compositorContext.acquireFrame(canvas)) {
if (compositorFrame != null && compositorFrame.raster(layerTree, true)) {
if (compositorFrame != null && compositorFrame.raster(layerTree, false)) {
frame.submit();
this._fireNextFrameCallbackIfPresent();
return true;

39
Runtime/foundation/basic_types.cs


using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;

var result = it[lastIndex];
it.RemoveAt(lastIndex);
return result;
}
public static int hashList<T>(this IList<T> it) {
unchecked {
var hashCode = 0;
if (it != null) {
foreach (var item in it) {
hashCode = (hashCode * 397) ^ item.GetHashCode();
}
}
return hashCode;
}
}
public static bool equalsList<T>(this IList<T> it, IList<T> list) {
if (it == null && list == null) {
return true;
}
if (it == null || list == null) {
return false;
}
if (it.Count != list.Count) {
return false;
}
for (int i = it.Count - 1; i >= 0; --i) {
if (!Equals(it[i], list[i])) {
return false;
}
}
return true;
}
public static string toStringList<T>(this IList<T> it) {
return "{ " + string.Join(", ", it.Select(item => item.ToString())) + " }";
}
}
}

9
Runtime/material/text_selection.cs


using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Canvas = Unity.UIWidgets.ui.Canvas;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
using Transform = Unity.UIWidgets.widgets.Transform;
// todo using material components: FlatButton & Material ...

switch (type) {
case TextSelectionHandleType.left: // points up-right
return new Transform(
transform: Matrix3.makeRotate(90),
transform: Matrix3.makeRotate(Mathf.PI / 2),
child: handle
);
case TextSelectionHandleType.right: // points up-left

transform: Matrix3.makeRotate(45),
transform: Matrix3.makeRotate(Mathf.PI / 4),
child: handle
);
}

2
Runtime/painting/box_decoration.cs


}
if (this._decoration.gradient != null) {
//paint.shader = this._decoration.gradient.createShader(rect);
paint.shader = this._decoration.gradient.createShader(rect);
this._rectForCachedBackgroundPaint = rect;
}

508
Runtime/painting/gradient.cs


using System;
using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
class _ColorsAndStops {
public _ColorsAndStops(List<Color> colors, List<double> stops) {
this.colors = colors;
this.stops = stops;
}
public readonly List<Color> colors;
public readonly List<double> stops;
public static _ColorsAndStops _interpolateColorsAndStops(
List<Color> aColors, List<double> aStops, List<Color> bColors, List<double> bStops, double t) {
D.assert(aColors.Count == bColors.Count,
"Cannot interpolate between two gradients with a different number of colors.");
D.assert((aStops == null && aColors.Count == 2) || (aStops != null && aStops.Count == aColors.Count));
D.assert((bStops == null && bColors.Count == 2) || (bStops != null && bStops.Count == bColors.Count));
List<Color> interpolatedColors = new List<Color>();
for (int i = 0; i < aColors.Count; i += 1) {
interpolatedColors.Add(Color.lerp(aColors[i], bColors[i], t));
}
List<double> interpolatedStops = null;
if (aStops != null || bStops != null) {
aStops = aStops ?? new List<double> {0.0, 1.0};
bStops = bStops ?? new List<double> {0.0, 1.0};
D.assert(aStops.Count == bStops.Count);
interpolatedStops = new List<double>();
for (int i = 0; i < aStops.Count; i += 1) {
interpolatedStops.Add(MathUtils.lerpDouble(aStops[i], bStops[i], t).clamp(0.0, 1.0));
}
}
return new _ColorsAndStops(interpolatedColors, interpolatedStops);
}
}
public Gradient(
List<Color> colors = null,
List<double> stops = null
) {
D.assert(colors != null);
this.colors = colors;
this.stops = stops;
}
public readonly List<Color> colors;
public readonly List<double> stops;
protected List<double> _impliedStops() {
if (this.stops != null) {
return this.stops;
}
if (this.colors.Count == 2) {
return null;
}
D.assert(this.colors.Count >= 2, "colors list must have at least two colors");
double separation = 1.0 / (this.colors.Count - 1);
return Enumerable.Range(0, this.colors.Count).Select(i => i * separation).ToList();
}
public abstract PaintShader createShader(Rect rect);
protected virtual Gradient lerpFrom(Gradient a, double t) {
if (a == null) {
return this.scale(t);
}
return null;
}
protected virtual Gradient lerpTo(Gradient b, double t) {
if (b == null) {
return this.scale(1.0 - t);
}
return null;
}
return null;
Gradient result = null;
if (b != null) {
result = b.lerpFrom(a, t); // if a is null, this must return non-null
}
if (result == null && a != null) {
result = a.lerpTo(b, t); // if b is null, this must return non-null
}
if (result != null) {
return result;
}
if (a == null && b == null) {
return null;
}
D.assert(a != null && b != null);
return t < 0.5 ? a.scale(1.0 - (t * 2.0)) : b.scale((t - 0.5) * 2.0);
}
}
public class LinearGradient : Gradient, IEquatable<LinearGradient> {
public LinearGradient(
Alignment begin = null,
Alignment end = null,
List<Color> colors = null,
List<double> stops = null,
TileMode tileMode = TileMode.clamp
) : base(colors: colors, stops: stops) {
this.begin = begin ?? Alignment.centerLeft;
this.end = end ?? Alignment.centerRight;
this.tileMode = tileMode;
}
public readonly Alignment begin;
public readonly Alignment end;
public readonly TileMode tileMode;
public override PaintShader createShader(Rect rect) {
return ui.Gradient.linear(
this.begin.withinRect(rect),
this.end.withinRect(rect),
this.colors, this._impliedStops(),
this.tileMode
);
}
public override Gradient scale(double factor) {
return new LinearGradient(
begin: this.begin,
end: this.end,
colors: this.colors.Select(color => Color.lerp(null, color, factor)).ToList(),
stops: this.stops,
tileMode: this.tileMode
);
}
protected override Gradient lerpFrom(Gradient a, double t) {
if (a == null || (a is LinearGradient && a.colors.Count == this.colors.Count)) {
return LinearGradient.lerp((LinearGradient) a, this, t);
}
return base.lerpFrom(a, t);
}
protected override Gradient lerpTo(Gradient b, double t) {
if (b == null || (b is LinearGradient && b.colors.Count == this.colors.Count)) {
return LinearGradient.lerp(this, (LinearGradient) b, t);
}
return base.lerpTo(b, t);
}
public static LinearGradient lerp(LinearGradient a, LinearGradient b, double t) {
if (a == null && b == null) {
return null;
}
if (a == null) {
return (LinearGradient) b.scale(t);
}
if (b == null) {
return (LinearGradient) a.scale(1.0 - t);
}
_ColorsAndStops interpolated =
_ColorsAndStops._interpolateColorsAndStops(a.colors, a.stops, b.colors, b.stops, t);
return new LinearGradient(
begin: Alignment.lerp(a.begin, b.begin, t),
end: Alignment.lerp(a.end, b.end, t),
colors: interpolated.colors,
stops: interpolated.stops,
tileMode: t < 0.5 ? a.tileMode : b.tileMode
);
}
public bool Equals(LinearGradient other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return
this.colors.equalsList(other.colors) &&
this.stops.equalsList(other.stops) &&
Equals(this.begin, other.begin) &&
Equals(this.end, other.end) &&
this.tileMode == other.tileMode;
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
return this.Equals((LinearGradient) obj);
}
public override int GetHashCode() {
unchecked {
var hashCode = this.colors.hashList();
hashCode = (hashCode * 397) ^ this.stops.hashList();
hashCode = (hashCode * 397) ^ (this.begin != null ? this.begin.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (this.end != null ? this.end.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (int) this.tileMode;
return hashCode;
}
}
public static bool operator ==(LinearGradient left, LinearGradient right) {
return Equals(left, right);
}
public static bool operator !=(LinearGradient left, LinearGradient right) {
return !Equals(left, right);
}
public override String ToString() {
return $"{this.GetType()}({this.begin}, {this.end}," +
$"{this.colors.toStringList()}, {this.stops.toStringList()}, {this.tileMode})";
}
}
public class RadialGradient : Gradient, IEquatable<RadialGradient> {
public RadialGradient(
Alignment center = null,
double radius = 0.5,
List<Color> colors = null,
List<double> stops = null,
TileMode tileMode = TileMode.clamp
) : base(colors: colors, stops: stops) {
this.center = center ?? Alignment.center;
this.radius = radius;
this.tileMode = tileMode;
}
public readonly Alignment center;
public readonly double radius;
public readonly TileMode tileMode;
public override PaintShader createShader(Rect rect) {
return ui.Gradient.radial(
this.center.withinRect(rect),
this.radius * rect.shortestSide,
this.colors, this._impliedStops(),
this.tileMode
);
}
public override Gradient scale(double factor) {
return new RadialGradient(
center: this.center,
radius: this.radius,
colors: this.colors.Select(color => Color.lerp(null, color, factor)).ToList(),
stops: this.stops,
tileMode: this.tileMode
);
}
protected override Gradient lerpFrom(Gradient a, double t) {
if (a == null || (a is RadialGradient && a.colors.Count == this.colors.Count)) {
return RadialGradient.lerp((RadialGradient) a, this, t);
}
return base.lerpFrom(a, t);
}
protected override Gradient lerpTo(Gradient b, double t) {
if (b == null || (b is RadialGradient && b.colors.Count == this.colors.Count)) {
return RadialGradient.lerp(this, (RadialGradient) b, t);
}
return base.lerpTo(b, t);
}
public static RadialGradient lerp(RadialGradient a, RadialGradient b, double t) {
if (a == null && b == null) {
return null;
}
if (a == null) {
return (RadialGradient) b.scale(t);
}
if (b == null) {
return (RadialGradient) a.scale(1.0 - t);
}
_ColorsAndStops interpolated =
_ColorsAndStops._interpolateColorsAndStops(a.colors, a.stops, b.colors, b.stops, t);
return new RadialGradient(
center: Alignment.lerp(a.center, b.center, t),
radius: Math.Max(0.0, MathUtils.lerpDouble(a.radius, b.radius, t)),
colors: interpolated.colors,
stops: interpolated.stops,
tileMode: t < 0.5 ? a.tileMode : b.tileMode
);
}
public bool Equals(RadialGradient other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return
this.colors.equalsList(other.colors) &&
this.stops.equalsList(other.stops) &&
Equals(this.center, other.center) &&
Equals(this.radius, other.radius) &&
this.tileMode == other.tileMode;
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
return this.Equals((RadialGradient) obj);
}
public override int GetHashCode() {
unchecked {
var hashCode = this.colors.hashList();
hashCode = (hashCode * 397) ^ this.stops.hashList();
hashCode = (hashCode * 397) ^ (this.center != null ? this.center.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ this.radius.GetHashCode();
hashCode = (hashCode * 397) ^ (int) this.tileMode;
return hashCode;
}
}
public static bool operator ==(RadialGradient left, RadialGradient right) {
return Equals(left, right);
}
public static bool operator !=(RadialGradient left, RadialGradient right) {
return !Equals(left, right);
}
public override String ToString() {
return $"{this.GetType()}({this.center}, {this.radius}," +
$"{this.colors.toStringList()}, {this.stops.toStringList()}, {this.tileMode})";
}
}
public class SweepGradient : Gradient, IEquatable<SweepGradient> {
public SweepGradient(
Alignment center = null,
double startAngle = 0.0,
double endAngle = Math.PI * 2,
List<Color> colors = null,
List<double> stops = null,
TileMode tileMode = TileMode.clamp
) : base(colors: colors, stops: stops) {
this.center = center ?? Alignment.center;
this.startAngle = startAngle;
this.endAngle = endAngle;
this.tileMode = tileMode;
}
public readonly Alignment center;
public readonly double startAngle;
public readonly double endAngle;
public readonly TileMode tileMode;
public override PaintShader createShader(Rect rect) {
return ui.Gradient.sweep(
this.center.withinRect(rect),
this.colors, this._impliedStops(),
this.tileMode,
this.startAngle, this.endAngle
);
}
public override Gradient scale(double factor) {
return new SweepGradient(
center: this.center,
startAngle: this.startAngle,
endAngle: this.endAngle,
colors: this.colors.Select(color => Color.lerp(null, color, factor)).ToList(),
stops: this.stops,
tileMode: this.tileMode
);
}
protected override Gradient lerpFrom(Gradient a, double t) {
if (a == null || (a is SweepGradient && a.colors.Count == this.colors.Count)) {
return SweepGradient.lerp((SweepGradient) a, this, t);
}
return base.lerpFrom(a, t);
}
protected override Gradient lerpTo(Gradient b, double t) {
if (b == null || (b is SweepGradient && b.colors.Count == this.colors.Count)) {
return SweepGradient.lerp(this, (SweepGradient) b, t);
}
return base.lerpTo(b, t);
}
public static SweepGradient lerp(SweepGradient a, SweepGradient b, double t) {
if (a == null && b == null) {
return null;
}
if (a == null) {
return (SweepGradient) b.scale(t);
}
if (b == null) {
return (SweepGradient) a.scale(1.0 - t);
}
_ColorsAndStops interpolated =
_ColorsAndStops._interpolateColorsAndStops(a.colors, a.stops, b.colors, b.stops, t);
return new SweepGradient(
center: Alignment.lerp(a.center, b.center, t),
startAngle: Math.Max(0.0, MathUtils.lerpDouble(a.startAngle, b.startAngle, t)),
endAngle: Math.Max(0.0, MathUtils.lerpDouble(a.endAngle, b.endAngle, t)),
colors: interpolated.colors,
stops: interpolated.stops,
tileMode: t < 0.5 ? a.tileMode : b.tileMode
);
}
public bool Equals(SweepGradient other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return
this.colors.equalsList(other.colors) &&
this.stops.equalsList(other.stops) &&
Equals(this.center, other.center) &&
Equals(this.startAngle, other.startAngle) &&
Equals(this.endAngle, other.endAngle) &&
this.tileMode == other.tileMode;
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
return this.Equals((SweepGradient) obj);
}
public override int GetHashCode() {
unchecked {
var hashCode = this.colors.hashList();
hashCode = (hashCode * 397) ^ this.stops.hashList();
hashCode = (hashCode * 397) ^ (this.center != null ? this.center.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ this.startAngle.GetHashCode();
hashCode = (hashCode * 397) ^ this.endAngle.GetHashCode();
hashCode = (hashCode * 397) ^ (int) this.tileMode;
return hashCode;
}
}
public static bool operator ==(SweepGradient left, SweepGradient right) {
return Equals(left, right);
}
public static bool operator !=(SweepGradient left, SweepGradient right) {
return !Equals(left, right);
}
public override String ToString() {
return $"{this.GetType()}({this.center}, {this.startAngle}, {this.endAngle}, " +
$"{this.colors.toStringList()}, {this.stops.toStringList()}, {this.tileMode})";
}
}
}

80
Runtime/painting/image_resolution.cs


public readonly string assetName;
public readonly AssetBundle bundle;
readonly Dictionary<ImageConfiguration, AssetBundleImageKey> _cache =
new Dictionary<ImageConfiguration, AssetBundleImageKey>();
AssetImageConfiguration assetConfig = new AssetImageConfiguration(configuration, this.assetName);
if (this._cache.TryGetValue(configuration, out key)) {
var cache = AssetBundleCache.instance.get(configuration.bundle);
if (cache.TryGetValue(assetConfig, out key)) {
return Promise<AssetBundleImageKey>.Resolved(key);
}

D.assert(result != null);
key = (AssetBundleImageKey) result;
this._cache[configuration] = key;
cache[assetConfig] = key;
return key;
});
}

public override string ToString() {
return $"{this.GetType()}(bundle: {this.bundle}, name: \"{this.assetName}\")";
}
}
public class AssetImageConfiguration: IEquatable<AssetImageConfiguration> {
public readonly ImageConfiguration configuration;
public readonly string assetName;
public AssetImageConfiguration(ImageConfiguration configuration, string assetName) {
this.configuration = configuration;
this.assetName = assetName;
}
public bool Equals(AssetImageConfiguration other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return Equals(this.configuration, other.configuration) && string.Equals(this.assetName, other.assetName);
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
return this.Equals((AssetImageConfiguration) obj);
}
public override int GetHashCode() {
unchecked {
return ((this.configuration != null ? this.configuration.GetHashCode() : 0) * 397) ^ (this.assetName != null ? this.assetName.GetHashCode() : 0);
}
}
public static bool operator ==(AssetImageConfiguration left, AssetImageConfiguration right) {
return Equals(left, right);
}
public static bool operator !=(AssetImageConfiguration left, AssetImageConfiguration right) {
return !Equals(left, right);
}
}
public class AssetBundleCache {
static readonly AssetBundleCache _instance = new AssetBundleCache();
public static AssetBundleCache instance => _instance;
readonly Dictionary<int, Dictionary<AssetImageConfiguration, AssetBundleImageKey>> _bundleCaches =
new Dictionary<int, Dictionary<AssetImageConfiguration, AssetBundleImageKey>>();
public Dictionary<AssetImageConfiguration, AssetBundleImageKey> get(AssetBundle bundle) {
Dictionary<AssetImageConfiguration, AssetBundleImageKey> result;
int id = bundle == null ? 0 : bundle.GetInstanceID();
if (this._bundleCaches.TryGetValue(id, out result)) {
return result;
}
result = new Dictionary<AssetImageConfiguration, AssetBundleImageKey>();
this._bundleCaches[id] = result;
return result;
}
}
}

20
Runtime/painting/matrix_utils.cs


using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using UnityEngine;
namespace Unity.UIWidgets.painting {
public static class MatrixUtils {

return result;
}
public static Matrix4x4 toMatrix4x4(this Matrix3 matrix3) {
var matrix = Matrix4x4.identity;
matrix[0, 0] = matrix3[0]; // row 0
matrix[0, 1] = matrix3[1];
matrix[0, 3] = matrix3[2];
matrix[1, 0] = matrix3[3]; // row 1
matrix[1, 1] = matrix3[4];
matrix[1, 3] = matrix3[5];
matrix[3, 0] = matrix3[6]; // row 2
matrix[3, 1] = matrix3[7];
matrix[3, 3] = matrix3[8];
return matrix;
}
}
public class TransformProperty : DiagnosticsProperty<Matrix3> {

6
Runtime/painting/text_painter.cs


Paragraph _layoutTemplate;
Paragraph _paragraph;
bool _needsLayout = true;
int _maxLines;
int? _maxLines;
string _ellipsis;
double _lastMinWidth;
double _lastMaxWidth;

TextDirection textDirection = TextDirection.ltr,
double textScaleFactor = 1.0,
int maxLines = 0,
int? maxLines = null,
string ellipsis = "") {
this._text = text;
this._textAlign = textAlign;

}
}
public int maxLines {
public int? maxLines {
get { return this._maxLines; }
set {
if (this._maxLines == value) {

2
Runtime/painting/text_style.cs


}
public ParagraphStyle getParagraphStyle(TextAlign textAlign,
TextDirection textDirection, string ellipsis, int maxLines, double textScaleFactor = 1.0) {
TextDirection textDirection, string ellipsis, int? maxLines, double textScaleFactor = 1.0) {
return new ParagraphStyle(
textAlign, textDirection, this.fontWeight, this.fontStyle,
maxLines, (this.fontSize ?? _defaultFontSize) * textScaleFactor, this.fontFamily, this.height,

15
Runtime/rendering/editable.cs


TextPainter _textPainter;
Color _cursorColor;
bool _hasFocus;
int _maxLines;
int? _maxLines;
Color _selectionColor;
ViewportOffset _offset;
ValueNotifier<bool> _showCursor;

public RenderEditable(TextSpan text, TextDirection textDirection, ViewportOffset offset,
ValueNotifier<bool> showCursor,
TextAlign textAlign = TextAlign.left, double textScaleFactor = 1.0, Color cursorColor = null,
bool? hasFocus = null, int maxLines = 1, Color selectionColor = null,
bool? hasFocus = null, int? maxLines = 1, Color selectionColor = null,
TextSelection selection = null, bool obscureText = false, SelectionChangedHandler onSelectionChanged = null,
CaretChangedHandler onCaretChanged = null, bool ignorePointer = false) {
this._textPainter = new TextPainter(text: text, textAlign: textAlign, textDirection: textDirection,

this.onCaretChanged = onCaretChanged;
this.onSelectionChanged = onSelectionChanged;
D.assert(this._maxLines == null || this._maxLines > 0);
D.assert(this._showCursor != null);
D.assert(!this._showCursor.value || cursorColor != null);

}
}
public int maxLines {
public int? maxLines {
D.assert(value > 0);
D.assert(value == null || value > 0);
if (this._maxLines == value) {
return;
}

}
double _preferredHeight(double width) {
if (this.maxLines <= 0) {
return this.preferredLineHeight * this.maxLines;
if (this.maxLines != null) {
return this.preferredLineHeight * this.maxLines.Value;
}
if (double.IsInfinity(width)) {

base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<Color>("cursorColor", this.cursorColor));
properties.add(new DiagnosticsProperty<ValueNotifier<bool>>("showCursor", this.showCursor));
properties.add(new DiagnosticsProperty<int>("maxLines", this.maxLines));
properties.add(new DiagnosticsProperty<int?>("maxLines", this.maxLines));
properties.add(new DiagnosticsProperty<Color>("selectionColor", this.selectionColor));
properties.add(new DiagnosticsProperty<double>("textScaleFactor", this.textScaleFactor));
properties.add(new DiagnosticsProperty<TextSelection>("selection", this.selection));

6
Runtime/rendering/paragraph.cs


bool softWrap = true,
TextOverflow overflow = TextOverflow.clip,
double textScaleFactor = 1.0,
int maxLines = 0
int? maxLines = null
D.assert(maxLines == null || maxLines > 0);
this._softWrap = softWrap;
this._overflow = overflow;
this._textPainter = new TextPainter(

}
}
public int maxLines {
public int? maxLines {
D.assert(this.maxLines == null || this.maxLines > 0);
if (this._textPainter.maxLines == value) {
return;
}

13
Runtime/ui/geometry.cs


public static readonly Offset zero = new Offset(0.0, 0.0);
public static readonly Offset infinite = new Offset(double.PositiveInfinity, double.PositiveInfinity);
public Offset scale(double scaleX, double scaleY) {
return new Offset(this.dx * scaleX, this.dy * scaleY);
public Offset scale(double scaleX, double? scaleY = null) {
scaleY = scaleY ?? scaleX;
return new Offset(this.dx * scaleX, this.dy * scaleY.Value);
}
public Offset translate(double translateX, double translateY) {

public static readonly Rect zero = new Rect(0, 0, 0, 0);
public static readonly Rect one = new Rect(0, 0, 1, 1);
public static readonly Rect infinity = new Rect(double.NegativeInfinity, double.NegativeInfinity,
double.PositiveInfinity, double.PositiveInfinity);

return fromLTRB(
Math.Floor(this.left), Math.Floor(this.top),
Math.Ceiling(this.right), Math.Ceiling(this.bottom));
}
public Rect roundIn() {
return fromLTRB(
Math.Ceiling(this.left), Math.Ceiling(this.top),
Math.Floor(this.right), Math.Floor(this.bottom));
}
public static Rect lerp(Rect a, Rect b, double t) {

50
Runtime/ui/matrix.cs


return m;
}
public static Matrix3 makeRotate(float degree) {
public static Matrix3 makeRotate(float radians) {
m.setRotate(degree);
m.setRotate(radians);
public static Matrix3 makeRotate(float degree, float px, float py) {
public static Matrix3 makeRotate(float radians, float px, float py) {
m.setRotate(degree, px, py);
m.setRotate(radians, px, py);
return m;
}

return m;
}
public static Matrix3 makeSkew(float dx, float dy) {
var m = new Matrix3();
m.setSkew(dx, dy);
return m;
}

}
}
public void setRotate(float degrees, float px, float py) {
public void setRotate(float radians, float px, float py) {
sinV = ScalarUtils.ScalarSinCos(ScalarUtils.DegreesToRadians(degrees), out cosV);
sinV = ScalarUtils.ScalarSinCos(radians, out cosV);
public void setRotate(float degrees) {
public void setRotate(float radians) {
sinV = ScalarUtils.ScalarSinCos(ScalarUtils.DegreesToRadians(degrees), out cosV);
sinV = ScalarUtils.ScalarSinCos(radians, out cosV);
this.setSinCos(sinV, cosV);
}

}
}
public void preRotate(float degrees, float px, float py) {
public void preRotate(float radians, float px, float py) {
m.setRotate(degrees, px, py);
m.setRotate(radians, px, py);
public void preRotate(float degrees) {
public void preRotate(float radians) {
m.setRotate(degrees);
m.setRotate(radians);
this.preConcat(m);
}

this.postConcat(m);
}
public void postRotate(float degrees, float px, float py) {
public void postRotate(float radians, float px, float py) {
m.setRotate(degrees, px, py);
m.setRotate(radians, px, py);
public void postRotate(float degrees) {
public void postRotate(float radians) {
m.setRotate(degrees);
m.setRotate(radians);
this.postConcat(m);
}

}
else {
var points = new[] {
new Offset(src.left, src.top),
new Offset(src.right, src.top),
new Offset(src.right, src.bottom),
new Offset(src.left, src.bottom),
this.mapXY((float) src.left, (float) src.top),
this.mapXY((float) src.right, (float) src.top),
this.mapXY((float) src.right, (float) src.bottom),
this.mapXY((float) src.left, (float) src.bottom),
this.mapPoints(points);
var minX = points[0].dx;
var minY = points[0].dy;

TypeMask.kPerspective_Mask) |
kRectStaysRect_Mask;
readonly float[] fMat = new float[9];
internal readonly float[] fMat = new float[9];
int fTypeMask = 0;
Matrix3() {

33
Runtime/ui/painting/canvas.cs


this._saveCount++;
this._recorder.addDrawCmd(new DrawSaveLayer {
rect = rect,
paint = paint,
paint = new Paint(paint),
});
}

this._recorder.addDrawCmd(new DrawPath {
path = path,
paint = paint,
paint = new Paint(paint),
if (rect.size.isEmpty) {
return;
}
paint = paint,
paint = new Paint(paint),
});
}

this._recorder.addDrawCmd(new DrawPath {
path = path,
paint = paint,
paint = new Paint(paint),
});
}

this._recorder.addDrawCmd(new DrawPath {
path = path,
paint = paint,
paint = new Paint(paint),
});
}

this._recorder.addDrawCmd(new DrawPath {
path = path,
paint = paint,
paint = new Paint(paint),
});
}

this._recorder.addDrawCmd(new DrawPath {
path = path,
paint = paint,
paint = new Paint(paint),
});
}

this._recorder.addDrawCmd(new DrawPath {
path = path,
paint = paint,
paint = new Paint(paint),
});
}

paint = paint,
paint = new Paint(paint),
});
}

offset = offset,
paint = paint,
paint = new Paint(paint),
});
}

dst = dst,
paint = paint,
paint = new Paint(paint),
});
}

src = src,
dst = dst,
paint = paint,
paint = new Paint(paint),
});
}

center = center,
dst = dst,
paint = paint,
paint = new Paint(paint),
});
}

src = src,
center = center,
dst = dst,
paint = paint,
paint = new Paint(paint),
});
}

this._recorder.addDrawCmd(new DrawTextBlob {
textBlob = textBlob,
offset = offset,
paint = paint,
paint = new Paint(paint),
});
}

75
Runtime/ui/painting/canvas_clip.cs


uint _genId;
bool _isIntersectionOfRects;
Rect _bound;
Matrix3 _invMat;
public ClipElement(int saveCount, Path path, float[] xform, float devicePixelRatio) {
public ClipElement(int saveCount, Path path, Matrix3 matrix, float scale) {
var pathCache = path.flatten(xform, devicePixelRatio);
this.mesh = pathCache.getFillMesh(out this.convex);
var pathCache = path.flatten(scale);
this.mesh = pathCache.getFillMesh(out this.convex).transform(matrix);
if (this.convex && vertices.Count == 4 &&
if (this.convex && vertices.Count == 4 && matrix.rectStaysRect() &&
var minx = Mathf.Min(vertices[0].x, vertices[1].x, vertices[2].x, vertices[3].x);
var miny = Mathf.Min(vertices[0].y, vertices[1].y, vertices[2].y, vertices[3].y);
var maxx = Mathf.Max(vertices[0].x, vertices[1].x, vertices[2].x, vertices[3].x);
var maxy = Mathf.Max(vertices[0].y, vertices[1].y, vertices[2].y, vertices[3].y);
this.rect = Rect.fromLTRB(minx, miny, maxx, maxy);
this.rect = this.mesh.bounds;
}
else {
this.isRect = false;

return this._genId;
}
bool _convexContains(float x, float y) {
if (this.mesh.vertices.Count <= 2) {
return false;
}
for (var i = 0; i < this.mesh.vertices.Count; i++) {
var p0 = this.mesh.vertices[i];
var p1 = this.mesh.vertices[i == this.mesh.vertices.Count - 1 ? 0 : i + 1];
if (PathUtils.triarea2(p0.x, p0.y, p1.x, p1.y, x, y) < 0.0f) {
return false;
}
}
return true;
}
// bool _convexContains(float x, float y) {
// if (this.mesh.vertices.Count <= 2) {
// return false;
// }
//
// if (this.mesh.matrix != null) {
// if (this._invMat == null) {
// this._invMat = Matrix3.I();
// this.mesh.matrix.invert(this._invMat); // ignore if not invertible for now.
// }
//
// var offset = this._invMat.mapXY(x, y);
// x = (float) offset.dx;
// y = (float) offset.dy;
// }
//
// for (var i = 0; i < this.mesh.vertices.Count; i++) {
// var p0 = this.mesh.vertices[i];
// var p1 = this.mesh.vertices[i == this.mesh.vertices.Count - 1 ? 0 : i + 1];
//
// if (PathUtils.triarea2(p0.x, p0.y, p1.x, p1.y, x, y) < 0.0f) {
// return false;
// }
// }
//
// return true;
// }
public bool contains(Rect rect) {
if (this.isRect) {

if (this.convex) {
return this._convexContains((float) rect.left, (float) rect.top) &&
this._convexContains((float) rect.left, (float) rect.bottom) &&
this._convexContains((float) rect.right, (float) rect.top) &&
this._convexContains((float) rect.right, (float) rect.bottom);
}
// this seems to be inefficient. disable it for now.
// if (this.convex) {
// return this._convexContains((float) rect.left, (float) rect.top) &&
// this._convexContains((float) rect.left, (float) rect.bottom) &&
// this._convexContains((float) rect.right, (float) rect.top) &&
// this._convexContains((float) rect.right, (float) rect.bottom);
// }
return false;
}

}
}
public void clipPath(Path path, float[] xform, float devicePixelRatio) {
var element = new ClipElement(this._saveCount, path, xform, devicePixelRatio);
public void clipPath(Path path, Matrix3 matrix, float scale) {
var element = new ClipElement(this._saveCount, path, matrix, scale);
this._pushElement(element);
}

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

112
Runtime/ui/painting/painting.cs


using UnityEngine;
namespace Unity.UIWidgets.ui {
class PaintingUtils {
internal static bool _offsetIsValid(Offset offset) {
D.assert(offset != null, "Offset argument was null.");
D.assert(!offset.dx.isNaN() && !offset.dy.isNaN(), "Offset argument contained a NaN value.");
return true;
}
internal static bool _radiusIsValid(Radius radius) {
D.assert(radius != null, "Radius argument was null.");
D.assert(!radius.x.isNaN() && !radius.y.isNaN(), "Radius argument contained a NaN value.");
return true;
}
internal static Color _scaleAlpha(Color a, double factor) {
return a.withAlpha((a.alpha * factor).round().clamp(0, 255));
}
}
public class Color : IEquatable<Color> {
public Color(long value) {
this.value = value & 0xFFFFFFFF;

}
}
public enum TileMode {
// todo: implement repeated, mirror.
clamp,
repeated,
mirror
}
public abstract class PaintShader {
}
public class Gradient : PaintShader {
internal float[] invXform;
internal float[] extent;
internal float radius;
internal float feather;
internal Color innerColor;
internal Color outerColor;
public static Gradient linear(
Offset from, Offset to,
Color color0, Color color1, TileMode tileMode = TileMode.clamp) {
const float large = 1e5f;
var dir = to - from;
var dx = (float) dir.dx;
var dy = (float) dir.dy;
var d = (float) dir.distance;
if (d > 0.0001f) {
dx /= d;
dy /= d;
}
else {
dx = 0;
dy = 1;
}
var xform = new[] {dy, -dx, dx, dy, (float) from.dx - dx * large, (float) from.dy - dy * large};
var invXform = new float[6];
XformUtils.transformInverse(invXform, xform);
return new Gradient {
invXform = invXform,
extent = new[] {large, large + d * 0.5f},
radius = 0.0f,
feather = Mathf.Max(1.0f, d),
innerColor = color0,
outerColor = color1
};
}
public static Gradient radial(
Offset center, double radius0, double radius1,
Color color0, Color color1, TileMode tileMode = TileMode.clamp) {
float r = (float) (radius0 + radius1) * 0.5f;
float f = (float) (radius1 - radius0);
var xform = new[] {1, 0, 0, 1, (float) center.dx, (float) center.dy};
var invXform = new float[6];
XformUtils.transformInverse(invXform, xform);
return new Gradient {
invXform = invXform,
extent = new[] {r, r},
radius = r,
feather = Mathf.Max(1.0f, f),
innerColor = color0,
outerColor = color1
};
}
public static Gradient box(
Rect rect, double radius, double feather,
Color color0, Color color1, TileMode tileMode = TileMode.clamp) {
var ext0 = (float) rect.width * 0.5f;
var ext1 = (float) rect.height * 0.5f;
var xform = new[] {1, 0, 0, 1, (float) rect.left + ext0, (float) rect.top + ext1};
var invXform = new float[6];
XformUtils.transformInverse(invXform, xform);
return new Gradient {
invXform = invXform,
extent = new[] {ext0, ext1},
radius = (float) radius,
feather = Mathf.Max(1.0f, (float) feather),
innerColor = color0,
outerColor = color1
};
}
}
public class Paint {
static readonly Color _kColorDefault = new Color(0xFFFFFFFF);

public PaintShader shader = null;
public bool invertColors;
public bool invertColors = false;
public Paint() {
}

136
Runtime/ui/painting/path.cs


PathCache _cache;
internal PathCache flatten(float[] xform, float devicePixelRatio) {
if (this._cache != null && this._cache.canReuse(xform, devicePixelRatio)) {
internal PathCache flatten(float scale) {
if (this._cache != null && this._cache.canReuse(scale)) {
this._cache = new PathCache(xform, devicePixelRatio);
this._cache = new PathCache(scale);
var i = 0;
while (i < this._commands.Count) {

}
class PathCache {
readonly float[] _xform;
readonly float _devicePixelRatio;
readonly float _scale;
readonly float _distTol;
readonly float _tessTol;

StrokeJoin _lineJoin;
float _miterLimit;
public PathCache(float[] xform, float devicePixelRatio) {
D.assert(xform != null && xform.Length == 6);
this._xform = xform;
this._devicePixelRatio = devicePixelRatio;
this._distTol = 0.01f / devicePixelRatio;
this._tessTol = 0.25f / devicePixelRatio;
public PathCache(float scale) {
this._scale = scale;
this._distTol = 0.01f / scale;
this._tessTol = 0.25f / scale;
public bool canReuse(float[] xform, float devicePixelRatio) {
D.assert(xform != null && xform.Length == 6);
for (var i = 0; i < 6; ++i) {
if (this._xform[i] != xform[i]) {
return false;
}
}
if (this._devicePixelRatio != devicePixelRatio) {
public bool canReuse(float scale) {
if (this._scale != scale) {
return false;
}

}
public void addPoint(float x, float y, PointFlags flags) {
PathUtils.transformPoint(out x, out y, this._xform, x, y);
this._addPoint(new PathPoint {x = x, y = y, flags = flags});
}

y1 = pt.y;
}
PathUtils.transformPoint(out x2, out y2, this._xform, x2, y2);
PathUtils.transformPoint(out x3, out y3, this._xform, x3, y3);
PathUtils.transformPoint(out x4, out y4, this._xform, x4, y4);
var points = TessellationGenerator.tessellateBezier(x1, y1, x2, y2, x3, y3, x4, y4, this._tessTol);
points[points.Count - 1].flags = flags;
foreach (var point in points) {

D.assert(indices.Count == cindices);
var mesh = new MeshMesh(this._vertices, indices);
var mesh = new MeshMesh(null, this._vertices, indices);
this._fillMesh = mesh;
this._fillConvex = false;

D.assert(indices.Count == cindices);
this._strokeMesh = new MeshMesh(this._vertices, indices);
this._strokeMesh = new MeshMesh(null, this._vertices, indices);
this._strokeWidth = strokeWidth;
this._lineCap = lineCap;
this._lineJoin = lineJoin;

public readonly List<Vector3> vertices;
public readonly List<int> triangles;
public readonly List<Vector2> uv;
public readonly Rect bounds;
public readonly Matrix3 matrix;
public readonly Rect rawBounds;
Rect _bounds;
public Rect bounds {
get {
if (this._bounds == null) {
this._bounds = this.matrix != null ? this.matrix.mapRect(this.rawBounds) : this.rawBounds;
}
return this._bounds;
}
}
static readonly List<int> _boundsTriangles = new List<int>(6) {0, 2, 1, 1, 2, 3};
static readonly List<int> _boundsTriangles = new List<int>(6) {
0, 2, 1, 1, 2, 3
};
if (this._boundsMesh != null) {
return this._boundsMesh;
if (this._boundsMesh == null) {
this._boundsMesh = new MeshMesh(this.bounds);
this._boundsMesh = new MeshMesh(this.bounds);
return this._boundsMesh;
}
}

};
this.triangles = _boundsTriangles;
this.bounds = rect;
this.rawBounds = rect;
this._bounds = this.rawBounds;
public MeshMesh(List<Vector3> vertices, List<int> triangles, List<Vector2> uv = null) {
public MeshMesh(Matrix3 matrix, List<Vector3> vertices, List<int> triangles, List<Vector2> uv = null, Rect rawBounds = null) {
D.assert(vertices != null);
D.assert(vertices.Count >= 0);
D.assert(triangles != null);

this.matrix = matrix;
if (vertices.Count > 0) {
double minX = vertices[0].x;
double maxX = vertices[0].x;
double minY = vertices[0].y;
double maxY = vertices[0].y;
if (rawBounds == null) {
if (vertices.Count > 0) {
double minX = vertices[0].x;
double maxX = vertices[0].x;
double minY = vertices[0].y;
double maxY = vertices[0].y;
for (int i = 1; i < vertices.Count; i++) {
var vertex = vertices[i];
if (vertex.x < minX) {
minX = vertex.x;
}
for (int i = 1; i < vertices.Count; i++) {
var vertex = vertices[i];
if (vertex.x < minX) {
minX = vertex.x;
}
if (vertex.x > maxX) {
maxX = vertex.x;
}
if (vertex.x > maxX) {
maxX = vertex.x;
}
if (vertex.y < minY) {
minY = vertex.y;
}
if (vertex.y < minY) {
minY = vertex.y;
}
if (vertex.y > maxY) {
maxY = vertex.y;
if (vertex.y > maxY) {
maxY = vertex.y;
}
}
this.bounds = Rect.fromLTRB(minX, minY, maxX, maxY);
}
else {
this.bounds = Rect.zero;
rawBounds = Rect.fromLTRB(minX, minY, maxX, maxY);
} else {
rawBounds = Rect.zero;
}
this.rawBounds = rawBounds;
public MeshMesh transform(float[] xform) {
var transVertices = new List<Vector3>(this.vertices.Count);
foreach (var vertex in this.vertices) {
float x, y;
PathUtils.transformPoint(out x, out y, xform, vertex.x, vertex.y);
transVertices.Add(new Vector3(x, y));
}
return new MeshMesh(transVertices, this.triangles, this.uv);
public MeshMesh transform(Matrix3 matrix) {
return new MeshMesh(matrix, this.vertices, this.triangles, this.uv, this.rawBounds);
}
}

public Mesh getMesh() {
if (this._pool.Count > 0) {
var mesh = this._pool.Dequeue();
D.assert(mesh);
return mesh;
}
else {

}
public void returnMesh(Mesh mesh) {
D.assert(mesh != null);
D.assert(mesh.hideFlags == HideFlags.HideAndDontSave);
this._pool.Enqueue(mesh);
}

296
Runtime/ui/painting/picture.cs


using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.foundation;
using UnityEngine;
namespace Unity.UIWidgets.ui {
public class Picture {

public void addDrawCmd(DrawCmd drawCmd) {
this._drawCmds.Add(drawCmd);
if (drawCmd is DrawSave) {
this._states.Add(this._getState().copy());
}
else if (drawCmd is DrawSaveLayer) {
var drawSaveLayer = (DrawSaveLayer) drawCmd;
switch (drawCmd) {
case DrawSave _:
this._states.Add(this._getState().copy());
break;
case DrawSaveLayer cmd: {
this._states.Add(new CanvasState {
xform = Matrix3.I(),
scissor = cmd.rect.shift(-cmd.rect.topLeft),
saveLayer = true,
layerOffset = cmd.rect.topLeft,
paintBounds = Rect.zero,
});
break;
}
case DrawRestore _: {
var stateToRestore = this._getState();
this._states.RemoveAt(this._states.Count - 1);
var state = this._getState();
this._states.Add(new CanvasState {
xform = Matrix3.I(),
scissor = drawSaveLayer.rect.shift(-drawSaveLayer.rect.topLeft),
saveLayer = true,
layerOffset = drawSaveLayer.rect.topLeft,
paintBounds = Rect.zero,
});
}
else if (drawCmd is DrawRestore) {
var stateToRestore = this._getState();
this._states.RemoveAt(this._states.Count - 1);
var state = this._getState();
if (!stateToRestore.saveLayer) {
state.paintBounds = stateToRestore.paintBounds;
if (!stateToRestore.saveLayer) {
state.paintBounds = stateToRestore.paintBounds;
}
else {
var paintBounds = stateToRestore.paintBounds.shift(stateToRestore.layerOffset);
paintBounds = state.xform.mapRect(paintBounds);
this._addPaintBounds(paintBounds);
}
break;
else {
var paintBounds = stateToRestore.paintBounds.shift(stateToRestore.layerOffset);
paintBounds = state.xform.mapRect(paintBounds);
this._addPaintBounds(paintBounds);
case DrawTranslate cmd: {
var state = this._getState();
state.xform.preTranslate((float) cmd.dx, (float) cmd.dy);
break;
}
else if (drawCmd is DrawTranslate) {
var drawTranslate = (DrawTranslate) drawCmd;
var state = this._getState();
state.xform.preTranslate((float) drawTranslate.dx, (float) drawTranslate.dy);
}
else if (drawCmd is DrawScale) {
var drawScale = (DrawScale) drawCmd;
var state = this._getState();
state.xform.preScale((float) drawScale.sx, (float) (drawScale.sy ?? drawScale.sx));
}
else if (drawCmd is DrawRotate) {
var drawRotate = (DrawRotate) drawCmd;
var state = this._getState();
if (drawRotate.offset == null) {
state.xform.preRotate((float) drawRotate.radians * Mathf.PI);
case DrawScale cmd: {
var state = this._getState();
state.xform.preScale((float) cmd.sx, (float) (cmd.sy ?? cmd.sx));
break;
else {
state.xform.preRotate((float) drawRotate.radians * Mathf.PI,
(float) drawRotate.offset.dx,
(float) drawRotate.offset.dy);
case DrawRotate cmd: {
var state = this._getState();
if (cmd.offset == null) {
state.xform.preRotate((float) cmd.radians);
}
else {
state.xform.preRotate((float) cmd.radians,
(float) cmd.offset.dx,
(float) cmd.offset.dy);
}
break;
}
else if (drawCmd is DrawSkew) {
var drawSkew = (DrawSkew) drawCmd;
var state = this._getState();
state.xform.preSkew((float) drawSkew.sx, (float) drawSkew.sy);
}
else if (drawCmd is DrawConcat) {
var drawConcat = (DrawConcat) drawCmd;
var state = this._getState();
state.xform.preConcat(drawConcat.matrix);
}
else if (drawCmd is DrawResetMatrix) {
var state = this._getState();
state.xform.reset();
}
else if (drawCmd is DrawSetMatrix) {
var drawSetMatrix = (DrawSetMatrix) drawCmd;
var state = this._getState();
state.xform = new Matrix3(drawSetMatrix.matrix);
}
else if (drawCmd is DrawClipRect) {
var drawClipRect = (DrawClipRect) drawCmd;
var state = this._getState();
case DrawSkew cmd: {
var state = this._getState();
state.xform.preSkew((float) cmd.sx, (float) cmd.sy);
break;
}
case DrawConcat cmd: {
var state = this._getState();
state.xform.preConcat(cmd.matrix);
break;
}
case DrawResetMatrix _: {
var state = this._getState();
state.xform.reset();
break;
}
case DrawSetMatrix cmd: {
var state = this._getState();
state.xform = new Matrix3(cmd.matrix);
break;
}
case DrawClipRect cmd: {
var state = this._getState();
var rect = state.xform.mapRect(drawClipRect.rect);
state.scissor = state.scissor == null ? rect : state.scissor.intersect(rect);
}
else if (drawCmd is DrawClipRRect) {
var drawClipRRect = (DrawClipRRect) drawCmd;
var state = this._getState();
var rect = state.xform.mapRect(cmd.rect);
state.scissor = state.scissor == null ? rect : state.scissor.intersect(rect);
break;
}
case DrawClipRRect cmd: {
var state = this._getState();
var rect = state.xform.mapRect(drawClipRRect.rrect.outerRect);
state.scissor = state.scissor == null ? rect : state.scissor.intersect(rect);
}
else if (drawCmd is DrawClipPath) {
var drawClipPath = (DrawClipPath) drawCmd;
var state = this._getState();
var rect = state.xform.mapRect(cmd.rrect.outerRect);
state.scissor = state.scissor == null ? rect : state.scissor.intersect(rect);
break;
}
case DrawClipPath cmd: {
var state = this._getState();
var scale = XformUtils.getScale(state.xform);
bool convex;
var rect = drawClipPath.path.flatten(
XformUtils.fromMatrix3(state.xform), (float) Window.instance.devicePixelRatio
).getFillMesh(out convex).bounds;
state.scissor = state.scissor == null ? rect : state.scissor.intersect(rect);
}
else if (drawCmd is DrawPath) {
var drawPath = (DrawPath) drawCmd;
var state = this._getState();
var xform = XformUtils.fromMatrix3(state.xform);
var path = drawPath.path;
var paint = drawPath.paint;
var devicePixelRatio = (float) Window.instance.devicePixelRatio;
var rect = cmd.path.flatten(
scale * (float) Window.instance.devicePixelRatio
).getFillMesh(out _).transform(state.xform).bounds;
state.scissor = state.scissor == null ? rect : state.scissor.intersect(rect);
break;
}
case DrawPath cmd: {
var state = this._getState();
var scale = XformUtils.getScale(state.xform);
var path = cmd.path;
var paint = cmd.paint;
var devicePixelRatio = (float) Window.instance.devicePixelRatio;
MeshMesh mesh;
if (paint.style == PaintingStyle.fill) {
var cache = path.flatten(xform, devicePixelRatio);
MeshMesh mesh;
if (paint.style == PaintingStyle.fill) {
var cache = path.flatten(scale * devicePixelRatio);
mesh = cache.getFillMesh(out _).transform(state.xform);
}
else {
float strokeWidth = ((float) paint.strokeWidth * scale).clamp(0, 200.0f);
float fringeWidth = 1 / devicePixelRatio;
bool convex;
mesh = cache.getFillMesh(out convex);
}
else {
float scale = XformUtils.getAverageScale(xform);
float strokeWidth = ((float) paint.strokeWidth * scale).clamp(0, 200.0f);
float fringeWidth = 1 / devicePixelRatio;
if (strokeWidth < fringeWidth) {
strokeWidth = fringeWidth;
}
if (strokeWidth < fringeWidth) {
strokeWidth = fringeWidth;
var cache = path.flatten(scale * devicePixelRatio);
mesh = cache.getStrokeMesh(
strokeWidth / scale * 0.5f,
paint.strokeCap,
paint.strokeJoin,
(float) paint.strokeMiterLimit).transform(state.xform);
var cache = path.flatten(xform, devicePixelRatio);
mesh = cache.getStrokeMesh(
strokeWidth * 0.5f,
paint.strokeCap,
paint.strokeJoin,
(float) paint.strokeMiterLimit);
this._addPaintBounds(mesh.bounds);
break;
this._addPaintBounds(mesh.bounds);
}
else if (drawCmd is DrawImage) {
var drawImage = (DrawImage) drawCmd;
var state = this._getState();
var rect = Rect.fromLTWH(drawImage.offset.dx, drawImage.offset.dy,
drawImage.image.width, drawImage.image.height);
rect = state.xform.mapRect(rect);
this._addPaintBounds(rect);
}
else if (drawCmd is DrawImageRect) {
var drawImageRect = (DrawImageRect) drawCmd;
var state = this._getState();
var rect = state.xform.mapRect(drawImageRect.dst);
this._addPaintBounds(rect);
}
else if (drawCmd is DrawImageNine) {
var drawImageNine = (DrawImageNine) drawCmd;
var state = this._getState();
var rect = state.xform.mapRect(drawImageNine.dst);
this._addPaintBounds(rect);
}
else if (drawCmd is DrawPicture) {
var drawPicture = (DrawPicture) drawCmd;
var state = this._getState();
var rect = state.xform.mapRect(drawPicture.picture.paintBounds);
this._addPaintBounds(rect);
}
else if (drawCmd is DrawTextBlob) {
var drawTextBlob = (DrawTextBlob) drawCmd;
var state = this._getState();
var rect = drawTextBlob.textBlob.boundsInText.shift(drawTextBlob.offset);
rect = state.xform.mapRect(rect);
this._addPaintBounds(rect);
}
else {
throw new Exception("unknown drawCmd: " + drawCmd);
case DrawImage cmd: {
var state = this._getState();
var rect = Rect.fromLTWH(cmd.offset.dx, cmd.offset.dy,
cmd.image.width, cmd.image.height);
rect = state.xform.mapRect(rect);
this._addPaintBounds(rect);
break;
}
case DrawImageRect cmd: {
var state = this._getState();
var rect = state.xform.mapRect(cmd.dst);
this._addPaintBounds(rect);
break;
}
case DrawImageNine cmd: {
var state = this._getState();
var rect = state.xform.mapRect(cmd.dst);
this._addPaintBounds(rect);
break;
}
case DrawPicture cmd: {
var state = this._getState();
var rect = state.xform.mapRect(cmd.picture.paintBounds);
this._addPaintBounds(rect);
break;
}
case DrawTextBlob cmd: {
var state = this._getState();
var rect = cmd.textBlob.boundsInText.shift(cmd.offset);
rect = state.xform.mapRect(rect);
this._addPaintBounds(rect);
break;
}
default:
throw new Exception("unknown drawCmd: " + drawCmd);
}
}

27
Runtime/ui/painting/txt/font_manager.cs


}
public class FontManager {
readonly List<FontInfo> _fonts = new List<FontInfo>();
readonly Dictionary<string, FontInfo> _fonts = new Dictionary<string, FontInfo>();
static readonly int defaultFontSize = 14;
public static readonly FontManager instance = new FontManager();

}
public bool addFont(Font font) {
var entry = this._fonts.Find(f => f.font == font);
if (entry != null) {
return false;
}
public void addFont(Font font) {
this._fonts.Add(fontInfo);
return true;
foreach (var fontName in font.fontNames) {
this._fonts[fontName] = fontInfo;
}
var founded = this._fonts.Find(info =>
info.font && info.font.fontNames.Contains(name));
if (founded != null) {
return founded;
if (this._fonts.TryGetValue(name, out var fontInfo)) {
return fontInfo;
}
var osFont = Font.CreateDynamicFontFromOSFont(name, defaultFontSize);

var newFont = new FontInfo(osFont);
this._fonts.Add(newFont);
foreach (var fontName in osFont.fontNames) {
this._fonts[fontName] = newFont;
}
var entry = this._fonts.Find(f => f.font == font);
var entry = this._fonts.Values.FirstOrDefault(f => f.font == font);
if (entry != null) {
entry.onTextureRebuilt();
}

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


namespace Unity.UIWidgets.ui {
class MeshKey : IEquatable<MeshKey> {
public readonly string text;
public readonly int fontId;
public readonly int textureVersion;
public readonly int fontSizeToLoad;
public readonly UnityEngine.FontStyle fontStyle;
public readonly long textBlobId;
public MeshKey(string text, int fontId, int textureVersion, int fontSizeToLoad,
UnityEngine.FontStyle fontStyle, float scale) {
this.text = text;
this.fontId = fontId;
this.textureVersion = textureVersion;
this.fontSizeToLoad = fontSizeToLoad;
this.fontStyle = fontStyle;
public MeshKey(long textBlobId, float scale) {
this.textBlobId = textBlobId;
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return string.Equals(this.text, other.text) && this.fontId == other.fontId &&
this.textureVersion == other.textureVersion && this.fontSizeToLoad == other.fontSizeToLoad &&
this.fontStyle == other.fontStyle && this.scale.Equals(other.scale);
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return this.textBlobId == other.textBlobId && this.scale.Equals(other.scale);
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
var hashCode = (this.text != null ? this.text.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ this.fontId;
hashCode = (hashCode * 397) ^ this.textureVersion;
hashCode = (hashCode * 397) ^ this.fontSizeToLoad;
hashCode = (hashCode * 397) ^ (int) this.fontStyle;
hashCode = (hashCode * 397) ^ this.scale.GetHashCode();
return hashCode;
return (this.textBlobId.GetHashCode() * 397) ^ this.scale.GetHashCode();
}
}

}
public override string ToString() {
return
$"MeshKey(text: {this.text}, " +
$"fontId: {this.fontId}, " +
$"textureVersion: {this.textureVersion}, " +
$"fontSizeToLoad: {this.fontSizeToLoad}, " +
$"fontStyle: {this.fontStyle}, " +
$"scale: {this.scale})";
return $"{nameof(this.textBlobId)}: {this.textBlobId}, {nameof(this.scale)}: {this.scale}";
public readonly long textureVersion;
public MeshInfo(MeshKey key, MeshMesh mesh, int timeToLive = 5) {
public MeshInfo(MeshKey key, MeshMesh mesh, long textureVersion, int timeToLive = 5) {
this.textureVersion = textureVersion;
this.touch(timeToLive);
}

public static MeshMesh generateMesh(TextBlob textBlob, float scale) {
var style = textBlob.style;
var fontInfo = FontManager.instance.getOrCreate(style.fontFamily);
var key = new MeshKey(textBlob.instanceId, scale);
_meshes.TryGetValue(key, out var meshInfo);
if (meshInfo != null && meshInfo.textureVersion == fontInfo.textureVersion) {
meshInfo.touch();
return meshInfo.mesh;
}
var length = textBlob.end - textBlob.start;
var length = textBlob.textSize;
var subText = textBlob.text.Substring(textBlob.start, textBlob.end - textBlob.start);
var subText = textBlob.text.Substring(textBlob.textOffset, textBlob.textSize);
var key = new MeshKey(subText, font.GetInstanceID(), fontInfo.textureVersion, fontSizeToLoad,
style.UnityFontStyle, scale);
_meshes.TryGetValue(key, out var meshInfo);
if (meshInfo != null) {
meshInfo.touch();
return meshInfo.mesh;
}
var ch = text[charIndex + textBlob.start];
var ch = text[charIndex + textBlob.textOffset];
var position = textBlob.positions[charIndex + textBlob.start];
if (Paragraph.isWordSpace(ch) || Paragraph.isLineEndSpace(ch) || ch == '\t') {
var position = textBlob.positions[charIndex];
if (LayoutUtils.isWordSpace(ch) || LayoutUtils.isLineEndSpace(ch) || ch == '\t') {
var minX = charInfo.minX / scale;
var maxX = charInfo.maxX / scale;
var minY = charInfo.minY / scale;

uv.Add(charInfo.uvBottomLeft);
}
MeshMesh mesh = new MeshMesh(vertices, triangles, uv);
_meshes[key] = new MeshInfo(key, mesh);
if (vertices.Count == 0) {
return null;
}
MeshMesh mesh = vertices.Count > 0 ? new MeshMesh(null, vertices, triangles, uv) : null;
_meshes[key] = new MeshInfo(key, mesh, fontInfo.textureVersion);
}
}

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


using Unity.UIWidgets.foundation;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
public TextBlob(string text, int start, int end, Vector2d[] positions, TextStyle style, Rect bounds) {
D.assert(start < end);
public TextBlob(string text, int textOffset, int textSize, Vector2d[] positions, Rect bounds, TextStyle style) {
this.instanceId = ++_nextInstanceId;
this.positions = positions;
this.start = start;
this.end = end;
this.positions = positions;
this.textOffset = textOffset;
this.textSize = textSize;
get { return this.bounds.shift(new Offset(this.positions[this.start].x, this.positions[this.start].y)); }
get { return this.bounds.shift(new Offset(this.positions[0].x, this.positions[0].y)); }
static long _nextInstanceId = 0;
public readonly long instanceId;
public readonly int start;
public readonly int end;
public readonly int textOffset;
public readonly int textSize;
}
public class TextBlobBuilder {
TextStyle _style;
public Vector2d[] positions;
string _text;
int _textOffset;
int _size;
Rect _bounds;
public 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;
}
}
}

4
Runtime/ui/text.cs


public readonly string fontFamily;
public readonly double? lineHeight;
public readonly string ellipsis;
public bool ellipsized() {
return !string.IsNullOrEmpty(this.ellipsis);
}
}
public enum TextDecorationStyle {

314
Runtime/ui/txt/linebreaker.cs


using System;
using System.Collections.Generic;
using System.Linq;
public 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) {
this._font.RequestCharactersInTexture(" ", this._fontSize);
CharacterInfo characterInfo;
this._font.GetCharacterInfo(' ', out characterInfo, this._fontSize);
this._tabWidth = characterInfo.advance * kTabSpaceCount;
}
if (this._tabWidth == 0) {
return widthSoFar;
}
return (float) (Math.Floor(widthSoFar / this._tabWidth + 1) * this._tabWidth);
}
}
public class Candidate {
public int offset;
public int pre;
public double preBreak;
public float penalty;
public double postBreak;
public int preSpaceCount;
public int postSpaceCount;
}
public class LineInfo {
public int start;
public double width;
}
const float ScoreInfty = float.MaxValue;
const float ScoreDesperate = 1e10f;
StyledRuns _runs;
string _textBuf;
int _textOffset;
int _textLength;
List<float> _charWidths = new List<float>();
List<int> _breaks = new List<int>();
List<float> _widths = new List<float>();
WordBreaker _wordBreaker = new WordBreaker();
double _width = 0.0;
double _preBreak;
double _lineWidth;
int _lastBreak;
int _bestBreak;
float _bestScore;
int _spaceCount;
TabStops _tabStops;
int mFirstTabIndex;
List<Candidate> _candidates = new List<Candidate>();
public Vector2d[] _characterPositions;
public double[] _characterWidth;
string _text;
double _width;
int _lineStart;
int _wordStart;
int _spaceCount = 0;
int tabCount = 4;
double _lineLength;
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, (float) (cand.postBreak - this._preBreak));
}
List<LineInfo> _lines;
return this._breaks.Count;
}
public void setup(string text, StyledRuns runs, double width, Vector2d[] characterPositions,
double[] characterWidth) {
this._text = text;
this._runs = runs;
this._characterPositions = characterPositions;
this._characterWidth = characterWidth;
this._width = width;
public List<int> getBreaks() {
return this._breaks;
public List<LineInfo> getLines() {
return this._lines;
public void resize(int size) {
if (this._charWidths.Count < size) {
this._charWidths.AddRange(Enumerable.Repeat(0.0f, size - this._charWidths.Count));
}
public void doBreak(int blockStart, int blockEnd) {
this._lines = new List<LineInfo>();
this._lineStart = blockStart;
this._wordStart = blockStart;
public void setText(string text, int textOffset, int textLength) {
this._textBuf = text;
this._textOffset = textOffset;
this._textLength = textLength;
this._wordBreaker.setText(this._textBuf, textOffset, textLength);
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;
}
double offsetX = 0.0;
var runIterator = this._runs.iterator();
for (var charIndex = blockStart; charIndex < blockEnd; charIndex++) {
runIterator.nextTo(charIndex);
var run = runIterator.run;
var font = FontManager.instance.getOrCreate(run.style.fontFamily).font;
public void setLineWidth(float lineWidth) {
this._lineWidth = lineWidth;
}
var style = run.style;
var charInfo = new CharacterInfo();
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 + this._textOffset, end - start, style,
this._charWidths, start, this._tabStops);
}
if (this._text[charIndex] == '\t') {
this._spaceCount++;
var font = FontManager.instance.getOrCreate(style.fontFamily).font;
int current = this._wordBreaker.current();
int afterWord = start;
int lastBreak = start;
font.GetCharacterInfo(' ', out charInfo,
style.UnityFontSize, style.UnityFontStyle);
double tabSize = charInfo.advance * this.tabCount;
var newX = Math.Floor(((offsetX / tabSize) + 1) * tabSize);
if (newX > this._width && this._lineStart != charIndex) {
this._characterWidth[charIndex] = tabSize;
this.makeLine(charIndex, charIndex);
}
else {
this._characterWidth[charIndex] = newX - offsetX;
this._characterPositions[charIndex].x = offsetX;
}
double lastBreakWidth = this._width;
double postBreak = this._width;
int postSpaceCount = this._spaceCount;
offsetX = this._characterPositions[charIndex].x + this._characterWidth[charIndex];
}
else if (this._text[charIndex] == ' ') {
font.GetCharacterInfo(this._text[charIndex], out charInfo, style.UnityFontSize,
run.style.UnityFontStyle);
this._spaceCount++;
this._characterPositions[charIndex].x = offsetX;
this._characterWidth[charIndex] = charInfo.advance;
offsetX = this._characterPositions[charIndex].x + this._characterWidth[charIndex];
// todo no wrap in space ?
for (int i = start; i < end; i++) {
char c = this._textBuf[i + this._textOffset];
if (c == '\t') {
this._width = this._preBreak + this._tabStops.nextTab((float) (this._width - this._preBreak));
if (this.mFirstTabIndex == int.MaxValue) {
this.mFirstTabIndex = i;
}
font.GetCharacterInfo(this._text[charIndex], out charInfo, style.UnityFontSize,
run.style.UnityFontStyle);
if (this._spaceCount > 0 || blockStart == charIndex) {
this._wordStart = charIndex;
if (LayoutUtils.isWordSpace(c)) {
this._spaceCount += 1;
this._characterPositions[charIndex].x = offsetX;
this._characterWidth[charIndex] = charInfo.advance;
this._width += this._charWidths[i];
if (!LayoutUtils.isLineEndSpace(c)) {
postBreak = this._width;
postSpaceCount = this._spaceCount;
afterWord = i + 1;
}
}
if (offsetX + charInfo.advance > this._width && this._lineStart != charIndex) {
if (this._lineStart == this._wordStart) {
this.makeLine(charIndex, charIndex);
this._wordStart = charIndex;
}
else {
this.makeLine(this._wordStart, charIndex);
}
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);
offsetX = this._characterPositions[charIndex].x + this._characterWidth[charIndex];
this._spaceCount = 0;
lastBreak = current;
lastBreakWidth = this._width;
current = this._wordBreaker.next();
this.makeLine(blockEnd, blockEnd);
return width;
}
public void finish() {
this._wordBreaker.finish();
this._width = 0;
this._candidates.Clear();
this._widths.Clear();
this._breaks.Clear();
this._textBuf = null;
public List<float> getWidths() {
return this._widths;
}
void makeLine(int end, int last) {
Debug.Assert(this._lineStart < end);
Debug.Assert(end <= last);
this._lines.Add(new LineInfo() {
start = this._lineStart,
width = this._characterPositions[end - 1].x + this._characterWidth[end - 1],
});
this._lineStart = end;
public void setTabStops(TabStops tabStops) {
this._tabStops = tabStops;
}
if (end >= this._characterPositions.Length) {
return;
void _addWordBreak(int offset, double preBreak, double postBreak, int preSpaceCount, int postSpaceCount,
float penalty) {
Candidate cand = new Candidate();
double 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) {
cand.offset = i;
cand.preBreak = width;
cand.postBreak = width;
cand.preSpaceCount = postSpaceCount;
cand.preSpaceCount = postSpaceCount;
cand.penalty = ScoreDesperate;
this._addCandidate(cand);
width += w;
}
}
var offset = new Vector2d(-this._characterPositions[end].x, 0);
this._characterPositions[end].x = 0;
if (end < last) {
Paragraph.offsetCharacters(offset, this._characterPositions, end + 1, last + 1);
cand.offset = offset;
cand.preBreak = preBreak;
cand.postBreak = postBreak;
cand.penalty = penalty;
cand.preSpaceCount = preSpaceCount;
cand.preSpaceCount = postSpaceCount;
this._addCandidate(cand);
}
void _addCandidate(Candidate cand) {
int candIndex = this._candidates.Count;
this._candidates.Add(cand);
if (cand.postBreak - this._preBreak > this._lineWidth) {
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, (float) (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);
}
}
}

721
Runtime/ui/txt/paragraph.cs


using System;
using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.foundation;
using UnityEngine;

}
public class CodeUnitRun {
public int lineNumber;
public TextDirection direction;
public IndexRange codeUnits;
public FontMetrics fontMetrics;
public readonly int lineNumber;
public readonly TextDirection direction;
public readonly Range<int> codeUnits;
public readonly FontMetrics fontMetrics;
public Range<double> xPos;
public readonly List<GlyphPosition> positions;
public CodeUnitRun(IndexRange cu, int line, FontMetrics fontMetrics, TextDirection direction) {
public CodeUnitRun(List<GlyphPosition> positions, Range<int> cu, int line, Range<double> xPos,
FontMetrics fontMetrics, TextDirection direction) {
this.xPos = xPos;
this.positions = positions;
}
public void Shift(double shift) {
this.xPos = RangeUtils.shift(this.xPos, shift);
for (int i = 0; i < this.positions.Count; ++i) {
this.positions[i] = this.positions[i].shift(shift);
}
public readonly double leading = 0.0;
public readonly double descent;
public readonly double? underlineThickness;
public readonly double? underlinePosition;

this.fxHeight = fxHeight;
}
public static FontMetrics fromFont(Font font, int fontSize, double? height) {
var ascent = font.ascent * (height ?? 1.0) * fontSize / font.fontSize;
var descent = (font.lineHeight - font.ascent) * (height ?? 1.0) * fontSize / font.fontSize;
public static FontMetrics fromFont(Font font, int fontSize) {
var ascent = -font.ascent * fontSize / font.fontSize;
var descent = (font.lineHeight - font.ascent) * fontSize / font.fontSize;
double? fxHeight = null;
font.RequestCharactersInTexture("x", fontSize);
CharacterInfo charInfo;

}
}
public struct IndexRange : IEquatable<IndexRange> {
public int start, end;
public class LineStyleRun {
public readonly int start;
public readonly int end;
public readonly TextStyle style;
public LineStyleRun(int start, int end, TextStyle style) {
this.start = start;
this.end = end;
this.style = style;
}
}
public class PositionWithAffinity {
public readonly int position;
public readonly TextAffinity affinity;
public PositionWithAffinity(int p, TextAffinity a) {
this.position = p;
this.affinity = a;
}
}
public class GlyphPosition {
public readonly Range<double> xPos;
public readonly Range<int> codeUnits;
public IndexRange(int s, int e) {
this.start = s;
this.end = e;
public GlyphPosition(double start, double advance, Range<int> codeUnits) {
this.xPos = new Range<double>(start, start + advance);
this.codeUnits = codeUnits;
int width() {
return this.end - this.start;
public GlyphPosition shift(double shift) {
return new GlyphPosition(this.xPos.start + shift, this.xPos.end - this.xPos.start, this.codeUnits);
}
void shift(int delta) {
this.start += delta;
this.end += delta;
public class Range<T> : IEquatable<Range<T>> {
public Range(T start, T end) {
this.start = start;
this.end = end;
public bool Equals(IndexRange other) {
return this.start == other.start && this.end == other.end;
public bool Equals(Range<T> other) {
return EqualityComparer<T>.Default.Equals(this.start, other.start) &&
EqualityComparer<T>.Default.Equals(this.end, other.end);
}
public override bool Equals(object obj) {

return obj is IndexRange && this.Equals((IndexRange) obj);
return obj is Range<T> && this.Equals((Range<T>) obj);
return (this.start * 397) ^ this.end;
return (EqualityComparer<T>.Default.GetHashCode(this.start) * 397) ^
EqualityComparer<T>.Default.GetHashCode(this.end);
public static bool operator ==(IndexRange left, IndexRange right) {
public static bool operator ==(Range<T> left, Range<T> right) {
public static bool operator !=(IndexRange left, IndexRange right) {
public static bool operator !=(Range<T> left, Range<T> right) {
public readonly T start, end;
public class PositionWithAffinity {
public readonly int position;
public readonly TextAffinity affinity;
public PositionWithAffinity(int p, TextAffinity a) {
this.position = p;
this.affinity = a;
public static class RangeUtils {
public static Range<double> shift(Range<double> value, double shift) {
return new Range<double>(value.start + shift, value.end + shift);
public class Paragraph {
struct Range<T> : IEquatable<Range<T>> {
public Range(T start, T end) {
this.start = start;
this.end = end;
}
public bool Equals(Range<T> other) {
return EqualityComparer<T>.Default.Equals(this.start, other.start) &&
EqualityComparer<T>.Default.Equals(this.end, other.end);
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
return obj is Range<T> && this.Equals((Range<T>) obj);
}
public override int GetHashCode() {
unchecked {
return (EqualityComparer<T>.Default.GetHashCode(this.start) * 397) ^
EqualityComparer<T>.Default.GetHashCode(this.end);
}
}
public static bool operator ==(Range<T> left, Range<T> right) {
return left.Equals(right);
}
public static bool operator !=(Range<T> left, Range<T> right) {
return !left.Equals(right);
}
public class GlyphLine {
public readonly List<GlyphPosition> positions;
public readonly int totalCountUnits;
public T start, end;
public GlyphLine(List<GlyphPosition> positions, int totalCountUnits) {
this.positions = positions;
this.totalCountUnits = totalCountUnits;
}
public class Paragraph {
public class LineRange {
public LineRange(int start, int end, int endExcludingWhitespace, int endIncludingNewLine, bool hardBreak) {
this.start = start;

public readonly int endIncludingNewLine;
public readonly bool hardBreak;
}
const int TabSpaceCount = 4;
bool _needsLayout = true;

List<LineRange> _lineRanges = new List<LineRange>();
List<double> _lineWidths = new List<double>();
List<double> _lineBaseLines = new List<double>();
Vector2d[] _characterPositions;
List<GlyphLine> _glyphLines = new List<GlyphLine>();
double _maxIntrinsicWidth;
double _minIntrinsicWidth;
double _alphabeticBaseline;

List<PaintRecord> _paintRecords = new List<PaintRecord>();
List<CodeUnitRun> _codeUnitRuns = new List<CodeUnitRun>();
bool _didExceedMaxLines;
TabStops _tabStops = new TabStops();
public const char CHAR_NBSP = '\u00A0';
public static bool isWordSpace(char ch) {
return ch == ' ' || ch == CHAR_NBSP;
}
// This function determines whether a character is a space that disappears at end of line.
// It is the Unicode set: [[:General_Category=Space_Separator:]-[:Line_Break=Glue:]],
// plus '\n'.
// Note: all such characters are in the BMP, so it's ok to use code units for this.
public static bool isLineEndSpace(char c) {
return c == '\n' || c == ' ' || c == 0x1680 || (0x2000 <= c && c <= 0x200A && c != 0x2007) ||
c == 0x205F || c == 0x3000;
}
public double height {
get { return this._lineHeights.Count == 0 ? 0 : this._lineHeights[this._lineHeights.Count - 1]; }
}

public void paint(Canvas canvas, Offset offset) {
foreach (var paintRecord in this._paintRecords) {
var paint = new Paint {
color = paintRecord.style.color,
filterMode = FilterMode.Bilinear,
color = paintRecord.style.color
canvas.drawTextBlob(paintRecord.text, offset, paint);
canvas.drawTextBlob(paintRecord.text, paintRecord.offset + offset, paint);
this.paintDecorations(canvas, paintRecord, offset);
}
}

return;
}
var textStyle = this._paragraphStyle.getTextStyle();
this._tabStops.setFont(FontManager.instance.getOrCreate(textStyle.fontFamily).font,
textStyle.UnityFontSize);
this.setup();
var maxLines = this._paragraphStyle.maxLines ?? 0;
this._didExceedMaxLines = !(maxLines == 0 || this._lineRanges.Count <= maxLines);
var lineLimits = maxLines == 0 ? this._lineRanges.Count : Math.Min(maxLines, this._lineRanges.Count);
this.layoutLines(lineLimits);
this._paintRecords.Clear();
this._lineHeights.Clear();
this._lineBaseLines.Clear();
this._codeUnitRuns.Clear();
this._glyphLines.Clear();
int styleMaxLines = this._paragraphStyle.maxLines ?? int.MaxValue;
var lineLimit = Math.Min(styleMaxLines, this._lineRanges.Count);
this._didExceedMaxLines = this._lineRanges.Count > styleMaxLines;
for (int lineNumber = 0; lineNumber < lineLimits; ++lineNumber) {
var line = this._lineRanges[lineNumber];
var words = this.findWords(line.start, line.end);
words.ForEach((word) => {
Debug.Assert(word.start < word.end);
double wordWidth = this._characterPositions[word.end - 1].x -
this._characterPositions[word.start].x + this._characterWidths[word.end - 1];
if (wordWidth > maxWordWidth) {
maxWordWidth = wordWidth;
Layout layout = new Layout();
layout.setTabStops(this._tabStops);
TextBlobBuilder builder = new TextBlobBuilder();
int styleRunIndex = 0;
double yOffset = 0;
double preMaxDescent = 0;
List<CodeUnitRun> lineCodeUnitRuns = new List<CodeUnitRun>();
List<GlyphPosition> glyphPositions = new List<GlyphPosition>();
for (int lineNumber = 0; lineNumber < lineLimit; ++lineNumber) {
var lineRange = this._lineRanges[lineNumber];
double wordGapWidth = 0;
// Break the line into words if justification should be applied.
int wordIndex = 0;
bool justifyLine = this._paragraphStyle.textAlign == TextAlign.justify &&
lineNumber != lineLimit - 1 &&
!lineRange.hardBreak;
var words = this.findWords(lineRange.start, lineRange.end);
if (justifyLine) {
if (words.Count > 1) {
wordGapWidth = (this._width - this._lineWidths[lineNumber]) / (words.Count - 1);
}
// Exclude trailing whitespace from right-justified lines so the last
// visible character in the line will be flush with the right margin.
int lineEndIndex = (this._paragraphStyle.textAlign == TextAlign.right ||
this._paragraphStyle.textAlign == TextAlign.center)
? lineRange.endExcludingWhitespace
: lineRange.end;
List<LineStyleRun> lineRuns = new List<LineStyleRun>();
while (styleRunIndex < this._runs.size) {
var styleRun = this._runs.getRun(styleRunIndex);
if (styleRun.start < lineEndIndex && styleRun.end > lineRange.start) {
lineRuns.Add(new LineStyleRun(Math.Max(styleRun.start, lineRange.start),
Math.Min(styleRun.end, lineEndIndex), styleRun.style));
}
if (styleRun.end >= lineEndIndex) {
break;
}
styleRunIndex++;
}
double runXOffset = 0;
double justifyXOffset = 0;
lineCodeUnitRuns.Clear();
List<GlyphPosition> lineGlyphPositions = new List<GlyphPosition>();
List<PaintRecord> paintRecords = new List<PaintRecord>();
for (int i = 0; i < lineRuns.Count; ++i) {
var run = lineRuns[i];
int textStart = Math.Max(run.start, lineRange.start);
int textEnd = Math.Min(run.end, lineEndIndex);
int textCount = textEnd - textStart;
layout.doLayout(runXOffset, this._text, textStart, textCount, run.style);
if (layout.nGlyphs() == 0) {
continue;
}
double wordStartPosition = double.NaN;
// var layoutAdvances = layout.getAdvances();
builder.allocRunPos(run.style, this._text, textStart, textCount);
builder.setBounds(layout.getBounds());
glyphPositions.Clear();
for (int glyphIndex = 0; glyphIndex < textCount; ++glyphIndex) {
double glyphXOffset = layout.getX(glyphIndex) + justifyXOffset;
builder.positions[glyphIndex] = new Vector2d(
glyphXOffset, layout.getY(glyphIndex)
);
float glyphAdvance = layout.getCharAdvance(glyphIndex);
glyphPositions.Add(new GlyphPosition(runXOffset + glyphXOffset, glyphAdvance,
new Range<int>(textStart + glyphIndex, textStart + glyphIndex + 1)));
if (wordIndex < words.Count && words[wordIndex].start == run.start + glyphIndex) {
wordStartPosition = runXOffset + glyphXOffset;
}
if (wordIndex < words.Count && words[wordIndex].end == run.start + glyphIndex + 1) {
if (justifyLine) {
justifyXOffset += wordGapWidth;
}
wordIndex++;
if (!double.IsNaN(wordStartPosition)) {
double wordWidth =
glyphPositions[glyphPositions.Count - 1].xPos.end - wordStartPosition;
maxWordWidth = Math.Max(wordWidth, maxWordWidth);
wordStartPosition = double.NaN;
}
}
}
if (glyphPositions.Count == 0) {
continue;
}
var font = FontManager.instance.getOrCreate(run.style.fontFamily).font;
var metrics = FontMetrics.fromFont(font, run.style.UnityFontSize);
paintRecords.Add(new PaintRecord(run.style, new Offset(runXOffset, 0),
builder.make(), metrics, lineNumber, layout.getAdvance()
));
lineGlyphPositions.AddRange(glyphPositions);
var codeUnitPositions = new List<GlyphPosition>(glyphPositions);
lineCodeUnitRuns.Add(new CodeUnitRun(codeUnitPositions, new Range<int>(run.start, run.end),
lineNumber,
new Range<double>(glyphPositions[0].xPos.start,
glyphPositions[glyphPositions.Count - 1].xPos.end),
metrics, TextDirection.ltr));
runXOffset += layout.getAdvance();
}
double lineXOffset = this.getLineXOffset(runXOffset);
if (lineXOffset != 0) {
foreach (var codeUnitRun in lineCodeUnitRuns) {
codeUnitRun.Shift(lineXOffset);
}
for (int i = 0; i < lineGlyphPositions.Count; ++i) {
lineGlyphPositions[i] = lineGlyphPositions[i].shift(lineXOffset);
}
}
int nextLineStart = (lineNumber < this._lineRanges.Count - 1)
? this._lineRanges[lineNumber + 1].start
: this._text.Length;
this._glyphLines.Add(new GlyphLine(lineGlyphPositions, nextLineStart - lineRange.start));
this._codeUnitRuns.AddRange(lineCodeUnitRuns);
double maxLineSpacing = 0;
double maxDescent = 0;
var updateLineMetrics = new Action<FontMetrics, TextStyle>((metrics, style) => {
double lineSpacing = (lineNumber == 0)
? -metrics.ascent * style.height
: (-metrics.ascent + metrics.leading) * (style.height);
if (lineSpacing > maxLineSpacing) {
maxLineSpacing = lineSpacing;
if (lineNumber == 0) {
this._alphabeticBaseline = lineSpacing;
this._ideographicBaseline =
(metrics.underlinePosition ?? 0.0 - metrics.ascent) * style.height;
}
}
double descent = metrics.descent * style.height;
maxDescent = Math.Max(descent, maxDescent);
if (this._paragraphStyle.TextAlign == TextAlign.justify && !this._lineRanges[lineNumber].hardBreak
&& lineNumber != lineLimits - 1) {
this.justifyLine(lineNumber, words);
foreach (var paintRecord in paintRecords) {
updateLineMetrics(paintRecord.metrics, paintRecord.style);
}
if (paintRecords.Count == 0) {
var defaultStyle = this._paragraphStyle.getTextStyle();
var defaultFont = FontManager.instance.getOrCreate(defaultStyle.fontFamily).font;
var metrics = FontMetrics.fromFont(defaultFont, defaultStyle.UnityFontSize);
updateLineMetrics(metrics, defaultStyle);
}
this._lineHeights.Add(
(this._lineHeights.Count == 0 ? 0 : this._lineHeights[this._lineHeights.Count - 1])
+ Math.Round(maxLineSpacing + maxDescent));
this._lineBaseLines.Add(this._lineHeights[this._lineHeights.Count - 1] - maxDescent);
yOffset += Math.Round(maxLineSpacing + preMaxDescent);
preMaxDescent = maxDescent;
foreach (var paintRecord in paintRecords) {
paintRecord.offset = new Offset(paintRecord.offset.dx + lineXOffset, yOffset);
this._paintRecords.Add(paintRecord);
else if (line.endExcludingWhitespace > line.start) {
Debug.Assert(!isLineEndSpace(this._text[line.endExcludingWhitespace - 1]));
var lineTotalAdvance = this._characterPositions[line.endExcludingWhitespace - 1].x +
this._characterWidths[line.endExcludingWhitespace - 1];
double xOffset = this.getLineXOffset(lineTotalAdvance);
if (xOffset > 0 || xOffset < 0) {
offsetCharacters(new Vector2d(xOffset, 0), this._characterPositions, line.start,
line.endExcludingWhitespace);
}
}
this._maxIntrinsicWidth = 0;
double lineBlockWidth = 0;
for (int i = 0; i < this._lineWidths.Count; ++i) {
lineBlockWidth += this._lineWidths[i];
if (this._lineRanges[i].hardBreak) {
this._maxIntrinsicWidth = Math.Max(lineBlockWidth, this._maxIntrinsicWidth);
lineBlockWidth = 0;
this.computeWidthMetrics(maxWordWidth);
this._maxIntrinsicWidth = Math.Max(lineBlockWidth, this._maxIntrinsicWidth);
if (this._paragraphStyle.maxLines == 1 || (this._paragraphStyle.maxLines == null &&
this._paragraphStyle.ellipsized())) {
this._minIntrinsicWidth = this.maxIntrinsicWidth;
}
else {
this._minIntrinsicWidth = Math.Min(maxWordWidth, this.maxIntrinsicWidth);
}
}

continue;
}
var baseLine = this._lineBaseLines[run.lineNumber];
double top = baseLine - run.fontMetrics.ascent;
double bottom = baseLine + run.fontMetrics.descent;
double top = (run.lineNumber == 0) ? 0 : this._lineHeights[run.lineNumber - 1];
double bottom = this._lineHeights[run.lineNumber];
double left, right;
if (run.codeUnits.start >= start && run.codeUnits.end <= end) {
left = run.xPos.start;
right = run.xPos.end;
}
else {
left = double.MaxValue;
right = double.MinValue;
foreach (var gp in run.positions) {
if (gp.codeUnits.start >= start && gp.codeUnits.end <= end) {
left = Math.Min(left, gp.xPos.start);
right = Math.Max(right, gp.xPos.end);
}
}
var from = Math.Max(start, run.codeUnits.start);
var to = Math.Min(end, run.codeUnits.end);
if (from < to) {
List<TextBox> boxs;
if (!lineBoxes.TryGetValue(run.lineNumber, out boxs)) {
boxs = new List<TextBox>();
lineBoxes.Add(run.lineNumber, boxs);
if (left == double.MaxValue || right == double.MinValue) {
continue;
}
double left = this._characterPositions[from].x;
double right = this._characterPositions[to - 1].x + this._characterWidths[to - 1];
boxs.Add(TextBox.fromLTBD(left, top, right, bottom, run.direction));
List<TextBox> boxs;
if (!lineBoxes.TryGetValue(run.lineNumber, out boxs)) {
boxs = new List<TextBox>();
lineBoxes.Add(run.lineNumber, boxs);
boxs.Add(TextBox.fromLTBD(left, top, right, bottom, run.direction));
}
for (int lineNumber = 0; lineNumber < this._lineRanges.Count; ++lineNumber) {

}
}
var line = this._lineRanges[yIndex];
if (line.start >= line.end) {
return new PositionWithAffinity(line.start, TextAffinity.downstream);
var lineGlyphPosition = this._glyphLines[yIndex].positions;
if (lineGlyphPosition.Count == 0) {
int lineStartIndex = this._glyphLines.Where((g, i) => i < yIndex).Sum((gl) => gl.totalCountUnits);
return new PositionWithAffinity(lineStartIndex, TextAffinity.downstream);
int index;
for (index = line.start; index < line.end; ++index) {
if (dx < this._characterPositions[index].x + this._characterWidths[index]) {
GlyphPosition gp = null;
for (int xIndex = 0; xIndex < lineGlyphPosition.Count; ++xIndex) {
double glyphEnd = xIndex < lineGlyphPosition.Count - 1
? lineGlyphPosition[xIndex + 1].xPos.start
: lineGlyphPosition[xIndex].xPos.end;
if (dx < glyphEnd) {
gp = lineGlyphPosition[xIndex];
if (index >= line.end) {
return new PositionWithAffinity(line.end, TextAffinity.upstream);
if (gp == null) {
GlyphPosition lastGlyph = lineGlyphPosition[lineGlyphPosition.Count - 1];
return new PositionWithAffinity(lastGlyph.codeUnits.end, TextAffinity.upstream);
var codeUnit = this._codeUnitRuns.Find((u) => u.codeUnits.start >= index && index < u.codeUnits.end);
if (codeUnit != null) {
direction = codeUnit.direction;
foreach (var run in this._codeUnitRuns) {
if (gp.codeUnits.start >= run.codeUnits.start && gp.codeUnits.end <= run.codeUnits.end) {
direction = run.direction;
break;
}
double glyphCenter = (this._characterPositions[index].x + this._characterPositions[index].x +
this._characterWidths[index]) / 2;
double glyphCenter = (gp.xPos.start + gp.xPos.end) / 2;
return new PositionWithAffinity(index, TextAffinity.downstream);
return new PositionWithAffinity(gp.codeUnits.start, TextAffinity.downstream);
return new PositionWithAffinity(index + 1, TextAffinity.upstream);
return new PositionWithAffinity(gp.codeUnits.end, TextAffinity.upstream);
}
}

return this._lineRanges[lineIndex];
}
public IndexRange getWordBoundary(int offset) {
public Range<int> getWordBoundary(int offset) {
public static void offsetCharacters(Vector2d offset, Vector2d[] characterPos, int start, int end) {
if (characterPos != null) {
for (int i = start; i < characterPos.Length && i < end; ++i) {
characterPos[i] = characterPos[i] + offset;
}
}
}
void computeWidthMetrics(double maxWordWidth) {
this._maxIntrinsicWidth = 0;
double lineBlockWidth = 0;
for (int i = 0; i < this._lineWidths.Count; ++i) {
var line = this._lineRanges[i];
lineBlockWidth += this._lineWidths[i];
if (line.hardBreak) {
this._maxIntrinsicWidth = Math.Max(lineBlockWidth, this._maxIntrinsicWidth);
lineBlockWidth = 0;
}
}
if (this._paragraphStyle.maxLines == 1 || (((this._paragraphStyle.maxLines ?? 0) == 0) &&
!string.IsNullOrEmpty(this._paragraphStyle.ellipsis))) {
this._minIntrinsicWidth = this._maxIntrinsicWidth;
}
else {
this._minIntrinsicWidth = Math.Min(maxWordWidth, this._maxIntrinsicWidth);
}
}
void setup() {
if (this._characterPositions == null || this._characterPositions.Length < this._text.Length) {
this._characterPositions = new Vector2d[this._text.Length];
}
this._lineHeights.Clear();
void computeLineBreak() {
this._lineBaseLines.Clear();
this._paintRecords.Clear();
this._codeUnitRuns.Clear();
this._characterWidths = new double[this._text.Length];
for (int i = 0; i < this._runs.size; ++i) {
var run = this._runs.getRun(i);
if (run.start < run.end) {
var font = FontManager.instance.getOrCreate(run.style.fontFamily).font;
font.RequestCharactersInTexture(this._text.Substring(run.start, run.end - run.start),
run.style.UnityFontSize,
run.style.UnityFontStyle);
}
}
}
void layoutLines(int lineLimits) {
double yOffset = 0;
var runIndex = 0;
double lastDescent = 0.0f;
var linePaintRecords = new List<PaintRecord>();
for (int lineNumber = 0; lineNumber < lineLimits; lineNumber++) {
var line = this._lineRanges[lineNumber];
double maxAscent = 0.0f;
double maxDescent = 0.0f;
linePaintRecords.Clear();
for (;;) {
var run = runIndex < this._runs.size ? this._runs.getRun(runIndex) : null;
if (run != null && run.start < run.end && run.start < line.end && run.end > line.start) {
var font = FontManager.instance.getOrCreate(run.style.fontFamily).font;
var metrics = FontMetrics.fromFont(font, run.style.UnityFontSize, run.style.height);
if (metrics.ascent > maxAscent) {
maxAscent = metrics.ascent;
}
if (metrics.descent > maxDescent) {
maxDescent = metrics.descent;
}
int start = Math.Max(run.start, line.start);
int end = Math.Min(run.end, line.end);
var width = this._characterPositions[end - 1].x + this._characterWidths[end - 1] -
this._characterPositions[start].x;
if (end > start) {
var bounds = Rect.fromLTWH(0, -metrics.ascent,
this._characterPositions[end - 1].x + this._characterWidths[end - 1] -
this._characterPositions[start].x,
metrics.ascent + metrics.descent);
linePaintRecords.Add(new PaintRecord(run.style,
new Offset(this._characterPositions[start].x, yOffset)
, new TextBlob(this._text, start, end, this._characterPositions, run.style, bounds),
metrics,
lineNumber, width));
this._codeUnitRuns.Add(new CodeUnitRun(
new IndexRange(start, end), lineNumber, metrics, TextDirection.ltr));
}
}
if (runIndex + 1 >= this._runs.size) {
break;
}
if (run.end < line.end) {
runIndex++;
}
else {
break;
}
}
if (lineNumber == 0) {
this._alphabeticBaseline = maxAscent;
this._ideographicBaseline = maxAscent; // todo Properly implement ideographic_baseline
}
yOffset = Utils.PixelCorrectRound(yOffset + maxAscent + lastDescent);
foreach (var record in linePaintRecords) {
record.offset = new Offset(record.offset.dx, yOffset);
}
this._maxIntrinsicWidth = 0;
this._paintRecords.AddRange(linePaintRecords);
for (var charIndex = line.start; charIndex < line.end; charIndex++) {
this._characterPositions[charIndex].y = yOffset;
}
this._lineHeights.Add(
(this._lineHeights.Count == 0 ? 0 : this._lineHeights[this._lineHeights.Count - 1]) +
Math.Round(maxAscent + maxDescent));
this._lineBaseLines.Add(yOffset);
lastDescent = maxDescent;
}
}
void computeLineBreak() {
var newLinePositions = new List<int>();
for (var i = 0; i < this._text.Length; i++) {
if (this._text[i] == '\n') {

newLinePositions.Add(this._text.Length);
var lineBreaker = new LineBreaker();
lineBreaker.setup(this._text, this._runs, this._width, this._characterPositions, this._characterWidths);
int runIndex = 0;
for (var newlineIndex = 0; newlineIndex < newLinePositions.Count; ++newlineIndex) {
var blockStart = newlineIndex > 0 ? newLinePositions[newlineIndex - 1] + 1 : 0;
var blockEnd = newLinePositions[newlineIndex];

continue;
}
lineBreaker.doBreak(blockStart, blockEnd);
var lines = lineBreaker.getLines();
for (int i = 0; i < lines.Count; ++i) {
var line = lines[i];
var end = i + 1 < lines.Count ? lines[i + 1].start : blockEnd;
lineBreaker.setLineWidth((float) this._width);
lineBreaker.resize(blockSize);
lineBreaker.setTabStops(this._tabStops);
lineBreaker.setText(this._text, blockStart, blockSize);
var nonSpaceEnd = end;
while (nonSpaceEnd > line.start && isLineEndSpace(this._text[nonSpaceEnd - 1])) {
nonSpaceEnd--;
while (runIndex < this._runs.size) {
var run = this._runs.getRun(runIndex);
if (run.start >= blockEnd) {
break;
this._lineRanges.Add(new LineRange(line.start, end, nonSpaceEnd,
end == blockEnd && end < this._text.Length ? end + 1 : end, end == blockEnd));
this._lineWidths.Add(line.width);
}
}
if (run.end < blockStart) {
runIndex++;
continue;
}
return;
}
int runStart = Math.Max(run.start, blockStart) - blockStart;
int runEnd = Math.Min(run.end, blockEnd) - blockStart;
lineBreaker.addStyleRun(run.style, runStart, runEnd);
double getLineXOffset(double lineTotalAdvance) {
if (double.IsInfinity(this._width)) {
return 0;
}
if (run.end > blockEnd) {
break;
}
var align = this._paragraphStyle.TextAlign;
if (align == TextAlign.right) {
return this._width - lineTotalAdvance;
}
else if (align == TextAlign.center) {
return Utils.PixelCorrectRound((this._width - lineTotalAdvance) / 2);
}
else {
return 0;
}
}
runIndex++;
}
void justifyLine(int lineNumber, List<Range<int>> words) {
if (words.Count <= 1) {
return;
}
int breaksCount = lineBreaker.computeBreaks();
List<int> breaks = lineBreaker.getBreaks();
List<float> widths = lineBreaker.getWidths();
for (int i = 0; i < breaksCount; ++i) {
var breakStart = (i > 0) ? breaks[i - 1] : 0;
var lineStart = breakStart + blockStart;
var lineEnd = breaks[i] + blockStart;
bool hardBreak = (i == breaksCount - 1);
var lineEndIncludingNewline =
(hardBreak && lineEnd < this._text.Length) ? lineEnd + 1 : lineEnd;
var lineEndExcludingWhitespace = lineEnd;
while (lineEndExcludingWhitespace > lineStart &&
LayoutUtils.isLineEndSpace(this._text[lineEndExcludingWhitespace - 1])) {
lineEndExcludingWhitespace--;
}
var line = this._lineRanges[lineNumber];
Debug.Assert(!isLineEndSpace(this._text[line.endExcludingWhitespace - 1]));
var lineTotalAdvance = this._characterPositions[line.endExcludingWhitespace - 1].x +
this._characterWidths[line.endExcludingWhitespace - 1];
double gapWidth = (this._width - lineTotalAdvance) / (words.Count - 1);
this._lineRanges.Add(new LineRange(lineStart, lineEnd,
lineEndExcludingWhitespace, lineEndIncludingNewline, hardBreak));
this._lineWidths.Add(widths[i]);
}
double justifyOffset = 0.0;
foreach (var word in words) {
offsetCharacters(new Vector2d(justifyOffset), this._characterPositions, word.start, word.end);
justifyOffset += gapWidth;
justifyOffset = Utils.PixelCorrectRound(justifyOffset);
lineBreaker.finish();
return;
}
List<Range<int>> findWords(int start, int end) {

for (int i = start; i < end; ++i) {
bool isSpace = isWordSpace(this._text[i]);
bool isSpace = LayoutUtils.isWordSpace(this._text[i]);
if (!inWord && !isSpace) {
wordStart = i;
inWord = true;

}
if (decoration != null && decoration.contains(TextDecoration.overline)) {
yOffset -= metrics.ascent;
yOffset += metrics.ascent;
canvas.drawLine(new Offset(x, y + yOffset), new Offset(x + width, y + yOffset), paint);
yOffset = yOffsetOriginal;
}

canvas.drawLine(new Offset(x, y + yOffset), new Offset(x + width, y + yOffset), paint);
yOffset = yOffsetOriginal;
}
}
}
double getLineXOffset(double lineTotalAdvance) {
if (double.IsInfinity(this._width)) {
return 0;
}
if (this._paragraphStyle.textAlign == TextAlign.right) {
return this._width - lineTotalAdvance;
}
else if (this._paragraphStyle.textAlign == TextAlign.center) {
return (this._width - lineTotalAdvance) / 2;
}
else {
return 0;
}
}

6
Runtime/ui/txt/word_separate.cs


this._text = text;
}
public IndexRange findWordRange(int index) {
public Range<int> findWordRange(int index) {
return new IndexRange(0, 0);
return new Range<int>(0, 0);
}
var t = this.classifyChar(index);

}
}
return new IndexRange(start, end + 1);
return new Range<int>(start, end + 1);
}

4
Runtime/widgets/basic.cs


softWrap: this.softWrap,
overflow: this.overflow,
textScaleFactor: this.textScaleFactor,
maxLines: this.maxLines ?? 0 // todo: maxLines should be nullable.
maxLines: this.maxLines
);
}

renderObject.softWrap = this.softWrap;
renderObject.overflow = this.overflow;
renderObject.textScaleFactor = this.textScaleFactor;
renderObject.maxLines = this.maxLines ?? 0; // todo: maxLines should be nullable.
renderObject.maxLines = this.maxLines;
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {

2
Runtime/widgets/routes.cs


}
}
abstract class PopupRoute : ModalRoute {
public abstract class PopupRoute : ModalRoute {
protected PopupRoute(
RouteSettings settings = null
) : base(settings: settings) {

105
Tests/Editor/CanvasAndLayers.cs


using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.editor;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using UnityEditor;

using Gradient = Unity.UIWidgets.ui.Gradient;
using Material = UnityEngine.Material;
using TextStyle = Unity.UIWidgets.ui.TextStyle;
namespace UIWidgets.Tests {
public class CanvasAndLayers : EditorWindow {

WindowAdapter _windowAdapter;
MeshPool _meshPool;
static Texture2D texture6;
CanvasAndLayers() {
this._options = new Action[] {
this.drawPloygon4,

this._windowAdapter = new EditorWindowAdapter(this);
this._windowAdapter.OnEnable();
this._meshPool = new MeshPool();
texture6 = Resources.Load<Texture2D>("6");
}
void OnDisable() {

var paint = new Paint {
color = new Color(0xFFFF0000),
shader = Gradient.linear(new Offset(80, 80), new Offset(180, 180), new List<Color>() {
Colors.red, Colors.black, Colors.green
}, null, TileMode.clamp)
};
// canvas.drawRect(

canvas.rotate(Math.PI * 15 / 180);
var path = new Path();
path.moveTo(10, 150);
path.lineTo(10, 160);

}
canvas.drawPath(path, paint);
canvas.translate(100, 100);
paint.shader = Gradient.radial(new Offset(80, 80), 100, new List<Color>() {
Colors.red, Colors.black, Colors.green
}, null, TileMode.clamp);
canvas.drawPath(path, paint);
canvas.translate(100, 100);
paint.shader = Gradient.sweep(new Offset(120, 100), new List<Color>() {
Colors.red, Colors.black, Colors.green, Colors.red,
}, null, TileMode.clamp, 10 * Math.PI / 180, 135 * Math.PI / 180);
canvas.drawPath(path, paint);
canvas.translate(100, 100);
//paint.maskFilter = MaskFilter.blur(BlurStyle.normal, 5);
paint.shader = new ImageShader(new Image(texture6, true), TileMode.mirror);
canvas.drawPath(path, paint);
canvas.flush();
}

color = new Color(0xFFFF0000),
style = PaintingStyle.stroke,
strokeWidth = 10,
shader = Gradient.linear(new Offset(10, 10), new Offset(180, 180), new List<Color>() {
Colors.red, Colors.green, Colors.yellow
}, null, TileMode.clamp)
};
canvas.drawLine(

new Offset(90, 10),
paint);
var widthPaint = new Paint {
color = new Color(0xFFFF0000),
style = PaintingStyle.stroke,
strokeWidth = 4,
};
paint.maskFilter = MaskFilter.blur(BlurStyle.normal, 1);
paint.strokeWidth = 4;
widthPaint);
paint);
canvas.scale(3);
TextBlob textBlob = new TextBlob("This is a text blob", 0, 19, 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),
}, Unity.UIWidgets.ui.Rect.fromLTWH(0 ,0, 200, 50), new TextStyle());
canvas.drawTextBlob(textBlob, new Offset(100, 100), paint);
canvas.drawLine(
new Offset(10, 30),
new Offset(10, 60),
new Paint() {style = PaintingStyle.stroke, strokeWidth = 0.1});
canvas.drawLine(
new Offset(20, 30),
new Offset(20, 60),
new Paint() {style = PaintingStyle.stroke, strokeWidth = 0.333});
canvas.flush();
}

canvas.flush();
}
void drawRectShadow() {
var canvas = new CommandBufferCanvas(this._renderTexture, (float) Window.instance.devicePixelRatio, this._meshPool);

canvas.clipRect(Unity.UIWidgets.ui.Rect.fromLTWH(25, 25, 300, 300));
canvas.rotate(-Math.PI/8.0);
canvas.drawRect(
Unity.UIWidgets.ui.Rect.fromLTWH(10, 10, 100, 100),

color = new Color(0xFFFFFF00),
maskFilter = MaskFilter.blur(BlurStyle.normal, 15),
maskFilter = MaskFilter.blur(BlurStyle.normal, 5),
style = PaintingStyle.stroke,
strokeWidth = 55,
shader = Gradient.linear(new Offset(10, 10), new Offset(180, 180), new List<Color>() {
Colors.red, Colors.green, Colors.yellow
}, null, TileMode.clamp)
};
canvas.drawRect(

canvas.drawImage(new Image(texture6, true),
new Offset(50, 150),
paint);
canvas.flush();
}

var canvas = new CommandBufferCanvas(this._renderTexture, (float) Window.instance.devicePixelRatio, this._meshPool);
var paint = new Paint {
color = new Color(0x7FFF0000),
// color = new Color(0x7FFF0000),
shader = Gradient.linear(new Offset(100, 100), new Offset(280, 280), new List<Color>() {
Colors.red, Colors.black, Colors.green
}, null, TileMode.clamp)
};
canvas.drawImageRect(this._stream.completer.currentImage.image,

8
Tests/Editor/EditableTextWiget.cs


cursorColor: Color.fromARGB(255, 0, 0, 0)
)
);
this.windowAdapter.attachRootWidget(this.root);
this.windowAdapter.attachRootWidget(new WidgetsApp(window: this.windowAdapter, home: this.root,
pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) =>
new PageRouteBuilder(
settings: settings,
pageBuilder: (BuildContext context, Unity.UIWidgets.animation.Animation<double> animation,
Unity.UIWidgets.animation.Animation<double> secondaryAnimation) => builder(context)
)));
this.titleContent = new GUIContent("EditableTextWidget");
}

11
Tests/Editor/Widgets.cs


void OnEnable() {
this.windowAdapter = new EditorWindowAdapter(this);
this.windowAdapter.OnEnable();
FontManager.instance.addFont(Resources.Load<Font>("MaterialIcons-Regular"));
}
void OnDisable() {

height: 200,
margin: EdgeInsets.all(30.0),
padding: EdgeInsets.all(15.0),
color: Color.fromARGB(255, 244, 190, 85),
child: image
child: image,
decoration: new BoxDecoration(
color: CLColors.white,
borderRadius: BorderRadius.all(30),
gradient: new LinearGradient(colors: new List<Color>{CLColors.blue, CLColors.red, CLColors.green})
)
);
return container;

178
Runtime/Resources/UIWidgets_canvas.cginc


float4 _viewport;
float _mat[9];
half4 _color;
fixed _alpha;
half4x4 _shaderMat;
sampler2D _shaderTex;
half4 _leftColor;
half4 _rightColor;
half _bias;
half _scale;
int _tileMode; // 0 = clamp, 1 = mirror, 2 = repeated
sampler2D _tex;
int _texMode; // 0 = post alpha, 1 = pre alpha, 2 = alpha only
half2 _mf_imgInc; // _mf stands for mask filter
int _mf_radius;
half _mf_kernel[25];
struct appdata_t {
float4 vertex : POSITION;
float2 tcoord : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
float2 ftcoord : TEXCOORD0;
float2 fpos : TEXCOORD1;
};
half shader_gradient_layout(half2 pos) {
half4 p4 = half4(pos, 0.0, 1.0);
#if defined(UIWIDGETS_LINEAR)
return mul(_shaderMat, p4).x;
#elif defined(UIWIDGETS_RADIAL)
return length(mul(_shaderMat, p4).xy);
#elif defined(UIWIDGETS_SWEEP)
half2 p2 = mul(_shaderMat, p4).xy;
half angle = atan2(-p2.y, -p2.x);
// 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
return (angle * 0.1591549430918 + 0.5 + _bias) * _scale;
#else
return 0;
#endif
}
half4 shader_gradient_colorize(half pt) {
if (_tileMode == 0) { // clamp
if (pt <= 0.0) {
return _leftColor;
} else if (pt >= 1.0) {
return _rightColor;
}
half2 coord = half2(pt, 0.5);
return tex2D(_shaderTex, coord);
} else if (_tileMode == 1) { // mirror
pt = pt - 1;
pt = pt - 2 * floor(pt * 0.5) - 1;
pt = abs(pt);
half2 coord = half2(pt, 0.5);
return tex2D(_shaderTex, coord);
} else if (_tileMode == 2) { // repeated
pt = frac(pt);
half2 coord = half2(pt, 0.5);
return tex2D(_shaderTex, coord);
}
return half4(0, 0, 0, 0);
}
half2 shader_image_layout(half2 pos) {
half4 p4 = half4(pos, 0.0, 1.0);
return mul(_shaderMat, p4).xy;
}
half4 shader_image_colorize(half2 pt) {
if (_tileMode == 0) { // clamp
pt.x = clamp(pt.x, 0.0, 1.0);
pt.y = clamp(pt.y, 0.0, 1.0);
} else if (_tileMode == 1) { // mirror
pt.x = pt.x - 1;
pt.x = pt.x - 2 * floor(pt.x * 0.5) - 1;
pt.x = abs(pt.x);
pt.y = pt.y - 1;
pt.y = pt.y - 2 * floor(pt.y * 0.5) - 1;
pt.y = abs(pt.y);
} else if (_tileMode == 2) { // repeated
pt.x = frac(pt.x);
pt.y = frac(pt.y);
}
return tex2D(_shaderTex, pt);
}
half4 prealpha(half4 color) {
return half4(color.x * color.w, color.y * color.w, color.z * color.w, color.w);
}
half4 shader_color (v2f i) {
half4 c;
#if defined(UIWIDGETS_COLOR)
c = _color;
#elif defined(UIWIDGETS_IMAGE)
half2 pt = shader_image_layout(i.fpos);
c = shader_image_colorize(pt);
#else
half pt = shader_gradient_layout(i.fpos);
c = shader_gradient_colorize(pt);
#endif
c.w *= _alpha;
return c;
}
v2f vert (appdata_t v) {
v2f o;
o.ftcoord = v.tcoord;
o.fpos = v.vertex;
float3x3 mat = float3x3(_mat[0], _mat[1], _mat[2], _mat[3], _mat[4], _mat[5], 0, 0, 1);
half2 p = mul(mat, half3(v.vertex.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;
}
fixed4 frag (v2f i) : SV_Target {
return prealpha(shader_color(i));
}
fixed4 frag_stencil (v2f i) : SV_Target {
return half4(0, 0, 0, 0);
}
fixed4 frag_tex (v2f i) : SV_Target {
half4 tintColor = shader_color(i);
half4 color = tex2D(_tex, i.ftcoord);
if (_texMode == 0) { // post alpha
color = color * tintColor;
color = prealpha(color);
} else if (_texMode == 1) { // pre alpha
color = color * tintColor;
color = half4(color.x * tintColor.w, color.y * tintColor.w, color.z * tintColor.w, color.w);
} else if (_texMode == 2) { // alpha only
color = half4(1, 1, 1, color.w) * tintColor;
color = prealpha(color);
}
return color;
}
fixed4 frag_mf (v2f i) : SV_Target {
half4 color = half4(0, 0, 0, 0);
float2 coord = i.ftcoord - _mf_radius * _mf_imgInc;
int width = _mf_radius * 2 + 1;
for (int i = 0; i < width; i++) {
color += tex2D(_tex, coord) * _mf_kernel[i];
coord += _mf_imgInc;
}
return color;
}

9
Runtime/Resources/UIWidgets_canvas.cginc.meta


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

64
Runtime/Resources/UIWidgets_canvas_convexFill.shader


Shader "UIWidgets/canvas_convexFill"
{
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 { // 0, color
CGPROGRAM
#define UIWIDGETS_COLOR
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass { // 1, linear
CGPROGRAM
#define UIWIDGETS_LINEAR
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass { // 2, radial
CGPROGRAM
#define UIWIDGETS_RADIAL
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass { // 3, sweep
CGPROGRAM
#define UIWIDGETS_SWEEP
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass { // 4, image
CGPROGRAM
#define UIWIDGETS_IMAGE
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}

9
Runtime/Resources/UIWidgets_canvas_convexfill.shader.meta


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

31
Runtime/Resources/UIWidgets_canvas_fill0.shader


Shader "UIWidgets/canvas_fill0"
{
Properties {
_StencilComp("_StencilComp", Float) = 3 // - Equal, 8 - Always
}
SubShader {
ZTest Always
ZWrite Off
Cull Off
ColorMask 0
Stencil {
Ref 128
CompFront [_StencilComp]
CompBack [_StencilComp]
ReadMask 128
WriteMask 127
PassFront IncrWrap
PassBack DecrWrap
}
Pass {
CGPROGRAM
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_stencil
ENDCG
}
}
}

9
Runtime/Resources/UIWidgets_canvas_fill0.shader.meta


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

66
Runtime/Resources/UIWidgets_canvas_fill1.shader


Shader "UIWidgets/canvas_fill1"
{
Properties {
_SrcBlend("_SrcBlend", Int) = 1 // One
_DstBlend("_DstBlend", Int) = 10 // OneMinusSrcAlpha
}
SubShader {
ZTest Always
ZWrite Off
Blend [_SrcBlend] [_DstBlend]
Stencil {
Ref 0
Comp NotEqual
ReadMask 127
WriteMask 127
Pass Zero
}
Pass { // 0, color
CGPROGRAM
#define UIWIDGETS_COLOR
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass { // 1, linear
CGPROGRAM
#define UIWIDGETS_LINEAR
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass { // 2, radial
CGPROGRAM
#define UIWIDGETS_RADIAL
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass { // 3, sweep
CGPROGRAM
#define UIWIDGETS_SWEEP
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass { // 4, image
CGPROGRAM
#define UIWIDGETS_IMAGE
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}

9
Runtime/Resources/UIWidgets_canvas_fill1.shader.meta


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

18
Runtime/Resources/UIWidgets_canvas_filter.shader


Shader "UIWidgets/canvas_filter"
{
Properties {
}
SubShader {
ZTest Always
ZWrite Off
Pass { // 0, mask filter
CGPROGRAM
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_mf
ENDCG
}
}
}

9
Runtime/Resources/UIWidgets_canvas_filter.shader.meta


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

56
Runtime/Resources/UIWidgets_canvas_stencil.shader


Shader "UIWidgets/canvas_stencil"
{
Properties {
}
SubShader {
ZTest Always
ZWrite Off
Pass { // 0, stencil clear
ColorMask 0
Stencil {
Ref 128
Pass Replace
}
CGPROGRAM
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_stencil
ENDCG
}
Pass { // 1, stencil intersect 0
Cull Off
ColorMask 0
Stencil {
WriteMask 127
PassFront IncrWrap
PassBack DecrWrap
}
CGPROGRAM
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_stencil
ENDCG
}
Pass { // 2, stencil intersect 1
ColorMask 0
Stencil {
Ref 128
Comp Less
Pass Replace
Fail Zero
}
CGPROGRAM
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_stencil
ENDCG
}
}
}

9
Runtime/Resources/UIWidgets_canvas_stencil.shader.meta


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

65
Runtime/Resources/UIWidgets_canvas_stroke0.shader


Shader "UIWidgets/canvas_stroke0"
{
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
ENDCG
}
Pass { // 1, linear
CGPROGRAM
#define UIWIDGETS_LINEAR
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass { // 2, radial
CGPROGRAM
#define UIWIDGETS_RADIAL
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass { // 3, sweep
CGPROGRAM
#define UIWIDGETS_SWEEP
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass { // 4, image
CGPROGRAM
#define UIWIDGETS_IMAGE
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}

9
Runtime/Resources/UIWidgets_canvas_stroke0.shader.meta


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

27
Runtime/Resources/UIWidgets_canvas_stroke1.shader


Shader "UIWidgets/canvas_stroke1"
{
Properties {
}
SubShader {
ZTest Always
ZWrite Off
ColorMask 0
Stencil {
Ref 0
Comp NotEqual
ReadMask 127
WriteMask 127
Pass Zero
}
Pass {
CGPROGRAM
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_stencil
ENDCG
}
}
}

9
Runtime/Resources/UIWidgets_canvas_stroke1.shader.meta


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

64
Runtime/Resources/UIWidgets_canvas_tex.shader


Shader "UIWidgets/canvas_tex"
{
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 { // 0, color
CGPROGRAM
#define UIWIDGETS_COLOR
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_tex
ENDCG
}
Pass { // 1, linear
CGPROGRAM
#define UIWIDGETS_LINEAR
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_tex
ENDCG
}
Pass { // 2, radial
CGPROGRAM
#define UIWIDGETS_RADIAL
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_tex
ENDCG
}
Pass { // 3, sweep
CGPROGRAM
#define UIWIDGETS_SWEEP
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_tex
ENDCG
}
Pass { // 4, image
CGPROGRAM
#define UIWIDGETS_IMAGE
#include "UIWidgets_canvas.cginc"
#pragma vertex vert
#pragma fragment frag_tex
ENDCG
}
}
}

9
Runtime/Resources/UIWidgets_canvas_tex.shader.meta


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

453
Runtime/ui/painting/canvas_shader.cs


using System;
using Unity.UIWidgets.painting;
using UnityEngine;
using UnityEngine.Rendering;
namespace Unity.UIWidgets.ui {
static class MaterialProps {
static readonly int _srcBlend = Shader.PropertyToID("_SrcBlend");
static readonly int _dstBlend = Shader.PropertyToID("_DstBlend");
static readonly int _stencilComp = Shader.PropertyToID("_StencilComp");
public static void set(Material mat, BlendMode op) {
if (op == BlendMode.srcOver) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.One);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
} else if (op == BlendMode.srcIn) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.DstAlpha);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
} else if (op == BlendMode.srcOut) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusDstAlpha);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
} else if (op == BlendMode.srcATop) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.DstAlpha);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
} else if (op == BlendMode.dstOver) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusDstAlpha);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.One);
} else if (op == BlendMode.dstIn) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.SrcAlpha);
} else if (op == BlendMode.dstOut) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
} else if (op == BlendMode.dstATop) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusDstAlpha);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.SrcAlpha);
} else if (op == BlendMode.plus) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.One);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.One);
} else if (op == BlendMode.src) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.One);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
} else if (op == BlendMode.dst) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.One);
} else if (op == BlendMode.xor) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusDstAlpha);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
} else if (op == BlendMode.clear) {
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
} else {
Debug.LogWarning("Not supported BlendMode: " + op + ". Defaults to srcOver");
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.One);
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
}
}
public static void set(Material mat, CompareFunction op) {
mat.SetFloat(_stencilComp, (int) op);
}
}
class MaterialByBlendMode {
public MaterialByBlendMode(Shader shader) {
this._shader = shader;
}
readonly Shader _shader;
readonly Material[] _materials = new Material[30];
public Material getMaterial(BlendMode op) {
var key = (int) op;
var mat = this._materials[key];
if (mat) {
return mat;
}
mat = new Material(this._shader) {hideFlags = HideFlags.HideAndDontSave};
MaterialProps.set(mat, op);
this._materials[key] = mat;
return mat;
}
}
class MaterialByStencilComp {
public MaterialByStencilComp(Shader shader) {
this._shader = shader;
}
readonly Shader _shader;
readonly Material[] _materials = new Material[2];
public Material getMaterial(bool ignoreClip) {
var key = ignoreClip ? 1 : 0;
var mat = this._materials[key];
if (mat) {
return mat;
}
mat = new Material(this._shader) {hideFlags = HideFlags.HideAndDontSave};
MaterialProps.set(mat, ignoreClip ? CompareFunction.Always : CompareFunction.Equal);
this._materials[key] = mat;
return mat;
}
}
class MaterialByBlendModeStencilComp {
public MaterialByBlendModeStencilComp(Shader shader) {
this._shader = shader;
}
readonly Shader _shader;
readonly Material[] _materials = new Material[30 * 2];
public Material getMaterial(BlendMode blend, bool ignoreClip) {
var key = (int) blend * 2 + (ignoreClip ? 1 : 0);
var mat = this._materials[key];
if (mat) {
return mat;
}
mat = new Material(this._shader) {hideFlags = HideFlags.HideAndDontSave};
MaterialProps.set(mat, blend);
MaterialProps.set(mat, ignoreClip ? CompareFunction.Always : CompareFunction.Equal);
this._materials[key] = mat;
return mat;
}
}
static class CanvasShader {
static readonly MaterialByBlendModeStencilComp _convexFillMat;
static readonly MaterialByStencilComp _fill0Mat;
static readonly MaterialByBlendMode _fill1Mat;
static readonly MaterialByBlendModeStencilComp _stroke0Mat;
static readonly Material _stroke1Mat;
static readonly MaterialByBlendModeStencilComp _texMat;
static readonly Material _stencilMat;
static readonly Material _filterMat;
static CanvasShader() {
var convexFillShader = Shader.Find("UIWidgets/canvas_convexFill");
if (convexFillShader == null) {
throw new Exception("UIWidgets/canvas_convexFill not found");
}
var fill0Shader = Shader.Find("UIWidgets/canvas_fill0");
if (fill0Shader == null) {
throw new Exception("UIWidgets/canvas_fill0 not found");
}
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");
}
_convexFillMat = new MaterialByBlendModeStencilComp(convexFillShader);
_fill0Mat = new MaterialByStencilComp(fill0Shader);
_fill1Mat = new MaterialByBlendMode(fill1Shader);
_stroke0Mat = new MaterialByBlendModeStencilComp(stroke0Shader);
_stroke1Mat = new Material(stroke1Shader) {hideFlags = HideFlags.HideAndDontSave};
_texMat = new MaterialByBlendModeStencilComp(texShader);
_stencilMat = new Material(stencilShader) {hideFlags = HideFlags.HideAndDontSave};
_filterMat = new Material(filterShader) {hideFlags = HideFlags.HideAndDontSave};
}
static Vector4 _colorToVector4(Color c) {
return new Vector4(
c.red / 255f,
c.green / 255f,
c.blue / 255f,
c.alpha / 255f
);
}
static void _getShaderPassAndProps(Vector4 viewport, Matrix3 ctm, Paint paint, float alpha,
out int pass, out MaterialPropertyBlock props) {
props = new MaterialPropertyBlock();
props.SetVector("_viewport", viewport);
props.SetFloat("_alpha", alpha);
switch (paint.shader) {
case null:
pass = 0;
props.SetVector("_color", _colorToVector4(paint.color));
return;
case _LinearGradient linear:
pass = 1;
props.SetMatrix("_shaderMat", linear.getGradientMat(ctm).toMatrix4x4());
props.SetTexture("_shaderTex", linear.gradientTex.texture);
props.SetVector("_leftColor", _colorToVector4(linear.leftColor));
props.SetVector("_rightColor", _colorToVector4(linear.rightColor));
props.SetInt("_tileMode", (int) linear.tileMode);
return;
case _RadialGradient radial:
pass = 2;
props.SetMatrix("_shaderMat", radial.getGradientMat(ctm).toMatrix4x4());
props.SetTexture("_shaderTex", radial.gradientTex.texture);
props.SetVector("_leftColor", _colorToVector4(radial.leftColor));
props.SetVector("_rightColor", _colorToVector4(radial.rightColor));
props.SetInt("_tileMode", (int) radial.tileMode);
return;
case _SweepGradient sweep:
pass = 3;
props.SetMatrix("_shaderMat", sweep.getGradientMat(ctm).toMatrix4x4());
props.SetTexture("_shaderTex", sweep.gradientTex.texture);
props.SetVector("_leftColor", _colorToVector4(sweep.leftColor));
props.SetVector("_rightColor", _colorToVector4(sweep.rightColor));
props.SetInt("_tileMode", (int) sweep.tileMode);
props.SetFloat("_bias", (float) sweep.bias);
props.SetFloat("_scale", (float) sweep.scale);
return;
case ImageShader image:
pass = 4;
props.SetMatrix("_shaderMat", image.getShaderMat(ctm).toMatrix4x4());
props.SetTexture("_shaderTex", image.image.texture);
props.SetInt("_tileMode", (int) image.tileMode);
return;
default:
throw new Exception("Unknown paint.shader: " + paint.shader);
}
}
public static CommandBufferCanvas.RenderDraw convexFill(CommandBufferCanvas.RenderLayer layer, Paint paint,
MeshMesh mesh) {
Vector4 viewport = layer.viewport;
Matrix3 ctm = layer.states[layer.states.Count - 1].matrix;
var mat = _convexFillMat.getMaterial(paint.blendMode, layer.ignoreClip);
_getShaderPassAndProps(viewport, ctm, paint, 1.0f, out var pass, out var props);
return new CommandBufferCanvas.RenderDraw {
mesh = mesh,
pass = pass,
material = mat,
properties = props,
};
}
public static CommandBufferCanvas.RenderDraw fill0(CommandBufferCanvas.RenderLayer layer, MeshMesh mesh) {
Vector4 viewport = layer.viewport;
var mat = _fill0Mat.getMaterial(layer.ignoreClip);
var pass = 0;
var props = new MaterialPropertyBlock();
props.SetVector("_viewport", viewport);
return new CommandBufferCanvas.RenderDraw {
mesh = mesh,
pass = pass,
material = mat,
properties = props,
};
}
public static CommandBufferCanvas.RenderDraw fill1(CommandBufferCanvas.RenderLayer layer, Paint paint,
MeshMesh mesh) {
Vector4 viewport = layer.viewport;
Matrix3 ctm = layer.states[layer.states.Count - 1].matrix;
var mat = _fill1Mat.getMaterial(paint.blendMode);
_getShaderPassAndProps(viewport, ctm, paint, 1.0f, out var pass, out var props);
return new CommandBufferCanvas.RenderDraw {
mesh = mesh.boundsMesh,
pass = pass,
material = mat,
properties = props,
};
}
public static CommandBufferCanvas.RenderDraw stroke0(CommandBufferCanvas.RenderLayer layer, Paint paint,
float alpha, MeshMesh mesh) {
Vector4 viewport = layer.viewport;
Matrix3 ctm = layer.states[layer.states.Count - 1].matrix;
var mat = _stroke0Mat.getMaterial(paint.blendMode, layer.ignoreClip);
_getShaderPassAndProps(viewport, ctm, paint, alpha, out var pass, out var props);
return new CommandBufferCanvas.RenderDraw {
mesh = mesh,
pass = pass,
material = mat,
properties = props,
};
}
public static CommandBufferCanvas.RenderDraw stroke1(CommandBufferCanvas.RenderLayer layer, MeshMesh mesh) {
Vector4 viewport = layer.viewport;
var mat = _stroke1Mat;
var pass = 0;
var props = new MaterialPropertyBlock();
props.SetVector("_viewport", viewport);
return new CommandBufferCanvas.RenderDraw {
mesh = mesh,
pass = pass,
material = mat,
properties = props,
};
}
public static CommandBufferCanvas.RenderDraw stencilClear(
CommandBufferCanvas.RenderLayer layer, MeshMesh mesh) {
Vector4 viewport = layer.viewport;
var mat = _stencilMat;
var pass = 0;
var props = new MaterialPropertyBlock();
props.SetVector("_viewport", viewport);
return new CommandBufferCanvas.RenderDraw {
mesh = mesh,
pass = pass,
material = mat,
properties = props,
};
}
public static CommandBufferCanvas.RenderDraw stencil0(CommandBufferCanvas.RenderLayer layer, MeshMesh mesh) {
Vector4 viewport = layer.viewport;
var mat = _stencilMat;
var pass = 1;
var props = new MaterialPropertyBlock();
props.SetVector("_viewport", viewport);
return new CommandBufferCanvas.RenderDraw {
mesh = mesh,
pass = pass,
material = mat,
properties = props,
};
}
public static CommandBufferCanvas.RenderDraw stencil1(CommandBufferCanvas.RenderLayer layer, MeshMesh mesh) {
Vector4 viewport = layer.viewport;
var mat = _stencilMat;
var pass = 2;
var props = new MaterialPropertyBlock();
props.SetVector("_viewport", viewport);
return new CommandBufferCanvas.RenderDraw {
mesh = mesh,
pass = pass,
material = mat,
properties = props,
};
}
public static CommandBufferCanvas.RenderDraw tex(CommandBufferCanvas.RenderLayer layer, Paint paint,
MeshMesh mesh, Image image) {
Vector4 viewport = layer.viewport;
Matrix3 ctm = layer.states[layer.states.Count - 1].matrix;
var mat = _texMat.getMaterial(paint.blendMode, layer.ignoreClip);
_getShaderPassAndProps(viewport, ctm, paint, 1.0f, out var pass, out var props);
props.SetTexture("_tex", image.texture);
props.SetInt("_texMode", image.texture is RenderTexture ? 1 : 0); // pre alpha if RT else post alpha
return new CommandBufferCanvas.RenderDraw {
mesh = mesh,
pass = pass,
material = mat,
properties = props,
image = image, // keep a reference to avoid GC.
};
}
public static CommandBufferCanvas.RenderDraw texRT(CommandBufferCanvas.RenderLayer layer, Paint paint,
MeshMesh mesh, CommandBufferCanvas.RenderLayer renderLayer) {
Vector4 viewport = layer.viewport;
Matrix3 ctm = layer.states[layer.states.Count - 1].matrix;
var mat = _texMat.getMaterial(paint.blendMode, layer.ignoreClip);
_getShaderPassAndProps(viewport, ctm, paint, 1.0f, out var pass, out var props);
props.SetInt("_texMode", 1); // pre alpha
return new CommandBufferCanvas.RenderDraw {
mesh = mesh,
pass = pass,
material = mat,
properties = props,
layer = renderLayer,
};
}
public static CommandBufferCanvas.RenderDraw texAlpha(CommandBufferCanvas.RenderLayer layer, Paint paint,
MeshMesh mesh, Texture tex) {
Vector4 viewport = layer.viewport;
Matrix3 ctm = layer.states[layer.states.Count - 1].matrix;
var mat = _texMat.getMaterial(paint.blendMode, layer.ignoreClip);
_getShaderPassAndProps(viewport, ctm, paint, 1.0f, out var pass, out var props);
props.SetTexture("_tex", tex);
props.SetInt("_texMode", 2); // alpha only
return new CommandBufferCanvas.RenderDraw {
mesh = mesh,
pass = pass,
material = mat,
properties = props,
};
}
public static CommandBufferCanvas.RenderDraw maskFilter(CommandBufferCanvas.RenderLayer layer, MeshMesh mesh,
CommandBufferCanvas.RenderLayer renderLayer, float radius, Vector2 imgInc, float[] kernel) {
Vector4 viewport = layer.viewport;
var mat = _filterMat;
var pass = 0;
var props = new MaterialPropertyBlock();
props.SetVector("_viewport", viewport);
props.SetFloat("_mf_radius", radius);
props.SetVector("_mf_imgInc", imgInc);
props.SetFloatArray("_mf_kernel", kernel);
return new CommandBufferCanvas.RenderDraw {
mesh = mesh,
pass = pass,
material = mat,
properties = props,
layer = renderLayer,
};
}
}
}

11
Runtime/ui/painting/canvas_shader.cs.meta


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

603
Runtime/ui/painting/shader.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using UnityEngine;
namespace Unity.UIWidgets.ui {
public enum TileMode : int {
clamp = 0,
mirror = 1,
repeated = 2,
}
public abstract class PaintShader {
}
public class Gradient : PaintShader {
public static Gradient linear(
Offset start, Offset end, List<Color> colors,
List<double> colorStops = null, TileMode tileMode = TileMode.clamp,
Matrix3 matrix = null) {
D.assert(PaintingUtils._offsetIsValid(start));
D.assert(PaintingUtils._offsetIsValid(end));
D.assert(colors != null && colors.Count >= 2);
_validateColorStops(ref colors, ref colorStops);
return new _LinearGradient(start, end, colors, colorStops, tileMode, matrix);
}
public static Gradient radial(
Offset center, double radius, List<Color> colors,
List<double> colorStops = null, TileMode tileMode = TileMode.clamp,
Matrix3 matrix = null) {
D.assert(PaintingUtils._offsetIsValid(center));
D.assert(colors != null && colors.Count >= 2);
_validateColorStops(ref colors, ref colorStops);
return new _RadialGradient(center, radius, colors, colorStops, tileMode, matrix);
}
public static Gradient sweep(
Offset center, List<Color> colors,
List<double> colorStops = null, TileMode tileMode = TileMode.clamp,
double startAngle = 0.0, double endAngle = Math.PI * 2,
Matrix3 matrix = null) {
D.assert(PaintingUtils._offsetIsValid(center));
D.assert(colors != null && colors.Count >= 2);
D.assert(startAngle < endAngle);
_validateColorStops(ref colors, ref colorStops);
return new _SweepGradient(center, colors, colorStops, tileMode, startAngle, endAngle, matrix);
}
static void _validateColorStops(ref List<Color> colors, ref List<double> colorStops) {
if (colorStops == null) {
colors = new List<Color>(colors);
colorStops = new List<double>(colors.Count);
colorStops.Add(0);
var stepCount = colors.Count - 1;
var step = 1.0 / stepCount;
for (int i = 1; i < stepCount; i++) {
colorStops.Add(colorStops[i - 1] + step);
}
colorStops.Add(1);
return;
}
if (colors.Count != colorStops.Count) {
throw new ArgumentException("\"colors\" and \"colorStops\" arguments must have equal length.");
}
var dummyFirst = colorStops[0] != 0;
var dummyLast = colorStops[colorStops.Count - 1] != 1;
var count = colors.Count + (dummyFirst ? 1 : 0) + (dummyFirst ? 1 : 0);
var newColors = new List<Color>(count);
if (dummyFirst) {
newColors.Add(colors[0]);
}
for (int i = 0; i < colors.Count; i++) {
newColors.Add(colors[i]);
}
if (dummyLast) {
newColors.Add(colors[colors.Count - 1]);
}
var newColorStops = new List<double>(count);
if (dummyFirst) {
newColorStops.Add(0.0);
}
var prevStop = 0.0;
for (int i = 0; i < colorStops.Count; i++) {
var stop = Math.Max(Math.Min(colorStops[i], 1.0), prevStop);
newColorStops.Add(stop);
prevStop = stop;
}
if (dummyLast) {
newColorStops.Add(1.0);
}
colors = newColors;
colorStops = newColorStops;
}
static readonly GradientBitmapCache _cache = new GradientBitmapCache();
internal static Image makeTexturedColorizer(List<Color> colors, List<double> positions) {
int count = colors.Count;
D.assert(count >= 2);
bool bottomHardStop = ScalarUtils.ScalarNearlyEqual((float) positions[0], (float) positions[1]);
bool topHardStop = ScalarUtils.ScalarNearlyEqual((float) positions[count - 2], (float) positions[count - 1]);
int offset = 0;
if (bottomHardStop) {
offset += 1;
count--;
}
if (topHardStop) {
count--;
}
if (offset != 0 || count != colors.Count) {
colors = colors.GetRange(offset, count);
positions = positions.GetRange(offset, count);
}
return _cache.getGradient(colors, positions);
}
}
class GradientBitmapCache : IDisposable {
public GradientBitmapCache(int maxEntries = 32, int resolution = 256) {
this.maxEntries = maxEntries;
this.resolution = resolution;
this._entryCount = 0;
this._head = this._tail = null;
D.assert(this.validate);
}
public readonly int maxEntries;
public readonly int resolution;
int _entryCount;
_Entry _head;
_Entry _tail;
public Image getGradient(List<Color> colors, List<double> positions) {
var key = new _Key(colors, positions);
if (!this.find(key, out var image)) {
image = this.fillGradient(colors, positions);
this.add(key, image);
}
return image;
}
public void Dispose() {
D.assert(this.validate);
// just remove the references, Image will dispose by themselves.
this._entryCount = 0;
this._head = this._tail = null;
}
_Entry release(_Entry entry) {
if (entry.prev != null) {
D.assert(this._head != entry);
entry.prev.next = entry.next;
} else {
D.assert(this._head == entry);
this._head = entry.next;
}
if (entry.next != null) {
D.assert(this._tail != entry);
entry.next.prev = entry.prev;
} else {
D.assert(this._tail == entry);
this._tail = entry.prev;
}
return entry;
}
void attachToHead(_Entry entry) {
entry.prev = null;
entry.next = this._head;
if (this._head != null) {
this._head.prev = entry;
} else {
this._tail = entry;
}
this._head = entry;
}
bool find(_Key key, out Image image) {
D.assert(this.validate);
var entry = this._head;
while (entry != null) {
if (entry.key == key) {
image = entry.image;
// move to the head of our list, so we purge it last
this.release(entry);
this.attachToHead(entry);
D.assert(this.validate);
return true;
}
entry = entry.next;
}
D.assert(this.validate);
image = null;
return false;
}
void add(_Key key, Image image) {
if (this._entryCount == this.maxEntries) {
D.assert(this._tail != null);
this.release(this._tail);
this._entryCount--;
}
var entry = new _Entry {key = key, image = image};
this.attachToHead(entry);
this._entryCount++;
}
Image fillGradient(List<Color> colors, List<double> positions) {
Texture2D tex = new Texture2D(this.resolution, 1, TextureFormat.RGBA32, false);
tex.hideFlags = HideFlags.HideAndDontSave;
tex.wrapMode = TextureWrapMode.Clamp;
var bytes = new byte[this.resolution * 4];
int count = colors.Count;
int prevIndex = 0;
for (int i = 1; i < count; i++) {
// Historically, stops have been mapped to [0, 256], with 256 then nudged to the next
// smaller value, then truncate for the texture index. This seems to produce the best
// results for some common distributions, so we preserve the behavior.
int nextIndex = (int) Mathf.Min((float) positions[i] * this.resolution, this.resolution - 1);
if (nextIndex > prevIndex) {
var c0 = colors[i - 1];
var c1 = colors[i];
var step = 1.0f / (nextIndex - prevIndex);
var t = 0.0;
for (int curIndex = prevIndex; curIndex <= nextIndex; ++curIndex) {
var c = Color.lerp(c0, c1, t);
var baseIndex = curIndex << 2;
bytes[baseIndex] = (byte) c.red;
bytes[baseIndex + 1] = (byte) c.green;
bytes[baseIndex + 2] = (byte) c.blue;
bytes[baseIndex + 3] = (byte) c.alpha;
t += step;
}
}
prevIndex = nextIndex;
}
D.assert(prevIndex == this.resolution - 1);
tex.LoadRawTextureData(bytes);
tex.Apply();
return new Image(tex);
}
bool validate() {
D.assert(this._entryCount >= 0 && this._entryCount <= this.maxEntries);
if (this._entryCount > 0) {
D.assert(null == this._head.prev);
D.assert(null == this._tail.next);
if (this._entryCount == 1) {
D.assert(this._head == this._tail);
} else {
D.assert(this._head != this._tail);
}
var entry = this._head;
int count = 0;
while (entry != null) {
count += 1;
entry = entry.next;
}
D.assert(count == this._entryCount);
entry = this._tail;
while (entry != null) {
count -= 1;
entry = entry.prev;
}
D.assert(0 == count);
} else {
D.assert(null == this._head);
D.assert(null == this._tail);
}
return true;
}
class _Entry {
public _Entry prev;
public _Entry next;
public _Key key;
public Image image;
}
class _Key : IEquatable<_Key> {
public _Key(List<Color> colors, List<double> positions) {
D.assert(colors != null);
D.assert(positions != null);
D.assert(colors.Count == positions.Count);
this.colors = colors;
this.positions = positions;
this._hashCode = _getHashCode(this.colors) ^ _getHashCode(this.positions);
;
}
public readonly List<Color> colors;
public readonly List<double> positions;
readonly int _hashCode;
public bool Equals(_Key other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return _listEquals(this.colors, other.colors) &&
_listEquals(this.positions, other.positions);
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
return this.Equals((_Key) obj);
}
public override int GetHashCode() {
return this._hashCode;
}
public static bool operator ==(_Key left, _Key right) {
return Equals(left, right);
}
public static bool operator !=(_Key left, _Key right) {
return !Equals(left, right);
}
static int _getHashCode<T>(List<T> list) {
unchecked {
var hashCode = 0;
foreach (var item in list) {
hashCode = (hashCode * 397) ^ item.GetHashCode();
}
return hashCode;
}
}
static bool _listEquals<T>(List<T> left, List<T> right) {
if (left.Count != right.Count) {
return false;
}
for (int i = 0; i < left.Count; i++) {
if (!left[i].Equals(right[i])) {
return false;
}
}
return true;
}
}
}
class _LinearGradient : Gradient {
public _LinearGradient(
Offset start, Offset end, List<Color> colors,
List<double> colorStops, TileMode tileMode,
Matrix3 matrix = null) {
this.start = start;
this.end = end;
this.colors = colors;
this.colorStops = colorStops;
this.tileMode = tileMode;
this.matrix = matrix;
this.ptsToUnit = ptsToUnitMatrix(start, end);
this.gradientTex = makeTexturedColorizer(colors, colorStops);
}
public readonly Offset start;
public readonly Offset end;
public readonly List<Color> colors;
public readonly List<double> colorStops;
public readonly TileMode tileMode;
public readonly Matrix3 matrix;
public readonly Matrix3 ptsToUnit;
public readonly Image gradientTex;
public Color leftColor {
get { return this.colors[0]; }
}
public Color rightColor {
get { return this.colors[this.colors.Count - 1]; }
}
public Matrix3 getGradientMat(Matrix3 ctm) {
var mat = Matrix3.I();
ctm.invert(mat); // just use I() if not invertible.
if (this.matrix != null) {
mat.postConcat(this.matrix);
}
mat.postConcat(this.ptsToUnit);
return mat;
}
static Matrix3 ptsToUnitMatrix(Offset start, Offset end) {
var vec = end - start;
var mag = vec.distance;
var inv = mag != 0 ? 1 / mag : 0;
vec = vec.scale(inv);
var matrix = Matrix3.I();
matrix.setSinCos((float) -vec.dy, (float) vec.dx, (float) start.dx, (float) start.dy);
matrix.postTranslate((float) -start.dx, (float) -start.dy);
matrix.postScale((float) inv, (float) inv);
return matrix;
}
}
class _RadialGradient : Gradient {
public _RadialGradient(
Offset center, double radius, List<Color> colors,
List<double> colorStops = null, TileMode tileMode = TileMode.clamp,
Matrix3 matrix = null
) {
this.center = center;
this.radius = radius;
this.colors = colors;
this.colorStops = colorStops;
this.tileMode = tileMode;
this.matrix = matrix;
this.ptsToUnit = radToUnitMatrix(center, radius);
this.gradientTex = makeTexturedColorizer(colors, colorStops);
}
public readonly Offset center;
public readonly double radius;
public readonly List<Color> colors;
public readonly List<double> colorStops;
public readonly TileMode tileMode;
public readonly Matrix3 matrix;
public readonly Matrix3 ptsToUnit;
public readonly Image gradientTex;
public Color leftColor {
get { return this.colors[0]; }
}
public Color rightColor {
get { return this.colors[this.colors.Count - 1]; }
}
public Matrix3 getGradientMat(Matrix3 ctm) {
var mat = Matrix3.I();
ctm.invert(mat); // just use I() if not invertible.
if (this.matrix != null) {
mat.postConcat(this.matrix);
}
mat.postConcat(this.ptsToUnit);
return mat;
}
static Matrix3 radToUnitMatrix(Offset center, double radius) {
var inv = radius != 0 ? 1 / radius : 0;
var matrix = Matrix3.I();
matrix.setTranslate((float) -center.dx, (float) -center.dy);
matrix.postScale((float) inv, (float) inv);
return matrix;
}
}
class _SweepGradient : Gradient {
public _SweepGradient(
Offset center, List<Color> colors,
List<double> colorStops = null, TileMode tileMode = TileMode.clamp,
double startAngle = 0.0, double endAngle = Math.PI * 2,
Matrix3 matrix = null
) {
this.center = center;
this.colors = colors;
this.colorStops = colorStops;
this.tileMode = tileMode;
this.startAngle = startAngle;
this.endAngle = endAngle;
this.matrix = matrix;
var t0 = startAngle / (Math.PI * 2f);
var t1 = endAngle / (Math.PI * 2f);
this.bias = -t0;
this.scale = 1f / (t1 - t0);
var ptsToUnit = Matrix3.I();
ptsToUnit.setTranslate((float) -center.dx, (float) -center.dy);
this.ptsToUnit = ptsToUnit;
this.gradientTex = makeTexturedColorizer(colors, colorStops);
}
public readonly Offset center;
public readonly List<Color> colors;
public readonly List<double> colorStops;
public readonly TileMode tileMode;
public readonly double startAngle;
public readonly double endAngle;
public readonly Matrix3 matrix;
public readonly Matrix3 ptsToUnit;
public readonly Image gradientTex;
public readonly double bias;
public readonly double scale;
public Color leftColor {
get { return this.colors[0]; }
}
public Color rightColor {
get { return this.colors[this.colors.Count - 1]; }
}
public Matrix3 getGradientMat(Matrix3 ctm) {
var mat = Matrix3.I();
ctm.invert(mat); // just use I() if not invertible.
if (this.matrix != null) {
mat.postConcat(this.matrix);
}
mat.postConcat(this.ptsToUnit);
return mat;
}
}
public class ImageShader : PaintShader {
public ImageShader(Image image,
TileMode tileMode = TileMode.clamp, Matrix3 matrix = null) {
this.image = image;
this.tileMode = tileMode;
this.matrix = matrix;
}
public readonly Image image;
public readonly TileMode tileMode;
public readonly Matrix3 matrix;
public Matrix3 getShaderMat(Matrix3 ctm) {
var mat = Matrix3.I();
ctm.invert(mat); // just use I() if not invertible.
if (this.matrix != null) {
mat.postConcat(this.matrix);
}
mat.postScale(1f / this.image.width, 1f / this.image.height);
return mat;
}
}
}

11
Runtime/ui/painting/shader.cs.meta


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

102
Runtime/ui/txt/layout.cs


using System.Collections.Generic;
using UnityEngine;
namespace Unity.UIWidgets.ui {
public class Layout {
int _start;
int _count;
List<float> _advances = new List<float>();
List<float> _positions = new List<float>();
float _advance;
Rect _bounds;
string _text;
TabStops _tabStops;
public static float measureText(double offset, string buf, int start, int count, TextStyle style,
List<float> advances, int advanceOffset, TabStops tabStops) {
Layout layout = new Layout();
layout.setTabStops(tabStops);
layout.doLayout(offset, buf, 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(double offset, string text, int start, int count, TextStyle style) {
this._text = text;
this._advances.Clear();
this._positions.Clear();
this._count = count;
var font = FontManager.instance.getOrCreate(style.fontFamily).font;
font.RequestCharactersInTexture(this._text.Substring(start, count),
style.UnityFontSize,
style.UnityFontStyle);
this._advance = 0;
this._bounds = null;
for (int i = 0; i < count; i++) {
int charIndex = start + i;
var ch = text[charIndex];
CharacterInfo characterInfo;
font.GetCharacterInfo(ch, out characterInfo, style.UnityFontSize, style.UnityFontStyle);
var rect = Rect.fromLTRB(characterInfo.minX, -characterInfo.maxY, characterInfo.maxX,
-characterInfo.minY);
rect = rect.translate(this._advance, 0);
if (this._bounds == null) {
this._bounds = rect;
}
else {
this._bounds = this._bounds.expandToInclude(rect);
}
this._positions.Add(this._advance);
float advance = characterInfo.advance;
if (ch == '\t') {
advance = this._tabStops.nextTab((float) (this._advance + offset)) - this._advance;
}
this._advances.Add(advance);
this._advance += advance;
}
}
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 this._bounds;
}
}
}

11
Runtime/ui/txt/layout.cs.meta


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

14
Runtime/ui/txt/layout_utils.cs


namespace Unity.UIWidgets.ui {
public static class LayoutUtils {
public const char CHAR_NBSP = '\u00A0';
public static bool isWordSpace(char ch) {
return ch == ' ' || ch == CHAR_NBSP;
}
public static bool isLineEndSpace(char c) {
return c == '\n' || c == ' ' || c == 0x1680 || (0x2000 <= c && c <= 0x200A && c != 0x2007) ||
c == 0x205F || c == 0x3000;
}
}
}

11
Runtime/ui/txt/layout_utils.cs.meta


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

147
Runtime/ui/txt/wordbreaker.cs


namespace Unity.UIWidgets.ui {
public class WordBreaker {
public const uint U16_SURROGATE_OFFSET = ((0xd800 << 10) + 0xdc00 - 0x10000);
string _text;
int _offset;
int _size;
int _current;
int _last;
int _scanOffset;
bool _inEmailOrUrl;
public int next() {
this._last = this._current;
this._detectEmailOrUrl();
if (this._inEmailOrUrl) {
this._current = this._findNextBreakInEmailOrUrl();
}
else {
this._current = this._findNextBreakNormal();
}
return this._current;
}
public void setText(string data, int offset, int size) {
this._text = data;
this._offset = offset;
this._size = size;
this._last = 0;
this._current = 0;
this._scanOffset = 0;
this._inEmailOrUrl = false;
}
public int current() {
return this._current;
}
public int wordStart() {
if (this._inEmailOrUrl) {
return this._last;
}
var result = this._last;
while (result < this._current) {
int ix = result;
uint c = nextCode(this._text, ref ix, this._current);
if (!LayoutUtils.isLineEndSpace((char) c)) {
break;
}
result = ix;
}
return result;
}
public int wordEnd() {
if (this._inEmailOrUrl) {
return this._last;
}
int result = this._current;
while (result > this._last) {
int ix = result;
uint ch = preCode(this._text, ref ix, this._last);
if (!LayoutUtils.isLineEndSpace((char) ch)) {
break;
}
result = ix;
}
return result;
}
public int breakBadness() {
return (this._inEmailOrUrl && this._current < this._scanOffset) ? 1 : 0;
}
public void finish() {
this._text = null;
}
int _findNextBreakInEmailOrUrl() {
return 0;
}
int _findNextBreakNormal() {
if (this._current == this._size) {
return -1;
}
this._current++;
for (; this._current < this._size; ++this._current) {
char c = this._text[this._current + this._offset];
if (LayoutUtils.isWordSpace(c) || c == '\t') {
return this._current;
}
}
return this._current;
}
void _detectEmailOrUrl() {
}
static uint nextCode(string text, ref int index, int end) {
uint ch = text[index++];
if (isLeadSurrogate(ch)) {
if (index < end && isTrailSurrogate(text[index])) {
char ch2 = text[index];
index++;
ch = getSupplementary(ch, ch2);
}
}
return ch;
}
static uint preCode(string text, ref int index, int start) {
uint ch = text[--index];
if (isTrailSurrogate(ch)) {
if (index > start && isLeadSurrogate(text[index - 1])) {
ch = getSupplementary(text[index - 1], ch);
--index;
}
}
return ch;
}
public static bool isLeadSurrogate(uint c) {
return ((c) & 0xfffffc00) == 0xd800;
}
public static bool isTrailSurrogate(uint c) {
return ((c) & 0xfffffc00) == 0xdc00;
}
public static uint getSupplementary(uint lead, uint trail) {
return (char) (((uint) (lead) << 10) + (uint) (trail - U16_SURROGATE_OFFSET));
}
}
}

11
Runtime/ui/txt/wordbreaker.cs.meta


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

8
Tests/Resources.meta


fileFormatVersion: 2
guid: a4bf4806dd96141b189111637bb1f450
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

3
Tests/Resources/6.png

之前 之后
宽度: 64  |  高度: 64  |  大小: 183 B

88
Tests/Resources/6.png.meta


fileFormatVersion: 2
guid: d8fa175306ac44f8ea120cf256fd21d9
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 7
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

3
Runtime/Resources/UIWidgets_canvas.shader.meta


fileFormatVersion: 2
guid: baf507ab371c4f4c9eb9ce70a004ba0d
timeCreated: 1543832597

259
Runtime/Resources/UIWidgets_canvas.shader


Shader "UIWidgets/canvas"
{
Properties {
_SrcBlend("_SrcBlend", Int) = 1 // One
_DstBlend("_DstBlend", Int) = 10 // OneMinusSrcAlpha
}
CGINCLUDE
float4 _viewport;
float4x4 _paintMat;
half4 _innerCol;
half4 _outerCol;
float2 _extent;
float _radius;
float _feather;
sampler2D _tex;
half2 _mf_imgInc; // _mf stands for mask filter
int _mf_radius;
half _mf_kernel[25];
struct appdata_t {
float4 vertex : POSITION;
float2 tcoord : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
float2 ftcoord : TEXCOORD0;
float2 fpos : TEXCOORD1;
};
float sdroundrect (float2 pt, float2 ext, float rad) {
float2 ext2 = ext - float2(rad, rad);
float2 d = abs(pt) - ext2;
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - rad;
}
#pragma vertex vert
v2f vert (appdata_t v) {
v2f o;
o.ftcoord = v.tcoord;
o.fpos = v.vertex;
float x = v.vertex.x - _viewport.x;
float y = v.vertex.y - _viewport.y;
#if UNITY_UV_STARTS_AT_TOP
o.vertex = float4(2.0 * x / _viewport.z - 1.0, 2.0 * y / _viewport.w - 1.0, 0, 1);
#else
o.vertex = float4(2.0 * x / _viewport.z - 1.0, 1.0 - 2.0 * y / _viewport.w, 0, 1);
#endif
return o;
}
fixed4 frag (v2f i) : SV_Target {
float2 pt = (mul(_paintMat, float3(i.fpos, 1.0))).xy;
float d = clamp((sdroundrect(pt, _extent, _radius) + _feather * 0.5) / _feather, 0.0, 1.0);
half4 color = lerp(_innerCol, _outerCol, d);
return color;
}
fixed4 frag_stencil (v2f i) : SV_Target {
return half4(0, 0, 0, 0);
}
fixed4 frag_tex (v2f i) : SV_Target {
half4 color = tex2D(_tex, i.ftcoord);
color = color * _innerCol; // tint color
color = half4(color.xyz * color.w, color.w);
return color;
}
fixed4 frag_texrt (v2f i) : SV_Target {
half4 color = tex2D(_tex, i.ftcoord);
color = color * _innerCol; // tint color
color = half4(color.xyz * _innerCol.w, color.w);
return color;
}
fixed4 frag_texfont (v2f i) : SV_Target {
half4 color = tex2D(_tex, i.ftcoord);
color = half4(1, 1, 1, color.w) * _innerCol; // tint color
color = half4(color.xyz * color.w, color.w);
return color;
}
fixed4 frag_mf (v2f i) : SV_Target {
half4 color = half4(0, 0, 0, 0);
float2 coord = i.ftcoord - _mf_radius * _mf_imgInc;
int width = _mf_radius * 2 + 1;
for (int i = 0; i < width; i++) {
color += tex2D(_tex, coord) * _mf_kernel[i];
coord += _mf_imgInc;
}
color = color * _innerCol; // tint color
color = half4(color.xyz * _innerCol.w, color.w);
return color;
}
ENDCG
SubShader {
ZTest Always
ZWrite Off
Blend [_SrcBlend] [_DstBlend]
Pass { // 0, fill pass 0
Cull Off
ColorMask 0
Stencil {
Ref 128
CompFront Equal
CompBack Equal
ReadMask 128
WriteMask 127
PassFront IncrWrap
PassBack DecrWrap
}
CGPROGRAM
#pragma fragment frag_stencil
ENDCG
}
Pass { // 1, fill pass 1
Stencil {
Ref 0
Comp NotEqual
ReadMask 127
WriteMask 127
Pass Zero
}
CGPROGRAM
#pragma fragment frag
ENDCG
}
Pass { // 2, convex fill
Stencil {
Ref 128
Comp Equal
}
CGPROGRAM
#pragma fragment frag
ENDCG
}
Pass { // 3, stroke pass 0
Stencil {
Ref 128
Comp Equal
Pass IncrSat
}
CGPROGRAM
#pragma fragment frag
ENDCG
}
Pass { // 4, stroke pass 1
ColorMask 0
Stencil {
Ref 0
Comp NotEqual
ReadMask 127
WriteMask 127
Pass Zero
}
CGPROGRAM
#pragma fragment frag_stencil
ENDCG
}
Pass { // 5, texture pass 0
Stencil {
Ref 128
Comp Equal
}
CGPROGRAM
#pragma fragment frag_tex
ENDCG
}
Pass { // 6, render texture pass 0
Stencil {
Ref 128
Comp Equal
}
CGPROGRAM
#pragma fragment frag_texrt
ENDCG
}
Pass { // 7, stencil clear
ColorMask 0
Stencil {
Ref 128
Pass Replace
}
CGPROGRAM
#pragma fragment frag_stencil
ENDCG
}
Pass { // 8, stencil intersect 0
Cull Off
ColorMask 0
Stencil {
WriteMask 127
PassFront IncrWrap
PassBack DecrWrap
}
CGPROGRAM
#pragma fragment frag_stencil
ENDCG
}
Pass { // 9, stencil intersect 1
ColorMask 0
Stencil {
Ref 128
Comp Less
Pass Replace
Fail Zero
}
CGPROGRAM
#pragma fragment frag_stencil
ENDCG
}
Pass { // 10, font texture pass 0
Stencil {
Ref 128
Comp Equal
}
CGPROGRAM
#pragma fragment frag_texfont
ENDCG
}
Pass { // 11, mask filter
CGPROGRAM
#pragma fragment frag_mf
ENDCG
}
}
}
正在加载...
取消
保存