diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 12bdf28269d67..69a96feeec142 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -32,20 +32,52 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree { } @end -// Determines if the final `clipBounds` from a clipRect/clipRRect/clipPath mutator contains the +// Determines if the `clip_rect` from a clipRect mutator contains the // `platformview_boundingrect`. // -// `clip_bounds` is the bounding rect of the rect/rrect/path in the clipRect/clipRRect/clipPath -// mutator. This rect is in its own coordinate space. The rect needs to be transformed by +// `clip_rect` is in its own coordinate space. The rect needs to be transformed by // `transform_matrix` to be in the coordinate space where the PlatformView is displayed. // // `platformview_boundingrect` is the final bounding rect of the PlatformView in the coordinate // space where the PlatformView is displayed. -static bool ClipBoundsContainsPlatformViewBoundingRect(const SkRect& clip_bounds, - const SkRect& platformview_boundingrect, - const SkMatrix& transform_matrix) { - SkRect transforme_clip_bounds = transform_matrix.mapRect(clip_bounds); - return transforme_clip_bounds.contains(platformview_boundingrect); +static bool ClipRectContainsPlatformViewBoundingRect(const SkRect& clip_rect, + const SkRect& platformview_boundingrect, + const SkMatrix& transform_matrix) { + SkRect transformed_rect = transform_matrix.mapRect(clip_rect); + return transformed_rect.contains(platformview_boundingrect); +} + +// Determines if the `clipRRect` from a clipRRect mutator contains the +// `platformview_boundingrect`. +// +// `clip_rrect` is in its own coordinate space. The rrect needs to be transformed by +// `transform_matrix` to be in the coordinate space where the PlatformView is displayed. +// +// `platformview_boundingrect` is the final bounding rect of the PlatformView in the coordinate +// space where the PlatformView is displayed. +static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, + const SkRect& platformview_boundingrect, + const SkMatrix& transform_matrix) { + SkVector upper_left = clip_rrect.radii(SkRRect::Corner::kUpperLeft_Corner); + SkVector upper_right = clip_rrect.radii(SkRRect::Corner::kUpperRight_Corner); + SkVector lower_right = clip_rrect.radii(SkRRect::Corner::kLowerRight_Corner); + SkVector lower_left = clip_rrect.radii(SkRRect::Corner::kLowerLeft_Corner); + SkScalar transformed_upper_left_x = transform_matrix.mapRadius(upper_left.x()); + SkScalar transformed_upper_left_y = transform_matrix.mapRadius(upper_left.y()); + SkScalar transformed_upper_right_x = transform_matrix.mapRadius(upper_right.x()); + SkScalar transformed_upper_right_y = transform_matrix.mapRadius(upper_right.y()); + SkScalar transformed_lower_right_x = transform_matrix.mapRadius(lower_right.x()); + SkScalar transformed_lower_right_y = transform_matrix.mapRadius(lower_right.y()); + SkScalar transformed_lower_left_x = transform_matrix.mapRadius(lower_left.x()); + SkScalar transformed_lower_left_y = transform_matrix.mapRadius(lower_left.y()); + SkRect transformed_clip_rect = transform_matrix.mapRect(clip_rrect.rect()); + SkRRect transformed_rrect; + SkVector corners[] = {{transformed_upper_left_x, transformed_upper_left_y}, + {transformed_upper_right_x, transformed_upper_right_y}, + {transformed_lower_right_x, transformed_lower_right_y}, + {transformed_lower_left_x, transformed_lower_left_y}}; + transformed_rrect.setRectRadii(transformed_clip_rect, corners); + return transformed_rrect.contains(platformview_boundingrect); } namespace flutter { @@ -450,8 +482,8 @@ static bool ClipBoundsContainsPlatformViewBoundingRect(const SkRect& clip_bounds break; } case kClipRect: { - if (ClipBoundsContainsPlatformViewBoundingRect((*iter)->GetRect(), bounding_rect, - transformMatrix)) { + if (ClipRectContainsPlatformViewBoundingRect((*iter)->GetRect(), bounding_rect, + transformMatrix)) { break; } [maskView clipRect:(*iter)->GetRect() matrix:transformMatrix]; @@ -459,8 +491,8 @@ static bool ClipBoundsContainsPlatformViewBoundingRect(const SkRect& clip_bounds break; } case kClipRRect: { - if (ClipBoundsContainsPlatformViewBoundingRect((*iter)->GetRRect().getBounds(), - bounding_rect, transformMatrix)) { + if (ClipRRectContainsPlatformViewBoundingRect((*iter)->GetRRect(), bounding_rect, + transformMatrix)) { break; } [maskView clipRRect:(*iter)->GetRRect() matrix:transformMatrix]; @@ -468,10 +500,9 @@ static bool ClipBoundsContainsPlatformViewBoundingRect(const SkRect& clip_bounds break; } case kClipPath: { - if (ClipBoundsContainsPlatformViewBoundingRect((*iter)->GetPath().getBounds(), - bounding_rect, transformMatrix)) { - break; - } + // TODO(cyanglaz): Find a way to pre-determine if path contains the PlatformView boudning + // rect. See `ClipRRectContainsPlatformViewBoundingRect`. + // https://github.com/flutter/flutter/issues/118650 [maskView clipPath:(*iter)->GetPath() matrix:transformMatrix]; clipView.maskView = maskView; break; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 9391a4b23b1da..9b8ee71ead7cd 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -1510,25 +1510,23 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)] autorelease]; flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params + // Create embedded view params. flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack + // Layer tree always pushes a screen scale factor to the stack. SkMatrix screenScaleMatrix = SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); stack.PushTransform(screenScaleMatrix); SkMatrix translateMatrix = SkMatrix::Translate(5, 5); - // The platform view's rect for this test will be (5, 5, 10, 10) + // The platform view's rect for this test will be (5, 5, 10, 10). stack.PushTransform(translateMatrix); - // Push a clip rect, big enough to contain the entire platform view bound + // Push a clip rect, big enough to contain the entire platform view bound. SkRect rect = SkRect::MakeXYWH(0, 0, 25, 25); stack.PushClipRect(rect); - // Push a clip rrect, big enough to contain the entire platform view bound - SkRect rect_for_rrect = SkRect::MakeXYWH(0, 0, 24, 24); + // Push a clip rrect, big enough to contain the entire platform view bound without clipping it. + // Make the origin (-1, -1) so that the top left rounded corner isn't clipping the PlatformView. + SkRect rect_for_rrect = SkRect::MakeXYWH(-1, -1, 25, 25); SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1); stack.PushClipRRect(rrect); - // Push a clip path, big enough to contain the entire platform view bound - SkPath path = SkPath::RRect(SkRect::MakeXYWH(0, 0, 23, 23), 1, 1); - stack.PushClipPath(path); auto embeddedViewParams = std::make_unique( SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); @@ -1542,10 +1540,73 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { [mockFlutterView setNeedsLayout]; [mockFlutterView layoutIfNeeded]; - XCTAssertNil(childClippingView.maskView); } +- (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { + flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; + auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, + /*platform=*/thread_task_runner, + /*raster=*/thread_task_runner, + /*ui=*/thread_task_runner, + /*io=*/thread_task_runner); + auto flutterPlatformViewsController = std::make_shared(); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)] autorelease]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack. + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack.PushTransform(screenScaleMatrix); + SkMatrix translateMatrix = SkMatrix::Translate(5, 5); + // The platform view's rect for this test will be (5, 5, 10, 10). + stack.PushTransform(translateMatrix); + + // Push a clip rrect, the rect of the rrect is the same as the PlatformView of the corner should. + // clip the PlatformView. + SkRect rect_for_rrect = SkRect::MakeXYWH(0, 0, 10, 10); + SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1); + stack.PushClipRRect(rrect); + + auto embeddedViewParams = std::make_unique( + SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + gMockPlatformView.backgroundColor = UIColor.redColor; + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + XCTAssertNotNil(childClippingView.maskView); +} + - (void)testClipRect { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index 51aa146f8f836..5294a9b254c98 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -37,6 +37,8 @@ 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */; }; 6816DBA12317573300A51400 /* GoldenImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA02317573300A51400 /* GoldenImage.m */; }; 6816DBA42318358200A51400 /* GoldenTestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA32318358200A51400 /* GoldenTestManager.m */; }; + 685B9F392977B73100B45442 /* golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 685B9F372977B73100B45442 /* golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png */; }; + 685B9F3A2977B73100B45442 /* golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 685B9F382977B73100B45442 /* golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png */; }; 687AF8E9291EBDE0003912C7 /* golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 687AF8E8291EBDE0003912C7 /* golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png */; }; 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */; }; 68D4017D2564859300ECD91A /* ContinuousTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 68D4017C2564859300ECD91A /* ContinuousTexture.m */; }; @@ -151,6 +153,8 @@ 6816DBA02317573300A51400 /* GoldenImage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoldenImage.m; sourceTree = ""; }; 6816DBA22318358200A51400 /* GoldenTestManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenTestManager.h; sourceTree = ""; }; 6816DBA32318358200A51400 /* GoldenTestManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoldenTestManager.m; sourceTree = ""; }; + 685B9F372977B73100B45442 /* golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png"; sourceTree = ""; }; + 685B9F382977B73100B45442 /* golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png"; sourceTree = ""; }; 687AF8E8291EBDE0003912C7 /* golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png"; sourceTree = ""; }; 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGestureRecognizerTests.m; sourceTree = ""; }; 68D4017B2564859300ECD91A /* ContinuousTexture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContinuousTexture.h; sourceTree = ""; }; @@ -296,6 +300,8 @@ F7B464DC2759D02B00079189 /* Goldens */ = { isa = PBXGroup; children = ( + 685B9F382977B73100B45442 /* golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png */, + 685B9F372977B73100B45442 /* golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png */, 68D50041291ED8CD001ACFE1 /* golden_platform_view_cliprect_with_transform_iPhone 8_16.0_simulator.png */, 68D5003D291ED645001ACFE1 /* golden_platform_view_cliprrect_with_transform_iPhone 8_16.0_simulator.png */, 687AF8E8291EBDE0003912C7 /* golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png */, @@ -452,10 +458,12 @@ F7B464F32759D0A900079189 /* golden_platform_view_multiple_background_foreground_iPhone 8_16.0_simulator.png in Resources */, F7B464F72759D0A900079189 /* golden_platform_view_rotate_iPhone 8_16.0_simulator.png in Resources */, F7B464ED2759D0A900079189 /* golden_platform_view_cliprrect_iPhone 8_16.0_simulator.png in Resources */, + 685B9F3A2977B73100B45442 /* golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png in Resources */, 68D5003F291ED645001ACFE1 /* golden_platform_view_cliprrect_with_transform_iPhone 8_16.0_simulator.png in Resources */, F7B464EB2759D0A900079189 /* golden_two_platform_views_with_other_backdrop_filter_iPhone 8_16.0_simulator.png in Resources */, F7B464F42759D0A900079189 /* golden_platform_view_with_other_backdrop_filter_iPhone 8_16.0_simulator.png in Resources */, 687AF8E9291EBDE0003912C7 /* golden_platform_view_clippath_with_transform_iPhone 8_16.0_simulator.png in Resources */, + 685B9F392977B73100B45442 /* golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png in Resources */, F769EB53276312BB007AC10F /* golden_platform_view_cliprect_iPhone 8_16.0_simulator.png in Resources */, F7B464EF2759D0A900079189 /* golden_platform_view_multiple_iPhone 8_16.0_simulator.png in Resources */, ); diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 4674126c7b47e..5118a4e6b4ad8 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -43,8 +43,11 @@ - (BOOL)application:(UIApplication*)application @"platform_view_multiple_background_foreground", @"--platform-view-cliprect" : @"platform_view_cliprect", @"--platform-view-cliprrect" : @"platform_view_cliprrect", + @"--platform-view-large-cliprrect" : @"platform_view_large_cliprrect", @"--platform-view-clippath" : @"platform_view_clippath", @"--platform-view-cliprrect-with-transform" : @"platform_view_cliprrect_with_transform", + @"--platform-view-large-cliprrect-with-transform" : + @"platform_view_large_cliprrect_with_transform", @"--platform-view-cliprect-with-transform" : @"platform_view_cliprect_with_transform", @"--platform-view-clippath-with-transform" : @"platform_view_clippath_with_transform", @"--platform-view-transform" : @"platform_view_transform", diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m index ad1a400bb7d9a..26838a3a47497 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m @@ -29,8 +29,11 @@ - (instancetype)initWithLaunchArg:(NSString*)launchArg { @"platform_view_multiple_background_foreground", @"--platform-view-cliprect" : @"platform_view_cliprect", @"--platform-view-cliprrect" : @"platform_view_cliprrect", + @"--platform-view-large-cliprrect" : @"platform_view_large_cliprrect", @"--platform-view-clippath" : @"platform_view_clippath", @"--platform-view-cliprrect-with-transform" : @"platform_view_cliprrect_with_transform", + @"--platform-view-large-cliprrect-with-transform" : + @"platform_view_large_cliprrect_with_transform", @"--platform-view-cliprect-with-transform" : @"platform_view_cliprect_with_transform", @"--platform-view-clippath-with-transform" : @"platform_view_clippath_with_transform", @"--platform-view-transform" : @"platform_view_transform", diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m index cfec0aa1ca84b..a37122b1a44a6 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m @@ -116,6 +116,24 @@ - (void)testPlatformView { @end +@interface PlatformViewMutationLargeClipRRectTests : GoldenPlatformViewTests + +@end + +@implementation PlatformViewMutationLargeClipRRectTests + +- (instancetype)initWithInvocation:(NSInvocation*)invocation { + GoldenTestManager* manager = + [[GoldenTestManager alloc] initWithLaunchArg:@"--platform-view-large-cliprrect"]; + return [super initWithManager:manager invocation:invocation]; +} + +- (void)testPlatformView { + [self checkPlatformViewGolden]; +} + +@end + @interface PlatformViewMutationClipPathTests : GoldenPlatformViewTests @end @@ -170,6 +188,24 @@ - (void)testPlatformView { @end +@interface PlatformViewMutationLargeClipRRectWithTransformTests : GoldenPlatformViewTests + +@end + +@implementation PlatformViewMutationLargeClipRRectWithTransformTests + +- (instancetype)initWithInvocation:(NSInvocation*)invocation { + GoldenTestManager* manager = [[GoldenTestManager alloc] + initWithLaunchArg:@"--platform-view-large-cliprrect-with-transform"]; + return [super initWithManager:manager invocation:invocation]; +} + +- (void)testPlatformView { + [self checkPlatformViewGolden]; +} + +@end + @interface PlatformViewMutationClipPathWithTransformTests : GoldenPlatformViewTests @end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000..a6965956007fa Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_large_cliprrect_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png new file mode 100644 index 0000000000000..f64d163c510ed Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_large_cliprrect_with_transform_iPhone 8_16.0_simulator.png differ diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 7e989b6998ed8..918d59f9fec11 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -618,6 +618,41 @@ class PlatformViewClipRRectScenario extends PlatformViewScenario { } } + +/// Platform view with clip rrect. +/// The bounding rect of the rrect is the same as PlatformView and only the corner radii clips the PlatformView. +class PlatformViewLargeClipRRectScenario extends PlatformViewScenario { + /// Constructs a platform view with large clip rrect scenario. + PlatformViewLargeClipRRectScenario( + PlatformDispatcher dispatcher, { + int id = 0, + }) : super(dispatcher, id: id); + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + builder.pushClipRRect( + RRect.fromLTRBAndCorners( + 0, + 0, + 500, + 500, + topLeft: const Radius.circular(15), + topRight: const Radius.circular(50), + bottomLeft: const Radius.circular(50), + ), + ); + + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + + finishBuilder(builder); + } +} + /// Platform view with clip path. class PlatformViewClipPathScenario extends PlatformViewScenario { /// Constructs a platform view with clip path scenario. @@ -730,6 +765,54 @@ class PlatformViewClipRRectWithTransformScenario extends PlatformViewScenario { } } +/// Platform view with clip rrect after transformed. +/// The bounding rect of the rrect is the same as PlatformView and only the corner radii clips the PlatformView. +class PlatformViewLargeClipRRectWithTransformScenario extends PlatformViewScenario { + /// Constructs a platform view with large clip rrect with transform scenario. + PlatformViewLargeClipRRectWithTransformScenario( + PlatformDispatcher dispatcher, { + int id = 0, + }) : super(dispatcher, id: id); + + @override + void onBeginFrame(Duration duration) { + final Matrix4 matrix4 = Matrix4.identity() + ..rotateZ(1) + ..scale(0.5, 0.5, 1.0) + ..translate(1000.0, 100.0); + + final SceneBuilder builder = SceneBuilder()..pushTransform(matrix4.storage); + builder.pushClipRRect( + RRect.fromLTRBAndCorners( + 0, + 0, + 500, + 500, + topLeft: const Radius.circular(15), + topRight: const Radius.circular(50), + bottomLeft: const Radius.circular(50), + ), + ); + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + + // Add a translucent rect that has the same size of PlatformView. + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawRect( + const Rect.fromLTWH(0, 0, 500, 500), + Paint()..color = const Color(0x22FF0000), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(Offset.zero, picture); + + finishBuilder(builder); + } +} + /// Platform view with clip path after transformed. class PlatformViewClipPathWithTransformScenario extends PlatformViewScenario { /// Constructs a platform view with clip path with transform scenario. diff --git a/testing/scenario_app/lib/src/scenarios.dart b/testing/scenario_app/lib/src/scenarios.dart index c992eea814fcd..61c9437cb0ae5 100644 --- a/testing/scenario_app/lib/src/scenarios.dart +++ b/testing/scenario_app/lib/src/scenarios.dart @@ -33,7 +33,9 @@ Map _scenarios = { 'platform_view_cliprect': () => PlatformViewClipRectScenario(PlatformDispatcher.instance, id: _viewId++), 'platform_view_cliprect_with_transform': () => PlatformViewClipRectWithTransformScenario(PlatformDispatcher.instance, id: _viewId++), 'platform_view_cliprrect': () => PlatformViewClipRRectScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_large_cliprrect': () => PlatformViewLargeClipRRectScenario(PlatformDispatcher.instance, id: _viewId++), 'platform_view_cliprrect_with_transform': () => PlatformViewClipRRectWithTransformScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_large_cliprrect_with_transform': () => PlatformViewLargeClipRRectWithTransformScenario(PlatformDispatcher.instance, id: _viewId++), 'platform_view_clippath': () => PlatformViewClipPathScenario(PlatformDispatcher.instance, id: _viewId++), 'platform_view_clippath_with_transform': () => PlatformViewClipPathWithTransformScenario(PlatformDispatcher.instance, id: _viewId++), 'platform_view_transform': () => PlatformViewTransformScenario(PlatformDispatcher.instance, id: _viewId++),