mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
parent
2ac5b3460a
commit
5c8a62608b
@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,370 +0,0 @@
|
||||
// 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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user