未验证 提交 5c8a6260 编写于 作者: Y Yegor 提交者: GitHub

Delete CSS paint code (#20426)

* Delete CSS paint code
上级 2ac5b346
......@@ -419,7 +419,6 @@ FILE: ../../../flutter/lib/ui/window/viewport_metrics.cc
FILE: ../../../flutter/lib/ui/window/viewport_metrics.h
FILE: ../../../flutter/lib/ui/window/window.cc
FILE: ../../../flutter/lib/ui/window/window.h
FILE: ../../../flutter/lib/web_ui/lib/assets/houdini_painter.js
FILE: ../../../flutter/lib/web_ui/lib/src/engine.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/alarm_clock.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/assets.dart
......@@ -463,7 +462,6 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/dom_renderer.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/engine_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/frame_reference.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/history.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/houdini_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html_image_codec.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/mouse_cursor.dart
......
此差异已折叠。
......@@ -64,7 +64,6 @@ part 'engine/dom_renderer.dart';
part 'engine/engine_canvas.dart';
part 'engine/frame_reference.dart';
part 'engine/history.dart';
part 'engine/houdini_canvas.dart';
part 'engine/html_image_codec.dart';
part 'engine/keyboard.dart';
part 'engine/mouse_cursor.dart';
......
......@@ -193,9 +193,9 @@ class BitmapCanvas extends EngineCanvas {
///
/// See also:
///
/// * [PersistedStandardPicture._applyBitmapPaint] which uses this method to
/// * [PersistedPicture._applyBitmapPaint] which uses this method to
/// decide whether to reuse this canvas or not.
/// * [PersistedStandardPicture._recycleCanvas] which also uses this method
/// * [PersistedPicture._recycleCanvas] which also uses this method
/// for the same reason.
bool isReusable() {
return _devicePixelRatio == EngineWindow.browserDevicePixelRatio;
......
......@@ -169,8 +169,4 @@ class EngineColorFilter implements ui.ColorFilter {
return 'Unknown ColorFilter type. This is an error. If you\'re seeing this, please file an issue at https://github.com/flutter/flutter/issues/new.';
}
}
List<dynamic> webOnlySerializeToCssPaint() {
throw UnsupportedError('ColorFilter for CSS paint not yet supported');
}
}
......@@ -282,3 +282,127 @@ html.Element _drawParagraphElement(
}
return paragraphElement;
}
class _SaveElementStackEntry {
_SaveElementStackEntry({
required this.savedElement,
required this.transform,
});
final html.Element savedElement;
final Matrix4 transform;
}
/// Provides save stack tracking functionality to implementations of
/// [EngineCanvas].
mixin SaveElementStackTracking on EngineCanvas {
static final Vector3 _unitZ = Vector3(0.0, 0.0, 1.0);
final List<_SaveElementStackEntry> _saveStack = <_SaveElementStackEntry>[];
/// The element at the top of the element stack, or [rootElement] if the stack
/// is empty.
html.Element get currentElement =>
_elementStack.isEmpty ? rootElement : _elementStack.last;
/// The stack that maintains the DOM elements used to express certain paint
/// operations, such as clips.
final List<html.Element> _elementStack = <html.Element>[];
/// Pushes the [element] onto the element stack for the purposes of applying
/// a paint effect using a DOM element, e.g. for clipping.
///
/// The [restore] method automatically pops the element off the stack.
void pushElement(html.Element element) {
_elementStack.add(element);
}
/// Empties the save stack and the element stack, and resets the transform
/// and clip parameters.
///
/// Classes that override this method must call `super.clear()`.
@override
void clear() {
_saveStack.clear();
_elementStack.clear();
_currentTransform = Matrix4.identity();
}
/// The current transformation matrix.
Matrix4 get currentTransform => _currentTransform;
Matrix4 _currentTransform = Matrix4.identity();
/// Saves current clip and transform on the save stack.
///
/// Classes that override this method must call `super.save()`.
@override
void save() {
_saveStack.add(_SaveElementStackEntry(
savedElement: currentElement,
transform: _currentTransform.clone(),
));
}
/// Restores current clip and transform from the save stack.
///
/// Classes that override this method must call `super.restore()`.
@override
void restore() {
if (_saveStack.isEmpty) {
return;
}
final _SaveElementStackEntry entry = _saveStack.removeLast();
_currentTransform = entry.transform;
// Pop out of any clips.
while (currentElement != entry.savedElement) {
_elementStack.removeLast();
}
}
/// Multiplies the [currentTransform] matrix by a translation.
///
/// Classes that override this method must call `super.translate()`.
@override
void translate(double dx, double dy) {
_currentTransform.translate(dx, dy);
}
/// Scales the [currentTransform] matrix.
///
/// Classes that override this method must call `super.scale()`.
@override
void scale(double sx, double sy) {
_currentTransform.scale(sx, sy);
}
/// Rotates the [currentTransform] matrix.
///
/// Classes that override this method must call `super.rotate()`.
@override
void rotate(double radians) {
_currentTransform.rotate(_unitZ, radians);
}
/// Skews the [currentTransform] matrix.
///
/// Classes that override this method must call `super.skew()`.
@override
void skew(double sx, double sy) {
// DO NOT USE Matrix4.skew(sx, sy)! It treats sx and sy values as radians,
// but in our case they are transform matrix values.
final Matrix4 skewMatrix = Matrix4.identity();
final Float32List storage = skewMatrix.storage;
storage[1] = sy;
storage[4] = sx;
_currentTransform.multiply(skewMatrix);
}
/// Multiplies the [currentTransform] matrix by another matrix.
///
/// Classes that override this method must call `super.transform()`.
@override
void transform(Float32List matrix4) {
_currentTransform.multiply(Matrix4.fromFloat32List(matrix4));
}
}
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO(yjbanov): optimization opportunities (see also houdini_painter.js)
// - collapse non-drawing paint operations
// - avoid producing DOM-based clips if there is no text
// - evaluate using stylesheets for static CSS properties
// - evaluate reusing houdini canvases
// @dart = 2.10
part of engine;
/// A canvas that renders to a combination of HTML DOM and CSS Custom Paint API.
///
/// This canvas produces paint commands for houdini_painter.js to apply. This
/// class must be kept in sync with houdini_painter.js.
class HoudiniCanvas extends EngineCanvas with SaveElementStackTracking {
@override
final html.Element rootElement = html.Element.tag('flt-houdini');
/// The rectangle positioned relative to the parent layer's coordinate system
/// where this canvas paints.
///
/// Painting outside the bounds of this rectangle is cropped.
final ui.Rect? bounds;
HoudiniCanvas(this.bounds) {
// TODO(yjbanov): would it be faster to specify static values in a
// stylesheet and let the browser apply them?
rootElement.style
..position = 'absolute'
..top = '0'
..left = '0'
..width = '${bounds!.size.width}px'
..height = '${bounds!.size.height}px'
..backgroundImage = 'paint(flt)';
}
/// Prepare to reuse this canvas by clearing it's current contents.
@override
void clear() {
super.clear();
_serializedCommands = <List<dynamic>>[];
// TODO(yjbanov): we should measure if reusing old elements is beneficial.
domRenderer.clearDom(rootElement);
}
/// Paint commands serialized for sending to the CSS custom painter.
List<List<dynamic>> _serializedCommands = <List<dynamic>>[];
void apply(PaintCommand command) {
// Some commands are applied purely in HTML DOM and do not need to be
// serialized.
if (command is! PaintDrawParagraph &&
command is! PaintDrawImageRect &&
command is! PaintTransform) {
command.serializeToCssPaint(_serializedCommands);
}
command.apply(this);
}
/// Sends the paint commands to the CSS custom painter for painting.
void commit() {
if (_serializedCommands.isNotEmpty) {
rootElement.style.setProperty('--flt', json.encode(_serializedCommands));
} else {
rootElement.style.removeProperty('--flt');
}
}
@override
void clipRect(ui.Rect rect) {
final html.Element clip = html.Element.tag('flt-clip-rect');
final String cssTransform = matrix4ToCssTransform(
transformWithOffset(currentTransform, ui.Offset(rect.left, rect.top)));
clip.style
..overflow = 'hidden'
..position = 'absolute'
..transform = cssTransform
..width = '${rect.width}px'
..height = '${rect.height}px';
// The clipping element will translate the coordinate system as well, which
// is not what a clip should do. To offset that we translate in the opposite
// direction.
super.translate(-rect.left, -rect.top);
currentElement.append(clip);
pushElement(clip);
}
@override
void clipRRect(ui.RRect rrect) {
final ui.Rect outer = rrect.outerRect;
if (rrect.isRect) {
clipRect(outer);
return;
}
final html.Element clip = html.Element.tag('flt-clip-rrect');
final html.CssStyleDeclaration style = clip.style;
style
..overflow = 'hidden'
..position = 'absolute'
..transform = 'translate(${outer.left}px, ${outer.right}px)'
..width = '${outer.width}px'
..height = '${outer.height}px';
if (rrect.tlRadiusY == rrect.tlRadiusX) {
style.borderTopLeftRadius = '${rrect.tlRadiusX}px';
} else {
style.borderTopLeftRadius = '${rrect.tlRadiusX}px ${rrect.tlRadiusY}px';
}
if (rrect.trRadiusY == rrect.trRadiusX) {
style.borderTopRightRadius = '${rrect.trRadiusX}px';
} else {
style.borderTopRightRadius = '${rrect.trRadiusX}px ${rrect.trRadiusY}px';
}
if (rrect.brRadiusY == rrect.brRadiusX) {
style.borderBottomRightRadius = '${rrect.brRadiusX}px';
} else {
style.borderBottomRightRadius =
'${rrect.brRadiusX}px ${rrect.brRadiusY}px';
}
if (rrect.blRadiusY == rrect.blRadiusX) {
style.borderBottomLeftRadius = '${rrect.blRadiusX}px';
} else {
style.borderBottomLeftRadius =
'${rrect.blRadiusX}px ${rrect.blRadiusY}px';
}
// The clipping element will translate the coordinate system as well, which
// is not what a clip should do. To offset that we translate in the opposite
// direction.
super.translate(-rrect.left, -rrect.top);
currentElement.append(clip);
pushElement(clip);
}
@override
void clipPath(ui.Path path) {
// TODO(yjbanov): implement.
}
@override
void drawColor(ui.Color color, ui.BlendMode blendMode) {
// Drawn using CSS Paint.
}
@override
void drawLine(ui.Offset p1, ui.Offset p2, SurfacePaintData paint) {
// Drawn using CSS Paint.
}
@override
void drawPaint(SurfacePaintData paint) {
// Drawn using CSS Paint.
}
@override
void drawRect(ui.Rect rect, SurfacePaintData paint) {
// Drawn using CSS Paint.
}
@override
void drawRRect(ui.RRect rrect, SurfacePaintData paint) {
// Drawn using CSS Paint.
}
@override
void drawDRRect(ui.RRect outer, ui.RRect inner, SurfacePaintData paint) {
// Drawn using CSS Paint.
}
@override
void drawOval(ui.Rect rect, SurfacePaintData paint) {
// Drawn using CSS Paint.
}
@override
void drawCircle(ui.Offset c, double radius, SurfacePaintData paint) {
// Drawn using CSS Paint.
}
@override
void drawPath(ui.Path path, SurfacePaintData paint) {
// Drawn using CSS Paint.
}
@override
void drawShadow(ui.Path path, ui.Color color, double elevation,
bool transparentOccluder) {
// Drawn using CSS Paint.
}
@override
void drawImage(ui.Image image, ui.Offset p, SurfacePaintData paint) {
// TODO(yjbanov): implement.
}
@override
void drawImageRect(
ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint) {
// TODO(yjbanov): implement src rectangle
final HtmlImage htmlImage = image as HtmlImage;
final html.Element imageBox = html.Element.tag('flt-img');
final String cssTransform = matrix4ToCssTransform(
transformWithOffset(currentTransform, ui.Offset(dst.left, dst.top)));
imageBox.style
..position = 'absolute'
..transformOrigin = '0 0 0'
..width = '${dst.width.toInt()}px'
..height = '${dst.height.toInt()}px'
..transform = cssTransform
..backgroundImage = 'url(${htmlImage.imgElement.src})'
..backgroundRepeat = 'norepeat'
..backgroundSize = '${dst.width}px ${dst.height}px';
currentElement.append(imageBox);
}
@override
void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) {
final html.Element paragraphElement =
_drawParagraphElement(paragraph as EngineParagraph, offset, transform: currentTransform);
currentElement.append(paragraphElement);
}
@override
void drawVertices(
ui.Vertices vertices, ui.BlendMode blendMode, SurfacePaintData paint) {
// TODO(flutter_web): implement.
}
@override
void drawPoints(ui.PointMode pointMode, Float32List points, SurfacePaintData paint) {
// TODO(flutter_web): implement.
}
@override
void endOfPaint() {}
}
class _SaveElementStackEntry {
_SaveElementStackEntry({
required this.savedElement,
required this.transform,
});
final html.Element savedElement;
final Matrix4 transform;
}
/// Provides save stack tracking functionality to implementations of
/// [EngineCanvas].
mixin SaveElementStackTracking on EngineCanvas {
static final Vector3 _unitZ = Vector3(0.0, 0.0, 1.0);
final List<_SaveElementStackEntry> _saveStack = <_SaveElementStackEntry>[];
/// The element at the top of the element stack, or [rootElement] if the stack
/// is empty.
html.Element get currentElement =>
_elementStack.isEmpty ? rootElement : _elementStack.last;
/// The stack that maintains the DOM elements used to express certain paint
/// operations, such as clips.
final List<html.Element> _elementStack = <html.Element>[];
/// Pushes the [element] onto the element stack for the purposes of applying
/// a paint effect using a DOM element, e.g. for clipping.
///
/// The [restore] method automatically pops the element off the stack.
void pushElement(html.Element element) {
_elementStack.add(element);
}
/// Empties the save stack and the element stack, and resets the transform
/// and clip parameters.
///
/// Classes that override this method must call `super.clear()`.
@override
void clear() {
_saveStack.clear();
_elementStack.clear();
_currentTransform = Matrix4.identity();
}
/// The current transformation matrix.
Matrix4 get currentTransform => _currentTransform;
Matrix4 _currentTransform = Matrix4.identity();
/// Saves current clip and transform on the save stack.
///
/// Classes that override this method must call `super.save()`.
@override
void save() {
_saveStack.add(_SaveElementStackEntry(
savedElement: currentElement,
transform: _currentTransform.clone(),
));
}
/// Restores current clip and transform from the save stack.
///
/// Classes that override this method must call `super.restore()`.
@override
void restore() {
if (_saveStack.isEmpty) {
return;
}
final _SaveElementStackEntry entry = _saveStack.removeLast();
_currentTransform = entry.transform;
// Pop out of any clips.
while (currentElement != entry.savedElement) {
_elementStack.removeLast();
}
}
/// Multiplies the [currentTransform] matrix by a translation.
///
/// Classes that override this method must call `super.translate()`.
@override
void translate(double dx, double dy) {
_currentTransform.translate(dx, dy);
}
/// Scales the [currentTransform] matrix.
///
/// Classes that override this method must call `super.scale()`.
@override
void scale(double sx, double sy) {
_currentTransform.scale(sx, sy);
}
/// Rotates the [currentTransform] matrix.
///
/// Classes that override this method must call `super.rotate()`.
@override
void rotate(double radians) {
_currentTransform.rotate(_unitZ, radians);
}
/// Skews the [currentTransform] matrix.
///
/// Classes that override this method must call `super.skew()`.
@override
void skew(double sx, double sy) {
// DO NOT USE Matrix4.skew(sx, sy)! It treats sx and sy values as radians,
// but in our case they are transform matrix values.
final Matrix4 skewMatrix = Matrix4.identity();
final Float32List storage = skewMatrix.storage;
storage[1] = sy;
storage[4] = sx;
_currentTransform.multiply(skewMatrix);
}
/// Multiplies the [currentTransform] matrix by another matrix.
///
/// Classes that override this method must call `super.transform()`.
@override
void transform(Float32List matrix4) {
_currentTransform.multiply(Matrix4.fromFloat32List(matrix4));
}
}
......@@ -7,7 +7,6 @@ part of engine;
/// Renders an RRect using path primitives.
abstract class _RRectRenderer {
// TODO(mdebbar): Backport the overlapping corners fix to houdini_painter.js
// To draw the rounded rectangle, perform the following steps:
// 0. Ensure border radius don't overlap
// 1. Flip left,right top,bottom since web doesn't support flipped
......
......@@ -1542,12 +1542,6 @@ class SurfacePath implements ui.Path {
ui.Rect? get webOnlyPathAsCircle =>
pathRef.isOval == -1 ? null : pathRef.getBounds();
/// Serializes this path to a value that's sent to a CSS custom painter for
/// painting.
List<dynamic> webOnlySerializeToCssPaint() {
throw UnimplementedError();
}
/// Returns if Path is empty.
/// Empty Path may have FillType but has no points, verbs or weights.
/// Constructor, reset and rewind makes SkPath empty.
......
......@@ -71,292 +71,9 @@ void _recycleCanvas(EngineCanvas? canvas) {
}
}
/// Signature of a function that instantiates a [PersistedPicture].
typedef PersistedPictureFactory = PersistedPicture Function(
double dx,
double dy,
ui.Picture picture,
int hints,
);
/// Function used by the [SceneBuilder] to instantiate a picture layer.
PersistedPictureFactory persistedPictureFactory = standardPictureFactory;
/// Instantiates an implementation of a picture layer that uses DOM, CSS, and
/// 2D canvas for painting.
PersistedStandardPicture standardPictureFactory(
double dx, double dy, ui.Picture picture, int hints) {
return PersistedStandardPicture(dx, dy, picture, hints);
}
/// Instantiates an implementation of a picture layer that uses CSS Paint API
/// (part of Houdini) for painting.
PersistedHoudiniPicture houdiniPictureFactory(
double dx, double dy, ui.Picture picture, int hints) {
return PersistedHoudiniPicture(dx, dy, picture, hints);
}
class PersistedHoudiniPicture extends PersistedPicture {
PersistedHoudiniPicture(double dx, double dy, ui.Picture picture, int hints)
: super(dx, dy, picture as EnginePicture, hints) {
if (!_cssPainterRegistered) {
_registerCssPainter();
}
}
static bool _cssPainterRegistered = false;
@override
double matchForUpdate(PersistedPicture existingSurface) {
// Houdini is display list-based so all pictures are cheap to repaint.
// However, if the picture hasn't changed at all then it's completely
// free.
return existingSurface.picture == picture ? 0.0 : 1.0;
}
static void _registerCssPainter() {
_cssPainterRegistered = true;
final dynamic css = js_util.getProperty(html.window, 'CSS');
final dynamic paintWorklet = js_util.getProperty(css, 'paintWorklet');
if (paintWorklet == null) {
html.window.console.warn(
'WARNING: CSS.paintWorklet not available. Paint worklets are only '
'supported on sites served from https:// or http://localhost.');
return;
}
js_util.callMethod(
paintWorklet,
'addModule',
<dynamic>[
'/packages/flutter_web_ui/assets/houdini_painter.js',
],
);
}
/// Houdini does not paint to bitmap.
@override
int get bitmapPixelCount => 0;
@override
void applyPaint(EngineCanvas? oldCanvas) {
_recycleCanvas(oldCanvas);
final HoudiniCanvas canvas = HoudiniCanvas(_optimalLocalCullRect);
_canvas = canvas;
domRenderer.clearDom(rootElement!);
rootElement!.append(_canvas!.rootElement);
picture.recordingCanvas!.apply(_canvas, _optimalLocalCullRect);
canvas.commit();
}
}
class PersistedStandardPicture extends PersistedPicture {
PersistedStandardPicture(double dx, double dy, ui.Picture picture, int hints)
: super(dx, dy, picture as EnginePicture, hints);
@override
double matchForUpdate(PersistedStandardPicture existingSurface) {
if (existingSurface.picture == picture) {
// Picture is the same, return perfect score.
return 0.0;
}
if (!existingSurface.picture.recordingCanvas!.didDraw) {
// The previous surface didn't draw anything and therefore has no
// resources to reuse.
return 1.0;
}
final bool didRequireBitmap =
existingSurface.picture.recordingCanvas!.hasArbitraryPaint;
final bool requiresBitmap = picture.recordingCanvas!.hasArbitraryPaint;
if (didRequireBitmap != requiresBitmap) {
// Switching canvas types is always expensive.
return 1.0;
} else if (!requiresBitmap) {
// Currently DomCanvas is always expensive to repaint, as we always throw
// out all the DOM we rendered before. This may change in the future, at
// which point we may return other values here.
return 1.0;
} else {
final BitmapCanvas? oldCanvas = existingSurface._canvas as BitmapCanvas?;
if (oldCanvas == null) {
// We did not allocate a canvas last time. This can happen when the
// picture is completely clipped out of the view.
return 1.0;
} else if (!oldCanvas.doesFitBounds(_exactLocalCullRect!)) {
// The canvas needs to be resized before painting.
return 1.0;
} else {
final int newPixelCount = BitmapCanvas._widthToPhysical(_exactLocalCullRect!.width)
* BitmapCanvas._heightToPhysical(_exactLocalCullRect!.height);
final int oldPixelCount =
oldCanvas._widthInBitmapPixels * oldCanvas._heightInBitmapPixels;
if (oldPixelCount == 0) {
return 1.0;
}
final double pixelCountRatio = newPixelCount / oldPixelCount;
assert(0 <= pixelCountRatio && pixelCountRatio <= 1.0,
'Invalid pixel count ratio $pixelCountRatio');
return 1.0 - pixelCountRatio;
}
}
}
@override
Matrix4? get localTransformInverse => null;
@override
int get bitmapPixelCount {
if (_canvas is! BitmapCanvas) {
return 0;
}
final BitmapCanvas bitmapCanvas = _canvas as BitmapCanvas;
return bitmapCanvas.bitmapPixelCount;
}
@override
void applyPaint(EngineCanvas? oldCanvas) {
if (picture.recordingCanvas!.hasArbitraryPaint) {
_applyBitmapPaint(oldCanvas);
} else {
_applyDomPaint(oldCanvas);
}
}
void _applyDomPaint(EngineCanvas? oldCanvas) {
_recycleCanvas(oldCanvas);
_canvas = DomCanvas();
domRenderer.clearDom(rootElement!);
rootElement!.append(_canvas!.rootElement);
picture.recordingCanvas!.apply(_canvas, _optimalLocalCullRect);
}
void _applyBitmapPaint(EngineCanvas? oldCanvas) {
if (oldCanvas is BitmapCanvas &&
oldCanvas.doesFitBounds(_optimalLocalCullRect!) &&
oldCanvas.isReusable()) {
if (_debugShowCanvasReuseStats) {
DebugCanvasReuseOverlay.instance.keptCount++;
}
oldCanvas.bounds = _optimalLocalCullRect!;
_canvas = oldCanvas;
oldCanvas.setElementCache(_elementCache);
_canvas!.clear();
picture.recordingCanvas!.apply(_canvas, _optimalLocalCullRect);
} else {
// We can't use the old canvas because the size has changed, so we put
// it in a cache for later reuse.
_recycleCanvas(oldCanvas);
// We cannot paint immediately because not all canvases that we may be
// able to reuse have been released yet. So instead we enqueue this
// picture to be painted after the update cycle is done syncing the layer
// tree then reuse canvases that were freed up.
_paintQueue.add(_PaintRequest(
canvasSize: _optimalLocalCullRect!.size,
paintCallback: () {
_canvas = _findOrCreateCanvas(_optimalLocalCullRect!);
assert(_canvas is BitmapCanvas
&& (_canvas as BitmapCanvas?)!._elementCache == _elementCache);
if (_debugExplainSurfaceStats) {
final BitmapCanvas bitmapCanvas = _canvas as BitmapCanvas;
_surfaceStatsFor(this).paintPixelCount +=
bitmapCanvas.bitmapPixelCount;
}
domRenderer.clearDom(rootElement!);
rootElement!.append(_canvas!.rootElement);
_canvas!.clear();
picture.recordingCanvas!.apply(_canvas, _optimalLocalCullRect);
},
));
}
}
/// Attempts to reuse a canvas from the [_recycledCanvases]. Allocates a new
/// one if unable to reuse.
///
/// The best recycled canvas is one that:
///
/// - Fits the requested [canvasSize]. This is a hard requirement. Otherwise
/// we risk clipping the picture.
/// - Is the smallest among all possible reusable canvases. This makes canvas
/// reuse more efficient.
/// - Contains no more than twice the number of requested pixels. This makes
/// sure we do not use too much memory for small canvases.
BitmapCanvas _findOrCreateCanvas(ui.Rect bounds) {
final ui.Size canvasSize = bounds.size;
BitmapCanvas? bestRecycledCanvas;
double lastPixelCount = double.infinity;
for (int i = 0; i < _recycledCanvases.length; i++) {
final BitmapCanvas candidate = _recycledCanvases[i];
if (!candidate.isReusable()) {
continue;
}
final ui.Size candidateSize = candidate.size;
final double candidatePixelCount =
candidateSize.width * candidateSize.height;
final bool fits = candidate.doesFitBounds(bounds);
final bool isSmaller = candidatePixelCount < lastPixelCount;
if (fits && isSmaller) {
// [isTooSmall] is used to make sure that a small picture doesn't
// reuse and hold onto memory of a large canvas.
final double requestedPixelCount = bounds.width * bounds.height;
final bool isTooSmall = isSmaller &&
requestedPixelCount > 1 &&
(candidatePixelCount / requestedPixelCount) > 4;
if (!isTooSmall) {
bestRecycledCanvas = candidate;
lastPixelCount = candidatePixelCount;
final bool fitsExactly = candidateSize.width == canvasSize.width &&
candidateSize.height == canvasSize.height;
if (fitsExactly) {
// No need to keep looking any more.
break;
}
}
}
}
if (bestRecycledCanvas != null) {
if (_debugExplainSurfaceStats) {
_surfaceStatsFor(this).reuseCanvasCount++;
}
_recycledCanvases.remove(bestRecycledCanvas);
if (_debugShowCanvasReuseStats) {
DebugCanvasReuseOverlay.instance.inRecycleCount =
_recycledCanvases.length;
}
if (_debugShowCanvasReuseStats) {
DebugCanvasReuseOverlay.instance.reusedCount++;
}
bestRecycledCanvas.bounds = bounds;
bestRecycledCanvas.setElementCache(_elementCache);
return bestRecycledCanvas;
}
if (_debugShowCanvasReuseStats) {
DebugCanvasReuseOverlay.instance.createdCount++;
}
final BitmapCanvas canvas = BitmapCanvas(bounds);
canvas.setElementCache(_elementCache);
if (_debugExplainSurfaceStats) {
_surfaceStatsFor(this)
..allocateBitmapCanvasCount += 1
..allocatedBitmapSizeInPixels =
canvas._widthInBitmapPixels * canvas._heightInBitmapPixels;
}
return canvas;
}
}
/// A surface that uses a combination of `<canvas>`, `<div>` and `<p>` elements
/// to draw shapes and text.
abstract class PersistedPicture extends PersistedLeafSurface {
class PersistedPicture extends PersistedLeafSurface {
PersistedPicture(this.dx, this.dy, this.picture, this.hints)
: localPaintBounds = picture.recordingCanvas!.pictureBounds;
......@@ -553,7 +270,14 @@ abstract class PersistedPicture extends PersistedLeafSurface {
///
/// If the implementation does not paint onto a bitmap canvas, it should
/// return zero.
int get bitmapPixelCount;
int get bitmapPixelCount {
if (_canvas is! BitmapCanvas) {
return 0;
}
final BitmapCanvas bitmapCanvas = _canvas as BitmapCanvas;
return bitmapCanvas.bitmapPixelCount;
}
void _applyPaint(PersistedPicture? oldSurface) {
final EngineCanvas? oldCanvas = oldSurface?._canvas;
......@@ -575,8 +299,193 @@ abstract class PersistedPicture extends PersistedLeafSurface {
applyPaint(oldCanvas);
}
/// Concrete implementations implement this method to do actual painting.
void applyPaint(EngineCanvas? oldCanvas);
@override
double matchForUpdate(PersistedPicture existingSurface) {
if (existingSurface.picture == picture) {
// Picture is the same, return perfect score.
return 0.0;
}
if (!existingSurface.picture.recordingCanvas!.didDraw) {
// The previous surface didn't draw anything and therefore has no
// resources to reuse.
return 1.0;
}
final bool didRequireBitmap =
existingSurface.picture.recordingCanvas!.hasArbitraryPaint;
final bool requiresBitmap = picture.recordingCanvas!.hasArbitraryPaint;
if (didRequireBitmap != requiresBitmap) {
// Switching canvas types is always expensive.
return 1.0;
} else if (!requiresBitmap) {
// Currently DomCanvas is always expensive to repaint, as we always throw
// out all the DOM we rendered before. This may change in the future, at
// which point we may return other values here.
return 1.0;
} else {
final BitmapCanvas? oldCanvas = existingSurface._canvas as BitmapCanvas?;
if (oldCanvas == null) {
// We did not allocate a canvas last time. This can happen when the
// picture is completely clipped out of the view.
return 1.0;
} else if (!oldCanvas.doesFitBounds(_exactLocalCullRect!)) {
// The canvas needs to be resized before painting.
return 1.0;
} else {
final int newPixelCount = BitmapCanvas._widthToPhysical(_exactLocalCullRect!.width)
* BitmapCanvas._heightToPhysical(_exactLocalCullRect!.height);
final int oldPixelCount =
oldCanvas._widthInBitmapPixels * oldCanvas._heightInBitmapPixels;
if (oldPixelCount == 0) {
return 1.0;
}
final double pixelCountRatio = newPixelCount / oldPixelCount;
assert(0 <= pixelCountRatio && pixelCountRatio <= 1.0,
'Invalid pixel count ratio $pixelCountRatio');
return 1.0 - pixelCountRatio;
}
}
}
@override
Matrix4? get localTransformInverse => null;
void applyPaint(EngineCanvas? oldCanvas) {
if (picture.recordingCanvas!.hasArbitraryPaint) {
_applyBitmapPaint(oldCanvas);
} else {
_applyDomPaint(oldCanvas);
}
}
void _applyDomPaint(EngineCanvas? oldCanvas) {
_recycleCanvas(oldCanvas);
_canvas = DomCanvas();
domRenderer.clearDom(rootElement!);
rootElement!.append(_canvas!.rootElement);
picture.recordingCanvas!.apply(_canvas, _optimalLocalCullRect);
}
void _applyBitmapPaint(EngineCanvas? oldCanvas) {
if (oldCanvas is BitmapCanvas &&
oldCanvas.doesFitBounds(_optimalLocalCullRect!) &&
oldCanvas.isReusable()) {
if (_debugShowCanvasReuseStats) {
DebugCanvasReuseOverlay.instance.keptCount++;
}
oldCanvas.bounds = _optimalLocalCullRect!;
_canvas = oldCanvas;
oldCanvas.setElementCache(_elementCache);
_canvas!.clear();
picture.recordingCanvas!.apply(_canvas, _optimalLocalCullRect);
} else {
// We can't use the old canvas because the size has changed, so we put
// it in a cache for later reuse.
_recycleCanvas(oldCanvas);
// We cannot paint immediately because not all canvases that we may be
// able to reuse have been released yet. So instead we enqueue this
// picture to be painted after the update cycle is done syncing the layer
// tree then reuse canvases that were freed up.
_paintQueue.add(_PaintRequest(
canvasSize: _optimalLocalCullRect!.size,
paintCallback: () {
_canvas = _findOrCreateCanvas(_optimalLocalCullRect!);
assert(_canvas is BitmapCanvas
&& (_canvas as BitmapCanvas?)!._elementCache == _elementCache);
if (_debugExplainSurfaceStats) {
final BitmapCanvas bitmapCanvas = _canvas as BitmapCanvas;
_surfaceStatsFor(this).paintPixelCount +=
bitmapCanvas.bitmapPixelCount;
}
domRenderer.clearDom(rootElement!);
rootElement!.append(_canvas!.rootElement);
_canvas!.clear();
picture.recordingCanvas!.apply(_canvas, _optimalLocalCullRect);
},
));
}
}
/// Attempts to reuse a canvas from the [_recycledCanvases]. Allocates a new
/// one if unable to reuse.
///
/// The best recycled canvas is one that:
///
/// - Fits the requested [canvasSize]. This is a hard requirement. Otherwise
/// we risk clipping the picture.
/// - Is the smallest among all possible reusable canvases. This makes canvas
/// reuse more efficient.
/// - Contains no more than twice the number of requested pixels. This makes
/// sure we do not use too much memory for small canvases.
BitmapCanvas _findOrCreateCanvas(ui.Rect bounds) {
final ui.Size canvasSize = bounds.size;
BitmapCanvas? bestRecycledCanvas;
double lastPixelCount = double.infinity;
for (int i = 0; i < _recycledCanvases.length; i++) {
final BitmapCanvas candidate = _recycledCanvases[i];
if (!candidate.isReusable()) {
continue;
}
final ui.Size candidateSize = candidate.size;
final double candidatePixelCount =
candidateSize.width * candidateSize.height;
final bool fits = candidate.doesFitBounds(bounds);
final bool isSmaller = candidatePixelCount < lastPixelCount;
if (fits && isSmaller) {
// [isTooSmall] is used to make sure that a small picture doesn't
// reuse and hold onto memory of a large canvas.
final double requestedPixelCount = bounds.width * bounds.height;
final bool isTooSmall = isSmaller &&
requestedPixelCount > 1 &&
(candidatePixelCount / requestedPixelCount) > 4;
if (!isTooSmall) {
bestRecycledCanvas = candidate;
lastPixelCount = candidatePixelCount;
final bool fitsExactly = candidateSize.width == canvasSize.width &&
candidateSize.height == canvasSize.height;
if (fitsExactly) {
// No need to keep looking any more.
break;
}
}
}
}
if (bestRecycledCanvas != null) {
if (_debugExplainSurfaceStats) {
_surfaceStatsFor(this).reuseCanvasCount++;
}
_recycledCanvases.remove(bestRecycledCanvas);
if (_debugShowCanvasReuseStats) {
DebugCanvasReuseOverlay.instance.inRecycleCount =
_recycledCanvases.length;
}
if (_debugShowCanvasReuseStats) {
DebugCanvasReuseOverlay.instance.reusedCount++;
}
bestRecycledCanvas.bounds = bounds;
bestRecycledCanvas.setElementCache(_elementCache);
return bestRecycledCanvas;
}
if (_debugShowCanvasReuseStats) {
DebugCanvasReuseOverlay.instance.createdCount++;
}
final BitmapCanvas canvas = BitmapCanvas(bounds);
canvas.setElementCache(_elementCache);
if (_debugExplainSurfaceStats) {
_surfaceStatsFor(this)
..allocateBitmapCanvasCount += 1
..allocatedBitmapSizeInPixels =
canvas._widthInBitmapPixels * canvas._heightInBitmapPixels;
}
return canvas;
}
void _applyTranslate() {
rootElement!.style.transform = 'translate(${dx}px, ${dy}px)';
......
......@@ -608,8 +608,6 @@ abstract class PaintCommand {
const PaintCommand();
void apply(EngineCanvas? canvas);
void serializeToCssPaint(List<List<dynamic>> serializedCommands);
}
/// A [PaintCommand] that affect pixels on the screen (unlike, for example, the
......@@ -665,11 +663,6 @@ class PaintSave extends PaintCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(const <int>[1]);
}
}
class PaintRestore extends PaintCommand {
......@@ -688,11 +681,6 @@ class PaintRestore extends PaintCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(const <int>[2]);
}
}
class PaintTranslate extends PaintCommand {
......@@ -714,11 +702,6 @@ class PaintTranslate extends PaintCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<num>[3, dx, dy]);
}
}
class PaintScale extends PaintCommand {
......@@ -740,11 +723,6 @@ class PaintScale extends PaintCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<num>[4, sx, sy]);
}
}
class PaintRotate extends PaintCommand {
......@@ -765,11 +743,6 @@ class PaintRotate extends PaintCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<num>[5, radians]);
}
}
class PaintTransform extends PaintCommand {
......@@ -790,11 +763,6 @@ class PaintTransform extends PaintCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[6]..addAll(matrix4));
}
}
class PaintSkew extends PaintCommand {
......@@ -816,11 +784,6 @@ class PaintSkew extends PaintCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<num>[7, sx, sy]);
}
}
class PaintClipRect extends DrawCommand {
......@@ -841,11 +804,6 @@ class PaintClipRect extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[8, _serializeRectToCssPaint(rect)]);
}
}
class PaintClipRRect extends DrawCommand {
......@@ -866,14 +824,6 @@ class PaintClipRRect extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[
9,
_serializeRRectToCssPaint(rrect),
]);
}
}
class PaintClipPath extends DrawCommand {
......@@ -894,11 +844,6 @@ class PaintClipPath extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[10, path.webOnlySerializeToCssPaint()]);
}
}
class PaintDrawColor extends DrawCommand {
......@@ -920,12 +865,6 @@ class PaintDrawColor extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands
.add(<dynamic>[11, colorToCssString(color), blendMode.index]);
}
}
class PaintDrawLine extends DrawCommand {
......@@ -948,18 +887,6 @@ class PaintDrawLine extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[
12,
p1.dx,
p1.dy,
p2.dx,
p2.dy,
_serializePaintToCssPaint(paint)
]);
}
}
class PaintDrawPaint extends DrawCommand {
......@@ -980,11 +907,6 @@ class PaintDrawPaint extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[13, _serializePaintToCssPaint(paint)]);
}
}
class PaintDrawVertices extends DrawCommand {
......@@ -1006,11 +928,6 @@ class PaintDrawVertices extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
throw UnimplementedError();
}
}
class PaintDrawPoints extends DrawCommand {
......@@ -1032,11 +949,6 @@ class PaintDrawPoints extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
throw UnimplementedError();
}
}
class PaintDrawRect extends DrawCommand {
......@@ -1058,15 +970,6 @@ class PaintDrawRect extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[
14,
_serializeRectToCssPaint(rect),
_serializePaintToCssPaint(paint)
]);
}
}
class PaintDrawRRect extends DrawCommand {
......@@ -1088,15 +991,6 @@ class PaintDrawRRect extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[
15,
_serializeRRectToCssPaint(rrect),
_serializePaintToCssPaint(paint),
]);
}
}
class PaintDrawDRRect extends DrawCommand {
......@@ -1125,16 +1019,6 @@ class PaintDrawDRRect extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[
16,
_serializeRRectToCssPaint(outer),
_serializeRRectToCssPaint(inner),
_serializePaintToCssPaint(paint),
]);
}
}
class PaintDrawOval extends DrawCommand {
......@@ -1156,15 +1040,6 @@ class PaintDrawOval extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[
17,
_serializeRectToCssPaint(rect),
_serializePaintToCssPaint(paint),
]);
}
}
class PaintDrawCircle extends DrawCommand {
......@@ -1187,17 +1062,6 @@ class PaintDrawCircle extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[
18,
c.dx,
c.dy,
radius,
_serializePaintToCssPaint(paint),
]);
}
}
class PaintDrawPath extends DrawCommand {
......@@ -1219,15 +1083,6 @@ class PaintDrawPath extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[
19,
path.webOnlySerializeToCssPaint(),
_serializePaintToCssPaint(paint),
]);
}
}
class PaintDrawShadow extends DrawCommand {
......@@ -1252,22 +1107,6 @@ class PaintDrawShadow extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
serializedCommands.add(<dynamic>[
20,
path.webOnlySerializeToCssPaint(),
<dynamic>[
color.alpha,
color.red,
color.green,
color.blue,
],
elevation,
transparentOccluder,
]);
}
}
class PaintDrawImage extends DrawCommand {
......@@ -1290,13 +1129,6 @@ class PaintDrawImage extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
if (assertionsEnabled) {
throw UnsupportedError('drawImage not serializable');
}
}
}
class PaintDrawImageRect extends DrawCommand {
......@@ -1320,13 +1152,6 @@ class PaintDrawImageRect extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
if (assertionsEnabled) {
throw UnsupportedError('drawImageRect not serializable');
}
}
}
class PaintDrawParagraph extends DrawCommand {
......@@ -1348,55 +1173,6 @@ class PaintDrawParagraph extends DrawCommand {
return super.toString();
}
}
@override
void serializeToCssPaint(List<List<dynamic>> serializedCommands) {
if (assertionsEnabled) {
throw UnsupportedError('drawParagraph not serializable');
}
}
}
List<dynamic> _serializePaintToCssPaint(SurfacePaintData paint) {
final EngineGradient? engineShader = paint.shader as EngineGradient?;
return <dynamic>[
paint.blendMode?.index,
paint.style?.index,
paint.strokeWidth,
paint.strokeCap?.index,
paint.isAntiAlias,
colorToCssString(paint.color),
engineShader?.webOnlySerializeToCssPaint(),
paint.maskFilter?.webOnlySerializeToCssPaint(),
paint.filterQuality?.index,
paint.colorFilter?.webOnlySerializeToCssPaint(),
];
}
List<dynamic> _serializeRectToCssPaint(ui.Rect rect) {
return <dynamic>[
rect.left,
rect.top,
rect.right,
rect.bottom,
];
}
List<dynamic> _serializeRRectToCssPaint(ui.RRect rrect) {
return <dynamic>[
rrect.left,
rrect.top,
rrect.right,
rrect.bottom,
rrect.tlRadiusX,
rrect.tlRadiusY,
rrect.trRadiusX,
rrect.trRadiusY,
rrect.brRadiusX,
rrect.brRadiusY,
rrect.blRadiusX,
rrect.blRadiusY,
];
}
class Subpath {
......@@ -1421,14 +1197,6 @@ class Subpath {
return result;
}
List<dynamic> serializeToCssPaint() {
final List<dynamic> serialization = <dynamic>[];
for (int i = 0; i < commands.length; i++) {
serialization.add(commands[i].serializeToCssPaint());
}
return serialization;
}
@override
String toString() {
if (assertionsEnabled) {
......@@ -1439,26 +1207,11 @@ class Subpath {
}
}
/// ! Houdini implementation relies on indices here. Keep in sync.
class PathCommandTypes {
static const int moveTo = 0;
static const int lineTo = 1;
static const int ellipse = 2;
static const int close = 3;
static const int quadraticCurveTo = 4;
static const int bezierCurveTo = 5;
static const int rect = 6;
static const int rRect = 7;
}
abstract class PathCommand {
final int type;
const PathCommand(this.type);
const PathCommand();
PathCommand shifted(ui.Offset offset);
List<dynamic> serializeToCssPaint();
/// Transform the command and add to targetPath.
void transform(Float32List matrix4, SurfacePath targetPath);
......@@ -1472,18 +1225,13 @@ class MoveTo extends PathCommand {
final double x;
final double y;
const MoveTo(this.x, this.y) : super(PathCommandTypes.moveTo);
const MoveTo(this.x, this.y);
@override
MoveTo shifted(ui.Offset offset) {
return MoveTo(x + offset.dx, y + offset.dy);
}
@override
List<dynamic> serializeToCssPaint() {
return <dynamic>[1, x, y];
}
@override
void transform(Float32List matrix4, ui.Path targetPath) {
final ui.Offset offset = PathCommand._transformOffset(x, y, matrix4);
......@@ -1504,18 +1252,13 @@ class LineTo extends PathCommand {
final double x;
final double y;
const LineTo(this.x, this.y) : super(PathCommandTypes.lineTo);
const LineTo(this.x, this.y);
@override
LineTo shifted(ui.Offset offset) {
return LineTo(x + offset.dx, y + offset.dy);
}
@override
List<dynamic> serializeToCssPaint() {
return <dynamic>[2, x, y];
}
@override
void transform(Float32List matrix4, ui.Path targetPath) {
final ui.Offset offset = PathCommand._transformOffset(x, y, matrix4);
......@@ -1543,8 +1286,7 @@ class Ellipse extends PathCommand {
final bool anticlockwise;
const Ellipse(this.x, this.y, this.radiusX, this.radiusY, this.rotation,
this.startAngle, this.endAngle, this.anticlockwise)
: super(PathCommandTypes.ellipse);
this.startAngle, this.endAngle, this.anticlockwise);
@override
Ellipse shifted(ui.Offset offset) {
......@@ -1552,21 +1294,6 @@ class Ellipse extends PathCommand {
startAngle, endAngle, anticlockwise);
}
@override
List<dynamic> serializeToCssPaint() {
return <dynamic>[
3,
x,
y,
radiusX,
radiusY,
rotation,
startAngle,
endAngle,
anticlockwise,
];
}
@override
void transform(Float32List matrix4, SurfacePath targetPath) {
final ui.Path bezierPath = ui.Path();
......@@ -1686,8 +1413,7 @@ class QuadraticCurveTo extends PathCommand {
final double x2;
final double y2;
const QuadraticCurveTo(this.x1, this.y1, this.x2, this.y2)
: super(PathCommandTypes.quadraticCurveTo);
const QuadraticCurveTo(this.x1, this.y1, this.x2, this.y2);
@override
QuadraticCurveTo shifted(ui.Offset offset) {
......@@ -1695,11 +1421,6 @@ class QuadraticCurveTo extends PathCommand {
x1 + offset.dx, y1 + offset.dy, x2 + offset.dx, y2 + offset.dy);
}
@override
List<dynamic> serializeToCssPaint() {
return <dynamic>[4, x1, y1, x2, y2];
}
@override
void transform(Float32List matrix4, ui.Path targetPath) {
final double m0 = matrix4[0];
......@@ -1734,8 +1455,7 @@ class BezierCurveTo extends PathCommand {
final double x3;
final double y3;
const BezierCurveTo(this.x1, this.y1, this.x2, this.y2, this.x3, this.y3)
: super(PathCommandTypes.bezierCurveTo);
const BezierCurveTo(this.x1, this.y1, this.x2, this.y2, this.x3, this.y3);
@override
BezierCurveTo shifted(ui.Offset offset) {
......@@ -1743,11 +1463,6 @@ class BezierCurveTo extends PathCommand {
y2 + offset.dy, x3 + offset.dx, y3 + offset.dy);
}
@override
List<dynamic> serializeToCssPaint() {
return <dynamic>[5, x1, y1, x2, y2, x3, y3];
}
@override
void transform(Float32List matrix4, ui.Path targetPath) {
final double s0 = matrix4[0];
......@@ -1782,8 +1497,7 @@ class RectCommand extends PathCommand {
final double width;
final double height;
const RectCommand(this.x, this.y, this.width, this.height)
: super(PathCommandTypes.rect);
const RectCommand(this.x, this.y, this.width, this.height);
@override
RectCommand shifted(ui.Offset offset) {
......@@ -1824,11 +1538,6 @@ class RectCommand extends PathCommand {
}
}
@override
List<dynamic> serializeToCssPaint() {
return <dynamic>[6, x, y, width, height];
}
@override
String toString() {
if (assertionsEnabled) {
......@@ -1842,18 +1551,13 @@ class RectCommand extends PathCommand {
class RRectCommand extends PathCommand {
final ui.RRect rrect;
const RRectCommand(this.rrect) : super(PathCommandTypes.rRect);
const RRectCommand(this.rrect);
@override
RRectCommand shifted(ui.Offset offset) {
return RRectCommand(rrect.shift(offset));
}
@override
List<dynamic> serializeToCssPaint() {
return <dynamic>[7, _serializeRRectToCssPaint(rrect)];
}
@override
void transform(Float32List matrix4, SurfacePath targetPath) {
final ui.Path roundRectPath = ui.Path();
......@@ -1872,18 +1576,11 @@ class RRectCommand extends PathCommand {
}
class CloseCommand extends PathCommand {
const CloseCommand() : super(PathCommandTypes.close);
@override
CloseCommand shifted(ui.Offset offset) {
return this;
}
@override
List<dynamic> serializeToCssPaint() {
return <dynamic>[8];
}
@override
void transform(Float32List matrix4, ui.Path targetPath) {
targetPath.close();
......
......@@ -363,7 +363,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
if (willChangeHint) {
hints |= 2;
}
_addSurface(persistedPictureFactory(offset.dx, offset.dy, picture, hints));
_addSurface(PersistedPicture(offset.dx, offset.dy, picture as EnginePicture, hints));
}
/// Adds a backend texture to the scene.
......
......@@ -11,10 +11,6 @@ abstract class EngineGradient implements ui.Gradient {
/// Creates a fill style to be used in painting.
Object createPaintStyle(html.CanvasRenderingContext2D? ctx);
List<dynamic> webOnlySerializeToCssPaint() {
throw UnsupportedError('CSS paint not implemented for this shader type');
}
}
class GradientSweep extends EngineGradient {
......@@ -100,24 +96,6 @@ class GradientLinear extends EngineGradient {
}
return gradient;
}
@override
List<dynamic> webOnlySerializeToCssPaint() {
final List<dynamic> serializedColors = <dynamic>[];
for (int i = 0; i < colors.length; i++) {
serializedColors.add(colorToCssString(colors[i]));
}
return <dynamic>[
1,
from.dx,
from.dy,
to.dx,
to.dy,
serializedColors,
colorStops,
tileMode.index
];
}
}
// TODO(flutter_web): For transforms and tile modes implement as webgl
......
......@@ -237,7 +237,7 @@ void _debugPrintSurfaceStats(PersistedScene scene, int frameNumber) {
elementReuseCount += stats.reuseElementCount;
totalAllocatedDomNodeCount += stats.allocatedDomNodeCount;
if (surface is PersistedStandardPicture) {
if (surface is PersistedPicture) {
pictureCount += 1;
paintCount += stats.paintCount;
......
......@@ -1323,10 +1323,6 @@ abstract class ColorFilter {
/// to the RGB channels.
const factory ColorFilter.srgbToLinearGamma() =
engine.EngineColorFilter.srgbToLinearGamma;
List<dynamic> webOnlySerializeToCssPaint() {
throw UnsupportedError('ColorFilter for CSS paint not yet supported');
}
}
/// Styles to use for blurs in [MaskFilter] objects.
......@@ -1402,10 +1398,6 @@ class MaskFilter {
@override
int get hashCode => hashValues(_style, _sigma);
List<dynamic> webOnlySerializeToCssPaint() {
return <dynamic>[_style.index, _sigma];
}
@override
String toString() => 'MaskFilter.blur($_style, ${_sigma.toStringAsFixed(1)})';
}
......
......@@ -26,7 +26,6 @@ void main() {
test(description, () {
testFn(BitmapCanvas(canvasSize));
testFn(DomCanvas());
testFn(HoudiniCanvas(canvasSize));
testFn(mockCanvas = MockEngineCanvas());
if (whenDone != null) {
whenDone();
......
......@@ -133,7 +133,7 @@ void _testCullRectComputation() {
});
builder.build();
final PersistedStandardPicture picture = enumeratePictures().single;
final PersistedPicture picture = enumeratePictures().single;
expect(picture.optimalLocalCullRect, const Rect.fromLTRB(0, 0, 500, 100));
}, skip: '''TODO(https://github.com/flutter/flutter/issues/40395)
Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500''');
......@@ -147,7 +147,7 @@ void _testCullRectComputation() {
});
builder.build();
final PersistedStandardPicture picture = enumeratePictures().single;
final PersistedPicture picture = enumeratePictures().single;
expect(picture.optimalLocalCullRect, const Rect.fromLTRB(0, 0, 20, 20));
});
......@@ -161,7 +161,7 @@ void _testCullRectComputation() {
});
builder.build();
final PersistedStandardPicture picture = enumeratePictures().single;
final PersistedPicture picture = enumeratePictures().single;
expect(picture.optimalLocalCullRect, Rect.zero);
expect(picture.debugExactGlobalCullRect, Rect.zero);
});
......@@ -176,7 +176,7 @@ void _testCullRectComputation() {
});
builder.build();
final PersistedStandardPicture picture = enumeratePictures().single;
final PersistedPicture picture = enumeratePictures().single;
expect(picture.optimalLocalCullRect, const Rect.fromLTRB(40, 40, 60, 60));
});
......@@ -195,7 +195,7 @@ void _testCullRectComputation() {
builder.build();
final PersistedStandardPicture picture = enumeratePictures().single;
final PersistedPicture picture = enumeratePictures().single;
expect(picture.optimalLocalCullRect, const Rect.fromLTRB(40, 40, 60, 60));
});
......@@ -213,7 +213,7 @@ void _testCullRectComputation() {
builder.build();
final PersistedStandardPicture picture = enumeratePictures().single;
final PersistedPicture picture = enumeratePictures().single;
expect(
picture.debugExactGlobalCullRect, const Rect.fromLTRB(0, 70, 20, 100));
expect(picture.optimalLocalCullRect, const Rect.fromLTRB(0, -20, 20, 10));
......@@ -244,7 +244,7 @@ void _testCullRectComputation() {
await matchGoldenFile('compositing_cull_rect_fills_layer_clip.png',
region: region);
final PersistedStandardPicture picture = enumeratePictures().single;
final PersistedPicture picture = enumeratePictures().single;
expect(picture.optimalLocalCullRect, const Rect.fromLTRB(40, 40, 70, 70));
});
......@@ -274,7 +274,7 @@ void _testCullRectComputation() {
'compositing_cull_rect_intersects_clip_and_paint_bounds.png',
region: region);
final PersistedStandardPicture picture = enumeratePictures().single;
final PersistedPicture picture = enumeratePictures().single;
expect(picture.optimalLocalCullRect, const Rect.fromLTRB(50, 40, 70, 70));
});
......@@ -305,7 +305,7 @@ void _testCullRectComputation() {
await matchGoldenFile('compositing_cull_rect_offset_inside_layer_clip.png',
region: region);
final PersistedStandardPicture picture = enumeratePictures().single;
final PersistedPicture picture = enumeratePictures().single;
expect(picture.optimalLocalCullRect,
const Rect.fromLTRB(-15.0, -20.0, 15.0, 0.0));
});
......@@ -335,7 +335,7 @@ void _testCullRectComputation() {
builder.build();
final PersistedStandardPicture picture = enumeratePictures().single;
final PersistedPicture picture = enumeratePictures().single;
expect(picture.optimalLocalCullRect, Rect.zero);
expect(picture.debugExactGlobalCullRect, Rect.zero);
});
......@@ -378,7 +378,7 @@ void _testCullRectComputation() {
await matchGoldenFile('compositing_cull_rect_rotated.png', region: region);
final PersistedStandardPicture picture = enumeratePictures().single;
final PersistedPicture picture = enumeratePictures().single;
expect(
picture.optimalLocalCullRect,
within(
......@@ -510,7 +510,7 @@ void _testCullRectComputation() {
await matchGoldenFile('compositing_3d_rotate1.png', region: region);
// ignore: unused_local_variable
final PersistedStandardPicture picture = enumeratePictures().single;
final PersistedPicture picture = enumeratePictures().single;
// TODO(https://github.com/flutter/flutter/issues/40395):
// Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500.
// expect(
......
......@@ -91,7 +91,7 @@ typedef CanvasTest = FutureOr<void> Function(EngineCanvas canvas);
/// Runs the given test [body] with each type of canvas.
void testEachCanvas(String description, CanvasTest body,
{double maxDiffRate, bool bSkipHoudini = false}) {
{double maxDiffRate}) {
const ui.Rect bounds = ui.Rect.fromLTWH(0, 0, 600, 800);
test('$description (bitmap)', () {
try {
......@@ -123,18 +123,6 @@ void testEachCanvas(String description, CanvasTest body,
TextMeasurementService.clearCache();
}
});
if (!bSkipHoudini) {
test('$description (houdini)', () {
try {
TextMeasurementService.initialize(rulerCacheCapacity: 2);
WebExperiments.instance.useCanvasText = false;
return body(HoudiniCanvas(bounds));
} finally {
WebExperiments.instance.useCanvasText = null;
TextMeasurementService.clearCache();
}
});
}
}
final ui.TextStyle _defaultTextStyle = ui.TextStyle(
......
......@@ -30,7 +30,7 @@ void main() async {
recordingCanvas.endRecording();
recordingCanvas.apply(canvas, screenRect);
return scuba.diffCanvasScreenshot(canvas, 'text_with_placeholders');
}, bSkipHoudini: true);
});
}
const Color black = Color(0xFF000000);
......
......@@ -220,7 +220,7 @@ void main() async {
testEachCanvas('draws text with a shadow', (EngineCanvas canvas) {
drawTextWithShadow(canvas);
return scuba.diffCanvasScreenshot(canvas, 'text_shadow', maxDiffRatePercent: 0.2);
}, bSkipHoudini: true);
});
testEachCanvas('Handles disabled strut style', (EngineCanvas canvas) {
// Flutter uses [StrutStyle.disabled] for the [SelectableText] widget. This
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册