浏览代码

Merge pull request #261 from UnityTech/layout

Layout
/main
GitHub 5 年前
当前提交
4532e4a1
共有 30 个文件被更改,包括 1409 次插入1781 次删除
  1. 19
      Runtime/painting/text_painter.cs
  2. 14
      Runtime/rendering/editable.cs
  3. 2
      Runtime/ui/painting/draw_cmd.cs
  4. 2
      Runtime/ui/painting/picture.cs
  5. 99
      Runtime/ui/painting/txt/mesh_generator.cs
  6. 164
      Runtime/ui/painting/txt/text_blob.cs
  7. 10
      Runtime/ui/renderer/cmdbufferCanvas/rendering/canvas_impl.cs
  8. 2
      Runtime/ui/renderer/common/draw_cmd.cs
  9. 3
      Runtime/ui/renderer/common/picture.cs
  10. 1
      Runtime/ui/renderer/compositeCanvas/flow/instrumentation.cs
  11. 1
      Runtime/ui/renderer/compositeCanvas/flow/performance_overlay_layer.cs
  12. 48
      Runtime/ui/text.cs
  13. 33
      Runtime/ui/txt/emoji.cs
  14. 500
      Runtime/ui/txt/layout.cs
  15. 50
      Runtime/ui/txt/layout_utils.cs
  16. 623
      Runtime/ui/txt/linebreaker.cs
  17. 32
      Runtime/ui/txt/paint_record.cs
  18. 1001
      Runtime/ui/txt/paragraph.cs
  19. 2
      Runtime/ui/txt/paragraph_builder.cs
  20. 6
      Runtime/ui/txt/styled_runs.cs
  21. 5
      Runtime/ui/txt/text_buff.cs
  22. 19
      Runtime/ui/txt/word_separate.cs
  23. 59
      Runtime/ui/txt/wordbreaker.cs
  24. 1
      Samples/UIWidgetSample/MaterialThemeSample.cs
  25. 3
      Samples/UIWidgetsGallery/demo/animation/home.cs
  26. 28
      Tests/Editor/CanvasAndLayers.cs
  27. 149
      Samples/UIWidgetSample/BenchMarkLayout.cs
  28. 11
      Samples/UIWidgetSample/BenchMarkLayout.cs.meta
  29. 296
      Samples/UIWidgetSample/TextLayoutSample.unity
  30. 7
      Samples/UIWidgetSample/TextLayoutSample.unity.meta

19
Runtime/painting/text_painter.cs


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

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

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

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

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

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

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

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

14
Runtime/rendering/editable.cs


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

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

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

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

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

2
Runtime/ui/painting/draw_cmd.cs


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

2
Runtime/ui/painting/picture.cs


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

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


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

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

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

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

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

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

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

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

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

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

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

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

vertices.Add(new Vector3((position.x + minX), (position.y + minY), 0));
vertices.Add(new Vector3((position.x + maxX), (position.y + minY), 0));
vertices.Add(new Vector3((position.x + maxX), (position.y + maxY), 0));
vertices.Add(new Vector3((position.x + minX), (position.y + maxY), 0));
vertices.Add(new Vector3(positionX + minX, minY, 0));
vertices.Add(new Vector3(positionX + maxX, minY, 0));
vertices.Add(new Vector3(positionX + maxX, maxY, 0));
vertices.Add(new Vector3(positionX + minX, maxY, 0));
triangles.Add(baseIndex);
triangles.Add(baseIndex + 1);

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

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


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

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


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

matrix.preTranslate(offset.dx, offset.dy);
var mesh = TextBlobMesh.create(textBlob, scale, matrix);
var textBlobBounds = matrix.mapRect(uiRectHelper.fromRect(textBlob.boundsInText));
var mesh = TextBlobMesh.create(textBlob.Value, scale, matrix);
var textBlobBounds = matrix.mapRect(uiRectHelper.fromRect(textBlob.Value.boundsInText));
var style = textBlob.style;
var style = textBlob.Value.style;
var subText = textBlob.text.Substring(textBlob.textOffset, textBlob.textSize);
var subText = textBlob.Value.text.Substring(textBlob.Value.textOffset, textBlob.Value.textSize);
Texture tex = null;
bool notEmoji = !char.IsHighSurrogate(subText[0]) && !EmojiUtils.isSingleCharEmoji(subText[0]);
if (notEmoji) {

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


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

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


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

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


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

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


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

48
Runtime/ui/text.cs


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

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

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

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

33
Runtime/ui/txt/emoji.cs


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

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

500
Runtime/ui/txt/layout.cs


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

50
Runtime/ui/txt/layout_utils.cs


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

623
Runtime/ui/txt/linebreaker.cs


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

32
Runtime/ui/txt/paint_record.cs


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

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

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

2
Runtime/ui/txt/paragraph_builder.cs


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

6
Runtime/ui/txt/styled_runs.cs


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

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

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

5
Runtime/ui/txt/text_buff.cs


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

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

19
Runtime/ui/txt/word_separate.cs


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

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

59
Runtime/ui/txt/wordbreaker.cs


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

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

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

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

1
Samples/UIWidgetSample/MaterialThemeSample.cs


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

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


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

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

28
Tests/Editor/CanvasAndLayers.cs


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

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

149
Samples/UIWidgetSample/BenchMarkLayout.cs


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

11
Samples/UIWidgetSample/BenchMarkLayout.cs.meta


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

296
Samples/UIWidgetSample/TextLayoutSample.unity


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

7
Samples/UIWidgetSample/TextLayoutSample.unity.meta


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