@@ -32,6 +32,22 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
3232}
3333@end
3434
35+ // Determines if the final `clipBounds` from a clipRect/clipRRect/clipPath mutator contains the
36+ // `platformview_boundingrect`.
37+ //
38+ // `clip_bounds` is the bounding rect of the rect/rrect/path in the clipRect/clipRRect/clipPath
39+ // mutator. This rect is in its own coordinate space. The rect needs to be transformed by
40+ // `transform_matrix` to be in the coordinate space where the PlatformView is displayed.
41+ //
42+ // `platformview_boundingrect` is the final bounding rect of the PlatformView in the coordinate
43+ // space where the PlatformView is displayed.
44+ static bool ClipBoundsContainsPlatformViewBoundingRect (const SkRect& clip_bounds,
45+ const SkRect& platformview_boundingrect,
46+ const SkMatrix& transform_matrix) {
47+ SkRect transforme_clip_bounds = transform_matrix.mapRect (clip_bounds);
48+ return transforme_clip_bounds.contains (platformview_boundingrect);
49+ }
50+
3551namespace flutter {
3652// Becomes NO if Apple's API changes and blurred backdrop filters cannot be applied.
3753BOOL canApplyBlurBackdrop = YES ;
@@ -404,47 +420,62 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
404420}
405421
406422void FlutterPlatformViewsController::ApplyMutators (const MutatorsStack& mutators_stack,
407- UIView* embedded_view) {
423+ UIView* embedded_view,
424+ const SkRect& bounding_rect) {
408425 if (flutter_view_ == nullptr ) {
409426 return ;
410427 }
411428 FML_DCHECK (CATransform3DEqualToTransform (embedded_view.layer .transform , CATransform3DIdentity));
412429 ResetAnchor (embedded_view.layer );
413430 ChildClippingView* clipView = (ChildClippingView*)embedded_view.superview ;
414431
415- // The UIKit frame is set based on the logical resolution instead of physical.
416- // (https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html).
417- // However, flow is based on the physical resolution. For example, 1000 pixels in flow equals
418- // 500 points in UIKit. And until this point, we did all the calculation based on the flow
419- // resolution. So we need to scale down to match UIKit's logical resolution.
420432 CGFloat screenScale = [UIScreen mainScreen ].scale ;
421- CATransform3D finalTransform = CATransform3DMakeScale (1 / screenScale, 1 / screenScale, 1 );
422433
423434 UIView* flutter_view = flutter_view_.get ();
424435 FlutterClippingMaskView* maskView = [[[FlutterClippingMaskView alloc ]
425436 initWithFrame: CGRectMake (-clipView.frame.origin.x, -clipView.frame.origin.y,
426437 CGRectGetWidth (flutter_view.bounds),
427- CGRectGetHeight (flutter_view.bounds))] autorelease ];
438+ CGRectGetHeight (flutter_view.bounds))
439+ screenScale: screenScale] autorelease ];
428440
441+ SkMatrix transformMatrix;
429442 NSMutableArray * blurFilters = [[[NSMutableArray alloc ] init ] autorelease ];
430443
444+ clipView.maskView = nil ;
431445 auto iter = mutators_stack.Begin ();
432446 while (iter != mutators_stack.End ()) {
433447 switch ((*iter)->GetType ()) {
434448 case kTransform : {
435- CATransform3D transform = GetCATransform3DFromSkMatrix ((*iter)->GetMatrix ());
436- finalTransform = CATransform3DConcat (transform, finalTransform);
449+ transformMatrix.preConcat ((*iter)->GetMatrix ());
437450 break ;
438451 }
439- case kClipRect :
440- [maskView clipRect: (*iter)->GetRect () matrix: finalTransform];
452+ case kClipRect : {
453+ if (ClipBoundsContainsPlatformViewBoundingRect ((*iter)->GetRect (), bounding_rect,
454+ transformMatrix)) {
455+ break ;
456+ }
457+ [maskView clipRect: (*iter)->GetRect () matrix: transformMatrix];
458+ clipView.maskView = maskView;
441459 break ;
442- case kClipRRect :
443- [maskView clipRRect: (*iter)->GetRRect () matrix: finalTransform];
460+ }
461+ case kClipRRect : {
462+ if (ClipBoundsContainsPlatformViewBoundingRect ((*iter)->GetRRect ().getBounds (),
463+ bounding_rect, transformMatrix)) {
464+ break ;
465+ }
466+ [maskView clipRRect: (*iter)->GetRRect () matrix: transformMatrix];
467+ clipView.maskView = maskView;
444468 break ;
445- case kClipPath :
446- [maskView clipPath: (*iter)->GetPath () matrix: finalTransform];
469+ }
470+ case kClipPath : {
471+ if (ClipBoundsContainsPlatformViewBoundingRect ((*iter)->GetPath ().getBounds (),
472+ bounding_rect, transformMatrix)) {
473+ break ;
474+ }
475+ [maskView clipPath: (*iter)->GetPath () matrix: transformMatrix];
476+ clipView.maskView = maskView;
447477 break ;
478+ }
448479 case kOpacity :
449480 embedded_view.alpha = (*iter)->GetAlphaFloat () * embedded_view.alpha ;
450481 break ;
@@ -489,17 +520,23 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
489520 [clipView applyBlurBackdropFilters: blurFilters];
490521 }
491522
523+ // The UIKit frame is set based on the logical resolution (points) instead of physical.
524+ // (https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html).
525+ // However, flow is based on the physical resolution. For example, 1000 pixels in flow equals
526+ // 500 points in UIKit for devices that has screenScale of 2. We need to scale the transformMatrix
527+ // down to the logical resoltion before applying it to the layer of PlatformView.
528+ transformMatrix.postScale (1 / screenScale, 1 / screenScale);
529+
492530 // Reverse the offset of the clipView.
493531 // The clipView's frame includes the final translate of the final transform matrix.
494532 // Thus, this translate needs to be reversed so the platform view can layout at the correct
495533 // offset.
496534 //
497535 // Note that the transforms are not applied to the clipping paths because clipping paths happen on
498536 // the mask view, whose origin is always (0,0) to the flutter_view.
499- CATransform3D reverseTranslate =
500- CATransform3DMakeTranslation (-clipView.frame .origin .x , -clipView.frame .origin .y , 0 );
501- embedded_view.layer .transform = CATransform3DConcat (finalTransform, reverseTranslate);
502- clipView.maskView = maskView;
537+ transformMatrix.postTranslate (-clipView.frame .origin .x , -clipView.frame .origin .y );
538+
539+ embedded_view.layer .transform = flutter::GetCATransform3DFromSkMatrix (transformMatrix);
503540}
504541
505542void FlutterPlatformViewsController::CompositeWithParams (int view_id,
@@ -538,7 +575,7 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
538575 CGFloat screenScale = [UIScreen mainScreen ].scale ;
539576 clippingView.frame = CGRectMake (rect.x () / screenScale, rect.y () / screenScale,
540577 rect.width () / screenScale, rect.height () / screenScale);
541- ApplyMutators (mutatorStack, touchInterceptor);
578+ ApplyMutators (mutatorStack, touchInterceptor, rect );
542579}
543580
544581EmbedderPaintContext FlutterPlatformViewsController::CompositeEmbeddedView (int view_id) {
0 commit comments