Revert "Reland [skwasm] Scene builder optimizations for platform view placement (#55468)" (flutter/engine#55715)
This reverts commit b99e758ee1025d5bab2ecff1d62ff75a17b68996 (https://github.com/flutter/engine/pull/55468) Reason for revert, devtools has been having rendering issues since this commit. See https://github.com/flutter/devtools/issues/8401
This commit is contained in:
parent
4d14f8631b
commit
db449a1603
@ -135,7 +135,4 @@ class EngineColorFilter implements SceneImageFilter, ui.ColorFilter {
|
|||||||
return 'ColorFilter.srgbToLinearGamma()';
|
return 'ColorFilter.srgbToLinearGamma()';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Matrix4? get transform => null;
|
|
||||||
}
|
}
|
||||||
|
@ -5,51 +5,15 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:ui/src/engine.dart';
|
import 'package:ui/src/engine/scene_painting.dart';
|
||||||
|
import 'package:ui/src/engine/vector_math.dart';
|
||||||
import 'package:ui/ui.dart' as ui;
|
import 'package:ui/ui.dart' as ui;
|
||||||
|
|
||||||
class EngineRootLayer with PictureEngineLayer {
|
class EngineRootLayer with PictureEngineLayer {}
|
||||||
@override
|
|
||||||
final NoopOperation operation = const NoopOperation();
|
|
||||||
|
|
||||||
@override
|
|
||||||
EngineRootLayer emptyClone() => EngineRootLayer();
|
|
||||||
}
|
|
||||||
|
|
||||||
class NoopOperation implements LayerOperation {
|
|
||||||
const NoopOperation();
|
|
||||||
|
|
||||||
@override
|
|
||||||
PlatformViewStyling createPlatformViewStyling() => const PlatformViewStyling();
|
|
||||||
|
|
||||||
@override
|
|
||||||
ui.Rect mapRect(ui.Rect contentRect) => contentRect;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void pre(SceneCanvas canvas) {
|
|
||||||
canvas.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void post(SceneCanvas canvas) {
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool get shouldDrawIfEmpty => false;
|
|
||||||
}
|
|
||||||
|
|
||||||
class BackdropFilterLayer
|
class BackdropFilterLayer
|
||||||
with PictureEngineLayer
|
with PictureEngineLayer
|
||||||
implements ui.BackdropFilterEngineLayer {
|
implements ui.BackdropFilterEngineLayer {}
|
||||||
BackdropFilterLayer(this.operation);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final LayerOperation operation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
BackdropFilterLayer emptyClone() => BackdropFilterLayer(operation);
|
|
||||||
}
|
|
||||||
class BackdropFilterOperation implements LayerOperation {
|
class BackdropFilterOperation implements LayerOperation {
|
||||||
BackdropFilterOperation(this.filter, this.mode);
|
BackdropFilterOperation(this.filter, this.mode);
|
||||||
|
|
||||||
@ -60,12 +24,12 @@ class BackdropFilterOperation implements LayerOperation {
|
|||||||
ui.Rect mapRect(ui.Rect contentRect) => contentRect;
|
ui.Rect mapRect(ui.Rect contentRect) => contentRect;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void pre(SceneCanvas canvas) {
|
void pre(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
canvas.saveLayerWithFilter(ui.Rect.largest, ui.Paint()..blendMode = mode, filter);
|
canvas.saveLayerWithFilter(contentRect, ui.Paint()..blendMode = mode, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void post(SceneCanvas canvas) {
|
void post(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,15 +44,7 @@ class BackdropFilterOperation implements LayerOperation {
|
|||||||
|
|
||||||
class ClipPathLayer
|
class ClipPathLayer
|
||||||
with PictureEngineLayer
|
with PictureEngineLayer
|
||||||
implements ui.ClipPathEngineLayer {
|
implements ui.ClipPathEngineLayer {}
|
||||||
ClipPathLayer(this.operation);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final ClipPathOperation operation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
ClipPathLayer emptyClone() => ClipPathLayer(operation);
|
|
||||||
}
|
|
||||||
class ClipPathOperation implements LayerOperation {
|
class ClipPathOperation implements LayerOperation {
|
||||||
ClipPathOperation(this.path, this.clip);
|
ClipPathOperation(this.path, this.clip);
|
||||||
|
|
||||||
@ -99,7 +55,7 @@ class ClipPathOperation implements LayerOperation {
|
|||||||
ui.Rect mapRect(ui.Rect contentRect) => contentRect.intersect(path.getBounds());
|
ui.Rect mapRect(ui.Rect contentRect) => contentRect.intersect(path.getBounds());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void pre(SceneCanvas canvas) {
|
void pre(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.clipPath(path, doAntiAlias: clip != ui.Clip.hardEdge);
|
canvas.clipPath(path, doAntiAlias: clip != ui.Clip.hardEdge);
|
||||||
if (clip == ui.Clip.antiAliasWithSaveLayer) {
|
if (clip == ui.Clip.antiAliasWithSaveLayer) {
|
||||||
@ -108,7 +64,7 @@ class ClipPathOperation implements LayerOperation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void post(SceneCanvas canvas) {
|
void post(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
if (clip == ui.Clip.antiAliasWithSaveLayer) {
|
if (clip == ui.Clip.antiAliasWithSaveLayer) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
@ -126,15 +82,7 @@ class ClipPathOperation implements LayerOperation {
|
|||||||
|
|
||||||
class ClipRectLayer
|
class ClipRectLayer
|
||||||
with PictureEngineLayer
|
with PictureEngineLayer
|
||||||
implements ui.ClipRectEngineLayer {
|
implements ui.ClipRectEngineLayer {}
|
||||||
ClipRectLayer(this.operation);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final ClipRectOperation operation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
ClipRectLayer emptyClone() => ClipRectLayer(operation);
|
|
||||||
}
|
|
||||||
class ClipRectOperation implements LayerOperation {
|
class ClipRectOperation implements LayerOperation {
|
||||||
const ClipRectOperation(this.rect, this.clip);
|
const ClipRectOperation(this.rect, this.clip);
|
||||||
|
|
||||||
@ -145,7 +93,7 @@ class ClipRectOperation implements LayerOperation {
|
|||||||
ui.Rect mapRect(ui.Rect contentRect) => contentRect.intersect(rect);
|
ui.Rect mapRect(ui.Rect contentRect) => contentRect.intersect(rect);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void pre(SceneCanvas canvas) {
|
void pre(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.clipRect(rect, doAntiAlias: clip != ui.Clip.hardEdge);
|
canvas.clipRect(rect, doAntiAlias: clip != ui.Clip.hardEdge);
|
||||||
if (clip == ui.Clip.antiAliasWithSaveLayer) {
|
if (clip == ui.Clip.antiAliasWithSaveLayer) {
|
||||||
@ -154,7 +102,7 @@ class ClipRectOperation implements LayerOperation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void post(SceneCanvas canvas) {
|
void post(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
if (clip == ui.Clip.antiAliasWithSaveLayer) {
|
if (clip == ui.Clip.antiAliasWithSaveLayer) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
@ -172,15 +120,7 @@ class ClipRectOperation implements LayerOperation {
|
|||||||
|
|
||||||
class ClipRRectLayer
|
class ClipRRectLayer
|
||||||
with PictureEngineLayer
|
with PictureEngineLayer
|
||||||
implements ui.ClipRRectEngineLayer {
|
implements ui.ClipRRectEngineLayer {}
|
||||||
ClipRRectLayer(this.operation);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final ClipRRectOperation operation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
ClipRRectLayer emptyClone() => ClipRRectLayer(operation);
|
|
||||||
}
|
|
||||||
class ClipRRectOperation implements LayerOperation {
|
class ClipRRectOperation implements LayerOperation {
|
||||||
const ClipRRectOperation(this.rrect, this.clip);
|
const ClipRRectOperation(this.rrect, this.clip);
|
||||||
|
|
||||||
@ -191,7 +131,7 @@ class ClipRRectOperation implements LayerOperation {
|
|||||||
ui.Rect mapRect(ui.Rect contentRect) => contentRect.intersect(rrect.outerRect);
|
ui.Rect mapRect(ui.Rect contentRect) => contentRect.intersect(rrect.outerRect);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void pre(SceneCanvas canvas) {
|
void pre(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.clipRRect(rrect, doAntiAlias: clip != ui.Clip.hardEdge);
|
canvas.clipRRect(rrect, doAntiAlias: clip != ui.Clip.hardEdge);
|
||||||
if (clip == ui.Clip.antiAliasWithSaveLayer) {
|
if (clip == ui.Clip.antiAliasWithSaveLayer) {
|
||||||
@ -200,7 +140,7 @@ class ClipRRectOperation implements LayerOperation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void post(SceneCanvas canvas) {
|
void post(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
if (clip == ui.Clip.antiAliasWithSaveLayer) {
|
if (clip == ui.Clip.antiAliasWithSaveLayer) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
@ -218,15 +158,7 @@ class ClipRRectOperation implements LayerOperation {
|
|||||||
|
|
||||||
class ColorFilterLayer
|
class ColorFilterLayer
|
||||||
with PictureEngineLayer
|
with PictureEngineLayer
|
||||||
implements ui.ColorFilterEngineLayer {
|
implements ui.ColorFilterEngineLayer {}
|
||||||
ColorFilterLayer(this.operation);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final ColorFilterOperation operation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
ColorFilterLayer emptyClone() => ColorFilterLayer(operation);
|
|
||||||
}
|
|
||||||
class ColorFilterOperation implements LayerOperation {
|
class ColorFilterOperation implements LayerOperation {
|
||||||
ColorFilterOperation(this.filter);
|
ColorFilterOperation(this.filter);
|
||||||
|
|
||||||
@ -236,12 +168,12 @@ class ColorFilterOperation implements LayerOperation {
|
|||||||
ui.Rect mapRect(ui.Rect contentRect) => contentRect;
|
ui.Rect mapRect(ui.Rect contentRect) => contentRect;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void pre(SceneCanvas canvas) {
|
void pre(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
canvas.saveLayer(ui.Rect.largest, ui.Paint()..colorFilter = filter);
|
canvas.saveLayer(contentRect, ui.Paint()..colorFilter = filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void post(SceneCanvas canvas) {
|
void post(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,15 +186,7 @@ class ColorFilterOperation implements LayerOperation {
|
|||||||
|
|
||||||
class ImageFilterLayer
|
class ImageFilterLayer
|
||||||
with PictureEngineLayer
|
with PictureEngineLayer
|
||||||
implements ui.ImageFilterEngineLayer {
|
implements ui.ImageFilterEngineLayer {}
|
||||||
ImageFilterLayer(this.operation);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final ImageFilterOperation operation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
ImageFilterLayer emptyClone() => ImageFilterLayer(operation);
|
|
||||||
}
|
|
||||||
class ImageFilterOperation implements LayerOperation {
|
class ImageFilterOperation implements LayerOperation {
|
||||||
ImageFilterOperation(this.filter, this.offset);
|
ImageFilterOperation(this.filter, this.offset);
|
||||||
|
|
||||||
@ -273,16 +197,17 @@ class ImageFilterOperation implements LayerOperation {
|
|||||||
ui.Rect mapRect(ui.Rect contentRect) => filter.filterBounds(contentRect);
|
ui.Rect mapRect(ui.Rect contentRect) => filter.filterBounds(contentRect);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void pre(SceneCanvas canvas) {
|
void pre(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
if (offset != ui.Offset.zero) {
|
if (offset != ui.Offset.zero) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.translate(offset.dx, offset.dy);
|
canvas.translate(offset.dx, offset.dy);
|
||||||
}
|
}
|
||||||
canvas.saveLayer(ui.Rect.largest, ui.Paint()..imageFilter = filter);
|
final ui.Rect adjustedContentRect = filter.filterBounds(contentRect);
|
||||||
|
canvas.saveLayer(adjustedContentRect, ui.Paint()..imageFilter = filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void post(SceneCanvas canvas) {
|
void post(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
if (offset != ui.Offset.zero) {
|
if (offset != ui.Offset.zero) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
@ -291,23 +216,14 @@ class ImageFilterOperation implements LayerOperation {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
PlatformViewStyling createPlatformViewStyling() {
|
PlatformViewStyling createPlatformViewStyling() {
|
||||||
PlatformViewStyling styling = const PlatformViewStyling();
|
|
||||||
if (offset != ui.Offset.zero) {
|
if (offset != ui.Offset.zero) {
|
||||||
styling = PlatformViewStyling(
|
return PlatformViewStyling(
|
||||||
position: PlatformViewPosition.offset(offset)
|
position: PlatformViewPosition.offset(offset)
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
final Matrix4? transform = filter.transform;
|
|
||||||
if (transform != null) {
|
|
||||||
styling = PlatformViewStyling.combine(
|
|
||||||
styling,
|
|
||||||
PlatformViewStyling(
|
|
||||||
position: PlatformViewPosition.transform(transform),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return const PlatformViewStyling();
|
return const PlatformViewStyling();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get shouldDrawIfEmpty => false;
|
bool get shouldDrawIfEmpty => false;
|
||||||
@ -315,15 +231,7 @@ class ImageFilterOperation implements LayerOperation {
|
|||||||
|
|
||||||
class OffsetLayer
|
class OffsetLayer
|
||||||
with PictureEngineLayer
|
with PictureEngineLayer
|
||||||
implements ui.OffsetEngineLayer {
|
implements ui.OffsetEngineLayer {}
|
||||||
OffsetLayer(this.operation);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final OffsetOperation operation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
OffsetLayer emptyClone() => OffsetLayer(operation);
|
|
||||||
}
|
|
||||||
class OffsetOperation implements LayerOperation {
|
class OffsetOperation implements LayerOperation {
|
||||||
OffsetOperation(this.dx, this.dy);
|
OffsetOperation(this.dx, this.dy);
|
||||||
|
|
||||||
@ -334,13 +242,13 @@ class OffsetOperation implements LayerOperation {
|
|||||||
ui.Rect mapRect(ui.Rect contentRect) => contentRect.shift(ui.Offset(dx, dy));
|
ui.Rect mapRect(ui.Rect contentRect) => contentRect.shift(ui.Offset(dx, dy));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void pre(SceneCanvas canvas) {
|
void pre(SceneCanvas canvas, ui.Rect cullRect) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.translate(dx, dy);
|
canvas.translate(dx, dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void post(SceneCanvas canvas) {
|
void post(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,15 +263,7 @@ class OffsetOperation implements LayerOperation {
|
|||||||
|
|
||||||
class OpacityLayer
|
class OpacityLayer
|
||||||
with PictureEngineLayer
|
with PictureEngineLayer
|
||||||
implements ui.OpacityEngineLayer {
|
implements ui.OpacityEngineLayer {}
|
||||||
OpacityLayer(this.operation);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final OpacityOperation operation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
OpacityLayer emptyClone() => OpacityLayer(operation);
|
|
||||||
}
|
|
||||||
class OpacityOperation implements LayerOperation {
|
class OpacityOperation implements LayerOperation {
|
||||||
OpacityOperation(this.alpha, this.offset);
|
OpacityOperation(this.alpha, this.offset);
|
||||||
|
|
||||||
@ -374,19 +274,20 @@ class OpacityOperation implements LayerOperation {
|
|||||||
ui.Rect mapRect(ui.Rect contentRect) => contentRect.shift(offset);
|
ui.Rect mapRect(ui.Rect contentRect) => contentRect.shift(offset);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void pre(SceneCanvas canvas) {
|
void pre(SceneCanvas canvas, ui.Rect cullRect) {
|
||||||
if (offset != ui.Offset.zero) {
|
if (offset != ui.Offset.zero) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.translate(offset.dx, offset.dy);
|
canvas.translate(offset.dx, offset.dy);
|
||||||
|
cullRect = cullRect.shift(-offset);
|
||||||
}
|
}
|
||||||
canvas.saveLayer(
|
canvas.saveLayer(
|
||||||
ui.Rect.largest,
|
cullRect,
|
||||||
ui.Paint()..color = ui.Color.fromARGB(alpha, 0, 0, 0)
|
ui.Paint()..color = ui.Color.fromARGB(alpha, 0, 0, 0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void post(SceneCanvas canvas) {
|
void post(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
if (offset != ui.Offset.zero) {
|
if (offset != ui.Offset.zero) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
@ -405,15 +306,7 @@ class OpacityOperation implements LayerOperation {
|
|||||||
|
|
||||||
class TransformLayer
|
class TransformLayer
|
||||||
with PictureEngineLayer
|
with PictureEngineLayer
|
||||||
implements ui.TransformEngineLayer {
|
implements ui.TransformEngineLayer {}
|
||||||
TransformLayer(this.operation);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final TransformOperation operation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
TransformLayer emptyClone() => TransformLayer(operation);
|
|
||||||
}
|
|
||||||
class TransformOperation implements LayerOperation {
|
class TransformOperation implements LayerOperation {
|
||||||
TransformOperation(this.transform);
|
TransformOperation(this.transform);
|
||||||
|
|
||||||
@ -426,13 +319,13 @@ class TransformOperation implements LayerOperation {
|
|||||||
ui.Rect mapRect(ui.Rect contentRect) => matrix.transformRect(contentRect);
|
ui.Rect mapRect(ui.Rect contentRect) => matrix.transformRect(contentRect);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void pre(SceneCanvas canvas) {
|
void pre(SceneCanvas canvas, ui.Rect cullRect) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.transform(transform);
|
canvas.transform(transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void post(SceneCanvas canvas) {
|
void post(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,15 +340,7 @@ class TransformOperation implements LayerOperation {
|
|||||||
|
|
||||||
class ShaderMaskLayer
|
class ShaderMaskLayer
|
||||||
with PictureEngineLayer
|
with PictureEngineLayer
|
||||||
implements ui.ShaderMaskEngineLayer {
|
implements ui.ShaderMaskEngineLayer {}
|
||||||
ShaderMaskLayer(this.operation);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final ShaderMaskOperation operation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
ShaderMaskLayer emptyClone() => ShaderMaskLayer(operation);
|
|
||||||
}
|
|
||||||
class ShaderMaskOperation implements LayerOperation {
|
class ShaderMaskOperation implements LayerOperation {
|
||||||
ShaderMaskOperation(this.shader, this.maskRect, this.blendMode);
|
ShaderMaskOperation(this.shader, this.maskRect, this.blendMode);
|
||||||
|
|
||||||
@ -467,15 +352,15 @@ class ShaderMaskOperation implements LayerOperation {
|
|||||||
ui.Rect mapRect(ui.Rect contentRect) => contentRect;
|
ui.Rect mapRect(ui.Rect contentRect) => contentRect;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void pre(SceneCanvas canvas) {
|
void pre(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
canvas.saveLayer(
|
canvas.saveLayer(
|
||||||
ui.Rect.largest,
|
contentRect,
|
||||||
ui.Paint(),
|
ui.Paint(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void post(SceneCanvas canvas) {
|
void post(SceneCanvas canvas, ui.Rect contentRect) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.translate(maskRect.left, maskRect.top);
|
canvas.translate(maskRect.left, maskRect.top);
|
||||||
canvas.drawRect(
|
canvas.drawRect(
|
||||||
@ -504,43 +389,47 @@ class PlatformView {
|
|||||||
final ui.Rect bounds;
|
final ui.Rect bounds;
|
||||||
|
|
||||||
final PlatformViewStyling styling;
|
final PlatformViewStyling styling;
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class LayerSlice {
|
||||||
|
void dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// A slice that contains one or more platform views to be rendered.
|
||||||
|
class PlatformViewSlice implements LayerSlice {
|
||||||
|
PlatformViewSlice(this.views, this.occlusionRect);
|
||||||
|
|
||||||
|
List<PlatformView> views;
|
||||||
|
|
||||||
|
// A conservative estimate of what area platform views in this slice may cover.
|
||||||
|
// This is expressed in the coordinate space of the parent.
|
||||||
|
ui.Rect? occlusionRect;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
void dispose() {}
|
||||||
return 'PlatformView(viewId: $viewId, bounds: $bounds, styling: $styling)';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class LayerSlice {
|
// A slice that contains flutter content to be rendered int he form of a single
|
||||||
LayerSlice(this.picture, this.platformViews);
|
// ScenePicture.
|
||||||
|
class PictureSlice implements LayerSlice {
|
||||||
|
PictureSlice(this.picture);
|
||||||
|
|
||||||
// The picture of native flutter content to be rendered
|
|
||||||
ScenePicture picture;
|
ScenePicture picture;
|
||||||
|
|
||||||
// Platform views to be placed on top of the flutter content.
|
@override
|
||||||
final List<PlatformView> platformViews;
|
void dispose() => picture.dispose();
|
||||||
|
|
||||||
void dispose() {
|
|
||||||
picture.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin PictureEngineLayer implements ui.EngineLayer {
|
mixin PictureEngineLayer implements ui.EngineLayer {
|
||||||
// Each layer is represented as a series of "slices" which contain flutter content
|
// Each layer is represented as a series of "slices" which contain either
|
||||||
// with platform views on top. This is ordered from bottommost to topmost.
|
// flutter content or platform views. Slices in this list are ordered from
|
||||||
List<LayerSlice?> slices = [];
|
// bottom to top.
|
||||||
|
List<LayerSlice> slices = <LayerSlice>[];
|
||||||
List<LayerDrawCommand> drawCommands = [];
|
|
||||||
PlatformViewStyling platformViewStyling = const PlatformViewStyling();
|
|
||||||
|
|
||||||
LayerOperation get operation;
|
|
||||||
|
|
||||||
PictureEngineLayer emptyClone();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
for (final LayerSlice? slice in slices) {
|
for (final LayerSlice slice in slices) {
|
||||||
slice?.dispose();
|
slice.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -553,8 +442,8 @@ abstract class LayerOperation {
|
|||||||
// layer operation.
|
// layer operation.
|
||||||
ui.Rect mapRect(ui.Rect contentRect);
|
ui.Rect mapRect(ui.Rect contentRect);
|
||||||
|
|
||||||
void pre(SceneCanvas canvas);
|
void pre(SceneCanvas canvas, ui.Rect contentRect);
|
||||||
void post(SceneCanvas canvas);
|
void post(SceneCanvas canvas, ui.Rect contentRect);
|
||||||
|
|
||||||
PlatformViewStyling createPlatformViewStyling();
|
PlatformViewStyling createPlatformViewStyling();
|
||||||
|
|
||||||
@ -564,29 +453,11 @@ abstract class LayerOperation {
|
|||||||
bool get shouldDrawIfEmpty;
|
bool get shouldDrawIfEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class LayerDrawCommand {
|
class PictureDrawCommand {
|
||||||
}
|
PictureDrawCommand(this.offset, this.picture);
|
||||||
|
|
||||||
class PictureDrawCommand extends LayerDrawCommand {
|
ui.Offset offset;
|
||||||
PictureDrawCommand(this.offset, this.picture, this.sliceIndex);
|
ui.Picture picture;
|
||||||
|
|
||||||
final int sliceIndex;
|
|
||||||
final ui.Offset offset;
|
|
||||||
final ScenePicture picture;
|
|
||||||
}
|
|
||||||
|
|
||||||
class PlatformViewDrawCommand extends LayerDrawCommand {
|
|
||||||
PlatformViewDrawCommand(this.viewId, this.bounds, this.sliceIndex);
|
|
||||||
|
|
||||||
final int sliceIndex;
|
|
||||||
final int viewId;
|
|
||||||
final ui.Rect bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
class RetainedLayerDrawCommand extends LayerDrawCommand {
|
|
||||||
RetainedLayerDrawCommand(this.layer);
|
|
||||||
|
|
||||||
final PictureEngineLayer layer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Represents how a platform view should be positioned in the scene.
|
// Represents how a platform view should be positioned in the scene.
|
||||||
@ -606,17 +477,6 @@ class PlatformViewPosition {
|
|||||||
|
|
||||||
bool get isZero => (offset == null) && (transform == null);
|
bool get isZero => (offset == null) && (transform == null);
|
||||||
|
|
||||||
ui.Rect mapLocalToGlobal(ui.Rect rect) {
|
|
||||||
if (offset != null) {
|
|
||||||
return rect.shift(offset!);
|
|
||||||
}
|
|
||||||
if (transform != null) {
|
|
||||||
return transform!.transformRect(rect);
|
|
||||||
}
|
|
||||||
return rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that by construction only one of these can be set at any given time, not both.
|
|
||||||
final ui.Offset? offset;
|
final ui.Offset? offset;
|
||||||
final Matrix4? transform;
|
final Matrix4? transform;
|
||||||
|
|
||||||
@ -667,17 +527,6 @@ class PlatformViewPosition {
|
|||||||
int get hashCode {
|
int get hashCode {
|
||||||
return Object.hash(offset, transform);
|
return Object.hash(offset, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
if (offset != null) {
|
|
||||||
return 'PlatformViewPosition(offset: $offset)';
|
|
||||||
}
|
|
||||||
if (transform != null) {
|
|
||||||
return 'PlatformViewPosition(transform: $transform)';
|
|
||||||
}
|
|
||||||
return 'PlatformViewPosition(zero)';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Represents the styling to be performed on a platform view when it is
|
// Represents the styling to be performed on a platform view when it is
|
||||||
@ -696,10 +545,6 @@ class PlatformViewStyling {
|
|||||||
final double opacity;
|
final double opacity;
|
||||||
final PlatformViewClip clip;
|
final PlatformViewClip clip;
|
||||||
|
|
||||||
ui.Rect mapLocalToGlobal(ui.Rect rect) {
|
|
||||||
return position.mapLocalToGlobal(rect).intersect(clip.outerRect);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PlatformViewStyling combine(PlatformViewStyling outer, PlatformViewStyling inner) {
|
static PlatformViewStyling combine(PlatformViewStyling outer, PlatformViewStyling inner) {
|
||||||
// Attempt to reuse one of the existing immutable objects.
|
// Attempt to reuse one of the existing immutable objects.
|
||||||
if (outer.isDefault) {
|
if (outer.isDefault) {
|
||||||
@ -730,11 +575,6 @@ class PlatformViewStyling {
|
|||||||
int get hashCode {
|
int get hashCode {
|
||||||
return Object.hash(position, opacity, clip);
|
return Object.hash(position, opacity, clip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'PlatformViewStyling(position: $position, clip: $clip, opacity: $opacity)';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class PlatformViewClip {
|
sealed class PlatformViewClip {
|
||||||
@ -802,7 +642,7 @@ class PlatformViewNoClip implements PlatformViewClip {
|
|||||||
ui.Rect get innerRect => ui.Rect.zero;
|
ui.Rect get innerRect => ui.Rect.zero;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ui.Rect get outerRect => ui.Rect.largest;
|
ui.Rect get outerRect => ui.Rect.zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlatformViewRectClip implements PlatformViewClip {
|
class PlatformViewRectClip implements PlatformViewClip {
|
||||||
@ -923,137 +763,164 @@ class PlatformViewPathClip implements PlatformViewClip {
|
|||||||
ui.Rect get outerRect => path.getBounds();
|
ui.Rect get outerRect => path.getBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
class LayerSliceBuilder {
|
|
||||||
factory LayerSliceBuilder() {
|
|
||||||
final (recorder, canvas) = debugRecorderFactory != null ? debugRecorderFactory!() : defaultRecorderFactory();
|
|
||||||
return LayerSliceBuilder._(recorder, canvas);
|
|
||||||
}
|
|
||||||
LayerSliceBuilder._(this.recorder, this.canvas);
|
|
||||||
|
|
||||||
@visibleForTesting
|
|
||||||
static (ui.PictureRecorder, SceneCanvas) Function()? debugRecorderFactory;
|
|
||||||
|
|
||||||
static (ui.PictureRecorder, SceneCanvas) defaultRecorderFactory() {
|
|
||||||
final ui.PictureRecorder recorder = ui.PictureRecorder();
|
|
||||||
final SceneCanvas canvas = ui.Canvas(recorder, ui.Rect.largest) as SceneCanvas;
|
|
||||||
return (recorder, canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
final ui.PictureRecorder recorder;
|
|
||||||
final SceneCanvas canvas;
|
|
||||||
final List<PlatformView> platformViews = <PlatformView>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
class LayerBuilder {
|
class LayerBuilder {
|
||||||
factory LayerBuilder.rootLayer() {
|
factory LayerBuilder.rootLayer() {
|
||||||
return LayerBuilder._(null, EngineRootLayer());
|
return LayerBuilder._(null, EngineRootLayer(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory LayerBuilder.childLayer({
|
factory LayerBuilder.childLayer({
|
||||||
required LayerBuilder parent,
|
required LayerBuilder parent,
|
||||||
required PictureEngineLayer layer,
|
required PictureEngineLayer layer,
|
||||||
|
required LayerOperation operation
|
||||||
}) {
|
}) {
|
||||||
return LayerBuilder._(parent, layer);
|
return LayerBuilder._(parent, layer, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
LayerBuilder._(
|
LayerBuilder._(
|
||||||
this.parent,
|
this.parent,
|
||||||
this.layer);
|
this.layer,
|
||||||
|
this.operation);
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
static (ui.PictureRecorder, SceneCanvas) Function(ui.Rect)? debugRecorderFactory;
|
||||||
|
|
||||||
final LayerBuilder? parent;
|
final LayerBuilder? parent;
|
||||||
final PictureEngineLayer layer;
|
final PictureEngineLayer layer;
|
||||||
|
final LayerOperation? operation;
|
||||||
final List<LayerSliceBuilder?> sliceBuilders = <LayerSliceBuilder?>[];
|
final List<PictureDrawCommand> pendingPictures = <PictureDrawCommand>[];
|
||||||
final List<LayerDrawCommand> drawCommands = <LayerDrawCommand>[];
|
List<PlatformView> pendingPlatformViews = <PlatformView>[];
|
||||||
|
ui.Rect? picturesRect;
|
||||||
|
ui.Rect? platformViewRect;
|
||||||
|
|
||||||
PlatformViewStyling? _memoizedPlatformViewStyling;
|
PlatformViewStyling? _memoizedPlatformViewStyling;
|
||||||
|
|
||||||
PlatformViewStyling get platformViewStyling {
|
PlatformViewStyling get platformViewStyling {
|
||||||
return _memoizedPlatformViewStyling ??= layer.operation.createPlatformViewStyling();
|
return _memoizedPlatformViewStyling ??= operation?.createPlatformViewStyling() ?? const PlatformViewStyling();
|
||||||
}
|
}
|
||||||
|
|
||||||
PlatformViewStyling? _memoizedGlobalPlatformViewStyling;
|
(ui.PictureRecorder, SceneCanvas) _createRecorder(ui.Rect rect) {
|
||||||
PlatformViewStyling get globalPlatformViewStyling {
|
if (debugRecorderFactory != null) {
|
||||||
if (_memoizedGlobalPlatformViewStyling != null) {
|
return debugRecorderFactory!(rect);
|
||||||
return _memoizedGlobalPlatformViewStyling!;
|
|
||||||
}
|
}
|
||||||
if (parent != null) {
|
final ui.PictureRecorder recorder = ui.PictureRecorder();
|
||||||
return _memoizedGlobalPlatformViewStyling ??= PlatformViewStyling.combine(parent!.globalPlatformViewStyling, platformViewStyling);
|
final SceneCanvas canvas = ui.Canvas(recorder, rect) as SceneCanvas;
|
||||||
}
|
return (recorder, canvas);
|
||||||
return _memoizedGlobalPlatformViewStyling ??= platformViewStyling;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LayerSliceBuilder getOrCreateSliceBuilderAtIndex(int index) {
|
void flushSlices() {
|
||||||
while (sliceBuilders.length <= index) {
|
if (pendingPictures.isNotEmpty || (operation?.shouldDrawIfEmpty ?? false)) {
|
||||||
sliceBuilders.add(null);
|
// Merge the existing draw commands into a single picture and add a slice
|
||||||
|
// with that picture to the slice list.
|
||||||
|
final ui.Rect drawnRect = picturesRect ?? ui.Rect.zero;
|
||||||
|
final ui.Rect rect = operation?.mapRect(drawnRect) ?? drawnRect;
|
||||||
|
final (ui.PictureRecorder recorder, SceneCanvas canvas) = _createRecorder(rect);
|
||||||
|
|
||||||
|
operation?.pre(canvas, rect);
|
||||||
|
for (final PictureDrawCommand command in pendingPictures) {
|
||||||
|
if (command.offset != ui.Offset.zero) {
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(command.offset.dx, command.offset.dy);
|
||||||
|
canvas.drawPicture(command.picture);
|
||||||
|
canvas.restore();
|
||||||
|
} else {
|
||||||
|
canvas.drawPicture(command.picture);
|
||||||
}
|
}
|
||||||
final LayerSliceBuilder? existingSliceBuilder = sliceBuilders[index];
|
|
||||||
if (existingSliceBuilder != null) {
|
|
||||||
return existingSliceBuilder;
|
|
||||||
}
|
}
|
||||||
final LayerSliceBuilder newSliceBuilder = LayerSliceBuilder();
|
operation?.post(canvas, rect);
|
||||||
layer.operation.pre(newSliceBuilder.canvas);
|
final ui.Picture picture = recorder.endRecording();
|
||||||
sliceBuilders[index] = newSliceBuilder;
|
layer.slices.add(PictureSlice(picture as ScenePicture));
|
||||||
return newSliceBuilder;
|
}
|
||||||
|
|
||||||
|
if (pendingPlatformViews.isNotEmpty) {
|
||||||
|
// Take any pending platform views and lower them into a platform view
|
||||||
|
// slice.
|
||||||
|
ui.Rect? occlusionRect = platformViewRect;
|
||||||
|
if (occlusionRect != null && operation != null) {
|
||||||
|
occlusionRect = operation!.mapRect(occlusionRect);
|
||||||
|
}
|
||||||
|
layer.slices.add(PlatformViewSlice(pendingPlatformViews, occlusionRect));
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingPictures.clear();
|
||||||
|
pendingPlatformViews = <PlatformView>[];
|
||||||
|
|
||||||
|
// All the pictures and platform views have been lowered into slices. Clear
|
||||||
|
// our occlusion rectangles.
|
||||||
|
picturesRect = null;
|
||||||
|
platformViewRect = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addPicture(
|
void addPicture(
|
||||||
ui.Offset offset,
|
ui.Offset offset,
|
||||||
ui.Picture picture, {
|
ui.Picture picture, {
|
||||||
required int sliceIndex,
|
bool isComplexHint = false,
|
||||||
|
bool willChangeHint = false
|
||||||
}) {
|
}) {
|
||||||
final LayerSliceBuilder sliceBuilder = getOrCreateSliceBuilderAtIndex(sliceIndex);
|
final ui.Rect cullRect = (picture as ScenePicture).cullRect;
|
||||||
final SceneCanvas canvas = sliceBuilder.canvas;
|
final ui.Rect shiftedRect = cullRect.shift(offset);
|
||||||
if (offset != ui.Offset.zero) {
|
|
||||||
canvas.save();
|
final ui.Rect? currentPlatformViewRect = platformViewRect;
|
||||||
canvas.translate(offset.dx, offset.dy);
|
if (currentPlatformViewRect != null) {
|
||||||
canvas.drawPicture(picture);
|
// Whenever we add a picture to our layer, we try to see if the picture
|
||||||
canvas.restore();
|
// will overlap with any platform views that are currently on top of our
|
||||||
} else {
|
// drawing surface. If they don't overlap with the platform views, they
|
||||||
canvas.drawPicture(picture);
|
// can be grouped with the existing pending pictures.
|
||||||
|
if (pendingPictures.isEmpty || currentPlatformViewRect.overlaps(shiftedRect)) {
|
||||||
|
// If they do overlap with the platform views, however, we should flush
|
||||||
|
// all the current content into slices and start anew with a fresh
|
||||||
|
// group of pictures and platform views that will be rendered on top of
|
||||||
|
// the previous content. Note that we also flush if we have no pending
|
||||||
|
// pictures to group with. This is the case when platform views are
|
||||||
|
// the first thing in our stack of objects to composite, and it doesn't
|
||||||
|
// make sense to try to put a picture slice below the first platform
|
||||||
|
// view slice, even if the picture doesn't overlap.
|
||||||
|
flushSlices();
|
||||||
}
|
}
|
||||||
drawCommands.add(PictureDrawCommand(offset, picture as ScenePicture, sliceIndex));
|
}
|
||||||
|
pendingPictures.add(PictureDrawCommand(offset, picture));
|
||||||
|
picturesRect = picturesRect?.expandToInclude(shiftedRect) ?? shiftedRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addPlatformView(
|
void addPlatformView(
|
||||||
int viewId, {
|
int viewId, {
|
||||||
required ui.Rect bounds,
|
ui.Offset offset = ui.Offset.zero,
|
||||||
required int sliceIndex,
|
double width = 0.0,
|
||||||
|
double height = 0.0
|
||||||
}) {
|
}) {
|
||||||
final LayerSliceBuilder sliceBuilder = getOrCreateSliceBuilderAtIndex(sliceIndex);
|
final ui.Rect bounds = ui.Rect.fromLTWH(offset.dx, offset.dy, width, height);
|
||||||
sliceBuilder.platformViews.add(PlatformView(viewId, bounds, platformViewStyling));
|
platformViewRect = platformViewRect?.expandToInclude(bounds) ?? bounds;
|
||||||
drawCommands.add(PlatformViewDrawCommand(viewId, bounds, sliceIndex));
|
pendingPlatformViews.add(PlatformView(viewId, bounds, platformViewStyling));
|
||||||
}
|
}
|
||||||
|
|
||||||
void mergeLayer(PictureEngineLayer layer) {
|
void mergeLayer(PictureEngineLayer layer) {
|
||||||
for (int i = 0; i < layer.slices.length; i++) {
|
// When we merge layers, we attempt to merge slices as much as possible as
|
||||||
final LayerSlice? slice = layer.slices[i];
|
// well, based on ordering of pictures and platform views and reusing the
|
||||||
if (slice != null) {
|
// occlusion logic for determining where we can lower each picture.
|
||||||
final LayerSliceBuilder sliceBuilder = getOrCreateSliceBuilderAtIndex(i);
|
for (final LayerSlice slice in layer.slices) {
|
||||||
sliceBuilder.canvas.drawPicture(slice.picture);
|
switch (slice) {
|
||||||
sliceBuilder.platformViews.addAll(slice.platformViews.map((PlatformView view) {
|
case PictureSlice():
|
||||||
return PlatformView(view.viewId, view.bounds, PlatformViewStyling.combine(platformViewStyling, view.styling));
|
addPicture(ui.Offset.zero, slice.picture);
|
||||||
}));
|
case PlatformViewSlice():
|
||||||
|
final ui.Rect? occlusionRect = slice.occlusionRect;
|
||||||
|
if (occlusionRect != null) {
|
||||||
|
platformViewRect = platformViewRect?.expandToInclude(occlusionRect) ?? occlusionRect;
|
||||||
|
}
|
||||||
|
for (final PlatformView view in slice.views) {
|
||||||
|
// Merge the platform view styling of this layer with the nested
|
||||||
|
// platform views.
|
||||||
|
final PlatformViewStyling styling = PlatformViewStyling.combine(
|
||||||
|
platformViewStyling,
|
||||||
|
view.styling,
|
||||||
|
);
|
||||||
|
pendingPlatformViews.add(PlatformView(view.viewId, view.bounds, styling));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drawCommands.add(RetainedLayerDrawCommand(layer));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PictureEngineLayer sliceUp() {
|
|
||||||
final List<LayerSlice?> slices = sliceBuilders.map((LayerSliceBuilder? builder) {
|
|
||||||
if (builder == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
layer.operation.post(builder.canvas);
|
|
||||||
final ScenePicture picture = builder.recorder.endRecording() as ScenePicture;
|
|
||||||
return LayerSlice(picture, builder.platformViews);
|
|
||||||
}).toList();
|
|
||||||
layer.slices = slices;
|
|
||||||
return layer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PictureEngineLayer build() {
|
PictureEngineLayer build() {
|
||||||
layer.drawCommands = drawCommands;
|
// Lower any pending pictures or platform views to their respective slices.
|
||||||
layer.platformViewStyling = platformViewStyling;
|
flushSlices();
|
||||||
return sliceUp();
|
return layer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:math' as math;
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:ui/src/engine.dart';
|
import 'package:ui/src/engine.dart';
|
||||||
@ -63,115 +62,17 @@ class EngineScene implements ui.Scene {
|
|||||||
final ui.Rect canvasRect = ui.Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble());
|
final ui.Rect canvasRect = ui.Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble());
|
||||||
final ui.Canvas canvas = ui.Canvas(recorder, canvasRect);
|
final ui.Canvas canvas = ui.Canvas(recorder, canvasRect);
|
||||||
|
|
||||||
// Only rasterizes the pictures.
|
// Only rasterizes the picture slices.
|
||||||
for (final LayerSlice? slice in rootLayer.slices) {
|
for (final PictureSlice slice in rootLayer.slices.whereType<PictureSlice>()) {
|
||||||
if (slice != null) {
|
|
||||||
canvas.drawPicture(slice.picture);
|
canvas.drawPicture(slice.picture);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return recorder.endRecording().toImageSync(width, height);
|
return recorder.endRecording().toImageSync(width, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class OcclusionMapNode {
|
|
||||||
bool overlaps(ui.Rect rect);
|
|
||||||
OcclusionMapNode insert(ui.Rect rect);
|
|
||||||
ui.Rect get boundingBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
class OcclusionMapEmpty implements OcclusionMapNode {
|
|
||||||
@override
|
|
||||||
ui.Rect get boundingBox => ui.Rect.zero;
|
|
||||||
|
|
||||||
@override
|
|
||||||
OcclusionMapNode insert(ui.Rect rect) => OcclusionMapLeaf(rect);
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool overlaps(ui.Rect rect) => false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class OcclusionMapLeaf implements OcclusionMapNode {
|
|
||||||
OcclusionMapLeaf(this.rect);
|
|
||||||
|
|
||||||
final ui.Rect rect;
|
|
||||||
|
|
||||||
@override
|
|
||||||
ui.Rect get boundingBox => rect;
|
|
||||||
|
|
||||||
@override
|
|
||||||
OcclusionMapNode insert(ui.Rect other) => OcclusionMapBranch(this, OcclusionMapLeaf(other));
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool overlaps(ui.Rect other) => rect.overlaps(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
class OcclusionMapBranch implements OcclusionMapNode {
|
|
||||||
OcclusionMapBranch(this.left, this.right)
|
|
||||||
: boundingBox = left.boundingBox.expandToInclude(right.boundingBox);
|
|
||||||
|
|
||||||
final OcclusionMapNode left;
|
|
||||||
final OcclusionMapNode right;
|
|
||||||
|
|
||||||
@override
|
|
||||||
final ui.Rect boundingBox;
|
|
||||||
|
|
||||||
double _areaOfUnion(ui.Rect first, ui.Rect second) {
|
|
||||||
return (math.max(first.right, second.right) - math.min(first.left, second.left))
|
|
||||||
* (math.max(first.bottom, second.bottom) - math.max(first.top, second.top));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
OcclusionMapNode insert(ui.Rect other) {
|
|
||||||
// Try to create nodes with the smallest possible area
|
|
||||||
final double leftOtherArea = _areaOfUnion(left.boundingBox, other);
|
|
||||||
final double rightOtherArea = _areaOfUnion(right.boundingBox, other);
|
|
||||||
final double leftRightArea = boundingBox.width * boundingBox.height;
|
|
||||||
if (leftOtherArea < rightOtherArea) {
|
|
||||||
if (leftOtherArea < leftRightArea) {
|
|
||||||
return OcclusionMapBranch(
|
|
||||||
left.insert(other),
|
|
||||||
right,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (rightOtherArea < leftRightArea) {
|
|
||||||
return OcclusionMapBranch(
|
|
||||||
left,
|
|
||||||
right.insert(other),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return OcclusionMapBranch(this, OcclusionMapLeaf(other));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool overlaps(ui.Rect rect) {
|
|
||||||
if (!boundingBox.overlaps(rect)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return left.overlaps(rect) || right.overlaps(rect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class OcclusionMap {
|
|
||||||
OcclusionMapNode root = OcclusionMapEmpty();
|
|
||||||
|
|
||||||
void addRect(ui.Rect rect) => root = root.insert(rect);
|
|
||||||
|
|
||||||
bool overlaps(ui.Rect rect) => root.overlaps(rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
class SceneSlice {
|
|
||||||
final OcclusionMap pictureOcclusionMap = OcclusionMap();
|
|
||||||
final OcclusionMap platformViewOcclusionMap = OcclusionMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
class EngineSceneBuilder implements ui.SceneBuilder {
|
class EngineSceneBuilder implements ui.SceneBuilder {
|
||||||
LayerBuilder currentBuilder = LayerBuilder.rootLayer();
|
LayerBuilder currentBuilder = LayerBuilder.rootLayer();
|
||||||
|
|
||||||
final List<SceneSlice> sceneSlices = <SceneSlice>[SceneSlice()];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void addPerformanceOverlay(int enabledOptions, ui.Rect bounds) {
|
void addPerformanceOverlay(int enabledOptions, ui.Rect bounds) {
|
||||||
// We don't plan to implement this on the web.
|
// We don't plan to implement this on the web.
|
||||||
@ -185,44 +86,15 @@ class EngineSceneBuilder implements ui.SceneBuilder {
|
|||||||
bool isComplexHint = false,
|
bool isComplexHint = false,
|
||||||
bool willChangeHint = false
|
bool willChangeHint = false
|
||||||
}) {
|
}) {
|
||||||
final int sliceIndex = _placePicture(offset, picture as ScenePicture);
|
|
||||||
currentBuilder.addPicture(
|
currentBuilder.addPicture(
|
||||||
offset,
|
offset,
|
||||||
picture,
|
picture,
|
||||||
sliceIndex: sliceIndex,
|
isComplexHint:
|
||||||
|
isComplexHint,
|
||||||
|
willChangeHint: willChangeHint
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function determines the lowest scene slice that this picture can be placed
|
|
||||||
// into and adds it to that slice's occlusion map.
|
|
||||||
//
|
|
||||||
// The picture is placed in the last slice where it either intersects with a picture
|
|
||||||
// in the slice or it intersects with a platform view in the preceding slice. If the
|
|
||||||
// picture intersects with a platform view in the last slice, a new slice is added at
|
|
||||||
// the end and the picture goes in there.
|
|
||||||
int _placePicture(ui.Offset offset, ScenePicture picture) {
|
|
||||||
final ui.Rect cullRect = picture.cullRect.shift(offset);
|
|
||||||
final ui.Rect mappedCullRect = currentBuilder.globalPlatformViewStyling.mapLocalToGlobal(cullRect);
|
|
||||||
int sliceIndex = sceneSlices.length;
|
|
||||||
while (sliceIndex > 0) {
|
|
||||||
final SceneSlice sliceBelow = sceneSlices[sliceIndex - 1];
|
|
||||||
if (sliceBelow.platformViewOcclusionMap.overlaps(mappedCullRect)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sliceIndex--;
|
|
||||||
if (sliceBelow.pictureOcclusionMap.overlaps(mappedCullRect)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sliceIndex == sceneSlices.length) {
|
|
||||||
// Insert a new slice.
|
|
||||||
sceneSlices.add(SceneSlice());
|
|
||||||
}
|
|
||||||
final SceneSlice slice = sceneSlices[sliceIndex];
|
|
||||||
slice.pictureOcclusionMap.addRect(mappedCullRect);
|
|
||||||
return sliceIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void addPlatformView(
|
void addPlatformView(
|
||||||
int viewId, {
|
int viewId, {
|
||||||
@ -230,94 +102,17 @@ class EngineSceneBuilder implements ui.SceneBuilder {
|
|||||||
double width = 0.0,
|
double width = 0.0,
|
||||||
double height = 0.0
|
double height = 0.0
|
||||||
}) {
|
}) {
|
||||||
final ui.Rect platformViewRect = ui.Rect.fromLTWH(offset.dx, offset.dy, width, height);
|
|
||||||
final int sliceIndex = _placePlatformView(viewId, platformViewRect);
|
|
||||||
currentBuilder.addPlatformView(
|
currentBuilder.addPlatformView(
|
||||||
viewId,
|
viewId,
|
||||||
bounds: platformViewRect,
|
offset: offset,
|
||||||
sliceIndex: sliceIndex,
|
width: width,
|
||||||
|
height: height
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function determines the lowest scene slice this platform view can be placed
|
|
||||||
// into and adds it to that slice's occlusion map.
|
|
||||||
//
|
|
||||||
// The platform view is placed into the last slice where it intersects with a picture
|
|
||||||
// or a platform view.
|
|
||||||
int _placePlatformView(
|
|
||||||
int viewId,
|
|
||||||
ui.Rect rect, {
|
|
||||||
PlatformViewStyling styling = const PlatformViewStyling(),
|
|
||||||
}) {
|
|
||||||
final PlatformViewStyling combinedStyling = PlatformViewStyling.combine(currentBuilder.globalPlatformViewStyling, styling);
|
|
||||||
final ui.Rect globalPlatformViewRect = combinedStyling.mapLocalToGlobal(rect);
|
|
||||||
int sliceIndex = sceneSlices.length - 1;
|
|
||||||
while (sliceIndex > 0) {
|
|
||||||
final SceneSlice slice = sceneSlices[sliceIndex];
|
|
||||||
if (slice.platformViewOcclusionMap.overlaps(globalPlatformViewRect) ||
|
|
||||||
slice.pictureOcclusionMap.overlaps(globalPlatformViewRect)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sliceIndex--;
|
|
||||||
}
|
|
||||||
final SceneSlice slice = sceneSlices[sliceIndex];
|
|
||||||
slice.platformViewOcclusionMap.addRect(globalPlatformViewRect);
|
|
||||||
return sliceIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void addRetained(ui.EngineLayer retainedLayer) {
|
void addRetained(ui.EngineLayer retainedLayer) {
|
||||||
final PictureEngineLayer placedEngineLayer = _placeRetainedLayer(retainedLayer as PictureEngineLayer);
|
currentBuilder.mergeLayer(retainedLayer as PictureEngineLayer);
|
||||||
currentBuilder.mergeLayer(placedEngineLayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
PictureEngineLayer _placeRetainedLayer(PictureEngineLayer retainedLayer) {
|
|
||||||
bool needsRebuild = false;
|
|
||||||
final List<LayerDrawCommand> revisedDrawCommands = [];
|
|
||||||
for (final LayerDrawCommand command in retainedLayer.drawCommands) {
|
|
||||||
switch (command) {
|
|
||||||
case PictureDrawCommand(offset: final ui.Offset offset, picture: final ScenePicture picture):
|
|
||||||
final int sliceIndex = _placePicture(offset, picture);
|
|
||||||
if (command.sliceIndex != sliceIndex) {
|
|
||||||
needsRebuild = true;
|
|
||||||
}
|
|
||||||
revisedDrawCommands.add(PictureDrawCommand(offset, picture, sliceIndex));
|
|
||||||
case PlatformViewDrawCommand(viewId: final int viewId, bounds: final ui.Rect bounds):
|
|
||||||
final int sliceIndex = _placePlatformView(viewId, bounds);
|
|
||||||
if (command.sliceIndex != sliceIndex) {
|
|
||||||
needsRebuild = true;
|
|
||||||
}
|
|
||||||
revisedDrawCommands.add(PlatformViewDrawCommand(viewId, bounds, sliceIndex));
|
|
||||||
case RetainedLayerDrawCommand(layer: final PictureEngineLayer sublayer):
|
|
||||||
final PictureEngineLayer revisedSublayer = _placeRetainedLayer(sublayer);
|
|
||||||
if (sublayer != revisedSublayer) {
|
|
||||||
needsRebuild = true;
|
|
||||||
}
|
|
||||||
revisedDrawCommands.add(RetainedLayerDrawCommand(revisedSublayer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!needsRebuild) {
|
|
||||||
// No elements changed which slice position they are in, so we can simply
|
|
||||||
// merge the existing layer down and don't have to redraw individual elements.
|
|
||||||
return retainedLayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we replace the commands of the layer to create a new one.
|
|
||||||
currentBuilder = LayerBuilder.childLayer(parent: currentBuilder, layer: retainedLayer.emptyClone());
|
|
||||||
for (final LayerDrawCommand command in revisedDrawCommands) {
|
|
||||||
switch (command) {
|
|
||||||
case PictureDrawCommand(offset: final ui.Offset offset, picture: final ScenePicture picture):
|
|
||||||
currentBuilder.addPicture(offset, picture, sliceIndex: command.sliceIndex);
|
|
||||||
case PlatformViewDrawCommand(viewId: final int viewId, bounds: final ui.Rect bounds):
|
|
||||||
currentBuilder.addPlatformView(viewId, bounds: bounds, sliceIndex: command.sliceIndex);
|
|
||||||
case RetainedLayerDrawCommand(layer: final PictureEngineLayer layer):
|
|
||||||
currentBuilder.mergeLayer(layer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final PictureEngineLayer newLayer = currentBuilder.build();
|
|
||||||
currentBuilder = currentBuilder.parent!;
|
|
||||||
return newLayer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -337,21 +132,30 @@ class EngineSceneBuilder implements ui.SceneBuilder {
|
|||||||
ui.ImageFilter filter, {
|
ui.ImageFilter filter, {
|
||||||
ui.BlendMode blendMode = ui.BlendMode.srcOver,
|
ui.BlendMode blendMode = ui.BlendMode.srcOver,
|
||||||
ui.BackdropFilterEngineLayer? oldLayer
|
ui.BackdropFilterEngineLayer? oldLayer
|
||||||
}) => pushLayer<BackdropFilterLayer>(BackdropFilterLayer(BackdropFilterOperation(filter, blendMode)));
|
}) => pushLayer<BackdropFilterLayer>(
|
||||||
|
BackdropFilterLayer(),
|
||||||
|
BackdropFilterOperation(filter, blendMode),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ui.ClipPathEngineLayer pushClipPath(
|
ui.ClipPathEngineLayer pushClipPath(
|
||||||
ui.Path path, {
|
ui.Path path, {
|
||||||
ui.Clip clipBehavior = ui.Clip.antiAlias,
|
ui.Clip clipBehavior = ui.Clip.antiAlias,
|
||||||
ui.ClipPathEngineLayer? oldLayer
|
ui.ClipPathEngineLayer? oldLayer
|
||||||
}) => pushLayer<ClipPathLayer>(ClipPathLayer(ClipPathOperation(path as ScenePath, clipBehavior)));
|
}) => pushLayer<ClipPathLayer>(
|
||||||
|
ClipPathLayer(),
|
||||||
|
ClipPathOperation(path as ScenePath, clipBehavior),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ui.ClipRRectEngineLayer pushClipRRect(
|
ui.ClipRRectEngineLayer pushClipRRect(
|
||||||
ui.RRect rrect, {
|
ui.RRect rrect, {
|
||||||
required ui.Clip clipBehavior,
|
required ui.Clip clipBehavior,
|
||||||
ui.ClipRRectEngineLayer? oldLayer
|
ui.ClipRRectEngineLayer? oldLayer
|
||||||
}) => pushLayer<ClipRRectLayer>(ClipRRectLayer(ClipRRectOperation(rrect, clipBehavior)));
|
}) => pushLayer<ClipRRectLayer>(
|
||||||
|
ClipRRectLayer(),
|
||||||
|
ClipRRectOperation(rrect, clipBehavior)
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ui.ClipRectEngineLayer pushClipRect(
|
ui.ClipRectEngineLayer pushClipRect(
|
||||||
@ -359,14 +163,20 @@ class EngineSceneBuilder implements ui.SceneBuilder {
|
|||||||
ui.Clip clipBehavior = ui.Clip.antiAlias,
|
ui.Clip clipBehavior = ui.Clip.antiAlias,
|
||||||
ui.ClipRectEngineLayer? oldLayer
|
ui.ClipRectEngineLayer? oldLayer
|
||||||
}) {
|
}) {
|
||||||
return pushLayer<ClipRectLayer>(ClipRectLayer(ClipRectOperation(rect, clipBehavior)));
|
return pushLayer<ClipRectLayer>(
|
||||||
|
ClipRectLayer(),
|
||||||
|
ClipRectOperation(rect, clipBehavior)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ui.ColorFilterEngineLayer pushColorFilter(
|
ui.ColorFilterEngineLayer pushColorFilter(
|
||||||
ui.ColorFilter filter, {
|
ui.ColorFilter filter, {
|
||||||
ui.ColorFilterEngineLayer? oldLayer
|
ui.ColorFilterEngineLayer? oldLayer
|
||||||
}) => pushLayer<ColorFilterLayer>(ColorFilterLayer(ColorFilterOperation(filter)));
|
}) => pushLayer<ColorFilterLayer>(
|
||||||
|
ColorFilterLayer(),
|
||||||
|
ColorFilterOperation(filter),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ui.ImageFilterEngineLayer pushImageFilter(
|
ui.ImageFilterEngineLayer pushImageFilter(
|
||||||
@ -374,7 +184,8 @@ class EngineSceneBuilder implements ui.SceneBuilder {
|
|||||||
ui.Offset offset = ui.Offset.zero,
|
ui.Offset offset = ui.Offset.zero,
|
||||||
ui.ImageFilterEngineLayer? oldLayer
|
ui.ImageFilterEngineLayer? oldLayer
|
||||||
}) => pushLayer<ImageFilterLayer>(
|
}) => pushLayer<ImageFilterLayer>(
|
||||||
ImageFilterLayer(ImageFilterOperation(filter as SceneImageFilter, offset)),
|
ImageFilterLayer(),
|
||||||
|
ImageFilterOperation(filter as SceneImageFilter, offset),
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -382,14 +193,19 @@ class EngineSceneBuilder implements ui.SceneBuilder {
|
|||||||
double dx,
|
double dx,
|
||||||
double dy, {
|
double dy, {
|
||||||
ui.OffsetEngineLayer? oldLayer
|
ui.OffsetEngineLayer? oldLayer
|
||||||
}) => pushLayer<OffsetLayer>(OffsetLayer(OffsetOperation(dx, dy)));
|
}) => pushLayer<OffsetLayer>(
|
||||||
|
OffsetLayer(),
|
||||||
|
OffsetOperation(dx, dy)
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ui.OpacityEngineLayer pushOpacity(int alpha, {
|
ui.OpacityEngineLayer pushOpacity(int alpha, {
|
||||||
ui.Offset offset = ui.Offset.zero,
|
ui.Offset offset = ui.Offset.zero,
|
||||||
ui.OpacityEngineLayer? oldLayer
|
ui.OpacityEngineLayer? oldLayer
|
||||||
}) => pushLayer<OpacityLayer>(OpacityLayer(OpacityOperation(alpha, offset)));
|
}) => pushLayer<OpacityLayer>(
|
||||||
|
OpacityLayer(),
|
||||||
|
OpacityOperation(alpha, offset),
|
||||||
|
);
|
||||||
@override
|
@override
|
||||||
ui.ShaderMaskEngineLayer pushShaderMask(
|
ui.ShaderMaskEngineLayer pushShaderMask(
|
||||||
ui.Shader shader,
|
ui.Shader shader,
|
||||||
@ -398,14 +214,18 @@ class EngineSceneBuilder implements ui.SceneBuilder {
|
|||||||
ui.ShaderMaskEngineLayer? oldLayer,
|
ui.ShaderMaskEngineLayer? oldLayer,
|
||||||
ui.FilterQuality filterQuality = ui.FilterQuality.low
|
ui.FilterQuality filterQuality = ui.FilterQuality.low
|
||||||
}) => pushLayer<ShaderMaskLayer>(
|
}) => pushLayer<ShaderMaskLayer>(
|
||||||
ShaderMaskLayer(ShaderMaskOperation(shader, maskRect, blendMode)),
|
ShaderMaskLayer(),
|
||||||
|
ShaderMaskOperation(shader, maskRect, blendMode)
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ui.TransformEngineLayer pushTransform(
|
ui.TransformEngineLayer pushTransform(
|
||||||
Float64List matrix4, {
|
Float64List matrix4, {
|
||||||
ui.TransformEngineLayer? oldLayer
|
ui.TransformEngineLayer? oldLayer
|
||||||
}) => pushLayer<TransformLayer>(TransformLayer(TransformOperation(matrix4)));
|
}) => pushLayer<TransformLayer>(
|
||||||
|
TransformLayer(),
|
||||||
|
TransformOperation(matrix4),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void setProperties(
|
void setProperties(
|
||||||
@ -440,10 +260,11 @@ class EngineSceneBuilder implements ui.SceneBuilder {
|
|||||||
currentBuilder.mergeLayer(layer);
|
currentBuilder.mergeLayer(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
T pushLayer<T extends PictureEngineLayer>(T layer) {
|
T pushLayer<T extends PictureEngineLayer>(T layer, LayerOperation operation) {
|
||||||
currentBuilder = LayerBuilder.childLayer(
|
currentBuilder = LayerBuilder.childLayer(
|
||||||
parent: currentBuilder,
|
parent: currentBuilder,
|
||||||
layer: layer,
|
layer: layer,
|
||||||
|
operation: operation
|
||||||
);
|
);
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
import 'package:ui/ui.dart' as ui;
|
import 'package:ui/ui.dart' as ui;
|
||||||
|
|
||||||
import 'vector_math.dart';
|
|
||||||
|
|
||||||
// These are additional APIs that are not part of the `dart:ui` interface that
|
// These are additional APIs that are not part of the `dart:ui` interface that
|
||||||
// are needed internally to properly implement a `SceneBuilder` on top of the
|
// are needed internally to properly implement a `SceneBuilder` on top of the
|
||||||
// generic Canvas/Picture api.
|
// generic Canvas/Picture api.
|
||||||
@ -24,10 +22,6 @@ abstract class SceneImageFilter implements ui.ImageFilter {
|
|||||||
// gives the maximum draw boundary for a picture with the given input bounds after it
|
// gives the maximum draw boundary for a picture with the given input bounds after it
|
||||||
// has been processed by the filter.
|
// has been processed by the filter.
|
||||||
ui.Rect filterBounds(ui.Rect inputBounds);
|
ui.Rect filterBounds(ui.Rect inputBounds);
|
||||||
|
|
||||||
// The matrix image filter changes the position of the content, so when positioning
|
|
||||||
// platform views and calculating occlusion we need to take its transform into account.
|
|
||||||
Matrix4? get transform;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ScenePath implements ui.Path {
|
abstract class ScenePath implements ui.Path {
|
||||||
|
@ -95,13 +95,11 @@ class EngineSceneView {
|
|||||||
flutterView.physicalSize.width,
|
flutterView.physicalSize.width,
|
||||||
flutterView.physicalSize.height,
|
flutterView.physicalSize.height,
|
||||||
);
|
);
|
||||||
final List<LayerSlice?> slices = scene.rootLayer.slices;
|
final List<LayerSlice> slices = scene.rootLayer.slices;
|
||||||
final List<ScenePicture> picturesToRender = <ScenePicture>[];
|
final List<ScenePicture> picturesToRender = <ScenePicture>[];
|
||||||
final List<ScenePicture> originalPicturesToRender = <ScenePicture>[];
|
final List<ScenePicture> originalPicturesToRender = <ScenePicture>[];
|
||||||
for (final LayerSlice? slice in slices) {
|
for (final LayerSlice slice in slices) {
|
||||||
if (slice == null) {
|
if (slice is PictureSlice) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final ui.Rect clippedRect = slice.picture.cullRect.intersect(screenBounds);
|
final ui.Rect clippedRect = slice.picture.cullRect.intersect(screenBounds);
|
||||||
if (clippedRect.isEmpty) {
|
if (clippedRect.isEmpty) {
|
||||||
// This picture is completely offscreen, so don't render it at all
|
// This picture is completely offscreen, so don't render it at all
|
||||||
@ -115,6 +113,7 @@ class EngineSceneView {
|
|||||||
picturesToRender.add(pictureRenderer.clipPicture(slice.picture, clippedRect));
|
picturesToRender.add(pictureRenderer.clipPicture(slice.picture, clippedRect));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
final Map<ScenePicture, DomImageBitmap> renderMap;
|
final Map<ScenePicture, DomImageBitmap> renderMap;
|
||||||
if (picturesToRender.isNotEmpty) {
|
if (picturesToRender.isNotEmpty) {
|
||||||
final RenderResult renderResult = await pictureRenderer.renderPictures(picturesToRender);
|
final RenderResult renderResult = await pictureRenderer.renderPictures(picturesToRender);
|
||||||
@ -133,12 +132,14 @@ class EngineSceneView {
|
|||||||
|
|
||||||
final List<SliceContainer?> reusableContainers = List<SliceContainer?>.from(containers);
|
final List<SliceContainer?> reusableContainers = List<SliceContainer?>.from(containers);
|
||||||
final List<SliceContainer> newContainers = <SliceContainer>[];
|
final List<SliceContainer> newContainers = <SliceContainer>[];
|
||||||
for (final LayerSlice? slice in slices) {
|
for (final LayerSlice slice in slices) {
|
||||||
if (slice == null) {
|
switch (slice) {
|
||||||
|
case PictureSlice():
|
||||||
|
final DomImageBitmap? bitmap = renderMap[slice.picture];
|
||||||
|
if (bitmap == null) {
|
||||||
|
// We didn't render this slice because no part of it is visible.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final DomImageBitmap? bitmap = renderMap[slice.picture];
|
|
||||||
if (bitmap != null) {
|
|
||||||
PictureSliceContainer? container;
|
PictureSliceContainer? container;
|
||||||
for (int j = 0; j < reusableContainers.length; j++) {
|
for (int j = 0; j < reusableContainers.length; j++) {
|
||||||
final SliceContainer? candidate = reusableContainers[j];
|
final SliceContainer? candidate = reusableContainers[j];
|
||||||
@ -158,9 +159,9 @@ class EngineSceneView {
|
|||||||
container.updateContents();
|
container.updateContents();
|
||||||
container.renderBitmap(bitmap);
|
container.renderBitmap(bitmap);
|
||||||
newContainers.add(container);
|
newContainers.add(container);
|
||||||
}
|
|
||||||
|
|
||||||
for (final PlatformView view in slice.platformViews) {
|
case PlatformViewSlice():
|
||||||
|
for (final PlatformView view in slice.views) {
|
||||||
// TODO(harryterkelsen): Inject the FlutterView instance from `renderScene`,
|
// TODO(harryterkelsen): Inject the FlutterView instance from `renderScene`,
|
||||||
// instead of using `EnginePlatformDispatcher...implicitView` directly,
|
// instead of using `EnginePlatformDispatcher...implicitView` directly,
|
||||||
// or make the FlutterView "register" like in canvaskit.
|
// or make the FlutterView "register" like in canvaskit.
|
||||||
@ -184,6 +185,7 @@ class EngineSceneView {
|
|||||||
newContainers.add(container);
|
newContainers.add(container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (final SliceContainer? container in reusableContainers) {
|
for (final SliceContainer? container in reusableContainers) {
|
||||||
if (container != null) {
|
if (container != null) {
|
||||||
|
@ -88,9 +88,6 @@ class SkwasmBlurFilter extends SkwasmImageFilter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'ImageFilter.blur($sigmaX, $sigmaY, ${tileModeString(tileMode)})';
|
String toString() => 'ImageFilter.blur($sigmaX, $sigmaY, ${tileModeString(tileMode)})';
|
||||||
|
|
||||||
@override
|
|
||||||
Matrix4? get transform => null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SkwasmDilateFilter extends SkwasmImageFilter {
|
class SkwasmDilateFilter extends SkwasmImageFilter {
|
||||||
@ -108,9 +105,6 @@ class SkwasmDilateFilter extends SkwasmImageFilter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'ImageFilter.dilate($radiusX, $radiusY)';
|
String toString() => 'ImageFilter.dilate($radiusX, $radiusY)';
|
||||||
|
|
||||||
@override
|
|
||||||
Matrix4? get transform => null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SkwasmErodeFilter extends SkwasmImageFilter {
|
class SkwasmErodeFilter extends SkwasmImageFilter {
|
||||||
@ -128,9 +122,6 @@ class SkwasmErodeFilter extends SkwasmImageFilter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'ImageFilter.erode($radiusX, $radiusY)';
|
String toString() => 'ImageFilter.erode($radiusX, $radiusY)';
|
||||||
|
|
||||||
@override
|
|
||||||
Matrix4? get transform => null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SkwasmMatrixFilter extends SkwasmImageFilter {
|
class SkwasmMatrixFilter extends SkwasmImageFilter {
|
||||||
@ -153,9 +144,6 @@ class SkwasmMatrixFilter extends SkwasmImageFilter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'ImageFilter.matrix($matrix4, $filterQuality)';
|
String toString() => 'ImageFilter.matrix($matrix4, $filterQuality)';
|
||||||
|
|
||||||
@override
|
|
||||||
Matrix4? get transform => Matrix4.fromFloat32List(toMatrix32(matrix4));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SkwasmColorImageFilter extends SkwasmImageFilter {
|
class SkwasmColorImageFilter extends SkwasmImageFilter {
|
||||||
@ -174,9 +162,6 @@ class SkwasmColorImageFilter extends SkwasmImageFilter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => filter.toString();
|
String toString() => filter.toString();
|
||||||
|
|
||||||
@override
|
|
||||||
Matrix4? get transform => null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SkwasmComposedImageFilter extends SkwasmImageFilter {
|
class SkwasmComposedImageFilter extends SkwasmImageFilter {
|
||||||
@ -198,16 +183,6 @@ class SkwasmComposedImageFilter extends SkwasmImageFilter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'ImageFilter.compose($outer, $inner)';
|
String toString() => 'ImageFilter.compose($outer, $inner)';
|
||||||
|
|
||||||
@override
|
|
||||||
Matrix4? get transform {
|
|
||||||
final outerTransform = outer.transform;
|
|
||||||
final innerTransform = inner.transform;
|
|
||||||
if (outerTransform != null && innerTransform != null) {
|
|
||||||
return outerTransform.multiplied(innerTransform);
|
|
||||||
}
|
|
||||||
return outerTransform ?? innerTransform;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef ColorFilterHandleBorrow = void Function(ColorFilterHandle handle);
|
typedef ColorFilterHandleBorrow = void Function(ColorFilterHandle handle);
|
||||||
|
@ -16,7 +16,7 @@ void main() {
|
|||||||
|
|
||||||
void testMain() {
|
void testMain() {
|
||||||
setUpAll(() {
|
setUpAll(() {
|
||||||
LayerSliceBuilder.debugRecorderFactory = () {
|
LayerBuilder.debugRecorderFactory = (ui.Rect rect) {
|
||||||
final StubSceneCanvas canvas = StubSceneCanvas();
|
final StubSceneCanvas canvas = StubSceneCanvas();
|
||||||
final StubPictureRecorder recorder = StubPictureRecorder(canvas);
|
final StubPictureRecorder recorder = StubPictureRecorder(canvas);
|
||||||
return (recorder, canvas);
|
return (recorder, canvas);
|
||||||
@ -24,7 +24,7 @@ void testMain() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tearDownAll(() {
|
tearDownAll(() {
|
||||||
LayerSliceBuilder.debugRecorderFactory = null;
|
LayerBuilder.debugRecorderFactory = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
group('EngineSceneBuilder', () {
|
group('EngineSceneBuilder', () {
|
||||||
@ -35,23 +35,23 @@ void testMain() {
|
|||||||
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect));
|
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect));
|
||||||
|
|
||||||
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
||||||
final List<LayerSlice?> slices = scene.rootLayer.slices;
|
final List<LayerSlice> slices = scene.rootLayer.slices;
|
||||||
expect(slices.length, 1);
|
expect(slices.length, 1);
|
||||||
expect(slices[0], layerSlice(withPictureRect: pictureRect));
|
expect(slices[0], pictureSliceWithRect(pictureRect));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('two pictures', () {
|
test('two pictures', () {
|
||||||
final EngineSceneBuilder sceneBuilder = EngineSceneBuilder();
|
final EngineSceneBuilder sceneBuilder = EngineSceneBuilder();
|
||||||
|
|
||||||
const ui.Rect pictureRect1 = ui.Rect.fromLTRB(100, 100, 200, 200);
|
const ui.Rect pictureRect1 = ui.Rect.fromLTRB(100, 100, 200, 200);
|
||||||
const ui.Rect pictureRect2 = ui.Rect.fromLTRB(300, 300, 400, 400);
|
const ui.Rect pictureRect2 = ui.Rect.fromLTRB(300, 400, 400, 400);
|
||||||
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect1));
|
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect1));
|
||||||
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect2));
|
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect2));
|
||||||
|
|
||||||
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
||||||
final List<LayerSlice?> slices = scene.rootLayer.slices;
|
final List<LayerSlice> slices = scene.rootLayer.slices;
|
||||||
expect(slices.length, 1);
|
expect(slices.length, 1);
|
||||||
expect(slices[0], layerSlice(withPictureRect: const ui.Rect.fromLTRB(100, 100, 400, 400)));
|
expect(slices[0], pictureSliceWithRect(const ui.Rect.fromLTRB(100, 100, 400, 400)));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('picture + platform view (overlapping)', () {
|
test('picture + platform view (overlapping)', () {
|
||||||
@ -68,11 +68,10 @@ void testMain() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
||||||
final List<LayerSlice?> slices = scene.rootLayer.slices;
|
final List<LayerSlice> slices = scene.rootLayer.slices;
|
||||||
expect(slices.length, 1);
|
expect(slices.length, 2);
|
||||||
expect(slices[0], layerSlice(
|
expect(slices[0], pictureSliceWithRect(pictureRect));
|
||||||
withPictureRect: pictureRect,
|
expect(slices[1], platformViewSliceWithViews(<PlatformView>[
|
||||||
withPlatformViews: <PlatformView>[
|
|
||||||
PlatformView(1, platformViewRect, const PlatformViewStyling())
|
PlatformView(1, platformViewRect, const PlatformViewStyling())
|
||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
@ -91,12 +90,12 @@ void testMain() {
|
|||||||
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect));
|
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect));
|
||||||
|
|
||||||
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
||||||
final List<LayerSlice?> slices = scene.rootLayer.slices;
|
final List<LayerSlice> slices = scene.rootLayer.slices;
|
||||||
expect(slices.length, 2);
|
expect(slices.length, 2);
|
||||||
expect(slices[0], layerSlice(withPlatformViews: <PlatformView>[
|
expect(slices[0], platformViewSliceWithViews(<PlatformView>[
|
||||||
PlatformView(1, platformViewRect, const PlatformViewStyling())
|
PlatformView(1, platformViewRect, const PlatformViewStyling())
|
||||||
]));
|
]));
|
||||||
expect(slices[1], layerSlice(withPictureRect: pictureRect));
|
expect(slices[1], pictureSliceWithRect(pictureRect));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('platform view sandwich (overlapping)', () {
|
test('platform view sandwich (overlapping)', () {
|
||||||
@ -115,14 +114,13 @@ void testMain() {
|
|||||||
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect2));
|
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect2));
|
||||||
|
|
||||||
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
||||||
final List<LayerSlice?> slices = scene.rootLayer.slices;
|
final List<LayerSlice> slices = scene.rootLayer.slices;
|
||||||
expect(slices.length, 2);
|
expect(slices.length, 3);
|
||||||
expect(slices[0], layerSlice(
|
expect(slices[0], pictureSliceWithRect(pictureRect1));
|
||||||
withPictureRect: pictureRect1,
|
expect(slices[1], platformViewSliceWithViews(<PlatformView>[
|
||||||
withPlatformViews: <PlatformView>[
|
|
||||||
PlatformView(1, platformViewRect, const PlatformViewStyling())
|
PlatformView(1, platformViewRect, const PlatformViewStyling())
|
||||||
]));
|
]));
|
||||||
expect(slices[1], layerSlice(withPictureRect: pictureRect2));
|
expect(slices[2], pictureSliceWithRect(pictureRect2));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('platform view sandwich (non-overlapping)', () {
|
test('platform view sandwich (non-overlapping)', () {
|
||||||
@ -141,15 +139,14 @@ void testMain() {
|
|||||||
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect2));
|
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect2));
|
||||||
|
|
||||||
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
||||||
final List<LayerSlice?> slices = scene.rootLayer.slices;
|
final List<LayerSlice> slices = scene.rootLayer.slices;
|
||||||
|
|
||||||
// The top picture does not overlap with the platform view, so it should
|
// The top picture does not overlap with the platform view, so it should
|
||||||
// be grouped into the slice below it to reduce the number of canvases we
|
// be grouped into the slice below it to reduce the number of canvases we
|
||||||
// need.
|
// need.
|
||||||
expect(slices.length, 1);
|
expect(slices.length, 2);
|
||||||
expect(slices[0], layerSlice(
|
expect(slices[0], pictureSliceWithRect(const ui.Rect.fromLTRB(50, 50, 200, 200)));
|
||||||
withPictureRect: const ui.Rect.fromLTRB(50, 50, 200, 200),
|
expect(slices[1], platformViewSliceWithViews(<PlatformView>[
|
||||||
withPlatformViews: <PlatformView>[
|
|
||||||
PlatformView(1, platformViewRect, const PlatformViewStyling())
|
PlatformView(1, platformViewRect, const PlatformViewStyling())
|
||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
@ -172,99 +169,34 @@ void testMain() {
|
|||||||
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(const ui.Rect.fromLTRB(0, 0, 100, 100)));
|
sceneBuilder.addPicture(ui.Offset.zero, StubPicture(const ui.Rect.fromLTRB(0, 0, 100, 100)));
|
||||||
|
|
||||||
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
||||||
final List<LayerSlice?> slices = scene.rootLayer.slices;
|
final List<LayerSlice> slices = scene.rootLayer.slices;
|
||||||
expect(slices.length, 2);
|
expect(slices.length, 3);
|
||||||
expect(slices[0], layerSlice(
|
expect(slices[0], pictureSliceWithRect(pictureRect1));
|
||||||
withPictureRect: pictureRect1,
|
expect(slices[1], platformViewSliceWithViews(<PlatformView>[
|
||||||
withPlatformViews: <PlatformView>[
|
|
||||||
PlatformView(1, platformViewRect, const PlatformViewStyling(position: PlatformViewPosition.offset(ui.Offset(150, 150))))
|
PlatformView(1, platformViewRect, const PlatformViewStyling(position: PlatformViewPosition.offset(ui.Offset(150, 150))))
|
||||||
]));
|
]));
|
||||||
expect(slices[1], layerSlice(withPictureRect: const ui.Rect.fromLTRB(200, 200, 300, 300)));
|
expect(slices[2], pictureSliceWithRect(const ui.Rect.fromLTRB(200, 200, 300, 300)));
|
||||||
});
|
|
||||||
|
|
||||||
test('grid view test', () {
|
|
||||||
// This test case covers a grid of elements, where each element is a platform
|
|
||||||
// view that has flutter content underneath it and on top of it.
|
|
||||||
// See a detailed explanation of this use-case in the following flutter issue:
|
|
||||||
// https://github.com/flutter/flutter/issues/149863
|
|
||||||
final EngineSceneBuilder sceneBuilder = EngineSceneBuilder();
|
|
||||||
|
|
||||||
const double padding = 10;
|
|
||||||
const double tileSize = 50;
|
|
||||||
final List<PlatformView> expectedPlatformViews = <PlatformView>[];
|
|
||||||
for (int x = 0; x < 10; x++) {
|
|
||||||
for (int y = 0; y < 10; y++) {
|
|
||||||
final ui.Offset offset = ui.Offset(
|
|
||||||
padding + (tileSize + padding) * x,
|
|
||||||
padding + (tileSize + padding) * y,
|
|
||||||
);
|
|
||||||
sceneBuilder.pushOffset(offset.dx, offset.dy);
|
|
||||||
sceneBuilder.addPicture(
|
|
||||||
ui.Offset.zero,
|
|
||||||
StubPicture(const ui.Rect.fromLTWH(0, 0, tileSize, tileSize))
|
|
||||||
);
|
|
||||||
sceneBuilder.addPlatformView(
|
|
||||||
1,
|
|
||||||
offset: const ui.Offset(5, 5),
|
|
||||||
width: tileSize - 10,
|
|
||||||
height: tileSize - 10,
|
|
||||||
);
|
|
||||||
sceneBuilder.addPicture(
|
|
||||||
const ui.Offset(10, 10),
|
|
||||||
StubPicture(const ui.Rect.fromLTWH(0, 0, tileSize - 20, tileSize - 20)),
|
|
||||||
);
|
|
||||||
sceneBuilder.pop();
|
|
||||||
expectedPlatformViews.add(PlatformView(
|
|
||||||
1,
|
|
||||||
const ui.Rect.fromLTRB(5.0, 5.0, tileSize - 5.0, tileSize - 5.0),
|
|
||||||
PlatformViewStyling(position: PlatformViewPosition.offset(offset))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final EngineScene scene = sceneBuilder.build() as EngineScene;
|
|
||||||
final List<LayerSlice?> slices = scene.rootLayer.slices;
|
|
||||||
|
|
||||||
// It is important that the optimizations of the scene builder result in
|
|
||||||
// there only being two scene slices.
|
|
||||||
expect(slices.length, 2);
|
|
||||||
expect(slices[0], layerSlice(
|
|
||||||
withPictureRect: const ui.Rect.fromLTRB(
|
|
||||||
padding,
|
|
||||||
padding,
|
|
||||||
10 * (padding + tileSize),
|
|
||||||
10 * (padding + tileSize)
|
|
||||||
),
|
|
||||||
withPlatformViews: expectedPlatformViews,
|
|
||||||
));
|
|
||||||
expect(slices[1], layerSlice(withPictureRect: const ui.Rect.fromLTRB(
|
|
||||||
padding + 10,
|
|
||||||
padding + 10,
|
|
||||||
10 * (padding + tileSize) - 10,
|
|
||||||
10 * (padding + tileSize) - 10,
|
|
||||||
)));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LayerSliceMatcher layerSlice({
|
PictureSliceMatcher pictureSliceWithRect(ui.Rect rect) => PictureSliceMatcher(rect);
|
||||||
ui.Rect withPictureRect = ui.Rect.zero,
|
PlatformViewSliceMatcher platformViewSliceWithViews(List<PlatformView> views)
|
||||||
List<PlatformView> withPlatformViews = const <PlatformView>[],
|
=> PlatformViewSliceMatcher(views);
|
||||||
}) => LayerSliceMatcher(withPictureRect, withPlatformViews);
|
|
||||||
class LayerSliceMatcher extends Matcher {
|
|
||||||
LayerSliceMatcher(this.expectedPictureRect, this.expectedPlatformViews);
|
|
||||||
|
|
||||||
final ui.Rect expectedPictureRect;
|
class PictureSliceMatcher extends Matcher {
|
||||||
final List<PlatformView> expectedPlatformViews;
|
PictureSliceMatcher(this.expectedRect);
|
||||||
|
|
||||||
|
final ui.Rect expectedRect;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Description describe(Description description) {
|
Description describe(Description description) {
|
||||||
return description.add('<picture slice with cullRect: $expectedPictureRect and platform views: $expectedPlatformViews>');
|
return description.add('<picture slice with cullRect: $expectedRect>');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
|
bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
|
||||||
if (item is! LayerSlice) {
|
if (item is! PictureSlice) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final ScenePicture picture = item.picture;
|
final ScenePicture picture = item.picture;
|
||||||
@ -272,28 +204,50 @@ class LayerSliceMatcher extends Matcher {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (picture.cullRect != expectedPictureRect) {
|
if (picture.cullRect != expectedRect) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.platformViews.length != expectedPlatformViews.length) {
|
return true;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < item.platformViews.length; i++) {
|
class PlatformViewSliceMatcher extends Matcher {
|
||||||
final PlatformView expectedView = expectedPlatformViews[i];
|
PlatformViewSliceMatcher(this.expectedPlatformViews);
|
||||||
final PlatformView actualView = item.platformViews[i];
|
|
||||||
if (expectedView.viewId != actualView.viewId) {
|
final List<PlatformView> expectedPlatformViews;
|
||||||
return false;
|
|
||||||
}
|
@override
|
||||||
if (expectedView.bounds != actualView.bounds) {
|
Description describe(Description description) {
|
||||||
return false;
|
return description.add('<platform view slice with platform views: $expectedPlatformViews>');
|
||||||
}
|
}
|
||||||
if (expectedView.styling != actualView.styling) {
|
|
||||||
return false;
|
@override
|
||||||
}
|
bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
|
||||||
}
|
if (item is! PlatformViewSlice) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.views.length != expectedPlatformViews.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < item.views.length; i++) {
|
||||||
|
final PlatformView expectedView = expectedPlatformViews[i];
|
||||||
|
final PlatformView actualView = item.views[i];
|
||||||
|
if (expectedView.viewId != actualView.viewId) {
|
||||||
|
print('viewID mismatch');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (expectedView.bounds != actualView.bounds) {
|
||||||
|
print('bounds mismatch');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (expectedView.styling != actualView.styling) {
|
||||||
|
print('styling mismatch');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,12 +36,8 @@ class StubPicture implements ScenePicture {
|
|||||||
class StubCompositePicture extends StubPicture {
|
class StubCompositePicture extends StubPicture {
|
||||||
StubCompositePicture(this.children) : super(
|
StubCompositePicture(this.children) : super(
|
||||||
children.fold(null, (ui.Rect? previousValue, StubPicture child) {
|
children.fold(null, (ui.Rect? previousValue, StubPicture child) {
|
||||||
final ui.Rect childRect = child.cullRect;
|
|
||||||
if (childRect.isEmpty) {
|
|
||||||
return previousValue;
|
|
||||||
}
|
|
||||||
return previousValue?.expandToInclude(child.cullRect) ?? child.cullRect;
|
return previousValue?.expandToInclude(child.cullRect) ?? child.cullRect;
|
||||||
}) ?? ui.Rect.zero
|
})!
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<StubPicture> children;
|
final List<StubPicture> children;
|
||||||
|
@ -172,7 +172,7 @@ void testMain() {
|
|||||||
120,
|
120,
|
||||||
));
|
));
|
||||||
final EngineRootLayer rootLayer = EngineRootLayer();
|
final EngineRootLayer rootLayer = EngineRootLayer();
|
||||||
rootLayer.slices.add(LayerSlice(picture, <PlatformView>[]));
|
rootLayer.slices.add(PictureSlice(picture));
|
||||||
final EngineScene scene = EngineScene(rootLayer);
|
final EngineScene scene = EngineScene(rootLayer);
|
||||||
await sceneView.renderScene(scene, null);
|
await sceneView.renderScene(scene, null);
|
||||||
|
|
||||||
@ -205,7 +205,7 @@ void testMain() {
|
|||||||
const ui.Rect.fromLTWH(50, 80, 100, 120),
|
const ui.Rect.fromLTWH(50, 80, 100, 120),
|
||||||
const PlatformViewStyling());
|
const PlatformViewStyling());
|
||||||
final EngineRootLayer rootLayer = EngineRootLayer();
|
final EngineRootLayer rootLayer = EngineRootLayer();
|
||||||
rootLayer.slices.add(LayerSlice(StubPicture(ui.Rect.zero), <PlatformView>[platformView]));
|
rootLayer.slices.add(PlatformViewSlice(<PlatformView>[platformView], null));
|
||||||
final EngineScene scene = EngineScene(rootLayer);
|
final EngineScene scene = EngineScene(rootLayer);
|
||||||
await sceneView.renderScene(scene, null);
|
await sceneView.renderScene(scene, null);
|
||||||
|
|
||||||
@ -246,7 +246,7 @@ void testMain() {
|
|||||||
));
|
));
|
||||||
pictures.add(picture);
|
pictures.add(picture);
|
||||||
final EngineRootLayer rootLayer = EngineRootLayer();
|
final EngineRootLayer rootLayer = EngineRootLayer();
|
||||||
rootLayer.slices.add(LayerSlice(picture, <PlatformView>[]));
|
rootLayer.slices.add(PictureSlice(picture));
|
||||||
final EngineScene scene = EngineScene(rootLayer);
|
final EngineScene scene = EngineScene(rootLayer);
|
||||||
renderFutures.add(sceneView.renderScene(scene, null));
|
renderFutures.add(sceneView.renderScene(scene, null));
|
||||||
}
|
}
|
||||||
@ -267,7 +267,7 @@ void testMain() {
|
|||||||
));
|
));
|
||||||
|
|
||||||
final EngineRootLayer rootLayer = EngineRootLayer();
|
final EngineRootLayer rootLayer = EngineRootLayer();
|
||||||
rootLayer.slices.add(LayerSlice(picture, <PlatformView>[]));
|
rootLayer.slices.add(PictureSlice(picture));
|
||||||
final EngineScene scene = EngineScene(rootLayer);
|
final EngineScene scene = EngineScene(rootLayer);
|
||||||
await sceneView.renderScene(scene, null);
|
await sceneView.renderScene(scene, null);
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ Future<void> testMain() async {
|
|||||||
region: region);
|
region: region);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('blur image filter layer', () async {
|
test('image filter layer', () async {
|
||||||
final ui.SceneBuilder sceneBuilder = ui.SceneBuilder();
|
final ui.SceneBuilder sceneBuilder = ui.SceneBuilder();
|
||||||
sceneBuilder.pushImageFilter(ui.ImageFilter.blur(
|
sceneBuilder.pushImageFilter(ui.ImageFilter.blur(
|
||||||
sigmaX: 5.0,
|
sigmaX: 5.0,
|
||||||
@ -239,23 +239,6 @@ Future<void> testMain() async {
|
|||||||
await matchGoldenFile('scene_builder_image_filter.png', region: region);
|
await matchGoldenFile('scene_builder_image_filter.png', region: region);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('matrix image filter layer', () async {
|
|
||||||
final ui.SceneBuilder sceneBuilder = ui.SceneBuilder();
|
|
||||||
sceneBuilder.pushOffset(50.0, 50.0);
|
|
||||||
|
|
||||||
final Matrix4 matrix = Matrix4.rotationZ(math.pi / 18);
|
|
||||||
final ui.ImageFilter matrixFilter = ui.ImageFilter.matrix(toMatrix64(matrix.storage));
|
|
||||||
sceneBuilder.pushImageFilter(matrixFilter);
|
|
||||||
sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) {
|
|
||||||
canvas.drawRect(
|
|
||||||
region,
|
|
||||||
ui.Paint()..color = const ui.Color(0xFF00FF00)
|
|
||||||
);
|
|
||||||
}));
|
|
||||||
await renderScene(sceneBuilder.build());
|
|
||||||
await matchGoldenFile('scene_builder_matrix_image_filter.png', region: region);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Regression test for https://github.com/flutter/flutter/issues/154303
|
// Regression test for https://github.com/flutter/flutter/issues/154303
|
||||||
test('image filter layer with offset', () async {
|
test('image filter layer with offset', () async {
|
||||||
final ui.SceneBuilder sceneBuilder = ui.SceneBuilder();
|
final ui.SceneBuilder sceneBuilder = ui.SceneBuilder();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user