浏览代码

Add comments, remove substring in request character in texture, make

font metrics struct.
/main
Yuncong Zhang 5 年前
当前提交
ce0bc3bd
共有 4 个文件被更改,包括 129 次插入153 次删除
  1. 4
      Runtime/ui/txt/layout.cs
  2. 5
      Runtime/ui/txt/linebreaker.cs
  3. 8
      Runtime/ui/txt/paint_record.cs
  4. 265
      Runtime/ui/txt/paragraph.cs

4
Runtime/ui/txt/layout.cs


}
else {
Font font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font;
font.RequestCharactersInTextureSafe(text.Substring(start, count), style.UnityFontSize,
style.UnityFontStyle);
// 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 (font.getGlyphInfo(ch, out var glyphInfo, style.UnityFontSize, style.UnityFontStyle)) {

5
Runtime/ui/txt/linebreaker.cs


// Layout.measureText(this._width - this._preBreak, this._textBuf,
// start, end - start, style,
// this._charWidths, start, this._tabStops);
Layout.computeCharWidths(this._textBuf.text, this._textBuf.offset + start, end - start, style, this._charWidths, start);
Layout.computeCharWidths(this._textBuf.text, this._textBuf.offset + start, end - start, style,
this._charWidths, start);
}
int current = this._wordBreaker.current();

void _pushGreedyBreak() {
var bestCandidate = this._candidates[this._bestBreak];
this._pushBreak(bestCandidate.offset, (bestCandidate.postBreak - this._preBreak));
this._pushBreak(bestCandidate.offset, bestCandidate.postBreak - this._preBreak);
this._bestScore = ScoreInfty;
this._lastBreak = this._bestBreak;
this._preBreak = bestCandidate.preBreak;

8
Runtime/ui/txt/paint_record.cs


get { return this._metrics; }
}
public float dx {
get { return this._dx; }
}
public float dy {
get { return this._dy; }
}
public void shift(float x, float y) {
this._dx += x;
this._dy += y;

265
Runtime/ui/txt/paragraph.cs


using UnityEngine;
namespace Unity.UIWidgets.ui {
public struct Vector2d {
public float x;
public float y;
public Vector2d(float x = 0.0f, float y = 0.0f) {
this.x = x;
this.y = y;
}
public static Vector2d operator +(Vector2d a, Vector2d b) {
return new Vector2d(a.x + b.x, a.y + b.y);
}
public static Vector2d operator -(Vector2d a, Vector2d b) {
return new Vector2d(a.x - b.x, a.y - b.y);
}
}
struct CodeUnitRun {
public readonly int lineNumber;
public readonly TextDirection direction;

}
class FontMetrics {
struct FontMetrics {
public readonly float leading = 0.0f;
public readonly float leading;
public readonly float descent;
public readonly float? underlineThickness;
public readonly float? underlinePosition;

static FontMetrics _previousFontMetrics;
static int _previousFontSize = 0;
static Font _previousFont;
static int _previousFontSize;
public FontMetrics(float ascent, float descent,
float? underlineThickness = null, float? underlinePosition = null, float? strikeoutPosition = null,

this.underlinePosition = underlinePosition;
this.strikeoutPosition = strikeoutPosition;
this.fxHeight = fxHeight;
this.leading = 0.0f;
if (fontSize == _previousFontSize && _previousFontMetrics != null) {
if (fontSize == _previousFontSize && font == _previousFont) {
return _previousFontMetrics;
}

float fxHeight = glyphInfo.glyphHeight;
_previousFontMetrics = new FontMetrics(ascent, descent, fxHeight: fxHeight);
_previousFontSize = fontSize;
_previousFont = font;
struct 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;
}
}
struct PositionWithAffinity {
public readonly int position;
public readonly TextAffinity affinity;

public readonly T start, end;
}
static class RangeUtils {
public static Range<float> shift(Range<float> value, float shift) {
return new Range<float>(value.start + shift, value.end + shift);
}
}
struct GlyphLine {
public readonly int start;

TextBlobBuilder builder = new TextBlobBuilder();
int ellipsizedLength = this._text.Length + (this._paragraphStyle.ellipsis?.Length ?? 0);
// All text blobs share a single position buffer, which is big enough taking ellipsis into consideration
// this._glyphLines and this._codeUnitRuns will refer to this array for glyph positions
// Pointer to the glyphPositions array, to keep track of where the next glyph is stored
int pGlyphPositions = 0;
// Compute max(NumberOfWords(line) for line in lines), to determine the size of word buffers
// Nothing to layout, if no visible character at all
int pGlyphPositions = 0;
// Iterate through line ranges
for (int lineNumber = 0; lineNumber < lineLimit; ++lineNumber) {
var lineRange = this._lineRanges[lineNumber];
int wordIndex = 0;

// Break the line into words if justification should be applied.
bool justifyLine = this._paragraphStyle.textAlign == TextAlign.justify &&
lineNumber != lineLimit - 1 && !lineRange.hardBreak;
lineNumber != lineLimit - 1 && !lineRange.hardBreak &&
// Do not apply justify if ellipsis should be added, or the ellipsis may be pushed
// out of the border.
// This is still not taken care of in the flutter engine.
!(this._paragraphStyle.ellipsized() && this._paragraphStyle.maxLines == null);
int wordCount = this._findWords(lineRange.start, lineRange.end, words);
float wordGapWidth = !(justifyLine && wordCount > 1)

// Count the number of style runs, and compute the character number of the longest run by the way
string ellipsis = this._paragraphStyle.ellipsis;
bool hardBreak = lineRange.hardBreak;
if (!string.IsNullOrEmpty(ellipsis) && !hardBreak && !this._width.isInfinite() &&
(lineNumber == lineLimit - 1 || this._paragraphStyle.maxLines == null)) {
maxTextCount += ellipsis.Length;
}
// Allocate the advances and positions to store the layout result
// TODO: find a way to compute the maxTextCount for the entire paragraph, so that this allocation
// happens only once
if (advances == null || advances.Length < maxTextCount) {
advances = new float[maxTextCount];
}

}
int glyphPositionStart = pGlyphPositions;
// Keep of the position in glyphPositions before evaluating this line
int glyphPositionLineStart = pGlyphPositions;
int tLineLimit = lineLimit;
float tMaxWordWidth = maxWordWidth;
// 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 ||

int lineStyleRunIndex = 0;
// Instead of computing all lineStyleRuns at once and store into an array and iterate through them,
// compute each lineStyleRun and deal with it on the fly, to save the storage for the runs
if (styleRun.start < lineEndIndex && styleRun.end > lineRange.start) {
int start = Mathf.Max(styleRun.start, lineRange.start);
int end = Mathf.Min(styleRun.end, lineEndIndex);
// Make sure that each run is not empty
if (start < end) {
var run = new LineStyleRun(start, end, styleRun.style);
string text = this._text;
int textStart = run.start;
int textEnd = run.end;
int textCount = textEnd - textStart;
// Compute the intersection between current style run intersects and the line
int start = Mathf.Max(styleRun.start, lineRange.start);
int end = Mathf.Min(styleRun.end, lineEndIndex);
// Make sure that each run is not empty
if (start < end) {
var style = styleRun.style;
string text = this._text;
int textStart = start;
int textEnd = end;
int textCount = textEnd - textStart;
// Keep track of the pointer to glyphPositions in the start of this run
int glyphPositionStyleRunStart = pGlyphPositions;
// It is assured in the _computeLineStyleRuns that run is not empty
D.assert(textCount != 0);
// Ellipsize the text if ellipsis string is set, and this is the last lineStyleRun of
// the current line, and this is the last line or max line is not set
if (!string.IsNullOrEmpty(ellipsis) && !hardBreak && !this._width.isInfinite() &&
lineStyleRunIndex == lineStyleRunCount - 1 &&
(lineNumber == lineLimit - 1 || this._paragraphStyle.maxLines == null)) {
float ellipsisWidth = Layout.measureText(ellipsis, style);
bool hardBreak = this._lineRanges[lineNumber].hardBreak;
if (!string.IsNullOrEmpty(this._paragraphStyle.ellipsis) && !hardBreak &&
!this._width.isInfinite() && lineStyleRunIndex == lineStyleRunCount - 1 &&
(lineNumber == lineLimit - 1 || this._paragraphStyle.maxLines == null)) {
string ellipsis = this._paragraphStyle.ellipsis;
float ellipsisWidth = Layout.measureText(ellipsis, run.style);
// Find the minimum number of characters to truncate, so that the truncated text
// appended with ellipsis is within the constraints of line width
int truncateCount = Layout.computeTruncateCount(runXOffset, text, textStart,
textCount, style, this._width - ellipsisWidth, this._tabStops);
// Find the minimum number of characters to truncate, so that the truncated text
// appended with ellipsis is within the constraints of line width
int truncateCount = Layout.computeTruncateCount(runXOffset, text, textStart,
textCount, run.style, this._width - ellipsisWidth, this._tabStops);
text = text.Substring(0, textStart + textCount - truncateCount) + ellipsis;
textCount = text.Length - textStart;
D.assert(textCount != 0);
if (this._paragraphStyle.maxLines == null) {
lineLimit = lineNumber + 1;
this._didExceedMaxLines = true;
}
text = text.Substring(0, textStart + textCount - truncateCount) + ellipsis;
textCount = text.Length - textStart;
D.assert(textCount != 0);
if (this._paragraphStyle.maxLines == null) {
lineLimit = lineNumber + 1;
this._didExceedMaxLines = true;
}
if (advances == null || advances.Length < textCount) {
advances = new float[textCount];
}
float advance = Layout.doLayout(runXOffset, text, textStart, textCount, style,
advances, positions, this._tabStops, out var bounds);
if (positions == null || positions.Length < textCount) {
positions = new float[textCount];
}
float advance = Layout.doLayout(runXOffset, text, textStart, textCount, run.style,
advances, positions, this._tabStops, out var bounds);
builder.allocRunPos(style, text, textStart, textCount);
// bounds relative to first character
bounds.x -= positions[0];
builder.setBounds(bounds);
builder.allocRunPos(run.style, text, textStart, textCount);
// bounds relative to first character
bounds.x -= positions[0];
builder.setBounds(bounds);
// Update the max width of the words
// Fill in the glyph positions, and the positions of the text blob builder
float wordStartPosition = float.NaN;
for (int glyphIndex = 0; glyphIndex < textCount; ++glyphIndex) {
float glyphXOffset = positions[glyphIndex] + justifyXOffset;
float glyphAdvance = advances[glyphIndex];
builder.setPosition(glyphIndex, glyphXOffset);
glyphPositions[pGlyphPositions++] = new GlyphPosition(runXOffset + glyphXOffset,
glyphAdvance, new Range<int>(textStart + glyphIndex, textStart + glyphIndex + 1));
if (wordIndex < wordCount) {
Range<int> word = words[wordIndex];
// Run into the start of current word, record the start position of this word
if (word.start == start + glyphIndex) {
wordStartPosition = runXOffset + glyphXOffset;
}
float wordStartPosition = float.NaN;
for (int glyphIndex = 0; glyphIndex < textCount; ++glyphIndex) {
float glyphXOffset = positions[glyphIndex] + justifyXOffset;
float glyphAdvance = advances[glyphIndex];
builder.setPosition(glyphIndex, glyphXOffset);
glyphPositions[pGlyphPositions++] = new GlyphPosition(runXOffset + glyphXOffset,
glyphAdvance,
new Range<int>(textStart + glyphIndex, textStart + glyphIndex + 1));
if (words != null && wordIndex < wordCount) {
Range<int> word = words[wordIndex];
if (word.start == run.start + glyphIndex) {
wordStartPosition = runXOffset + glyphXOffset;
// Run into the end of current word
if (word.end == start + glyphIndex + 1) {
if (justifyLine) {
justifyXOffset += wordGapWidth;
if (word.end == run.start + glyphIndex + 1) {
if (justifyLine) {
justifyXOffset += wordGapWidth;
}
wordIndex++;
if (!float.IsNaN(wordStartPosition)) {
maxWordWidth = Mathf.Max(maxWordWidth,
glyphPositions[pGlyphPositions - 1].xPos.end - wordStartPosition);
wordStartPosition = float.NaN;
}
// Update the current word
wordIndex++;
// If the start position of this word has been recorded, calculate the
// width of this word, and update the entire word
if (!float.IsNaN(wordStartPosition)) {
maxWordWidth = Mathf.Max(maxWordWidth,
glyphPositions[pGlyphPositions - 1].xPos.end - wordStartPosition);
wordStartPosition = float.NaN;
}
TextBlob textBlob = builder.make();
var font = FontManager.instance.getOrCreate(run.style.fontFamily,
run.style.fontWeight, run.style.fontStyle).font;
var metrics = FontMetrics.fromFont(font, run.style.UnityFontSize);
PaintRecord paintRecord = new PaintRecord(run.style, runXOffset, 0, textBlob, metrics,
advance);
// Create paint record
var font = FontManager.instance.getOrCreate(style.fontFamily,
style.fontWeight, style.fontStyle).font;
var metrics = FontMetrics.fromFont(font, style.UnityFontSize);
PaintRecord paintRecord = new PaintRecord(style, runXOffset, 0, builder.make(),
metrics, advance);
runXOffset += advance;
runXOffset += advance;
this._codeUnitRuns.Add(new CodeUnitRun(
glyphPositions,
new Range<int>(run.start, run.end),
new Range<float>(glyphPositions[0].xPos.start, glyphPositions.last().xPos.end),
lineNumber, TextDirection.ltr, pGlyphPositions - textCount, textCount));
// Create code unit run
this._codeUnitRuns.Add(new CodeUnitRun(
glyphPositions,
new Range<int>(start, end),
new Range<float>(glyphPositions[0].xPos.start, glyphPositions.last().xPos.end),
lineNumber, TextDirection.ltr, glyphPositionStyleRunStart, textCount));
this._paintRecords.Add(paintRecord);
}
this._paintRecords.Add(paintRecord);
lineStyleRunIndex++;
}
if (styleRun.end >= lineEndIndex) {

styleRunIndex++;
}
lineLimit = tLineLimit;
maxWordWidth = tMaxWordWidth;
}
float maxLineSpacing = 0;

yOffset += Mathf.Round(maxLineSpacing + preMaxDescent);
preMaxDescent = maxDescent;
float lineXOffset = this.getLineXOffset(runXOffset);
int count = pGlyphPositions - glyphPositionStart;
int count = pGlyphPositions - glyphPositionLineStart;
if (lineXOffset != 0 && glyphPositions != null) {
for (int i = 0; i < count; ++i) {
glyphPositions[glyphPositions.Length - i - 1].shiftSelf(lineXOffset);

int lineStart = this._lineRanges[lineNumber].start;
int lineStart = lineRange.start;
new GlyphLine(glyphPositions, glyphPositionStart, count, nextLineStart - lineStart));
new GlyphLine(glyphPositions, glyphPositionLineStart, count, nextLineStart - lineStart));
for (int i = 0; i < lineStyleRunCount; i++) {
var paintRecord = this._paintRecords[this._paintRecords.Count - 1 - i];
paintRecord.shift(lineXOffset, yOffset);

int lineStyleRunCount = 0;
for (int i = styleRunIndex; i < this._runs.size; i++) {
var styleRun = this._runs.getRun(i);
if (styleRun.start < lineEndIndex && styleRun.end > lineRange.start) {
int start = Mathf.Max(styleRun.start, lineRange.start);
int end = Mathf.Min(styleRun.end, lineEndIndex);
// Make sure that each line is not empty
if (start < end) {
lineStyleRunCount++;
maxTextCount = Math.Max(end - start, maxTextCount);
}
int start = Mathf.Max(styleRun.start, lineRange.start);
int end = Mathf.Min(styleRun.end, lineEndIndex);
// Make sure that each line is not empty
if (start < end) {
lineStyleRunCount++;
maxTextCount = Math.Max(end - start, maxTextCount);
}
if (styleRun.end >= lineEndIndex) {

正在加载...
取消
保存