[skwasm] Combine offset and transform properly. (flutter/engine#53967)

Our treatment of the interaction between offset and transform was incorrect. Modified our platform view unit tests to cover more cases of nested offsets and transforms.
This commit is contained in:
Jackson Gardner 2024-07-18 14:44:08 -07:00 committed by GitHub
parent 767eb5b265
commit f4bab93f77
4 changed files with 67 additions and 30 deletions

View File

@ -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

View File

@ -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

View File

@ -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);
});

View File

@ -132,6 +132,39 @@ Future<void> 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);