[web] Put the paragraph painting logic in the Paragraph class (flutter/engine#22239)

This commit is contained in:
Mouad Debbar 2020-11-03 21:10:44 -08:00 committed by GitHub
parent fc87f2df63
commit d4e264f851
4 changed files with 120 additions and 84 deletions

View File

@ -777,70 +777,37 @@ class BitmapCanvas extends EngineCanvas {
_childOverdraw = true;
}
void _drawTextLine(
ParagraphGeometricStyle style,
EngineLineMetrics line,
double x,
double y,
) {
html.CanvasRenderingContext2D ctx = _canvasPool.context;
x += line.left;
final double? letterSpacing = style.letterSpacing;
if (letterSpacing == null || letterSpacing == 0.0) {
ctx.fillText(line.displayText!, x, y);
} else {
// When letter-spacing is set, we go through a more expensive code path
// that renders each character separately with the correct spacing
// between them.
//
// We are drawing letter spacing like the web does it, by adding the
// spacing after each letter. This is different from Flutter which puts
// the spacing around each letter i.e. for a 10px letter spacing, Flutter
// would put 5px before each letter and 5px after it, but on the web, we
// put no spacing before the letter and 10px after it. This is how the DOM
// does it.
final int len = line.displayText!.length;
for (int i = 0; i < len; i++) {
final String char = line.displayText![i];
ctx.fillText(char, x, y);
x += letterSpacing + ctx.measureText(char).width!;
}
void setFontFromParagraphStyle(ParagraphGeometricStyle style) {
if (style != _cachedLastStyle) {
html.CanvasRenderingContext2D ctx = _canvasPool.context;
ctx.font = style.cssFontString;
_cachedLastStyle = style;
}
}
/// Measures the given [text] and returns a [html.TextMetrics] object that
/// contains information about the measurement.
///
/// The text is measured using the font set by the most recent call to
/// [setFontFromParagraphStyle].
html.TextMetrics measureText(String text) {
return _canvasPool.context.measureText(text);
}
/// Draws text to the canvas starting at coordinate ([x], [y]).
///
/// The text is drawn starting at coordinates ([x], [y]). It uses the current
/// font set by the most recent call to [setFontFromParagraphStyle].
void fillText(String text, double x, double y) {
_canvasPool.context.fillText(text, x, y);
}
@override
void drawParagraph(EngineParagraph paragraph, ui.Offset offset) {
assert(paragraph._isLaidOut);
final ParagraphGeometricStyle style = paragraph._geometricStyle;
if (paragraph._drawOnCanvas && _childOverdraw == false) {
// !Do not move this assignment above this if clause since, accessing
// context will generate extra <canvas> tags.
final List<EngineLineMetrics> lines =
paragraph._measurementResult!.lines!;
final SurfacePaintData? backgroundPaint =
paragraph._background?.paintData;
if (backgroundPaint != null) {
final ui.Rect rect = ui.Rect.fromLTWH(
offset.dx, offset.dy, paragraph.width, paragraph.height);
drawRect(rect, backgroundPaint);
}
if (style != _cachedLastStyle) {
html.CanvasRenderingContext2D ctx = _canvasPool.context;
ctx.font = style.cssFontString;
_cachedLastStyle = style;
}
_setUpPaint(paragraph._paint!.paintData, null);
double y = offset.dy + paragraph.alphabeticBaseline;
final int len = lines.length;
for (int i = 0; i < len; i++) {
_drawTextLine(style, lines[i], offset.dx, y);
y += paragraph._lineHeight;
}
_tearDownPaint();
assert(paragraph.isLaidOut);
if (paragraph.drawOnCanvas && _childOverdraw == false) {
paragraph.paint(this, offset);
return;
}

View File

@ -251,18 +251,12 @@ html.Element _drawParagraphElement(
ui.Offset offset, {
Matrix4? transform,
}) {
assert(paragraph._isLaidOut);
assert(paragraph.isLaidOut);
final html.Element paragraphElement = paragraph._paragraphElement.clone(true) as html.Element;
final html.CssStyleDeclaration paragraphStyle = paragraphElement.style;
paragraphStyle
..position = 'absolute'
..whiteSpace = 'pre-wrap'
..overflowWrap = 'break-word'
..overflow = 'hidden'
..height = '${paragraph.height}px'
..width = '${paragraph.width}px';
final html.HtmlElement paragraphElement = paragraph.toDomElement();
paragraphElement.style
..height = '${paragraph.height}px'
..width = '${paragraph.width}px';
if (transform != null) {
setElementTransform(
@ -270,16 +264,6 @@ html.Element _drawParagraphElement(
transformWithOffset(transform, offset).storage,
);
}
final ParagraphGeometricStyle style = paragraph._geometricStyle;
// TODO(flutter_web): https://github.com/flutter/flutter/issues/33223
if (style.ellipsis != null &&
(style.maxLines == null || style.maxLines == 1)) {
paragraphStyle
..whiteSpace = 'pre'
..textOverflow = 'ellipsis';
}
return paragraphElement;
}

View File

@ -522,13 +522,13 @@ class RecordingCanvas {
void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) {
assert(!_recordingEnded);
final EngineParagraph engineParagraph = paragraph as EngineParagraph;
if (!engineParagraph._isLaidOut) {
if (!engineParagraph.isLaidOut) {
// Ignore non-laid out paragraphs. This matches Flutter's behavior.
return;
}
_didDraw = true;
if (engineParagraph._geometricStyle.ellipsis != null) {
if (engineParagraph.hasArbitraryPaint) {
_hasArbitraryPaint = true;
}
final double left = offset.dx;

View File

@ -335,9 +335,94 @@ class EngineParagraph implements ui.Paragraph {
}
}
bool get hasArbitraryPaint => _geometricStyle.ellipsis != null;
void paint(BitmapCanvas canvas, ui.Offset offset) {
assert(drawOnCanvas);
assert(isLaidOut);
// Paint the background first.
final SurfacePaint? background = _background;
if (background != null) {
final ui.Rect rect = ui.Rect.fromLTWH(offset.dx, offset.dy, width, height);
canvas.drawRect(rect, background.paintData);
}
final List<EngineLineMetrics> lines = _measurementResult!.lines!;
canvas.setFontFromParagraphStyle(_geometricStyle);
// Then paint the text.
canvas._setUpPaint(_paint!.paintData, null);
double y = offset.dy + alphabeticBaseline;
final int len = lines.length;
for (int i = 0; i < len; i++) {
_paintLine(canvas, lines[i], offset.dx, y);
y += _lineHeight;
}
canvas._tearDownPaint();
}
void _paintLine(
BitmapCanvas canvas,
EngineLineMetrics line,
double x,
double y,
) {
x += line.left;
final double? letterSpacing = _geometricStyle.letterSpacing;
if (letterSpacing == null || letterSpacing == 0.0) {
canvas.fillText(line.displayText!, x, y);
} else {
// When letter-spacing is set, we go through a more expensive code path
// that renders each character separately with the correct spacing
// between them.
//
// We are drawing letter spacing like the web does it, by adding the
// spacing after each letter. This is different from Flutter which puts
// the spacing around each letter i.e. for a 10px letter spacing, Flutter
// would put 5px before each letter and 5px after it, but on the web, we
// put no spacing before the letter and 10px after it. This is how the DOM
// does it.
//
// TODO(mdebbar): Implement letter-spacing on canvas more efficiently:
// https://github.com/flutter/flutter/issues/51234
final int len = line.displayText!.length;
for (int i = 0; i < len; i++) {
final String char = line.displayText![i];
canvas.fillText(char, x, y);
x += letterSpacing + canvas.measureText(char).width!;
}
}
}
html.HtmlElement toDomElement() {
assert(isLaidOut);
final html.HtmlElement paragraphElement =
_paragraphElement.clone(true) as html.HtmlElement;
final html.CssStyleDeclaration paragraphStyle = paragraphElement.style;
paragraphStyle
..position = 'absolute'
..whiteSpace = 'pre-wrap'
..overflowWrap = 'break-word'
..overflow = 'hidden';
final ParagraphGeometricStyle style = _geometricStyle;
// TODO(flutter_web): https://github.com/flutter/flutter/issues/33223
if (style.ellipsis != null &&
(style.maxLines == null || style.maxLines == 1)) {
paragraphStyle
..whiteSpace = 'pre'
..textOverflow = 'ellipsis';
}
return paragraphElement;
}
@override
List<ui.TextBox> getBoxesForPlaceholders() {
assert(_isLaidOut);
assert(isLaidOut);
return _measurementResult!.placeholderBoxes;
}
@ -351,7 +436,7 @@ class EngineParagraph implements ui.Paragraph {
/// - Paragraphs that contain decorations.
/// - Paragraphs that have a non-null word-spacing.
/// - Paragraphs with a background.
bool get _drawOnCanvas {
bool get drawOnCanvas {
if (!_hasLineMetrics) {
return false;
}
@ -370,7 +455,7 @@ class EngineParagraph implements ui.Paragraph {
}
/// Whether this paragraph has been laid out.
bool get _isLaidOut => _measurementResult != null;
bool get isLaidOut => _measurementResult != null;
/// Asserts that the properties used to measure paragraph layout are the same
/// as the properties of this paragraphs root style.