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

Revert "PlatformView partial blur" #37085

Merged
merged 1 commit into from
Oct 27, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions flow/embedded_views.cc
Original file line number Diff line number Diff line change
@@ -100,10 +100,8 @@ void MutatorsStack::PushOpacity(const int& alpha) {
};

void MutatorsStack::PushBackdropFilter(
const std::shared_ptr<const DlImageFilter>& filter,
const SkRect& filter_rect) {
std::shared_ptr<Mutator> element =
std::make_shared<Mutator>(filter, filter_rect);
const std::shared_ptr<const DlImageFilter>& filter) {
std::shared_ptr<Mutator> element = std::make_shared<Mutator>(filter);
vector_.push_back(element);
};

56 changes: 11 additions & 45 deletions flow/embedded_views.h
Original file line number Diff line number Diff line change
@@ -32,33 +32,6 @@ enum MutatorType {
kBackdropFilter
};

// Represents an image filter mutation.
//
// Should be used for image_filter_layer and backdrop_filter_layer.
// TODO(cyanglaz): Refactor this into a ImageFilterMutator class.
// https://github.com/flutter/flutter/issues/108470
class ImageFilterMutation {
public:
ImageFilterMutation(std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect)
: filter_(filter), filter_rect_(filter_rect) {}

const DlImageFilter& GetFilter() const { return *filter_; }
const SkRect& GetFilterRect() const { return filter_rect_; }

bool operator==(const ImageFilterMutation& other) const {
return *filter_ == *other.filter_ && filter_rect_ == other.filter_rect_;
}

bool operator!=(const ImageFilterMutation& other) const {
return !operator==(other);
}

private:
std::shared_ptr<const DlImageFilter> filter_;
const SkRect filter_rect_;
};

// Stores mutation information like clipping or kTransform.
//
// The `type` indicates the type of the mutation: kClipRect, kTransform and etc.
@@ -86,7 +59,7 @@ class Mutator {
alpha_ = other.alpha_;
break;
case kBackdropFilter:
filter_mutation_ = other.filter_mutation_;
filter_ = other.filter_;
break;
default:
break;
@@ -100,20 +73,15 @@ class Mutator {
explicit Mutator(const SkMatrix& matrix)
: type_(kTransform), matrix_(matrix) {}
explicit Mutator(const int& alpha) : type_(kOpacity), alpha_(alpha) {}
explicit Mutator(std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect)
: type_(kBackdropFilter),
filter_mutation_(
std::make_shared<ImageFilterMutation>(filter, filter_rect)) {}
explicit Mutator(std::shared_ptr<const DlImageFilter> filter)
: type_(kBackdropFilter), filter_(filter) {}

const MutatorType& GetType() const { return type_; }
const SkRect& GetRect() const { return rect_; }
const SkRRect& GetRRect() const { return rrect_; }
const SkPath& GetPath() const { return *path_; }
const SkMatrix& GetMatrix() const { return matrix_; }
const ImageFilterMutation& GetFilterMutation() const {
return *filter_mutation_;
}
const DlImageFilter& GetFilter() const { return *filter_; }
const int& GetAlpha() const { return alpha_; }
float GetAlphaFloat() const { return (alpha_ / 255.0); }

@@ -133,7 +101,7 @@ class Mutator {
case kOpacity:
return alpha_ == other.alpha_;
case kBackdropFilter:
return *filter_mutation_ == *other.filter_mutation_;
return *filter_ == *other.filter_;
}

return false;
@@ -164,7 +132,8 @@ class Mutator {
int alpha_;
};

std::shared_ptr<ImageFilterMutation> filter_mutation_;
std::shared_ptr<const DlImageFilter> filter_;

}; // Mutator

// A stack of mutators that can be applied to an embedded platform view.
@@ -185,8 +154,7 @@ class MutatorsStack {
void PushClipPath(const SkPath& path);
void PushTransform(const SkMatrix& matrix);
void PushOpacity(const int& alpha);
void PushBackdropFilter(const std::shared_ptr<const DlImageFilter>& filter,
const SkRect& filter_rect);
void PushBackdropFilter(const std::shared_ptr<const DlImageFilter>& filter);

// Removes the `Mutator` on the top of the stack
// and destroys it.
@@ -284,9 +252,8 @@ class EmbeddedViewParams {
const SkRect& finalBoundingRect() const { return final_bounding_rect_; }

// Pushes the stored DlImageFilter object to the mutators stack.
void PushImageFilter(std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect) {
mutators_stack_.PushBackdropFilter(filter, filter_rect);
void PushImageFilter(std::shared_ptr<const DlImageFilter> filter) {
mutators_stack_.PushBackdropFilter(filter);
}

// Whether the embedder should construct DisplayList objects to hold the
@@ -490,8 +457,7 @@ class ExternalViewEmbedder {
// See also: |PushVisitedPlatformView| for pushing platform view ids to the
// visited platform views list.
virtual void PushFilterToVisitedPlatformViews(
std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect) {}
std::shared_ptr<const DlImageFilter> filter) {}

private:
bool used_this_frame_ = false;
3 changes: 1 addition & 2 deletions flow/layers/backdrop_filter_layer.cc
Original file line number Diff line number Diff line change
@@ -43,8 +43,7 @@ void BackdropFilterLayer::Preroll(PrerollContext* context) {
Layer::AutoPrerollSaveLayerState save =
Layer::AutoPrerollSaveLayerState::Create(context, true, bool(filter_));
if (context->view_embedder != nullptr) {
context->view_embedder->PushFilterToVisitedPlatformViews(
filter_, context->cull_rect);
context->view_embedder->PushFilterToVisitedPlatformViews(filter_);
}
SkRect child_paint_bounds = SkRect::MakeEmpty();
PrerollChildren(context, &child_paint_bounds);
30 changes: 12 additions & 18 deletions flow/mutators_stack_unittests.cc
Original file line number Diff line number Diff line change
@@ -94,19 +94,14 @@ TEST(MutatorsStack, PushBackdropFilter) {
const int num_of_mutators = 10;
for (int i = 0; i < num_of_mutators; i++) {
auto filter = std::make_shared<DlBlurImageFilter>(i, 5, DlTileMode::kClamp);
stack.PushBackdropFilter(filter, SkRect::MakeXYWH(i, i, i, i));
stack.PushBackdropFilter(filter);
}

auto iter = stack.Begin();
int i = 0;
while (iter != stack.End()) {
ASSERT_EQ(iter->get()->GetType(), MutatorType::kBackdropFilter);
ASSERT_EQ(iter->get()->GetFilterMutation().GetFilter().asBlur()->sigma_x(),
i);
ASSERT_EQ(iter->get()->GetFilterMutation().GetFilterRect().x(), i);
ASSERT_EQ(iter->get()->GetFilterMutation().GetFilterRect().x(), i);
ASSERT_EQ(iter->get()->GetFilterMutation().GetFilterRect().width(), i);
ASSERT_EQ(iter->get()->GetFilterMutation().GetFilterRect().height(), i);
ASSERT_EQ(iter->get()->GetFilter().asBlur()->sigma_x(), i);
++iter;
++i;
}
@@ -169,7 +164,7 @@ TEST(MutatorsStack, Equality) {
int alpha = 240;
stack.PushOpacity(alpha);
auto filter = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
stack.PushBackdropFilter(filter, SkRect::MakeEmpty());
stack.PushBackdropFilter(filter);

MutatorsStack stack_other;
SkMatrix matrix_other = SkMatrix::Scale(1, 1);
@@ -184,7 +179,7 @@ TEST(MutatorsStack, Equality) {
stack_other.PushOpacity(other_alpha);
auto other_filter =
std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
stack_other.PushBackdropFilter(other_filter, SkRect::MakeEmpty());
stack_other.PushBackdropFilter(other_filter);

ASSERT_TRUE(stack == stack_other);
}
@@ -216,9 +211,9 @@ TEST(Mutator, Initialization) {
ASSERT_TRUE(mutator5.GetType() == MutatorType::kOpacity);

auto filter = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
Mutator mutator6 = Mutator(filter, SkRect::MakeEmpty());
Mutator mutator6 = Mutator(filter);
ASSERT_TRUE(mutator6.GetType() == MutatorType::kBackdropFilter);
ASSERT_TRUE(mutator6.GetFilterMutation().GetFilter() == *filter);
ASSERT_TRUE(mutator6.GetFilter() == *filter);
}

TEST(Mutator, CopyConstructor) {
@@ -249,7 +244,7 @@ TEST(Mutator, CopyConstructor) {
ASSERT_TRUE(mutator5 == copy5);

auto filter = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
Mutator mutator6 = Mutator(filter, SkRect::MakeEmpty());
Mutator mutator6 = Mutator(filter);
Mutator copy6 = Mutator(mutator6);
ASSERT_TRUE(mutator6 == copy6);
}
@@ -281,10 +276,9 @@ TEST(Mutator, Equality) {
Mutator other_mutator5 = Mutator(alpha);
ASSERT_TRUE(mutator5 == other_mutator5);

auto filter1 = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
auto filter2 = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
Mutator mutator6 = Mutator(filter1, SkRect::MakeEmpty());
Mutator other_mutator6 = Mutator(filter2, SkRect::MakeEmpty());
auto filter = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
Mutator mutator6 = Mutator(filter);
Mutator other_mutator6 = Mutator(filter);
ASSERT_TRUE(mutator6 == other_mutator6);
}

@@ -305,8 +299,8 @@ TEST(Mutator, UnEquality) {
auto filter = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
auto filter2 =
std::make_shared<DlBlurImageFilter>(10, 10, DlTileMode::kClamp);
Mutator mutator3 = Mutator(filter, SkRect::MakeEmpty());
Mutator other_mutator3 = Mutator(filter2, SkRect::MakeEmpty());
Mutator mutator3 = Mutator(filter);
Mutator other_mutator3 = Mutator(filter2);
ASSERT_TRUE(mutator3 != other_mutator3);
}

5 changes: 2 additions & 3 deletions shell/common/shell_test_external_view_embedder.cc
Original file line number Diff line number Diff line change
@@ -89,11 +89,10 @@ void ShellTestExternalViewEmbedder::PushVisitedPlatformView(int64_t view_id) {

// |ExternalViewEmbedder|
void ShellTestExternalViewEmbedder::PushFilterToVisitedPlatformViews(
std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect) {
std::shared_ptr<const DlImageFilter> filter) {
for (int64_t id : visited_platform_views_) {
EmbeddedViewParams params = current_composition_params_[id];
params.PushImageFilter(filter, filter_rect);
params.PushImageFilter(filter);
current_composition_params_[id] = params;
mutators_stacks_[id] = params.mutatorsStack();
}
3 changes: 1 addition & 2 deletions shell/common/shell_test_external_view_embedder.h
Original file line number Diff line number Diff line change
@@ -76,8 +76,7 @@ class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder {

// |ExternalViewEmbedder|
void PushFilterToVisitedPlatformViews(
std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect) override;
std::shared_ptr<const DlImageFilter> filter) override;

// |ExternalViewEmbedder|
void SubmitFrame(GrDirectContext* context,
2 changes: 1 addition & 1 deletion shell/common/shell_unittests.cc
Original file line number Diff line number Diff line change
@@ -845,7 +845,7 @@ TEST_F(ShellTest, PushBackdropFilterToVisitedPlatformViews) {
auto filter = DlBlurImageFilter(5, 5, DlTileMode::kClamp);
auto mutator = *external_view_embedder->GetStack(50).Begin();
ASSERT_EQ(mutator->GetType(), MutatorType::kBackdropFilter);
ASSERT_EQ(mutator->GetFilterMutation().GetFilter(), filter);
ASSERT_EQ(mutator->GetFilter(), filter);

DestroyShell(std::move(shell));
}
52 changes: 14 additions & 38 deletions shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm
Original file line number Diff line number Diff line change
@@ -321,11 +321,10 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
}

void FlutterPlatformViewsController::PushFilterToVisitedPlatformViews(
std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect) {
std::shared_ptr<const DlImageFilter> filter) {
for (int64_t id : visited_platform_views_) {
EmbeddedViewParams params = current_composition_params_[id];
params.PushImageFilter(filter, filter_rect);
params.PushImageFilter(filter);
current_composition_params_[id] = params;
}
}
@@ -426,7 +425,7 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
CGRectGetWidth(flutter_view.bounds),
CGRectGetHeight(flutter_view.bounds))] autorelease];

NSMutableArray* blurFilters = [[[NSMutableArray alloc] init] autorelease];
NSMutableArray* blurRadii = [[[NSMutableArray alloc] init] autorelease];

auto iter = mutators_stack.Begin();
while (iter != mutators_stack.End()) {
@@ -449,35 +448,13 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
embedded_view.alpha = (*iter)->GetAlphaFloat() * embedded_view.alpha;
break;
case kBackdropFilter: {
// Only support DlBlurImageFilter for BackdropFilter.
if (!(*iter)->GetFilterMutation().GetFilter().asBlur() || !canApplyBlurBackdrop) {
break;
}
CGRect filterRect =
flutter::GetCGRectFromSkRect((*iter)->GetFilterMutation().GetFilterRect());
// `filterRect` reprents the rect that should be filtered inside the `flutter_view_`.
// The `PlatformViewFilter` needs the frame inside the `clipView` that needs to be
// filtered.
if (CGRectIsNull(CGRectIntersection(filterRect, clipView.frame))) {
break;
}
CGRect intersection = CGRectIntersection(filterRect, clipView.frame);
CGRect frameInClipView = [flutter_view_.get() convertRect:intersection toView:clipView];
// sigma_x is arbitrarily chosen as the radius value because Quartz sets
// sigma_x and sigma_y equal to each other. DlBlurImageFilter's Tile Mode
// is not supported in Quartz's gaussianBlur CAFilter, so it is not used
// to blur the PlatformView.
CGFloat blurRadius = (*iter)->GetFilterMutation().GetFilter().asBlur()->sigma_x();
UIVisualEffectView* visualEffectView = [[[UIVisualEffectView alloc]
initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]] autorelease];
PlatformViewFilter* filter =
[[[PlatformViewFilter alloc] initWithFrame:frameInClipView
blurRadius:blurRadius
visualEffectView:visualEffectView] autorelease];
if (!filter) {
canApplyBlurBackdrop = NO;
} else {
[blurFilters addObject:filter];
// We only support DlBlurImageFilter for BackdropFilter.
if ((*iter)->GetFilter().asBlur() && canApplyBlurBackdrop) {
// sigma_x is arbitrarily chosen as the radius value because Quartz sets
// sigma_x and sigma_y equal to each other. DlBlurImageFilter's Tile Mode
// is not supported in Quartz's gaussianBlur CAFilter, so it is not used
// to blur the PlatformView.
[blurRadii addObject:@((*iter)->GetFilter().asBlur()->sigma_x())];
}
break;
}
@@ -486,16 +463,15 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
}

if (canApplyBlurBackdrop) {
[clipView applyBlurBackdropFilters:blurFilters];
canApplyBlurBackdrop = [clipView applyBlurBackdropFilters:blurRadii];
}

// Reverse the offset of the clipView.
// The clipView's frame includes the final translate of the final transform matrix.
// Thus, this translate needs to be reversed so the platform view can layout at the correct
// offset.
// So we need to revese this translate so the platform view can layout at the correct offset.
//
// Note that the transforms are not applied to the clipping paths because clipping paths happen on
// the mask view, whose origin is always (0,0) to the flutter_view.
// Note that we don't apply this transform matrix the clippings because clippings happen on the
// mask view, whose origin is always (0,0) to the flutter_view.
CATransform3D reverseTranslate =
CATransform3DMakeTranslation(-clipView.frame.origin.x, -clipView.frame.origin.y, 0);
embedded_view.layer.transform = CATransform3DConcat(finalTransform, reverseTranslate);
578 changes: 200 additions & 378 deletions shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -45,54 +45,18 @@

@end

// An object represents a blur filter.
//
// This object produces a `backdropFilterView`.
// To blur a View, add `backdropFilterView` as a subView of the View.
@interface PlatformViewFilter : NSObject

// Determines the rect of the blur effect in the coordinate system of `backdropFilterView`'s
// parentView.
@property(assign, nonatomic, readonly) CGRect frame;

// Determines the blur intensity.
//
// It is set as the value of `inputRadius` of the `gaussianFilter` that is internally used.
@property(assign, nonatomic, readonly) CGFloat blurRadius;

// This is the view to use to blur the PlatformView.
//
// It is a modified version of UIKit's `UIVisualEffectView`.
// The inputRadius can be customized and it doesn't add any color saturation to the blurred view.
@property(nonatomic, retain, readonly) UIVisualEffectView* backdropFilterView;

// For testing only.
+ (void)resetPreparation;

- (instancetype)init NS_UNAVAILABLE;

// Initialize the filter object.
//
// The `frame` determines the rect of the blur effect in the coordinate system of
// `backdropFilterView`'s parentView. The `blurRadius` determines the blur intensity. It is set as
// the value of `inputRadius` of the `gaussianFilter` that is internally used. The
// `UIVisualEffectView` is the view that is used to add the blur effects. It is modified to become
// `backdropFilterView`, which better supports the need of Flutter.
//
// Note: if the implementation of UIVisualEffectView changes in a way that affects the
// implementation in `PlatformViewFilter`, this method will return nil.
- (instancetype)initWithFrame:(CGRect)frame
blurRadius:(CGFloat)blurRadius
visualEffectView:(UIVisualEffectView*)visualEffectView NS_DESIGNATED_INITIALIZER;

@end

// The parent view handles clipping to its subViews.
// The parent view handles clipping to its subviews.
@interface ChildClippingView : UIView

// Applies blur backdrop filters to the ChildClippingView with blur values from
// filters.
- (void)applyBlurBackdropFilters:(NSMutableArray<PlatformViewFilter*>*)filters;
// Applies blur backdrop filters to the ChildClippingView with blur radius values from
// blurRadii. Returns NO if Apple's API has changed and blurred backdrop filters cannot
// be applied, otherwise returns YES.
- (BOOL)applyBlurBackdropFilters:(NSArray*)blurRadii;

// The UIView used to extract the gaussianBlur filter. This must be a UIVisualEffectView
// initalized with UIBlurEffect to extract the correct filter. Made a public property
// for custom unit tests.
@property(nonatomic, retain) UIView* blurEffectView;

@end

@@ -105,9 +69,6 @@ CATransform3D GetCATransform3DFromSkMatrix(const SkMatrix& matrix);
// The position of the `layer` should be unchanged after resetting the anchor.
void ResetAnchor(CALayer* layer);

CGRect GetCGRectFromSkRect(const SkRect& clipSkRect);
BOOL BlurRadiusEqualToBlurRadius(CGFloat radius1, CGFloat radius2);

class IOSContextGL;
class IOSSurface;

@@ -236,8 +197,7 @@ class FlutterPlatformViewsController {
long FindFirstResponderPlatformViewId();

// Pushes backdrop filter mutation to the mutator stack of each visited platform view.
void PushFilterToVisitedPlatformViews(std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect);
void PushFilterToVisitedPlatformViews(std::shared_ptr<const DlImageFilter> filter);

// Pushes the view id of a visted platform view to the list of visied platform views.
void PushVisitedPlatformView(int64_t view_id) { visited_platform_views_.push_back(view_id); }
Original file line number Diff line number Diff line change
@@ -55,165 +55,124 @@ void ResetAnchor(CALayer* layer) {
layer.position = CGPointZero;
}

CGRect GetCGRectFromSkRect(const SkRect& clipSkRect) {
return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft,
clipSkRect.fBottom - clipSkRect.fTop);
}
} // namespace flutter

BOOL BlurRadiusEqualToBlurRadius(CGFloat radius1, CGFloat radius2) {
const CGFloat epsilon = 0.01;
return radius1 - radius2 < epsilon;
@implementation ChildClippingView {
// A gaussianFilter from UIVisualEffectView that can be copied for new backdrop filters.
NSObject* _gaussianFilter;
}

} // namespace flutter
// Lazy initializes blurEffectView as the expected UIVisualEffectView. The backdropFilter blur
// requires this UIVisualEffectView initialization. The lazy initalization is only used to allow
// custom unit tests.
- (UIView*)blurEffectView {
if (!_blurEffectView) {
// blurEffectView is only needed to extract its gaussianBlur filter. It is released after
// searching its subviews and extracting the filter.
_blurEffectView = [[[UIVisualEffectView alloc]
initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]] retain];
}
return _blurEffectView;
}

@implementation PlatformViewFilter

static NSObject* _gaussianBlurFilter = nil;
// The index of "_UIVisualEffectBackdropView" in UIVisualEffectView's subViews.
static NSInteger _indexOfBackdropView = -1;
// The index of "_UIVisualEffectSubview" in UIVisualEffectView's subViews.
static NSInteger _indexOfVisualEffectSubview = -1;
static BOOL _preparedOnce = NO;

- (instancetype)initWithFrame:(CGRect)frame
blurRadius:(CGFloat)blurRadius
visualEffectView:(UIVisualEffectView*)visualEffectView {
if (self = [super init]) {
_frame = frame;
_blurRadius = blurRadius;
[PlatformViewFilter prepareOnce:visualEffectView];
if (![PlatformViewFilter isUIVisualEffectViewImplementationValid]) {
FML_DLOG(ERROR) << "Apple's API for UIVisualEffectView changed. Update the implementation to "
"access the gaussianBlur CAFilter.";
[self release];
return nil;
// The ChildClippingView's frame is the bounding rect of the platform view. we only want touches to
// be hit tested and consumed by this view if they are inside the embedded platform view which could
// be smaller the embedded platform view is rotated.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
for (UIView* view in self.subviews) {
if ([view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
return YES;
}
NSObject* gaussianBlurFilter = [[_gaussianBlurFilter copy] autorelease];
FML_DCHECK(gaussianBlurFilter);
UIView* backdropView = visualEffectView.subviews[_indexOfBackdropView];
[gaussianBlurFilter setValue:@(_blurRadius) forKey:@"inputRadius"];
backdropView.layer.filters = @[ gaussianBlurFilter ];

UIView* visualEffectSubview = visualEffectView.subviews[_indexOfVisualEffectSubview];
visualEffectSubview.layer.backgroundColor = UIColor.clearColor.CGColor;

_backdropFilterView = [visualEffectView retain];
_backdropFilterView.frame = _frame;
}
return self;
return NO;
}

+ (void)resetPreparation {
_preparedOnce = NO;
[_gaussianBlurFilter release];
_gaussianBlurFilter = nil;
_indexOfBackdropView = -1;
_indexOfVisualEffectSubview = -1;
}
// Creates and initializes a UIVisualEffectView with a UIBlurEffect. Extracts and returns its
// gaussianFilter. Returns nil if Apple's API has changed and the filter cannot be extracted.
- (NSObject*)extractGaussianFilter {
NSObject* gaussianFilter = nil;

+ (void)prepareOnce:(UIVisualEffectView*)visualEffectView {
if (_preparedOnce) {
return;
}
for (NSUInteger i = 0; i < visualEffectView.subviews.count; i++) {
UIView* view = visualEffectView.subviews[i];
for (UIView* view in self.blurEffectView.subviews) {
if ([view isKindOfClass:NSClassFromString(@"_UIVisualEffectBackdropView")]) {
_indexOfBackdropView = i;
for (NSObject* filter in view.layer.filters) {
if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"] &&
[[filter valueForKey:@"inputRadius"] isKindOfClass:[NSNumber class]]) {
_gaussianBlurFilter = [filter retain];
for (CIFilter* filter in view.layer.filters) {
if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) {
if ([[filter valueForKey:@"inputRadius"] isKindOfClass:[NSNumber class]]) {
gaussianFilter = filter;
}
// No need to look at other CIFilters. If the API structure has not changed, the
// gaussianBlur filter was succesfully saved. Otherwise, still exit the loop because the
// filter cannot be extracted.
break;
}
}
} else if ([view isKindOfClass:NSClassFromString(@"_UIVisualEffectSubview")]) {
_indexOfVisualEffectSubview = i;
// No need to look at other UIViews. If the API structure has not changed, the gaussianBlur
// filter was succesfully saved. Otherwise, still exit the loop because the filter cannot
// be extracted.
break;
}
}
_preparedOnce = YES;
}

+ (BOOL)isUIVisualEffectViewImplementationValid {
return _indexOfBackdropView > -1 && _indexOfVisualEffectSubview > -1 && _gaussianBlurFilter;
return gaussianFilter;
}

- (void)dealloc {
[_backdropFilterView release];
_backdropFilterView = nil;

[super dealloc];
}

@end
- (BOOL)applyBlurBackdropFilters:(NSArray*)blurRadii {
// The outer if-statement checks for the first time this method is called and _gaussianFilter is
// not initialized. The inner if-statement checks if extracting the gaussianBlur was successful.
// If it was not successful, this method will not be called again. Thus the if-statements check
// for different conditions.
if (!_gaussianFilter) {
_gaussianFilter = [self extractGaussianFilter];

@interface ChildClippingView ()

@property(retain, nonatomic) NSMutableArray<PlatformViewFilter*>* filters;

@end

@implementation ChildClippingView

// The ChildClippingView's frame is the bounding rect of the platform view. we only want touches to
// be hit tested and consumed by this view if they are inside the embedded platform view which could
// be smaller the embedded platform view is rotated.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
for (UIView* view in self.subviews) {
if ([view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
return YES;
if (!_gaussianFilter) {
FML_DLOG(ERROR) << "Apple's API for UIVisualEffectView changed. Update the implementation to "
"access the gaussianBlur CAFilter.";
return NO;
}
}
return NO;
}

- (void)applyBlurBackdropFilters:(NSMutableArray<PlatformViewFilter*>*)filters {
BOOL needUpdateFilterViews = NO;
if (self.filters.count != filters.count) {
needUpdateFilterViews = YES;
BOOL newRadiusValues = NO;

if ([blurRadii count] != [self.layer.filters count]) {
newRadiusValues = YES;
} else {
for (NSUInteger i = 0; i < filters.count; i++) {
if (!CGRectEqualToRect(self.filters[i].frame, filters[i].frame) ||
!flutter::BlurRadiusEqualToBlurRadius(self.filters[i].blurRadius,
filters[i].blurRadius)) {
needUpdateFilterViews = YES;
for (NSUInteger i = 0; i < [blurRadii count]; i++) {
if ([self.layer.filters[i] valueForKey:@"inputRadius"] != blurRadii[i]) {
newRadiusValues = YES;
break;
}
}
}
if (needUpdateFilterViews) {
// Clear the old filter views.
for (PlatformViewFilter* filter in self.filters) {
[[filter backdropFilterView] removeFromSuperview];
}
// Update to the new filters.
self.filters = [filters retain];
// Add new filter views.
for (PlatformViewFilter* filter in self.filters) {
UIView* backdropFilterView = [filter backdropFilterView];
[self addSubview:backdropFilterView];

if (newRadiusValues) {
NSMutableArray* newGaussianFilters = [[[NSMutableArray alloc] init] autorelease];

for (NSUInteger i = 0; i < [blurRadii count]; i++) {
NSObject* newGaussianFilter = [[_gaussianFilter copy] autorelease];
[newGaussianFilter setValue:blurRadii[i] forKey:@"inputRadius"];
[newGaussianFilters addObject:newGaussianFilter];
}

self.layer.filters = newGaussianFilters;
}

return YES;
}

- (void)dealloc {
[_filters release];
_filters = nil;
[_blurEffectView release];
_blurEffectView = nil;

[_gaussianFilter release];
_gaussianFilter = nil;
[super dealloc];
}

- (NSMutableArray*)filters {
if (!_filters) {
_filters = [[[NSMutableArray alloc] init] retain];
}
return _filters;
}

@end

@interface FlutterClippingMaskView ()

- (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix;
- (CGRect)getCGRectFromSkRect:(const SkRect&)clipSkRect;

@end

@@ -253,7 +212,7 @@ - (void)drawRect:(CGRect)rect {
}

- (void)clipRect:(const SkRect&)clipSkRect matrix:(const CATransform3D&)matrix {
CGRect clipRect = flutter::GetCGRectFromSkRect(clipSkRect);
CGRect clipRect = [self getCGRectFromSkRect:clipSkRect];
CGPathRef path = CGPathCreateWithRect(clipRect, nil);
paths_.push_back([self getTransformedPath:path matrix:matrix]);
}
@@ -270,7 +229,7 @@ - (void)clipRRect:(const SkRRect&)clipSkRRect matrix:(const CATransform3D&)matri
}
case SkRRect::kOval_Type:
case SkRRect::kSimple_Type: {
CGRect clipRect = flutter::GetCGRectFromSkRect(clipSkRRect.rect());
CGRect clipRect = [self getCGRectFromSkRect:clipSkRRect.rect()];
pathRef = CGPathCreateWithRoundedRect(clipRect, clipSkRRect.getSimpleRadii().x(),
clipSkRRect.getSimpleRadii().y(), nil);
break;
@@ -337,7 +296,7 @@ - (void)clipPath:(const SkPath&)path matrix:(const CATransform3D&)matrix {
SkPath::Iter iter(path, true);
SkPoint pts[kMaxPointsInVerb];
SkPath::Verb verb = iter.next(pts);
SkPoint last_pt_from_last_verb = SkPoint::Make(0, 0);
SkPoint last_pt_from_last_verb;
while (verb != SkPath::kDone_Verb) {
if (verb == SkPath::kLine_Verb || verb == SkPath::kQuad_Verb || verb == SkPath::kConic_Verb ||
verb == SkPath::kCubic_Verb) {
@@ -394,4 +353,9 @@ - (void)clipPath:(const SkPath&)path matrix:(const CATransform3D&)matrix {
return fml::CFRef<CGPathRef>(transformedPath);
}

- (CGRect)getCGRectFromSkRect:(const SkRect&)clipSkRect {
return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft,
clipSkRect.fBottom - clipSkRect.fTop);
}

@end
3 changes: 1 addition & 2 deletions shell/platform/darwin/ios/ios_external_view_embedder.h
Original file line number Diff line number Diff line change
@@ -69,8 +69,7 @@ class IOSExternalViewEmbedder : public ExternalViewEmbedder {

// |ExternalViewEmbedder|
void PushFilterToVisitedPlatformViews(
std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect) override;
std::shared_ptr<const DlImageFilter> filter) override;

// |ExternalViewEmbedder|
void PushVisitedPlatformView(int64_t view_id) override;
5 changes: 2 additions & 3 deletions shell/platform/darwin/ios/ios_external_view_embedder.mm
Original file line number Diff line number Diff line change
@@ -100,9 +100,8 @@

// |ExternalViewEmbedder|
void IOSExternalViewEmbedder::PushFilterToVisitedPlatformViews(
std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect) {
platform_views_controller_->PushFilterToVisitedPlatformViews(filter, filter_rect);
std::shared_ptr<const DlImageFilter> filter) {
platform_views_controller_->PushFilterToVisitedPlatformViews(filter);
}

// |ExternalViewEmbedder|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.