Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 8 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: 4 additions & 2 deletions flow/embedded_views.cc
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,10 @@ void MutatorsStack::PushOpacity(const int& alpha) {
};

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

Expand Down
56 changes: 45 additions & 11 deletions flow/embedded_views.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,33 @@ 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_;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comparing pointer equality?

@cyanglaz cyanglaz Oct 21, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is comparing the data. I added a test for it in my latest commit

}

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.
Expand Down Expand Up @@ -59,7 +86,7 @@ class Mutator {
alpha_ = other.alpha_;
break;
case kBackdropFilter:
filter_ = other.filter_;
filter_mutation_ = other.filter_mutation_;
break;
default:
break;
Expand All @@ -73,15 +100,20 @@ 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)
: type_(kBackdropFilter), filter_(filter) {}
explicit Mutator(std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect)
: type_(kBackdropFilter),
filter_mutation_(
std::make_shared<ImageFilterMutation>(filter, filter_rect)) {}

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 DlImageFilter& GetFilter() const { return *filter_; }
const ImageFilterMutation& GetFilterMutation() const {
return *filter_mutation_;
}
const int& GetAlpha() const { return alpha_; }
float GetAlphaFloat() const { return (alpha_ / 255.0); }

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

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

std::shared_ptr<const DlImageFilter> filter_;

std::shared_ptr<ImageFilterMutation> filter_mutation_;
}; // Mutator

// A stack of mutators that can be applied to an embedded platform view.
Expand All @@ -154,7 +185,8 @@ 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);
void PushBackdropFilter(const std::shared_ptr<const DlImageFilter>& filter,
const SkRect& filter_rect);

// Removes the `Mutator` on the top of the stack
// and destroys it.
Expand Down Expand Up @@ -249,8 +281,9 @@ 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) {
mutators_stack_.PushBackdropFilter(filter);
void PushImageFilter(std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect) {
mutators_stack_.PushBackdropFilter(filter, filter_rect);
}

// Whether the embedder should construct DisplayList objects to hold the
Expand Down Expand Up @@ -454,7 +487,8 @@ 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) {}
std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect) {}

private:
bool used_this_frame_ = false;
Expand Down
3 changes: 2 additions & 1 deletion flow/layers/backdrop_filter_layer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ 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->view_embedder->PushFilterToVisitedPlatformViews(
filter_, context->cull_rect);
}
SkRect child_paint_bounds = SkRect::MakeEmpty();
PrerollChildren(context, matrix, &child_paint_bounds);
Expand Down
30 changes: 18 additions & 12 deletions flow/mutators_stack_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,19 @@ 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);
stack.PushBackdropFilter(filter, SkRect::MakeXYWH(i, i, i, i));
}

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

MutatorsStack stack_other;
SkMatrix matrix_other = SkMatrix::Scale(1, 1);
Expand All @@ -179,7 +184,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);
stack_other.PushBackdropFilter(other_filter, SkRect::MakeEmpty());

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

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

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

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

auto filter = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
Mutator mutator6 = Mutator(filter);
Mutator other_mutator6 = Mutator(filter);
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());
ASSERT_TRUE(mutator6 == other_mutator6);
}

Expand All @@ -299,8 +305,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);
Mutator other_mutator3 = Mutator(filter2);
Mutator mutator3 = Mutator(filter, SkRect::MakeEmpty());
Mutator other_mutator3 = Mutator(filter2, SkRect::MakeEmpty());
ASSERT_TRUE(mutator3 != other_mutator3);
}

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

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

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

// |ExternalViewEmbedder|
void SubmitFrame(GrDirectContext* context,
Expand Down
2 changes: 1 addition & 1 deletion shell/common/shell_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,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->GetFilter(), filter);
ASSERT_EQ(mutator->GetFilterMutation().GetFilter(), filter);

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

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

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

auto iter = mutators_stack.Begin();
while (iter != mutators_stack.End()) {
Expand All @@ -448,13 +449,35 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
embedded_view.alpha = (*iter)->GetAlphaFloat() * embedded_view.alpha;
break;
case kBackdropFilter: {
// 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())];
// 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];
}
break;
}
Expand All @@ -463,15 +486,16 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
}

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

// Reverse the offset of the clipView.
// The clipView's frame includes the final translate of the final transform matrix.
// So we need to revese this translate so the platform view can layout at the correct offset.
// Thus, this translate needs to be reversed so the platform view can layout at the correct
// offset.
//
// 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.
// 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.
CATransform3D reverseTranslate =
CATransform3DMakeTranslation(-clipView.frame.origin.x, -clipView.frame.origin.y, 0);
embedded_view.layer.transform = CATransform3DConcat(finalTransform, reverseTranslate);
Expand Down
Loading