7 #include "flutter/display_list/effects/dl_image_filter.h"
8 #include "flutter/fml/platform/darwin/cf_utils.h"
17 const fml::scoped_nsobject<UIView>& overlay_view,
18 const fml::scoped_nsobject<UIView>& overlay_view_wrapper,
19 std::unique_ptr<IOSSurface> ios_surface,
20 std::unique_ptr<Surface> surface)
21 : overlay_view(overlay_view),
22 overlay_view_wrapper(overlay_view_wrapper),
23 ios_surface(std::move(ios_surface)),
24 surface(std::move(surface)){};
31 mask_view_pool_.reset(
38 return weak_factory_->GetWeakPtr();
43 CATransform3D transform = CATransform3DIdentity;
44 transform.m11 = matrix.getScaleX();
45 transform.m21 = matrix.getSkewX();
46 transform.m41 = matrix.getTranslateX();
47 transform.m14 = matrix.getPerspX();
49 transform.m12 = matrix.getSkewY();
50 transform.m22 = matrix.getScaleY();
51 transform.m42 = matrix.getTranslateY();
52 transform.m24 = matrix.getPerspY();
58 layer.anchorPoint = CGPointZero;
59 layer.position = CGPointZero;
63 return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft,
64 clipSkRect.fBottom - clipSkRect.fTop);
68 const CGFloat epsilon = 0.01;
69 return radius1 - radius2 < epsilon;
77 @property(nonatomic) BOOL backdropFilterViewConfigured;
82 - (void)updateVisualEffectView:(UIVisualEffectView*)visualEffectView;
96 blurRadius:(CGFloat)blurRadius
97 visualEffectView:(UIVisualEffectView*)visualEffectView {
98 if (
self = [super init]) {
103 FML_DLOG(ERROR) <<
"Apple's API for UIVisualEffectView changed. Update the implementation to "
104 "access the gaussianBlur CAFilter.";
108 _backdropFilterView = [visualEffectView retain];
109 _backdropFilterViewConfigured = NO;
116 [_gaussianBlurFilter release];
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 retain];
137 }
else if ([NSStringFromClass([view
class]) hasSuffix:
@"VisualEffectSubview"]) {
144 + (BOOL)isUIVisualEffectViewImplementationValid {
149 [_backdropFilterView release];
150 _backdropFilterView = nil;
156 FML_DCHECK(_backdropFilterView);
157 if (!
self.backdropFilterViewConfigured) {
158 [
self updateVisualEffectView:_backdropFilterView];
159 self.backdropFilterViewConfigured = YES;
161 return _backdropFilterView;
164 - (void)updateVisualEffectView:(UIVisualEffectView*)visualEffectView {
165 NSObject* gaussianBlurFilter = [[_gaussianBlurFilter copy] autorelease];
166 FML_DCHECK(gaussianBlurFilter);
167 UIView* backdropView = visualEffectView.subviews[_indexOfBackdropView];
168 [gaussianBlurFilter setValue:@(_blurRadius) forKey:@"inputRadius"];
169 backdropView.layer.filters = @[ gaussianBlurFilter ];
171 UIView* visualEffectSubview = visualEffectView.subviews[_indexOfVisualEffectSubview];
172 visualEffectSubview.layer.backgroundColor = UIColor.clearColor.CGColor;
173 visualEffectView.frame = _frame;
175 if (_backdropFilterView != visualEffectView) {
176 _backdropFilterView = [visualEffectView retain];
184 @property(retain, nonatomic) NSArray<PlatformViewFilter*>* filters;
185 @property(retain, nonatomic) NSMutableArray<UIVisualEffectView*>* backdropFilterSubviews;
194 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
195 for (UIView* view in
self.subviews) {
196 if ([view pointInside:[
self convertPoint:point toView:view] withEvent:event]) {
204 FML_DCHECK(
self.filters.count ==
self.backdropFilterSubviews.count);
205 if (
self.filters.count == 0 && filters.count == 0) {
208 self.filters = filters;
209 NSUInteger index = 0;
210 for (index = 0; index <
self.filters.count; index++) {
211 UIVisualEffectView* backdropFilterView;
213 if (
self.backdropFilterSubviews.count <= index) {
215 [
self addSubview:backdropFilterView];
216 [
self.backdropFilterSubviews addObject:backdropFilterView];
218 [filter updateVisualEffectView:self.backdropFilterSubviews[index]];
221 for (NSUInteger i =
self.backdropFilterSubviews.count; i > index; i--) {
222 [
self.backdropFilterSubviews[i - 1] removeFromSuperview];
223 [
self.backdropFilterSubviews removeLastObject];
231 [_backdropFilterSubviews release];
232 _backdropFilterSubviews = nil;
237 - (NSMutableArray*)backdropFilterSubviews {
238 if (!_backdropFilterSubviews) {
239 _backdropFilterSubviews = [[[NSMutableArray alloc] init] retain];
241 return _backdropFilterSubviews;
256 @property(nonatomic) CATransform3D reverseScreenScale;
258 - (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix;
263 std::vector<fml::CFRef<CGPathRef>> paths_;
267 return [
self initWithFrame:frame screenScale:[UIScreen mainScreen].scale];
270 - (instancetype)
initWithFrame:(CGRect)frame screenScale:(CGFloat)screenScale {
272 self.backgroundColor = UIColor.clearColor;
273 _reverseScreenScale = CATransform3DMakeScale(1 / screenScale, 1 / screenScale, 1);
280 [
self setNeedsDisplay];
288 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
292 - (void)drawRect:(CGRect)rect {
293 CGContextRef context = UIGraphicsGetCurrentContext();
294 CGContextSaveGState(context);
297 CGContextSetAlpha(context, 1);
299 for (
size_t i = 0; i < paths_.size(); i++) {
300 CGContextAddPath(context, paths_.at(i));
301 CGContextClip(context);
303 CGContextFillRect(context, rect);
304 CGContextRestoreGState(context);
307 - (void)clipRect:(const SkRect&)clipSkRect matrix:(const SkMatrix&)matrix {
309 CGPathRef path = CGPathCreateWithRect(clipRect, nil);
311 CATransform3D matrixInPoints =
313 paths_.push_back([
self getTransformedPath:path matrix:matrixInPoints]);
316 - (void)clipRRect:(const SkRRect&)clipSkRRect matrix:(const SkMatrix&)matrix {
317 CGPathRef pathRef =
nullptr;
318 switch (clipSkRRect.getType()) {
319 case SkRRect::kEmpty_Type: {
322 case SkRRect::kRect_Type: {
326 case SkRRect::kOval_Type:
327 case SkRRect::kSimple_Type: {
329 pathRef = CGPathCreateWithRoundedRect(clipRect, clipSkRRect.getSimpleRadii().x(),
330 clipSkRRect.getSimpleRadii().y(), nil);
333 case SkRRect::kNinePatch_Type:
334 case SkRRect::kComplex_Type: {
335 CGMutablePathRef mutablePathRef = CGPathCreateMutable();
337 SkRect clipSkRect = clipSkRRect.rect();
338 SkVector topLeftRadii = clipSkRRect.radii(SkRRect::kUpperLeft_Corner);
339 SkVector topRightRadii = clipSkRRect.radii(SkRRect::kUpperRight_Corner);
340 SkVector bottomRightRadii = clipSkRRect.radii(SkRRect::kLowerRight_Corner);
341 SkVector bottomLeftRadii = clipSkRRect.radii(SkRRect::kLowerLeft_Corner);
345 CGPathMoveToPoint(mutablePathRef, nil, clipSkRect.fLeft + topLeftRadii.x(), clipSkRect.fTop);
347 CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fRight - topRightRadii.x(),
349 CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fRight, clipSkRect.fTop,
350 clipSkRect.fRight, clipSkRect.fTop + topRightRadii.y(),
351 clipSkRect.fRight, clipSkRect.fTop + topRightRadii.y());
353 CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fRight,
354 clipSkRect.fBottom - bottomRightRadii.y());
355 CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fRight, clipSkRect.fBottom,
356 clipSkRect.fRight - bottomRightRadii.x(), clipSkRect.fBottom,
357 clipSkRect.fRight - bottomRightRadii.x(), clipSkRect.fBottom);
359 CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fLeft + bottomLeftRadii.x(),
361 CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fLeft, clipSkRect.fBottom,
362 clipSkRect.fLeft, clipSkRect.fBottom - bottomLeftRadii.y(),
363 clipSkRect.fLeft, clipSkRect.fBottom - bottomLeftRadii.y());
365 CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fLeft,
366 clipSkRect.fTop + topLeftRadii.y());
367 CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fLeft, clipSkRect.fTop,
368 clipSkRect.fLeft + topLeftRadii.x(), clipSkRect.fTop,
369 clipSkRect.fLeft + topLeftRadii.x(), clipSkRect.fTop);
370 CGPathCloseSubpath(mutablePathRef);
372 pathRef = mutablePathRef;
377 CATransform3D matrixInPoints =
382 paths_.push_back([
self getTransformedPath:pathRef matrix:matrixInPoints]);
385 - (void)clipPath:(const SkPath&)path matrix:(const SkMatrix&)matrix {
386 if (!path.isValid()) {
389 if (path.isEmpty()) {
392 CGMutablePathRef pathRef = CGPathCreateMutable();
395 SkPath::Iter iter(path,
true);
396 SkPoint pts[kMaxPointsInVerb];
397 SkPath::Verb verb = iter.next(pts);
398 SkPoint last_pt_from_last_verb = SkPoint::Make(0, 0);
399 while (verb != SkPath::kDone_Verb) {
400 if (verb == SkPath::kLine_Verb || verb == SkPath::kQuad_Verb || verb == SkPath::kConic_Verb ||
401 verb == SkPath::kCubic_Verb) {
402 FML_DCHECK(last_pt_from_last_verb == pts[0]);
405 case SkPath::kMove_Verb: {
406 CGPathMoveToPoint(pathRef, nil, pts[0].x(), pts[0].y());
407 last_pt_from_last_verb = pts[0];
410 case SkPath::kLine_Verb: {
411 CGPathAddLineToPoint(pathRef, nil, pts[1].x(), pts[1].y());
412 last_pt_from_last_verb = pts[1];
415 case SkPath::kQuad_Verb: {
416 CGPathAddQuadCurveToPoint(pathRef, nil, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
417 last_pt_from_last_verb = pts[2];
420 case SkPath::kConic_Verb: {
424 CGPathAddQuadCurveToPoint(pathRef, nil, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
425 last_pt_from_last_verb = pts[2];
428 case SkPath::kCubic_Verb: {
429 CGPathAddCurveToPoint(pathRef, nil, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y(),
430 pts[3].x(), pts[3].y());
431 last_pt_from_last_verb = pts[3];
434 case SkPath::kClose_Verb: {
435 CGPathCloseSubpath(pathRef);
438 case SkPath::kDone_Verb: {
442 verb = iter.next(pts);
445 CATransform3D matrixInPoints =
447 paths_.push_back([
self getTransformedPath:pathRef matrix:matrixInPoints]);
450 - (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix {
451 CGAffineTransform affine =
452 CGAffineTransformMake(matrix.m11, matrix.m12, matrix.m21, matrix.m22, matrix.m41, matrix.m42);
453 CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &affine);
455 return fml::CFRef<CGPathRef>(transformedPath);
464 @property(assign, nonatomic) NSUInteger capacity;
468 @property(retain, nonatomic) NSMutableSet<FlutterClippingMaskView*>* pool;
474 - (instancetype)initWithCapacity:(NSInteger)capacity {
475 if (
self = [super init]) {
478 _pool = [[NSMutableSet alloc] initWithCapacity:1];
479 _capacity = capacity;
485 FML_DCHECK(
self.pool.count <=
self.capacity);
486 if (
self.pool.count == 0) {
490 screenScale:[UIScreen mainScreen].scale] autorelease];
493 maskView.frame = frame;
495 [
self.pool removeObject:maskView];
500 FML_DCHECK(![
self.pool containsObject:maskView]);
501 FML_DCHECK(
self.pool.count <=
self.capacity);
502 if (
self.pool.count ==
self.capacity) {
505 [
self.pool addObject:maskView];