Skip to content

Commit bb4628e

Browse files
Scale any clip path by 1 / DPR. (#161190)
When applying a clip path, we need to scale it by the DPR in order for it to be placed and sized correctly. This addresses flutter/flutter#157603
1 parent e66e04f commit bb4628e

File tree

2 files changed

+136
-2
lines changed

2 files changed

+136
-2
lines changed

engine/src/flutter/lib/web_ui/lib/src/engine/scene_view.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,12 @@ final class PlatformViewContainer extends SliceContainer {
334334
final double blRadiusY = clip.rrect.blRadiusY / devicePixelRatio;
335335
return 'rect(${top}px ${right}px ${bottom}px ${left}px round ${tlRadiusX}px ${trRadiusX}px ${brRadiusX}px ${blRadiusX}px / ${tlRadiusY}px ${trRadiusY}px ${brRadiusY}px ${blRadiusY}px)';
336336
case PlatformViewPathClip():
337-
clipPath = clip.path;
337+
ScenePath path = clip.path;
338+
if (devicePixelRatio != 1.0) {
339+
final dprTransform = Matrix4.identity()..scale(1 / devicePixelRatio);
340+
path = path.transform(dprTransform.toFloat64()) as ScenePath;
341+
}
342+
clipPath = path;
338343
return "path('$_clipPathString')";
339344
}
340345
}

engine/src/flutter/lib/web_ui/test/engine/scene_view_test.dart

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:async';
66
import 'dart:js_interop';
7+
import 'dart:typed_data';
78

89
import 'package:test/bootstrap/browser.dart';
910
import 'package:test/test.dart';
@@ -145,6 +146,126 @@ class StubFlutterView implements EngineFlutterView {
145146
EngineSemanticsOwner get semantics => throw UnimplementedError();
146147
}
147148

149+
class StubPath implements ScenePath {
150+
StubPath(this.pathString, this.transformedPathString);
151+
152+
final String pathString;
153+
final String transformedPathString;
154+
Float64List? appliedTransform;
155+
156+
@override
157+
ui.PathFillType get fillType => throw UnimplementedError();
158+
159+
@override
160+
set fillType(ui.PathFillType value) => throw UnimplementedError();
161+
162+
@override
163+
void moveTo(double x, double y) => throw UnimplementedError();
164+
165+
@override
166+
void relativeMoveTo(double dx, double dy) => throw UnimplementedError();
167+
168+
@override
169+
void lineTo(double x, double y) => throw UnimplementedError();
170+
171+
@override
172+
void relativeLineTo(double dx, double dy) => throw UnimplementedError();
173+
174+
@override
175+
void quadraticBezierTo(double x1, double y1, double x2, double y2) => throw UnimplementedError();
176+
177+
@override
178+
void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) =>
179+
throw UnimplementedError();
180+
181+
@override
182+
void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) =>
183+
throw UnimplementedError();
184+
185+
@override
186+
void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3) =>
187+
throw UnimplementedError();
188+
189+
@override
190+
void conicTo(double x1, double y1, double x2, double y2, double w) => throw UnimplementedError();
191+
192+
@override
193+
void relativeConicTo(double x1, double y1, double x2, double y2, double w) =>
194+
throw UnimplementedError();
195+
196+
@override
197+
void arcTo(ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) =>
198+
throw UnimplementedError();
199+
200+
@override
201+
void arcToPoint(
202+
ui.Offset arcEnd, {
203+
ui.Radius radius = ui.Radius.zero,
204+
double rotation = 0.0,
205+
bool largeArc = false,
206+
bool clockwise = true,
207+
}) => throw UnimplementedError();
208+
209+
@override
210+
void relativeArcToPoint(
211+
ui.Offset arcEndDelta, {
212+
ui.Radius radius = ui.Radius.zero,
213+
double rotation = 0.0,
214+
bool largeArc = false,
215+
bool clockwise = true,
216+
}) => throw UnimplementedError();
217+
218+
@override
219+
void addRect(ui.Rect rect) => throw UnimplementedError();
220+
221+
@override
222+
void addOval(ui.Rect oval) => throw UnimplementedError();
223+
224+
@override
225+
void addArc(ui.Rect oval, double startAngle, double sweepAngle) => throw UnimplementedError();
226+
227+
@override
228+
void addPolygon(List<ui.Offset> points, bool close) => throw UnimplementedError();
229+
230+
@override
231+
void addRRect(ui.RRect rrect) => throw UnimplementedError();
232+
233+
@override
234+
void addPath(ui.Path path, ui.Offset offset, {Float64List? matrix4}) =>
235+
throw UnimplementedError();
236+
237+
@override
238+
void extendWithPath(ui.Path path, ui.Offset offset, {Float64List? matrix4}) =>
239+
throw UnimplementedError();
240+
241+
@override
242+
void close() => throw UnimplementedError();
243+
244+
@override
245+
void reset() => throw UnimplementedError();
246+
247+
@override
248+
bool contains(ui.Offset point) => throw UnimplementedError();
249+
250+
@override
251+
ui.Path shift(ui.Offset offset) => throw UnimplementedError();
252+
253+
@override
254+
StubPath transform(Float64List matrix4) {
255+
appliedTransform = matrix4;
256+
return StubPath(transformedPathString, '');
257+
}
258+
259+
@override
260+
ui.Rect getBounds() => throw UnimplementedError();
261+
262+
@override
263+
ui.PathMetrics computeMetrics({bool forceClosed = false}) => throw UnimplementedError();
264+
265+
@override
266+
String toSvgString() => pathString;
267+
}
268+
148269
void testMain() {
149270
late EngineSceneView sceneView;
150271
late StubPictureRenderer stubPictureRenderer;
@@ -187,10 +308,12 @@ void testMain() {
187308
test('SceneView places platform view according to device-pixel ratio', () async {
188309
debugOverrideDevicePixelRatio(2.0);
189310

311+
final StubPath clipPath = StubPath('M 2 2', 'M 1 1');
312+
190313
final PlatformView platformView = PlatformView(
191314
1,
192315
const ui.Rect.fromLTWH(50, 80, 100, 120),
193-
const PlatformViewStyling(),
316+
PlatformViewStyling(clip: PlatformViewPathClip(clipPath)),
194317
);
195318
final EngineRootLayer rootLayer = EngineRootLayer();
196319
rootLayer.slices.add(LayerSlice(StubPicture(ui.Rect.zero), <PlatformView>[platformView]));
@@ -204,6 +327,12 @@ void testMain() {
204327
final DomElement clipElement = children.first;
205328
expect(clipElement.tagName, equalsIgnoringCase('flt-clip'));
206329

330+
expect(clipElement.style.clipPath, 'path("M 1 1")');
331+
332+
// We expect the path to be scaled down by 1 / DPR
333+
final Matrix4 expectedTransform = Matrix4.identity()..scale(0.5);
334+
expect(clipPath.appliedTransform, expectedTransform.toFloat64());
335+
207336
final List<DomElement> clipChildren = clipElement.children.toList();
208337
expect(clipChildren.length, 1);
209338

0 commit comments

Comments
 (0)