Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@GaryQian
Copy link
Contributor

@GaryQian GaryQian commented Aug 6, 2019

This PR performs the tonic/custom transition for the new LibTxt metrics between native and dart.

Part of a series of changes to fix flutter/flutter#35994

Adds getLineMetrics() to dart:ui Paragraph. This is a catch-all "return all of the metrics at once" call that is a good point to start.

This blanket method will provide full access to the data we track. I am currently considering adding additional API to slice the data into more manageable chunks so that the data required can be queried more directly, reducing the amount of unused data passed through tonic.

The dart:ui tests will reside in the framework test suite due to a lack of direct dart:ui testing setup and will be in a separate PR (I will link shortly).

}

/// Data class that holds the metrics for a single line in the [Paragraph].
class LineMetrics {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add this class to stub ui and file an issue to implement for the web

///
/// This can potentially return a large amount of data, so it is not recommended
/// to repeatedly call this. Instead, cache the results.
List<LineMetrics> getLineMetrics();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These will need implementations in the _engine sub directory. In this case I would return an empty list similar to what the implementation for getBoxesForPlaceholders does.

cc @yjbanov

Copy link
Contributor Author

@GaryQian GaryQian Aug 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if any of the metrics here are difficult to obtain for web. Some of them are quite libtxt/skia specific. I can still remove some of them easily without hurting functionality too much.

cc @mdebbar

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unlikely to get to it before @mdebbar is back. @mdebbar is currently redesigning out text layout. The new design will give us much more information, such as this. Our current design won't give us per-line information like this.

this.lineNumber,
List<RunMetrics> rMetrics,
List<int> rMetricsIndexes,
) : runMetrics = Map() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use an immutable map here? If it's too expensive, we could use an immutable map in debug builds and a regular map in profile/release builds.

/// metrics of each laid out line.
///
/// This can potentially return a large amount of data, so it is not recommended
/// to repeatedly call this. Instead, cache the results.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this value available prior to calling layout? If not, let's document that.

Does the value depend on ParagraphConstraints passed to the layout method? If not, we could cache right here and not scare the user with the large amount of data disclaimer. Actually, even if it does depend on the ParagraphConstratins, we can still cache and invalidate the cache next time layout is called.

// Zero indexed line number.
size_t line_number = 0;

std::map<size_t, txt::RunMetrics> run_metrics;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fields are immutable in Dart. Do they have to be publicly mutable in the engine?

}

std::vector<LineMetrics> Paragraph::getLineMetrics() {
return m_paragraphImpl->getLineMetrics();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be doing a lot of copying.

std::vector<TextBox> getRectsForPlaceholders();
Dart_Handle getPositionForOffset(double dx, double dy);
Dart_Handle getWordBoundary(unsigned offset);
std::vector<LineMetrics> getLineMetrics();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like an expensive data structure, because it doesn't use pointers. Passing it around would involve, copying the vector, copying each LineMetrics object, and recursively copying std::map<size_t, txt::RunMetrics> that's inside it.


namespace flutter {

struct LineMetrics {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs? For example, it would be interesting to know why the memory layout is chosen to be flat.

lib/ui/text.dart Outdated
this.lineNumber,
List<RunMetrics> rMetrics,
List<int> rMetricsIndexes,
) : runMetrics = Map() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use Map.fromIterables(rMetricsIndexes, rMetrics)

lib/ui/text.dart Outdated
}
}

/// The index in the text buffer the line begins.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change this to "where the line begins" (same for endIndex, endExcludingWhitespace, endIncludingNewline)

txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());

txt::TextStyle text_style;
// We must supply a font here, as the default is Arial, and we do not
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove this comment. This test seems similar to most other paragraph unit test which use Roboto because it is available as a test font.

Copy link
Member

@goderbauer goderbauer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the dart files


/// Data class that holds the metrics for a single line in the [Paragraph].
class LineMetrics {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove empty line

}
}

/// The index in the text buffer the line begins.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this missing a "where" or some word like it before "the line begins"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Same comment for many of the following docs).

this.lineNumber,
List<RunMetrics> rMetrics,
List<int> rMetricsIndexes,
) : runMetrics = Map() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
) : runMetrics = Map() {
) : runMetrics = <int, RunMetrics>{} {

List<int> rMetricsIndexes,
) : runMetrics = Map() {
for (int i = 0; i < rMetrics.length; ++i) {
runMetrics[rMetricsIndexes[i]] = rMetrics[i];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

/// The index in the text buffer the line begins.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(maybe this is clear from context where this will be used, but...)

While reading through these API docs I kept wondering what "the test buffer" is that's mentioned here over and over.


/// Extent above baseline.
final double top;
/// Distance to reserve above baseline.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

insert empty line after each property?

final double top;
/// Distance to reserve above baseline.
///
/// Since this is the raw metric from the font, [ascent] is usually negative.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this only true for ascent? Or also for some of the other metrics here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me double check, but it may also apply to top, although we dont track top separately so an abs is never applied to it.

final double avgCharWidth;
/// Maximum character width.
final double maxCharWidth;
/// Minimum x.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's x?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good question, I am not totally sure myself, but I will find out. This is one of the metrics from SkFontMetrics, but we don't seem to use it anywhere. It is provided to us nonetheless.

There is some ambiguous naming going on where X refers to x-coordinate as well as x the ascii letter. I can try to clarify this in our API.

final double capHeight;
/// Underline thickness.
///
/// Can be null.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is it null, can we say?

(same for other properties)

lib/ui/text.dart Outdated
middle,
}

/// Data class that holds the metrics for a single line in the [Paragraph].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All comments I made on the previous file also apply here.

@jonahwilliams I thought we no longer repeat doc comments in the stub impl... Is the doc comment duplication still necessary?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to repeat doc comments.

That said, I'm not enforcing this via a lint until there is an unfork, because the code in stub_ui is not the source of truth.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GaryQian Can you remove the comments from the stubs then?

@yjbanov
Copy link
Contributor

yjbanov commented Aug 9, 2019

@GaryQian Heads up! I just pushed a change that renamed stub_ui to web_ui.

}
}

/// The index in the text buffer the line begins.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this index? number of UTF-16 words?
We shouldn't add any new APIs that use UTF-16 word counts.

/// This is equivalent to `ascent + descent`
final double height;

/// Width of the line.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this documentation violates our style guide

/// The right edge can be obtained with `left + width`.
final double left;

/// The y position of the baseline for this line from the top of the paragraph.
Copy link
Contributor

@Hixie Hixie Aug 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be consistent in our style here: x-coordinate, y position, etc. Maybe see what we do today in the framework (e.g. it might be _x_ coordinate)

/// The y position of the baseline for this line from the top of the paragraph.
final double baseline;

/// Zero indexed line number.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Zero indexed line number" tells me nothing really. What is a line number?

Maybe "The number of this line in the overall paragraph, with the first line being index zero" or something?

///
/// Each entry will be keyed under the ending index the run applies to. Each
/// [RunMetrics] entry corresponds to the text covered by the range of
/// `[previous key, current key)`. For a given index `i`, the [RunMetrics]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generally speaking many of our developers don't know the [/( ... )/] convention. Please be explicit in prose instead.

class LineMetrics {

@pragma('vm:entry-point')
LineMetrics._(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this this constructor have to be private?

Might be worth having this be a public constructor with named arguments and having a private constructor with positional arguments for calling from C++. Having a way to create this class from Dart seems valuable (e.g. for testing).

final Map<int, RunMetrics> runMetrics;
}

/// Data class that tracks the raw font metrics for a particular text buffer index.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is a "Data class"?

/// Data class that tracks the raw font metrics for a particular text buffer index.
class RunMetrics {
@pragma('vm:entry-point')
RunMetrics._(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about constructors

///
/// Can be null.
final double strikeoutPosition;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these docs all need much more detail. see the style guide for writing prompts.

///
/// This can potentially return a large amount of data, so it is not recommended
/// to repeatedly call this. Instead, cache the results.
List<LineMetrics> getLineMetrics();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would call this computeLineMetrics to make it sound expensive. Things called getFoo should really be getters, and those look cheap.

@GaryQian
Copy link
Contributor Author

Status Update: This is on hold momentarily as I fix a segfault in the tracking of the metrics in LibTxt.

Next, I'll strip this down to minimize UTF-16 index usage in anticipation for dart's updated text system.

@GaryQian
Copy link
Contributor Author

GaryQian commented Aug 22, 2019

Waiting on #11064 to land in order to continue on this.

@GaryQian
Copy link
Contributor Author

Ok, this simplified version should be ready for another round of review. We no longer track anything that is connected to or indexed by UTF-16 codepoints. This means the RunMetrics class is on hold. We now just provide the main measurement metrics about the line itself.

Copy link
Member

@goderbauer goderbauer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM for the .dart files with some minor nits

middle,
}

class LineMetrics {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a doc comment for the class and constructor?

lib/ui/text.dart Outdated
/// The [ascent] is provided as a positive value, even though it is typically defined
/// in fonts as negative. This is to ensure the signage of operations with these
/// metrics directly reflects the intended signage of the value. For example,
/// the y coordinate of the top edge of the line is 'baseline - ascent`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should ' be a `?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question everywhere below.

lib/ui/text.dart Outdated
this.left = null,
this.baseline = null,
this.lineNumber = null,
}) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just be : instead of {}

lib/ui/text.dart Outdated
this.left,
this.baseline,
this.lineNumber,
) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same.

lib/ui/text.dart Outdated

class LineMetrics {
LineMetrics({
this.hardBreak = null,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the = null here? That's the default.

lib/ui/text.dart Outdated
middle,
}

/// `LineMetrics` stores the measurements and statistics of a single line in the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be enclosed in [] instead of `

lib/ui/text.dart Outdated
/// extent of the line instead of per-run or per-glyph metrics. For more detailed
/// metrics, see [TextBox] and [Paragraph.getBoxesForRange].
///
/// `LineMetrics` should be obtained directly from the [Paragraph.computeLineMetrics]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

@GaryQian GaryQian changed the title Expose LineMetrics and RunMetrics in dart:ui Expose LineMetrics in dart:ui Aug 26, 2019
@GaryQian GaryQian merged commit 9f1eab2 into flutter:master Aug 26, 2019
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Aug 26, 2019
engine-flutter-autoroll added a commit to flutter/flutter that referenced this pull request Aug 26, 2019
[email protected]:flutter/engine.git/compare/ba1a303ae2d7...9f1eab2

git log ba1a303..9f1eab2 --no-merges --oneline
2019-08-26 [email protected] Expose LineMetrics in dart:ui (flutter/engine#10670)
2019-08-26 [email protected] Roll buildroot and update gn script for bitcode_marker (flutter/engine#11449)
2019-08-26 [email protected] Roll src/third_party/skia d617d5d29ca5..464eb78e444b (10 commits) (flutter/engine#11444)
2019-08-26 [email protected] Roll fuchsia/clang/mac-amd64 from RDjN2... to CGYQg... (flutter/engine#11442)


If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC [email protected] on the revert to ensure that a human
is aware of the problem.

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose enough of LibTxt to make custom text layout practical

7 participants