7 #import <WebKit/WebKit.h>
9 #include "flutter/display_list/effects/dl_image_filter.h"
10 #include "flutter/fml/platform/darwin/cf_utils.h"
18 CGRect GetCGRectFromSkRect(
const SkRect& clipSkRect) {
19 return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft,
20 clipSkRect.fBottom - clipSkRect.fTop);
23 CATransform3D GetCATransform3DFromSkMatrix(
const SkMatrix& matrix) {
25 CATransform3D transform = CATransform3DIdentity;
26 transform.m11 = matrix.getScaleX();
27 transform.m21 = matrix.getSkewX();
28 transform.m41 = matrix.getTranslateX();
29 transform.m14 = matrix.getPerspX();
31 transform.m12 = matrix.getSkewY();
32 transform.m22 = matrix.getScaleY();
33 transform.m42 = matrix.getTranslateY();
34 transform.m24 = matrix.getPerspY();
42 @property(nonatomic) BOOL backdropFilterViewConfigured;
47 - (void)updateVisualEffectView:(UIVisualEffectView*)visualEffectView;
61 blurRadius:(CGFloat)blurRadius
62 visualEffectView:(UIVisualEffectView*)visualEffectView {
63 if (
self = [super init]) {
68 FML_DLOG(ERROR) <<
"Apple's API for UIVisualEffectView changed. Update the implementation to "
69 "access the gaussianBlur CAFilter.";
72 _backdropFilterView = visualEffectView;
73 _backdropFilterViewConfigured = NO;
85 + (void)prepareOnce:(UIVisualEffectView*)visualEffectView {
89 for (NSUInteger i = 0; i < visualEffectView.subviews.count; i++) {
90 UIView* view = visualEffectView.subviews[i];
91 if ([NSStringFromClass([view
class]) hasSuffix:
@"BackdropView"]) {
93 for (NSObject* filter in view.layer.filters) {
94 if ([[filter valueForKey:
@"name"] isEqual:
@"gaussianBlur"] &&
95 [[filter valueForKey:
@"inputRadius"] isKindOfClass:[NSNumber class]]) {
96 _gaussianBlurFilter = filter;
100 }
else if ([NSStringFromClass([view
class]) hasSuffix:
@"VisualEffectSubview"]) {
107 + (BOOL)isUIVisualEffectViewImplementationValid {
112 FML_DCHECK(_backdropFilterView);
113 if (!
self.backdropFilterViewConfigured) {
114 [
self updateVisualEffectView:_backdropFilterView];
115 self.backdropFilterViewConfigured = YES;
117 return _backdropFilterView;
120 - (void)updateVisualEffectView:(UIVisualEffectView*)visualEffectView {
121 NSObject* gaussianBlurFilter = [_gaussianBlurFilter copy];
122 FML_DCHECK(gaussianBlurFilter);
123 UIView* backdropView = visualEffectView.subviews[_indexOfBackdropView];
124 [gaussianBlurFilter setValue:@(_blurRadius) forKey:@"inputRadius"];
125 backdropView.layer.filters = @[ gaussianBlurFilter ];
127 UIView* visualEffectSubview = visualEffectView.subviews[_indexOfVisualEffectSubview];
128 visualEffectSubview.layer.backgroundColor = UIColor.clearColor.CGColor;
129 visualEffectView.frame = _frame;
131 self.backdropFilterView = visualEffectView;
138 @property(nonatomic, copy) NSArray<PlatformViewFilter*>* filters;
148 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
149 for (UIView* view in
self.subviews) {
150 if ([view pointInside:[
self convertPoint:point toView:view] withEvent:event]) {
158 FML_DCHECK(
self.filters.count ==
self.backdropFilterSubviews.count);
159 if (
self.filters.count == 0 && filters.count == 0) {
162 self.filters = filters;
163 NSUInteger index = 0;
164 for (index = 0; index <
self.filters.count; index++) {
165 UIVisualEffectView* backdropFilterView;
169 [
self addSubview:backdropFilterView];
170 [
self.backdropFilterSubviews addObject:backdropFilterView];
172 [filter updateVisualEffectView:self.backdropFilterSubviews[index]];
176 [
self.backdropFilterSubviews[i - 1] removeFromSuperview];
177 [
self.backdropFilterSubviews removeLastObject];
182 if (!_backdropFilterSubviews) {
183 _backdropFilterSubviews = [[NSMutableArray alloc] init];
185 return _backdropFilterSubviews;
200 @property(nonatomic) CATransform3D reverseScreenScale;
202 - (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix;
207 std::vector<fml::CFRef<CGPathRef>> paths_;
213 return [
self initWithFrame:frame screenScale:[UIScreen mainScreen].scale];
216 - (instancetype)
initWithFrame:(CGRect)frame screenScale:(CGFloat)screenScale {
218 self.backgroundColor = UIColor.clearColor;
219 _reverseScreenScale = CATransform3DMakeScale(1 / screenScale, 1 / screenScale, 1);
226 + (Class)layerClass {
227 return [CAShapeLayer class];
230 - (CAShapeLayer*)shapeLayer {
231 return (CAShapeLayer*)
self.layer;
238 [
self shapeLayer].path = nil;
239 [
self setNeedsDisplay];
247 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
251 - (void)drawRect:(CGRect)rect {
255 CGContextRef context = UIGraphicsGetCurrentContext();
256 CGContextSaveGState(context);
259 CGContextSetAlpha(context, 1);
261 for (
size_t i = 0; i < paths_.size(); i++) {
262 CGContextAddPath(context, paths_.at(i));
263 CGContextClip(context);
265 CGContextFillRect(context, rect);
266 CGContextRestoreGState(context);
270 [
super drawRect:rect];
271 if (![
self shapeLayer].path) {
272 if (paths_.size() == 1) {
274 [
self shapeLayer].path = paths_.at(0);
277 CGPathRef pathSoFar = CGPathCreateWithRect(
rectSoFar_, nil);
278 [
self shapeLayer].path = pathSoFar;
279 CGPathRelease(pathSoFar);
285 - (void)clipRect:(const SkRect&)clipSkRect matrix:(const SkMatrix&)matrix {
286 CGRect clipRect = GetCGRectFromSkRect(clipSkRect);
287 CGPathRef path = CGPathCreateWithRect(clipRect, nil);
289 CATransform3D matrixInPoints =
290 CATransform3DConcat(GetCATransform3DFromSkMatrix(matrix), _reverseScreenScale);
291 paths_.push_back([
self getTransformedPath:path matrix:matrixInPoints]);
292 CGAffineTransform affine = [
self affineWithMatrix:matrixInPoints];
294 if (affine.b == 0 && affine.c == 0) {
301 - (void)clipRRect:(const SkRRect&)clipSkRRect matrix:(const SkMatrix&)matrix {
303 CGPathRef pathRef =
nullptr;
304 switch (clipSkRRect.getType()) {
305 case SkRRect::kEmpty_Type: {
308 case SkRRect::kRect_Type: {
312 case SkRRect::kOval_Type:
313 case SkRRect::kSimple_Type: {
314 CGRect clipRect = GetCGRectFromSkRect(clipSkRRect.rect());
315 pathRef = CGPathCreateWithRoundedRect(clipRect, clipSkRRect.getSimpleRadii().x(),
316 clipSkRRect.getSimpleRadii().y(), nil);
319 case SkRRect::kNinePatch_Type:
320 case SkRRect::kComplex_Type: {
321 CGMutablePathRef mutablePathRef = CGPathCreateMutable();
323 SkRect clipSkRect = clipSkRRect.rect();
324 SkVector topLeftRadii = clipSkRRect.radii(SkRRect::kUpperLeft_Corner);
325 SkVector topRightRadii = clipSkRRect.radii(SkRRect::kUpperRight_Corner);
326 SkVector bottomRightRadii = clipSkRRect.radii(SkRRect::kLowerRight_Corner);
327 SkVector bottomLeftRadii = clipSkRRect.radii(SkRRect::kLowerLeft_Corner);
331 CGPathMoveToPoint(mutablePathRef, nil, clipSkRect.fLeft + topLeftRadii.x(), clipSkRect.fTop);
333 CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fRight - topRightRadii.x(),
335 CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fRight, clipSkRect.fTop,
336 clipSkRect.fRight, clipSkRect.fTop + topRightRadii.y(),
337 clipSkRect.fRight, clipSkRect.fTop + topRightRadii.y());
339 CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fRight,
340 clipSkRect.fBottom - bottomRightRadii.y());
341 CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fRight, clipSkRect.fBottom,
342 clipSkRect.fRight - bottomRightRadii.x(), clipSkRect.fBottom,
343 clipSkRect.fRight - bottomRightRadii.x(), clipSkRect.fBottom);
345 CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fLeft + bottomLeftRadii.x(),
347 CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fLeft, clipSkRect.fBottom,
348 clipSkRect.fLeft, clipSkRect.fBottom - bottomLeftRadii.y(),
349 clipSkRect.fLeft, clipSkRect.fBottom - bottomLeftRadii.y());
351 CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fLeft,
352 clipSkRect.fTop + topLeftRadii.y());
353 CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fLeft, clipSkRect.fTop,
354 clipSkRect.fLeft + topLeftRadii.x(), clipSkRect.fTop,
355 clipSkRect.fLeft + topLeftRadii.x(), clipSkRect.fTop);
356 CGPathCloseSubpath(mutablePathRef);
358 pathRef = mutablePathRef;
363 CATransform3D matrixInPoints =
364 CATransform3DConcat(GetCATransform3DFromSkMatrix(matrix), _reverseScreenScale);
368 paths_.push_back([
self getTransformedPath:pathRef matrix:matrixInPoints]);
371 - (void)clipPath:(const SkPath&)path matrix:(const SkMatrix&)matrix {
372 if (!path.isValid()) {
375 if (path.isEmpty()) {
379 CGMutablePathRef pathRef = CGPathCreateMutable();
382 SkPath::Iter iter(path,
true);
383 SkPoint pts[kMaxPointsInVerb];
384 SkPath::Verb verb = iter.next(pts);
385 SkPoint last_pt_from_last_verb = SkPoint::Make(0, 0);
386 while (verb != SkPath::kDone_Verb) {
387 if (verb == SkPath::kLine_Verb || verb == SkPath::kQuad_Verb || verb == SkPath::kConic_Verb ||
388 verb == SkPath::kCubic_Verb) {
389 FML_DCHECK(last_pt_from_last_verb == pts[0]);
392 case SkPath::kMove_Verb: {
393 CGPathMoveToPoint(pathRef, nil, pts[0].x(), pts[0].y());
394 last_pt_from_last_verb = pts[0];
397 case SkPath::kLine_Verb: {
398 CGPathAddLineToPoint(pathRef, nil, pts[1].x(), pts[1].y());
399 last_pt_from_last_verb = pts[1];
402 case SkPath::kQuad_Verb: {
403 CGPathAddQuadCurveToPoint(pathRef, nil, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
404 last_pt_from_last_verb = pts[2];
407 case SkPath::kConic_Verb: {
411 CGPathAddQuadCurveToPoint(pathRef, nil, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
412 last_pt_from_last_verb = pts[2];
415 case SkPath::kCubic_Verb: {
416 CGPathAddCurveToPoint(pathRef, nil, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y(),
417 pts[3].x(), pts[3].y());
418 last_pt_from_last_verb = pts[3];
421 case SkPath::kClose_Verb: {
422 CGPathCloseSubpath(pathRef);
425 case SkPath::kDone_Verb: {
429 verb = iter.next(pts);
432 CATransform3D matrixInPoints =
433 CATransform3DConcat(GetCATransform3DFromSkMatrix(matrix), _reverseScreenScale);
434 paths_.push_back([
self getTransformedPath:pathRef matrix:matrixInPoints]);
437 - (CGAffineTransform)affineWithMatrix:(CATransform3D)matrix {
438 return CGAffineTransformMake(matrix.m11, matrix.m12, matrix.m21, matrix.m22, matrix.m41,
442 - (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix {
443 CGAffineTransform affine = [
self affineWithMatrix:matrix];
444 CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &affine);
447 return fml::CFRef<CGPathRef>(transformedPath);
456 @property(nonatomic) NSUInteger capacity;
460 @property(nonatomic) NSMutableSet<FlutterClippingMaskView*>* pool;
466 - (instancetype)initWithCapacity:(NSInteger)capacity {
467 if (
self = [super init]) {
470 _pool = [[NSMutableSet alloc] initWithCapacity:1];
471 _capacity = capacity;
477 FML_DCHECK(
self.pool.count <=
self.capacity);
478 if (
self.pool.count == 0) {
481 screenScale:UIScreen.mainScreen.scale];
484 maskView.frame = frame;
486 [
self.pool removeObject:maskView];
491 FML_DCHECK(![
self.pool containsObject:maskView]);
492 FML_DCHECK(
self.pool.count <=
self.capacity);
493 if (
self.pool.count ==
self.capacity) {
496 [
self.pool addObject:maskView];
502 - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
503 if (
self.isFirstResponder) {
506 for (UIView* subview in
self.subviews) {
507 if (subview.flt_hasFirstResponderInViewHierarchySubtree) {
522 - (instancetype)initWithEmbeddedView:(UIView*)embeddedView
523 platformViewsController:
524 (fml::WeakPtr<
flutter::PlatformViewsController>)platformViewsController
525 gestureRecognizersBlockingPolicy:
527 self = [
super initWithFrame:embeddedView.frame];
529 self.multipleTouchEnabled = YES;
532 (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
534 [
self addSubview:embeddedView];
538 platformViewsController:platformViewsController];
540 _delayingRecognizer =
543 forwardingRecognizer:forwardingRecognizer];
544 _blockingPolicy = blockingPolicy;
546 [
self addGestureRecognizer:_delayingRecognizer];
547 [
self addGestureRecognizer:forwardingRecognizer];
553 self.delayingRecognizer.state = UIGestureRecognizerStateFailed;
556 - (BOOL)containsWebView:(UIView*)view remainingSubviewDepth:(
int)remainingSubviewDepth {
557 if (remainingSubviewDepth < 0) {
560 if ([view isKindOfClass:[WKWebView
class]]) {
563 for (UIView* subview in view.subviews) {
564 if ([
self containsWebView:subview remainingSubviewDepth:remainingSubviewDepth - 1]) {
572 switch (_blockingPolicy) {
575 self.delayingRecognizer.state = UIGestureRecognizerStateEnded;
585 if (@available(iOS 18.2, *)) {
592 if ([
self containsWebView:
self.
embeddedView remainingSubviewDepth:1]) {
593 [
self removeGestureRecognizer:self.delayingRecognizer];
594 [
self addGestureRecognizer:self.delayingRecognizer];
600 if (
self.delayingRecognizer.touchedEndedWithoutBlocking) {
604 self.delayingRecognizer.state = UIGestureRecognizerStateEnded;
609 self.delayingRecognizer.shouldEndInNextTouchesEnded = YES;
620 - (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
623 - (void)touchesMoved:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
626 - (void)touchesCancelled:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
629 - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
633 return self.flutterAccessibilityContainer;
640 - (instancetype)initWithTarget:(
id)target
642 forwardingRecognizer:(UIGestureRecognizer*)forwardingRecognizer {
643 self = [
super initWithTarget:target action:action];
645 self.delaysTouchesBegan = YES;
646 self.delaysTouchesEnded = YES;
647 self.delegate =
self;
648 _shouldEndInNextTouchesEnded = NO;
649 _touchedEndedWithoutBlocking = NO;
655 - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
656 shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
659 return otherGestureRecognizer != _forwardingRecognizer && otherGestureRecognizer !=
self;
662 - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
663 shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
664 return otherGestureRecognizer ==
self;
667 - (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
668 self.touchedEndedWithoutBlocking = NO;
669 [
super touchesBegan:touches withEvent:event];
672 - (void)touchesEnded:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
674 self.state = UIGestureRecognizerStateEnded;
675 self.shouldEndInNextTouchesEnded = NO;
677 self.touchedEndedWithoutBlocking = YES;
679 [
super touchesEnded:touches withEvent:event];
682 - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
683 self.state = UIGestureRecognizerStateFailed;
705 - (instancetype)initWithTarget:(
id)target
706 platformViewsController:
707 (fml::WeakPtr<
flutter::PlatformViewsController>)platformViewsController {
708 self = [
super initWithTarget:target action:nil];
710 self.delegate =
self;
711 FML_DCHECK(platformViewsController.get() !=
nullptr);
718 - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
726 [_flutterViewController.get() touchesBegan:touches withEvent:event];
730 - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
731 [_flutterViewController.get() touchesMoved:touches withEvent:event];
734 - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
735 [_flutterViewController.get() touchesEnded:touches withEvent:event];
742 self.state = UIGestureRecognizerStateFailed;
747 - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
753 [_flutterViewController.get() forceTouchesCancelled:touches];
756 self.state = UIGestureRecognizerStateFailed;
761 - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
762 shouldRecognizeSimultaneouslyWithGestureRecognizer:
763 (UIGestureRecognizer*)otherGestureRecognizer {