From d60f2ce32aa8ce127b9321d500842ecd6ad7d6d0 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Mon, 28 Aug 2023 17:42:02 -0700 Subject: [PATCH 1/2] Fix scene view canvas/platform view placement. --- lib/web_ui/lib/src/engine/scene_view.dart | 12 +- .../test/engine/scene_builder_test.dart | 182 +---------------- .../test/engine/scene_builder_utils.dart | 186 ++++++++++++++++++ lib/web_ui/test/engine/scene_view_test.dart | 117 +++++++++++ 4 files changed, 313 insertions(+), 184 deletions(-) create mode 100644 lib/web_ui/test/engine/scene_builder_utils.dart create mode 100644 lib/web_ui/test/engine/scene_view_test.dart diff --git a/lib/web_ui/lib/src/engine/scene_view.dart b/lib/web_ui/lib/src/engine/scene_view.dart index a8c4a17307a27..f1c75c0939d25 100644 --- a/lib/web_ui/lib/src/engine/scene_view.dart +++ b/lib/web_ui/lib/src/engine/scene_view.dart @@ -163,11 +163,13 @@ final class PictureSliceContainer extends SliceContainer { final DomCSSStyleDeclaration style = canvas.style; final double logicalWidth = roundedOutBounds.width / window.devicePixelRatio; final double logicalHeight = roundedOutBounds.height / window.devicePixelRatio; + final double logicalLeft = roundedOutBounds.left / window.devicePixelRatio; + final double logicalTop = roundedOutBounds.top / window.devicePixelRatio; style.width = '${logicalWidth}px'; style.height = '${logicalHeight}px'; style.position = 'absolute'; - style.left = '${roundedOutBounds.left}px'; - style.top = '${roundedOutBounds.top}px'; + style.left = '${logicalLeft}px'; + style.top = '${logicalTop}px'; canvas.width = roundedOutBounds.width.ceilToDouble(); canvas.height = roundedOutBounds.height.ceilToDouble(); } @@ -221,8 +223,10 @@ final class PlatformViewContainer extends SliceContainer { style.position = 'absolute'; final ui.Offset? offset = _styling!.position.offset; - style.left = '${offset?.dx ?? 0}px'; - style.top = '${offset?.dy ?? 0}px'; + final double logicalLeft = (offset?.dx ?? 0) / window.devicePixelRatio; + final double logicalTop = (offset?.dy ?? 0) / window.devicePixelRatio; + style.left = '${logicalLeft}px'; + style.top = '${logicalTop}px'; final Matrix4? transform = _styling!.position.transform; style.transform = transform != null ? float64ListToCssTransform3d(transform.storage) : ''; diff --git a/lib/web_ui/test/engine/scene_builder_test.dart b/lib/web_ui/test/engine/scene_builder_test.dart index 693f39203dad0..0a80cb2cad987 100644 --- a/lib/web_ui/test/engine/scene_builder_test.dart +++ b/lib/web_ui/test/engine/scene_builder_test.dart @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:typed_data'; - import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import 'scene_builder_utils.dart'; + void main() { internalBootstrapBrowserTest(() => testMain); } @@ -229,181 +229,3 @@ class PlatformViewSliceMatcher extends Matcher { return true; } } - -class StubPicture implements ScenePicture { - StubPicture(this.cullRect); - - @override - final ui.Rect cullRect; - - @override - int get approximateBytesUsed => throw UnimplementedError(); - - @override - bool get debugDisposed => throw UnimplementedError(); - - @override - void dispose() {} - - @override - Future toImage(int width, int height) { - throw UnimplementedError(); - } - - @override - ui.Image toImageSync(int width, int height) { - throw UnimplementedError(); - } -} - -class StubCompositePicture extends StubPicture { - StubCompositePicture(this.children) : super( - children.fold(null, (ui.Rect? previousValue, StubPicture child) { - return previousValue?.expandToInclude(child.cullRect) ?? child.cullRect; - })! - ); - - final List children; -} - -class StubPictureRecorder implements ui.PictureRecorder { - StubPictureRecorder(this.canvas); - - final StubSceneCanvas canvas; - - @override - ui.Picture endRecording() { - return StubCompositePicture(canvas.pictures); - } - - @override - bool get isRecording => throw UnimplementedError(); -} - -class StubSceneCanvas implements SceneCanvas { - List pictures = []; - - @override - void drawPicture(ui.Picture picture) { - pictures.add(picture as StubPicture); - } - - @override - void clipPath(ui.Path path, {bool doAntiAlias = true}) {} - - @override - void clipRRect(ui.RRect rrect, {bool doAntiAlias = true}) {} - - @override - void clipRect(ui.Rect rect, {ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true}) {} - - @override - void drawArc(ui.Rect rect, double startAngle, double sweepAngle, bool useCenter, ui.Paint paint) {} - - @override - void drawAtlas(ui.Image atlas, List transforms, List rects, List? colors, ui.BlendMode? blendMode, ui.Rect? cullRect, ui.Paint paint) {} - - @override - void drawCircle(ui.Offset c, double radius, ui.Paint paint) {} - - @override - void drawColor(ui.Color color, ui.BlendMode blendMode) {} - - @override - void drawDRRect(ui.RRect outer, ui.RRect inner, ui.Paint paint) {} - - @override - void drawImage(ui.Image image, ui.Offset offset, ui.Paint paint) {} - - @override - void drawImageNine(ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) {} - - @override - void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, ui.Paint paint) {} - - @override - void drawLine(ui.Offset p1, ui.Offset p2, ui.Paint paint) {} - - @override - void drawOval(ui.Rect rect, ui.Paint paint) {} - - @override - void drawPaint(ui.Paint paint) {} - - @override - void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) {} - - @override - void drawPath(ui.Path path, ui.Paint paint) {} - - @override - void drawPoints(ui.PointMode pointMode, List points, ui.Paint paint) {} - - @override - void drawRRect(ui.RRect rrect, ui.Paint paint) {} - - @override - void drawRawAtlas(ui.Image atlas, Float32List rstTransforms, Float32List rects, Int32List? colors, ui.BlendMode? blendMode, ui.Rect? cullRect, ui.Paint paint) {} - - @override - void drawRawPoints(ui.PointMode pointMode, Float32List points, ui.Paint paint) {} - - @override - void drawRect(ui.Rect rect, ui.Paint paint) {} - - @override - void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) {} - - @override - void drawVertices(ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) {} - - @override - ui.Rect getDestinationClipBounds() { - throw UnimplementedError(); - } - - @override - ui.Rect getLocalClipBounds() { - throw UnimplementedError(); - } - - @override - int getSaveCount() { - throw UnimplementedError(); - } - - @override - Float64List getTransform() { - throw UnimplementedError(); - } - - @override - void restore() {} - - @override - void restoreToCount(int count) {} - - @override - void rotate(double radians) {} - - @override - void save() {} - - @override - void saveLayer(ui.Rect? bounds, ui.Paint paint) {} - - @override - void saveLayerWithFilter(ui.Rect? bounds, ui.Paint paint, ui.ImageFilter backdropFilter) {} - - @override - void scale(double sx, [double? sy]) {} - - @override - void skew(double sx, double sy) {} - - @override - void transform(Float64List matrix4) {} - - @override - void translate(double dx, double dy) {} -} diff --git a/lib/web_ui/test/engine/scene_builder_utils.dart b/lib/web_ui/test/engine/scene_builder_utils.dart new file mode 100644 index 0000000000000..72541786f819f --- /dev/null +++ b/lib/web_ui/test/engine/scene_builder_utils.dart @@ -0,0 +1,186 @@ +// 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. + +import 'dart:typed_data'; + +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +class StubPicture implements ScenePicture { + StubPicture(this.cullRect); + + @override + final ui.Rect cullRect; + + @override + int get approximateBytesUsed => throw UnimplementedError(); + + @override + bool get debugDisposed => throw UnimplementedError(); + + @override + void dispose() {} + + @override + Future toImage(int width, int height) { + throw UnimplementedError(); + } + + @override + ui.Image toImageSync(int width, int height) { + throw UnimplementedError(); + } +} + +class StubCompositePicture extends StubPicture { + StubCompositePicture(this.children) : super( + children.fold(null, (ui.Rect? previousValue, StubPicture child) { + return previousValue?.expandToInclude(child.cullRect) ?? child.cullRect; + })! + ); + + final List children; +} + +class StubPictureRecorder implements ui.PictureRecorder { + StubPictureRecorder(this.canvas); + + final StubSceneCanvas canvas; + + @override + ui.Picture endRecording() { + return StubCompositePicture(canvas.pictures); + } + + @override + bool get isRecording => throw UnimplementedError(); +} + +class StubSceneCanvas implements SceneCanvas { + List pictures = []; + + @override + void drawPicture(ui.Picture picture) { + pictures.add(picture as StubPicture); + } + + @override + void clipPath(ui.Path path, {bool doAntiAlias = true}) {} + + @override + void clipRRect(ui.RRect rrect, {bool doAntiAlias = true}) {} + + @override + void clipRect(ui.Rect rect, {ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true}) {} + + @override + void drawArc(ui.Rect rect, double startAngle, double sweepAngle, bool useCenter, ui.Paint paint) {} + + @override + void drawAtlas(ui.Image atlas, List transforms, List rects, List? colors, ui.BlendMode? blendMode, ui.Rect? cullRect, ui.Paint paint) {} + + @override + void drawCircle(ui.Offset c, double radius, ui.Paint paint) {} + + @override + void drawColor(ui.Color color, ui.BlendMode blendMode) {} + + @override + void drawDRRect(ui.RRect outer, ui.RRect inner, ui.Paint paint) {} + + @override + void drawImage(ui.Image image, ui.Offset offset, ui.Paint paint) {} + + @override + void drawImageNine(ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) {} + + @override + void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, ui.Paint paint) {} + + @override + void drawLine(ui.Offset p1, ui.Offset p2, ui.Paint paint) {} + + @override + void drawOval(ui.Rect rect, ui.Paint paint) {} + + @override + void drawPaint(ui.Paint paint) {} + + @override + void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) {} + + @override + void drawPath(ui.Path path, ui.Paint paint) {} + + @override + void drawPoints(ui.PointMode pointMode, List points, ui.Paint paint) {} + + @override + void drawRRect(ui.RRect rrect, ui.Paint paint) {} + + @override + void drawRawAtlas(ui.Image atlas, Float32List rstTransforms, Float32List rects, Int32List? colors, ui.BlendMode? blendMode, ui.Rect? cullRect, ui.Paint paint) {} + + @override + void drawRawPoints(ui.PointMode pointMode, Float32List points, ui.Paint paint) {} + + @override + void drawRect(ui.Rect rect, ui.Paint paint) {} + + @override + void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) {} + + @override + void drawVertices(ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) {} + + @override + ui.Rect getDestinationClipBounds() { + throw UnimplementedError(); + } + + @override + ui.Rect getLocalClipBounds() { + throw UnimplementedError(); + } + + @override + int getSaveCount() { + throw UnimplementedError(); + } + + @override + Float64List getTransform() { + throw UnimplementedError(); + } + + @override + void restore() {} + + @override + void restoreToCount(int count) {} + + @override + void rotate(double radians) {} + + @override + void save() {} + + @override + void saveLayer(ui.Rect? bounds, ui.Paint paint) {} + + @override + void saveLayerWithFilter(ui.Rect? bounds, ui.Paint paint, ui.ImageFilter backdropFilter) {} + + @override + void scale(double sx, [double? sy]) {} + + @override + void skew(double sx, double sy) {} + + @override + void transform(Float64List matrix4) {} + + @override + void translate(double dx, double dy) {} +} diff --git a/lib/web_ui/test/engine/scene_view_test.dart b/lib/web_ui/test/engine/scene_view_test.dart new file mode 100644 index 0000000000000..3da90341e5335 --- /dev/null +++ b/lib/web_ui/test/engine/scene_view_test.dart @@ -0,0 +1,117 @@ +// 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. + +import 'dart:async'; +import 'dart:js_interop'; + +import 'package:quiver/strings.dart'; +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart'; + +import 'scene_builder_utils.dart'; + +@JS('createImageBitmap') +external JSPromise createImageBitmap( + JSAny source, + JSNumber x, + JSNumber y, + JSNumber width, + JSNumber height +); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +class StubPictureRenderer implements PictureRenderer { + final DomCanvasElement scratchCanvasElement = createDomCanvasElement( + width: 500, height: 500 + ); + + @override + Future renderPicture(ScenePicture picture) async { + final ui.Rect cullRect = picture.cullRect; + final DomImageBitmap bitmap = (await createImageBitmap( + scratchCanvasElement as JSAny, + 0.toJS, + 0.toJS, + cullRect.width.toJS, + cullRect.height.toJS + ).toDart)! as DomImageBitmap; + return bitmap; + } +} + +void testMain() { + late EngineSceneView sceneView; + setUp(() { + sceneView = EngineSceneView(StubPictureRenderer()); + }); + + test('SceneView places canvas according to device-pixel ratio', () async { + debugOverrideDevicePixelRatio(2.0); + + final StubPicture picture = StubPicture(const ui.Rect.fromLTWH( + 50, + 80, + 100, + 120, + )); + final EngineRootLayer rootLayer = EngineRootLayer(); + rootLayer.slices.add(PictureSlice(picture)); + final EngineScene scene = EngineScene(rootLayer); + await sceneView.renderScene(scene); + + final DomElement sceneElement = sceneView.sceneElement; + final List children = sceneElement.children.toList(); + expect(children.length, 1); + final DomElement containerElement = children.first; + expect(containerElement.tagName, equalsIgnoringCase('flt-canvas-container')); + + final List containerChildren = containerElement.children.toList(); + expect(containerChildren.length, 1); + final DomElement canvasElement = containerChildren.first; + final DomCSSStyleDeclaration style = canvasElement.style; + expect(style.left, '25px'); + expect(style.top, '40px'); + expect(style.width, '50px'); + expect(style.height, '60px'); + + debugOverrideDevicePixelRatio(null); + }); + + test('SceneView places canvas according to device-pixel ratio', () async { + debugOverrideDevicePixelRatio(2.0); + + final PlatformView platformView = PlatformView( + 1, + const ui.Size(100, 120), + const PlatformViewStyling( + position: PlatformViewPosition.offset(ui.Offset(50, 80)), + ) + ); + final EngineRootLayer rootLayer = EngineRootLayer(); + rootLayer.slices.add(PlatformViewSlice([platformView], null)); + final EngineScene scene = EngineScene(rootLayer); + await sceneView.renderScene(scene); + + final DomElement sceneElement = sceneView.sceneElement; + final List children = sceneElement.children.toList(); + expect(children.length, 1); + final DomElement containerElement = children.first; + expect(containerElement.tagName, equalsIgnoringCase('flt-platform-view-slot')); + + final DomCSSStyleDeclaration style = containerElement.style; + expect(style.left, '25px'); + expect(style.top, '40px'); + expect(style.width, '50px'); + expect(style.height, '60px'); + + debugOverrideDevicePixelRatio(null); + }); +} From 83c9939fc05957d2b3ab422d160ee2ac145a4c68 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Mon, 28 Aug 2023 18:09:48 -0700 Subject: [PATCH 2/2] Remove some random import the IDE jammed in there. --- lib/web_ui/test/engine/scene_view_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/web_ui/test/engine/scene_view_test.dart b/lib/web_ui/test/engine/scene_view_test.dart index 3da90341e5335..5551ee6a3b47e 100644 --- a/lib/web_ui/test/engine/scene_view_test.dart +++ b/lib/web_ui/test/engine/scene_view_test.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'dart:js_interop'; -import 'package:quiver/strings.dart'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart';