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

Reland "PlatformView partial blur #36015" #37086

Merged
Merged
Show file tree
Hide file tree
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: 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_;
}

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 @@ -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->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