7 #include "flutter/display_list/effects/dl_image_filter.h"
8 #include "flutter/fml/platform/darwin/cf_utils.h"
19 const fml::scoped_nsobject<UIView>& overlay_view,
20 const fml::scoped_nsobject<UIView>& overlay_view_wrapper,
21 std::unique_ptr<IOSSurface> ios_surface,
22 std::unique_ptr<Surface> surface)
23 : overlay_view(overlay_view),
24 overlay_view_wrapper(overlay_view_wrapper),
25 ios_surface(std::move(ios_surface)),
26 surface(std::move(surface)){};
33 mask_view_pool_.reset(
40 return weak_factory_->GetWeakPtr();
45 CATransform3D transform = CATransform3DIdentity;
46 transform.m11 = matrix.getScaleX();
47 transform.m21 = matrix.getSkewX();
48 transform.m41 = matrix.getTranslateX();
49 transform.m14 = matrix.getPerspX();
51 transform.m12 = matrix.getSkewY();
52 transform.m22 = matrix.getScaleY();
53 transform.m42 = matrix.getTranslateY();
54 transform.m24 = matrix.getPerspY();
60 layer.anchorPoint = CGPointZero;
61 layer.position = CGPointZero;
65 return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft,
66 clipSkRect.fBottom - clipSkRect.fTop);
70 const CGFloat epsilon = 0.01;
71 return radius1 - radius2 < epsilon;
79 @property(nonatomic) BOOL backdropFilterViewConfigured;
84 - (void)updateVisualEffectView:(UIVisualEffectView*)visualEffectView;
98 blurRadius:(CGFloat)blurRadius
99 visualEffectView:(UIVisualEffectView*)visualEffectView {
100 if (
self = [super init]) {
105 FML_DLOG(ERROR) <<
"Apple's API for UIVisualEffectView changed. Update the implementation to "
106 "access the gaussianBlur CAFilter.";
109 _backdropFilterView = visualEffectView;
110 _backdropFilterViewConfigured = NO;
122 + (void)prepareOnce:(UIVisualEffectView*)visualEffectView {
126 for (NSUInteger i = 0; i < visualEffectView.subviews.count; i++) {
127 UIView* view = visualEffectView.subviews[i];
128 if ([NSStringFromClass([view
class]) hasSuffix:
@"BackdropView"]) {
130 for (NSObject* filter in view.layer.filters) {
131 if ([[filter valueForKey:
@"name"] isEqual:
@"gaussianBlur"] &&
132 [[filter valueForKey:
@"inputRadius"] isKindOfClass:[NSNumber class]]) {
133 _gaussianBlurFilter = filter;
137 }
else if ([NSStringFromClass([view
class]) hasSuffix:
@"VisualEffectSubview"]) {
144 + (BOOL)isUIVisualEffectViewImplementationValid {
149 FML_DCHECK(_backdropFilterView);
150 if (!
self.backdropFilterViewConfigured) {
151 [
self updateVisualEffectView:_backdropFilterView];
152 self.backdropFilterViewConfigured = YES;
154 return _backdropFilterView;
157 - (void)updateVisualEffectView:(UIVisualEffectView*)visualEffectView {
158 NSObject* gaussianBlurFilter = [_gaussianBlurFilter copy];
159 FML_DCHECK(gaussianBlurFilter);
160 UIView* backdropView = visualEffectView.subviews[_indexOfBackdropView];
161 [gaussianBlurFilter setValue:@(_blurRadius) forKey:@"inputRadius"];
162 backdropView.layer.filters = @[ gaussianBlurFilter ];
164 UIView* visualEffectSubview = visualEffectView.subviews[_indexOfVisualEffectSubview];
165 visualEffectSubview.layer.backgroundColor = UIColor.clearColor.CGColor;
166 visualEffectView.frame = _frame;
168 self.backdropFilterView = visualEffectView;
175 @property(nonatomic, copy) NSArray<PlatformViewFilter*>* filters;
185 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
186 for (UIView* view in
self.subviews) {
187 if ([view pointInside:[
self convertPoint:point toView:view] withEvent:event]) {
195 FML_DCHECK(
self.filters.count ==
self.backdropFilterSubviews.count);
196 if (
self.filters.count == 0 && filters.count == 0) {
199 self.filters = filters;
200 NSUInteger index = 0;
201 for (index = 0; index <
self.filters.count; index++) {
202 UIVisualEffectView* backdropFilterView;
206 [
self addSubview:backdropFilterView];
207 [
self.backdropFilterSubviews addObject:backdropFilterView];
209 [filter updateVisualEffectView:self.backdropFilterSubviews[index]];
213 [
self.backdropFilterSubviews[i - 1] removeFromSuperview];
214 [
self.backdropFilterSubviews removeLastObject];
219 if (!_backdropFilterSubviews) {
220 _backdropFilterSubviews = [[NSMutableArray alloc] init];
222 return _backdropFilterSubviews;
237 @property(nonatomic) CATransform3D reverseScreenScale;
239 - (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix;
244 std::vector<fml::CFRef<CGPathRef>> paths_;
250 return [
self initWithFrame:frame screenScale:[UIScreen mainScreen].scale];
253 - (instancetype)
initWithFrame:(CGRect)frame screenScale:(CGFloat)screenScale {
255 self.backgroundColor = UIColor.clearColor;
256 _reverseScreenScale = CATransform3DMakeScale(1 / screenScale, 1 / screenScale, 1);
263 + (Class)layerClass {
264 return [CAShapeLayer class];
267 - (CAShapeLayer*)shapeLayer {
268 return (CAShapeLayer*)
self.layer;
275 [
self shapeLayer].path = nil;
276 [
self setNeedsDisplay];
284 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
288 - (void)drawRect:(CGRect)rect {
292 CGContextRef context = UIGraphicsGetCurrentContext();
293 CGContextSaveGState(context);
296 CGContextSetAlpha(context, 1);
298 for (
size_t i = 0; i < paths_.size(); i++) {
299 CGContextAddPath(context, paths_.at(i));
300 CGContextClip(context);
302 CGContextFillRect(context, rect);
303 CGContextRestoreGState(context);
307 [
super drawRect:rect];
308 if (![
self shapeLayer].path) {
309 if (paths_.size() == 1) {
311 [
self shapeLayer].path = paths_.at(0);
314 CGPathRef pathSoFar = CGPathCreateWithRect(
rectSoFar_, nil);
315 [
self shapeLayer].path = pathSoFar;
316 CGPathRelease(pathSoFar);
322 - (void)clipRect:(const SkRect&)clipSkRect matrix:(const SkMatrix&)matrix {
324 CGPathRef path = CGPathCreateWithRect(clipRect, nil);
326 CATransform3D matrixInPoints =
328 paths_.push_back([
self getTransformedPath:path matrix:matrixInPoints]);
329 CGAffineTransform affine = [
self affineWithMatrix:matrixInPoints];
331 if (affine.b == 0 && affine.c == 0) {
338 - (void)clipRRect:(const SkRRect&)clipSkRRect matrix:(const SkMatrix&)matrix {
340 CGPathRef pathRef =
nullptr;
341 switch (clipSkRRect.getType()) {
342 case SkRRect::kEmpty_Type: {
345 case SkRRect::kRect_Type: {
349 case SkRRect::kOval_Type:
350 case SkRRect::kSimple_Type: {
352 pathRef = CGPathCreateWithRoundedRect(clipRect, clipSkRRect.getSimpleRadii().x(),
353 clipSkRRect.getSimpleRadii().y(), nil);
356 case SkRRect::kNinePatch_Type:
357 case SkRRect::kComplex_Type: {
358 CGMutablePathRef mutablePathRef = CGPathCreateMutable();
360 SkRect clipSkRect = clipSkRRect.rect();
361 SkVector topLeftRadii = clipSkRRect.radii(SkRRect::kUpperLeft_Corner);
362 SkVector topRightRadii = clipSkRRect.radii(SkRRect::kUpperRight_Corner);
363 SkVector bottomRightRadii = clipSkRRect.radii(SkRRect::kLowerRight_Corner);
364 SkVector bottomLeftRadii = clipSkRRect.radii(SkRRect::kLowerLeft_Corner);
368 CGPathMoveToPoint(mutablePathRef, nil, clipSkRect.fLeft + topLeftRadii.x(), clipSkRect.fTop);
370 CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fRight - topRightRadii.x(),
372 CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fRight, clipSkRect.fTop,
373 clipSkRect.fRight, clipSkRect.fTop + topRightRadii.y(),
374 clipSkRect.fRight, clipSkRect.fTop + topRightRadii.y());
376 CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fRight,
377 clipSkRect.fBottom - bottomRightRadii.y());
378 CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fRight, clipSkRect.fBottom,
379 clipSkRect.fRight - bottomRightRadii.x(), clipSkRect.fBottom,
380 clipSkRect.fRight - bottomRightRadii.x(), clipSkRect.fBottom);
382 CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fLeft + bottomLeftRadii.x(),
384 CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fLeft, clipSkRect.fBottom,
385 clipSkRect.fLeft, clipSkRect.fBottom - bottomLeftRadii.y(),
386 clipSkRect.fLeft, clipSkRect.fBottom - bottomLeftRadii.y());
388 CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fLeft,
389 clipSkRect.fTop + topLeftRadii.y());
390 CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fLeft, clipSkRect.fTop,
391 clipSkRect.fLeft + topLeftRadii.x(), clipSkRect.fTop,
392 clipSkRect.fLeft + topLeftRadii.x(), clipSkRect.fTop);
393 CGPathCloseSubpath(mutablePathRef);
395 pathRef = mutablePathRef;
400 CATransform3D matrixInPoints =
405 paths_.push_back([
self getTransformedPath:pathRef matrix:matrixInPoints]);
408 - (void)clipPath:(const SkPath&)path matrix:(const SkMatrix&)matrix {
409 if (!path.isValid()) {
412 if (path.isEmpty()) {
416 CGMutablePathRef pathRef = CGPathCreateMutable();
419 SkPath::Iter iter(path,
true);
420 SkPoint pts[kMaxPointsInVerb];
421 SkPath::Verb verb = iter.next(pts);
422 SkPoint last_pt_from_last_verb = SkPoint::Make(0, 0);
423 while (verb != SkPath::kDone_Verb) {
424 if (verb == SkPath::kLine_Verb || verb == SkPath::kQuad_Verb || verb == SkPath::kConic_Verb ||
425 verb == SkPath::kCubic_Verb) {
426 FML_DCHECK(last_pt_from_last_verb == pts[0]);
429 case SkPath::kMove_Verb: {
430 CGPathMoveToPoint(pathRef, nil, pts[0].x(), pts[0].y());
431 last_pt_from_last_verb = pts[0];
434 case SkPath::kLine_Verb: {
435 CGPathAddLineToPoint(pathRef, nil, pts[1].x(), pts[1].y());
436 last_pt_from_last_verb = pts[1];
439 case SkPath::kQuad_Verb: {
440 CGPathAddQuadCurveToPoint(pathRef, nil, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
441 last_pt_from_last_verb = pts[2];
444 case SkPath::kConic_Verb: {
448 CGPathAddQuadCurveToPoint(pathRef, nil, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
449 last_pt_from_last_verb = pts[2];
452 case SkPath::kCubic_Verb: {
453 CGPathAddCurveToPoint(pathRef, nil, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y(),
454 pts[3].x(), pts[3].y());
455 last_pt_from_last_verb = pts[3];
458 case SkPath::kClose_Verb: {
459 CGPathCloseSubpath(pathRef);
462 case SkPath::kDone_Verb: {
466 verb = iter.next(pts);
469 CATransform3D matrixInPoints =
471 paths_.push_back([
self getTransformedPath:pathRef matrix:matrixInPoints]);
474 - (CGAffineTransform)affineWithMatrix:(CATransform3D)matrix {
475 return CGAffineTransformMake(matrix.m11, matrix.m12, matrix.m21, matrix.m22, matrix.m41,
479 - (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix {
480 CGAffineTransform affine = [
self affineWithMatrix:matrix];
481 CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &affine);
484 return fml::CFRef<CGPathRef>(transformedPath);
493 @property(nonatomic) NSUInteger capacity;
497 @property(nonatomic) NSMutableSet<FlutterClippingMaskView*>* pool;
503 - (instancetype)initWithCapacity:(NSInteger)capacity {
504 if (
self = [super init]) {
507 _pool = [[NSMutableSet alloc] initWithCapacity:1];
508 _capacity = capacity;
514 FML_DCHECK(
self.pool.count <=
self.capacity);
515 if (
self.pool.count == 0) {
518 screenScale:UIScreen.mainScreen.scale];
521 maskView.frame = frame;
523 [
self.pool removeObject:maskView];
528 FML_DCHECK(![
self.pool containsObject:maskView]);
529 FML_DCHECK(
self.pool.count <=
self.capacity);
530 if (
self.pool.count ==
self.capacity) {
533 [
self.pool addObject:maskView];