Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 704d519

Browse files
author
Chris Yang
committed
only clip when necessary
tests fix tests format
1 parent 722414a commit 704d519

File tree

3 files changed

+119
-14
lines changed

3 files changed

+119
-14
lines changed

shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
3232
}
3333
@end
3434

35+
static bool ClipBoundsContainsPlatformViewBoundingRect(const SkRect& clipBounds,
36+
const SkRect& platformViewBoundingRect,
37+
const SkMatrix& transformMatrix,
38+
CGFloat screenScale) {
39+
SkRect transformedClipBounds = transformMatrix.mapRect(clipBounds);
40+
SkMatrix reverseScreenScale = SkMatrix::Scale(1 / screenScale, 1 / screenScale);
41+
SkRect scaledBoundingRect = reverseScreenScale.mapRect(platformViewBoundingRect);
42+
return transformedClipBounds.contains(scaledBoundingRect);
43+
}
44+
3545
namespace flutter {
3646
// Becomes NO if Apple's API changes and blurred backdrop filters cannot be applied.
3747
BOOL canApplyBlurBackdrop = YES;
@@ -404,7 +414,8 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
404414
}
405415

406416
void FlutterPlatformViewsController::ApplyMutators(const MutatorsStack& mutators_stack,
407-
UIView* embedded_view) {
417+
UIView* embedded_view,
418+
const SkRect& bounding_rect) {
408419
if (flutter_view_ == nullptr) {
409420
return;
410421
}
@@ -418,7 +429,7 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
418429
// 500 points in UIKit. And until this point, we did all the calculation based on the flow
419430
// resolution. So we need to scale down to match UIKit's logical resolution.
420431
CGFloat screenScale = [UIScreen mainScreen].scale;
421-
CATransform3D finalTransform = CATransform3DMakeScale(1 / screenScale, 1 / screenScale, 1);
432+
SkMatrix transformMatrix = SkMatrix::Scale(1 / screenScale, 1 / screenScale);
422433

423434
UIView* flutter_view = flutter_view_.get();
424435
FlutterClippingMaskView* maskView = [[[FlutterClippingMaskView alloc]
@@ -429,22 +440,41 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
429440
NSMutableArray* blurFilters = [[[NSMutableArray alloc] init] autorelease];
430441

431442
auto iter = mutators_stack.Begin();
443+
BOOL needMask = NO;
432444
while (iter != mutators_stack.End()) {
433445
switch ((*iter)->GetType()) {
434446
case kTransform: {
435-
CATransform3D transform = GetCATransform3DFromSkMatrix((*iter)->GetMatrix());
436-
finalTransform = CATransform3DConcat(transform, finalTransform);
447+
transformMatrix = SkMatrix::Concat((*iter)->GetMatrix(), transformMatrix);
437448
break;
438449
}
439-
case kClipRect:
440-
[maskView clipRect:(*iter)->GetRect() matrix:finalTransform];
450+
case kClipRect: {
451+
if (ClipBoundsContainsPlatformViewBoundingRect((*iter)->GetRect(), bounding_rect,
452+
transformMatrix, screenScale)) {
453+
break;
454+
}
455+
[maskView clipRect:(*iter)->GetRect() matrix:GetCATransform3DFromSkMatrix(transformMatrix)];
456+
needMask = YES;
441457
break;
442-
case kClipRRect:
443-
[maskView clipRRect:(*iter)->GetRRect() matrix:finalTransform];
458+
}
459+
case kClipRRect: {
460+
if (ClipBoundsContainsPlatformViewBoundingRect(
461+
(*iter)->GetRRect().getBounds(), bounding_rect, transformMatrix, screenScale)) {
462+
break;
463+
}
464+
[maskView clipRRect:(*iter)->GetRRect()
465+
matrix:GetCATransform3DFromSkMatrix(transformMatrix)];
466+
needMask = YES;
444467
break;
445-
case kClipPath:
446-
[maskView clipPath:(*iter)->GetPath() matrix:finalTransform];
468+
}
469+
case kClipPath: {
470+
if (ClipBoundsContainsPlatformViewBoundingRect(
471+
(*iter)->GetPath().getBounds(), bounding_rect, transformMatrix, screenScale)) {
472+
break;
473+
}
474+
[maskView clipPath:(*iter)->GetPath() matrix:GetCATransform3DFromSkMatrix(transformMatrix)];
475+
needMask = YES;
447476
break;
477+
}
448478
case kOpacity:
449479
embedded_view.alpha = (*iter)->GetAlphaFloat() * embedded_view.alpha;
450480
break;
@@ -498,8 +528,13 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
498528
// the mask view, whose origin is always (0,0) to the flutter_view.
499529
CATransform3D reverseTranslate =
500530
CATransform3DMakeTranslation(-clipView.frame.origin.x, -clipView.frame.origin.y, 0);
501-
embedded_view.layer.transform = CATransform3DConcat(finalTransform, reverseTranslate);
502-
clipView.maskView = maskView;
531+
embedded_view.layer.transform =
532+
CATransform3DConcat(GetCATransform3DFromSkMatrix(transformMatrix), reverseTranslate);
533+
if (needMask) {
534+
clipView.maskView = maskView;
535+
} else {
536+
clipView.maskView = nil;
537+
}
503538
}
504539

505540
void FlutterPlatformViewsController::CompositeWithParams(int view_id,
@@ -538,7 +573,7 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
538573
CGFloat screenScale = [UIScreen mainScreen].scale;
539574
clippingView.frame = CGRectMake(rect.x() / screenScale, rect.y() / screenScale,
540575
rect.width() / screenScale, rect.height() / screenScale);
541-
ApplyMutators(mutatorStack, touchInterceptor);
576+
ApplyMutators(mutatorStack, touchInterceptor, rect);
542577
}
543578

544579
EmbedderPaintContext FlutterPlatformViewsController::CompositeEmbeddedView(int view_id) {

shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,6 +1478,74 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView {
14781478
kFloatCompareEpsilon);
14791479
}
14801480

1481+
- (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView {
1482+
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1483+
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1484+
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1485+
/*platform=*/thread_task_runner,
1486+
/*raster=*/thread_task_runner,
1487+
/*ui=*/thread_task_runner,
1488+
/*io=*/thread_task_runner);
1489+
auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1490+
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1491+
/*delegate=*/mock_delegate,
1492+
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
1493+
/*platform_views_controller=*/flutterPlatformViewsController,
1494+
/*task_runners=*/runners);
1495+
1496+
FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
1497+
[[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease];
1498+
flutterPlatformViewsController->RegisterViewFactory(
1499+
factory, @"MockFlutterPlatformView",
1500+
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
1501+
FlutterResult result = ^(id result) {
1502+
};
1503+
flutterPlatformViewsController->OnMethodCall(
1504+
[FlutterMethodCall
1505+
methodCallWithMethodName:@"create"
1506+
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1507+
result);
1508+
1509+
XCTAssertNotNil(gMockPlatformView);
1510+
1511+
UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)] autorelease];
1512+
flutterPlatformViewsController->SetFlutterView(mockFlutterView);
1513+
// Create embedded view params
1514+
flutter::MutatorsStack stack;
1515+
// Layer tree always pushes a screen scale factor to the stack
1516+
SkMatrix screenScaleMatrix =
1517+
SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1518+
stack.PushTransform(screenScaleMatrix);
1519+
SkMatrix translateMatrix = SkMatrix::Translate(5, 5);
1520+
// The platform view's rect for this test will be (5, 5, 10, 10)
1521+
stack.PushTransform(translateMatrix);
1522+
// Push a clip rect, big enough to contain the entire platform view bound
1523+
SkRect rect = SkRect::MakeXYWH(0, 0, 25, 25);
1524+
stack.PushClipRect(rect);
1525+
// Push a clip rrect, big enough to contain the entire platform view bound
1526+
SkRect rect_for_rrect = SkRect::MakeXYWH(0, 0, 24, 24);
1527+
SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1);
1528+
stack.PushClipRRect(rrect);
1529+
// Push a clip path, big enough to contain the entire platform view bound
1530+
SkPath path = SkPath::RRect(SkRect::MakeXYWH(0, 0, 23, 23), 1, 1);
1531+
stack.PushClipPath(path);
1532+
1533+
auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
1534+
SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack);
1535+
1536+
flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1537+
flutterPlatformViewsController->CompositeEmbeddedView(2);
1538+
gMockPlatformView.backgroundColor = UIColor.redColor;
1539+
XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1540+
ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1541+
[mockFlutterView addSubview:childClippingView];
1542+
1543+
[mockFlutterView setNeedsLayout];
1544+
[mockFlutterView layoutIfNeeded];
1545+
1546+
XCTAssertNil(childClippingView.maskView);
1547+
}
1548+
14811549
- (void)testClipRect {
14821550
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
14831551
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");

shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,9 @@ class FlutterPlatformViewsController {
280280
// T_1 is applied to C_2, T_3 and T_4 are applied to C_5, and T_6 is applied to PLATFORM_VIEW.
281281
//
282282
// After each clip operation, we update the head to the super view of the current head.
283-
void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view);
283+
void ApplyMutators(const MutatorsStack& mutators_stack,
284+
UIView* embedded_view,
285+
const SkRect& bounding_rect);
284286
void CompositeWithParams(int view_id, const EmbeddedViewParams& params);
285287

286288
// Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from

0 commit comments

Comments
 (0)