diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/layers.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/layers.dart index ecccd02e784..a0568c120f4 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/layers.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/layers.dart @@ -499,18 +499,19 @@ class PlatformViewPosition { } // Otherwise, at least one of the positions involves a matrix transform. - final Matrix4 newTransform; - if (outerOffset != null) { - newTransform = Matrix4.translationValues(outerOffset.dx, outerOffset.dy, 0); - } else { - newTransform = outer.transform!.clone(); - } + final Matrix4 innerTransform; + final Matrix4 outerTransform; if (innerOffset != null) { - newTransform.translate(innerOffset.dx, innerOffset.dy); + innerTransform = Matrix4.translationValues(innerOffset.dx, innerOffset.dy, 0); } else { - newTransform.multiply(inner.transform!); + innerTransform = inner.transform!; } - return PlatformViewPosition.transform(newTransform); + if (outerOffset != null) { + outerTransform = Matrix4.translationValues(outerOffset.dx, outerOffset.dy, 0); + } else { + outerTransform = outer.transform!; + } + return PlatformViewPosition.transform(outerTransform.multiplied(innerTransform)); } @override diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/scene_view.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/scene_view.dart index 65ee7480cba..fc32b872104 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/scene_view.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/scene_view.dart @@ -308,26 +308,26 @@ final class PlatformViewContainer extends SliceContainer { assert(_bounds != null); if (_dirty) { final DomCSSStyleDeclaration style = container.style; - final double devicePixelRatio = EngineFlutterDisplay.instance.devicePixelRatio; - final double logicalWidth = _bounds!.width / devicePixelRatio; - final double logicalHeight = _bounds!.height / devicePixelRatio; - style.width = '${logicalWidth}px'; - style.height = '${logicalHeight}px'; style.position = 'absolute'; + style.width = '${_bounds!.width}px'; + style.height = '${_bounds!.height}px'; - final PlatformViewPosition position = PlatformViewPosition.combine( - _styling!.position, - PlatformViewPosition.offset(_bounds!.topLeft), - ); + final double devicePixelRatio = EngineFlutterDisplay.instance.devicePixelRatio; + final PlatformViewPosition position = _styling!.position; - final ui.Offset offset = position.offset ?? ui.Offset.zero; - final double logicalLeft = offset.dx / devicePixelRatio; - final double logicalTop = offset.dy / devicePixelRatio; - style.left = '${logicalLeft}px'; - style.top = '${logicalTop}px'; - - final Matrix4? transform = position.transform; - style.transform = transform != null ? float64ListToCssTransform3d(transform.storage) : ''; + final Matrix4 transform; + if (position.transform != null) { + transform = position.transform!.clone()..translate(_bounds!.left, _bounds!.top); + } else { + final ui.Offset offset = position.offset ?? ui.Offset.zero; + transform = Matrix4.translationValues(_bounds!.left + offset.dx, _bounds!.top + offset.dy, 0); + } + final double inverseScale = 1.0 / devicePixelRatio; + final Matrix4 scaleMatrix = + Matrix4.diagonal3Values(inverseScale, inverseScale, 1); + scaleMatrix.multiply(transform); + style.transform = float64ListToCssTransform(scaleMatrix.storage); + style.transformOrigin = '0 0 0'; style.opacity = _styling!.opacity != 1.0 ? '${_styling!.opacity}' : ''; // TODO(jacksongardner): Implement clip styling for platform views diff --git a/engine/src/flutter/lib/web_ui/test/engine/scene_view_test.dart b/engine/src/flutter/lib/web_ui/test/engine/scene_view_test.dart index 64229dfb017..37606a01f61 100644 --- a/engine/src/flutter/lib/web_ui/test/engine/scene_view_test.dart +++ b/engine/src/flutter/lib/web_ui/test/engine/scene_view_test.dart @@ -167,10 +167,13 @@ void testMain() { 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'); + expect(style.left, ''); + expect(style.top, ''); + expect(style.width, '100px'); + expect(style.height, '120px'); + + // The heavy lifting of offsetting and sizing is done by the transform + expect(style.transform, 'matrix(0.5, 0, 0, 0.5, 25, 40)'); debugOverrideDevicePixelRatio(null); }); diff --git a/engine/src/flutter/lib/web_ui/test/ui/platform_view_test.dart b/engine/src/flutter/lib/web_ui/test/ui/platform_view_test.dart index 321cf4bea32..8d12d424535 100644 --- a/engine/src/flutter/lib/web_ui/test/ui/platform_view_test.dart +++ b/engine/src/flutter/lib/web_ui/test/ui/platform_view_test.dart @@ -132,6 +132,39 @@ Future testMain() async { await matchGoldenFile('platformview_transformed.png', region: region); }); + test('transformed and offset platformview', () async { + await _createPlatformView(1, platformViewType); + + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final ui.Canvas canvas = ui.Canvas(recorder); + canvas.drawCircle( + const ui.Offset(50, 50), + 50, + ui.Paint() + ..style = ui.PaintingStyle.fill + ..color = const ui.Color(0xFFFF0000) + ); + + final ui.SceneBuilder sb = ui.SceneBuilder(); + sb.pushOffset(0, 0); + sb.addPicture(const ui.Offset(100, 100), recorder.endRecording()); + + // Nest offsets both before and after the transform to make sure that they + // are applied properly. + sb.pushOffset(50, 50); + sb.pushTransform(Matrix4.rotationZ(0.1).toFloat64()); + sb.pushOffset(25, 25); + sb.addPlatformView( + 1, + offset: const ui.Offset(50, 50), + width: 50, + height: 50, + ); + await renderScene(sb.build()); + + await matchGoldenFile('platformview_transformed_offset.png', region: region); + }); + test('offset platformview', () async { await _createPlatformView(1, platformViewType);