diff --git a/engine/src/flutter/flow/layers/backdrop_filter_layer.cc b/engine/src/flutter/flow/layers/backdrop_filter_layer.cc index cc3a00753c1..8b77c77d954 100644 --- a/engine/src/flutter/flow/layers/backdrop_filter_layer.cc +++ b/engine/src/flutter/flow/layers/backdrop_filter_layer.cc @@ -47,28 +47,28 @@ void BackdropFilterLayer::Preroll(PrerollContext* context, PrerollChildren(context, matrix, &child_paint_bounds); child_paint_bounds.join(context->cull_rect); set_paint_bounds(child_paint_bounds); + context->subtree_can_inherit_opacity = true; } void BackdropFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "BackdropFilterLayer::Paint"); FML_DCHECK(needs_painting(context)); + AutoCachePaint save_paint(context); + save_paint.setBlendMode(blend_mode_); if (context.leaf_nodes_builder) { - DlPaint paint; - paint.setBlendMode(blend_mode_); - context.leaf_nodes_builder->saveLayer(&paint_bounds(), &paint, - filter_.get()); + context.leaf_nodes_builder->saveLayer(&paint_bounds(), + save_paint.dl_paint(), filter_.get()); PaintChildren(context); context.leaf_nodes_builder->restore(); } else { - SkPaint paint; - paint.setBlendMode(ToSk(blend_mode_)); auto sk_filter = filter_ ? filter_->skia_object() : nullptr; Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create( context, - SkCanvas::SaveLayerRec{&paint_bounds(), &paint, sk_filter.get(), 0}, + SkCanvas::SaveLayerRec{&paint_bounds(), save_paint.sk_paint(), + sk_filter.get(), 0}, // BackdropFilter should only happen on the leaf nodes canvas. // See https:://flutter.dev/go/backdrop-filter-with-overlay-canvas AutoSaveLayer::SaveMode::kLeafNodesCanvas); diff --git a/engine/src/flutter/flow/layers/backdrop_filter_layer_unittests.cc b/engine/src/flutter/flow/layers/backdrop_filter_layer_unittests.cc index 0cb82f39c59..16cf3bc2422 100644 --- a/engine/src/flutter/flow/layers/backdrop_filter_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/backdrop_filter_layer_unittests.cc @@ -277,6 +277,52 @@ TEST_F(BackdropFilterLayerTest, Readback) { EXPECT_FALSE(preroll_context()->surface_needs_readback); } +TEST_F(BackdropFilterLayerTest, OpacityInheritance) { + auto backdrop_filter = DlBlurImageFilter(5, 5, DlTileMode::kClamp); + const SkPath mock_path = SkPath().addRect(SkRect::MakeLTRB(0, 0, 10, 10)); + const SkPaint mock_paint = SkPaint(SkColors::kRed); + const SkRect clip_rect = SkRect::MakeLTRB(0, 0, 100, 100); + + auto clip = std::make_shared(clip_rect, Clip::hardEdge); + auto parent = std::make_shared(128, SkPoint::Make(0, 0)); + auto layer = std::make_shared(backdrop_filter.shared(), + DlBlendMode::kSrcOver); + auto child = std::make_shared(mock_path, mock_paint); + layer->Add(child); + parent->Add(layer); + clip->Add(parent); + + clip->Preroll(preroll_context(), SkMatrix::I()); + + clip->Paint(display_list_paint_context()); + + DisplayListBuilder expected_builder(clip_rect); + /* ClipRectLayer::Paint */ { + expected_builder.save(); + { + expected_builder.clipRect(clip_rect, SkClipOp::kIntersect, false); + /* OpacityLayer::Paint */ { + // NOP - it hands opacity down to BackdropFilterLayer + /* BackdropFilterLayer::Paint */ { + DlPaint save_paint; + save_paint.setAlpha(128); + expected_builder.saveLayer(&clip_rect, &save_paint, &backdrop_filter); + { + /* MockLayer::Paint */ { + DlPaint child_paint; + child_paint.setColor(DlColor::kRed()); + expected_builder.drawPath(mock_path, child_paint); + } + } + expected_builder.restore(); + } + } + } + expected_builder.restore(); + } + EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build())); +} + using BackdropLayerDiffTest = DiffContextTest; TEST_F(BackdropLayerDiffTest, BackdropLayer) { diff --git a/engine/src/flutter/flow/layers/clip_shape_layer.h b/engine/src/flutter/flow/layers/clip_shape_layer.h index d8729e8e83c..faf4c26d31b 100644 --- a/engine/src/flutter/flow/layers/clip_shape_layer.h +++ b/engine/src/flutter/flow/layers/clip_shape_layer.h @@ -89,13 +89,13 @@ class ClipShapeLayer : public CacheableContainerLayer { AutoCachePaint cache_paint(context); if (context.raster_cache) { - if (layer_raster_cache_item_->Draw(context, cache_paint.paint())) { + if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { return; } } Layer::AutoSaveLayer save_layer = Layer::AutoSaveLayer::Create( - context, paint_bounds(), cache_paint.paint()); + context, paint_bounds(), cache_paint.sk_paint()); PaintChildren(context); } diff --git a/engine/src/flutter/flow/layers/color_filter_layer.cc b/engine/src/flutter/flow/layers/color_filter_layer.cc index 3c1f50157cc..c82ae04c0cd 100644 --- a/engine/src/flutter/flow/layers/color_filter_layer.cc +++ b/engine/src/flutter/flow/layers/color_filter_layer.cc @@ -51,7 +51,7 @@ void ColorFilterLayer::Paint(PaintContext& context) const { if (layer_raster_cache_item_->IsCacheChildren()) { cache_paint.setColorFilter(filter_); } - if (layer_raster_cache_item_->Draw(context, cache_paint.paint())) { + if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { return; } } @@ -59,7 +59,7 @@ void ColorFilterLayer::Paint(PaintContext& context) const { cache_paint.setColorFilter(filter_); Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create( - context, paint_bounds(), cache_paint.paint()); + context, paint_bounds(), cache_paint.sk_paint()); PaintChildren(context); } diff --git a/engine/src/flutter/flow/layers/display_list_layer.cc b/engine/src/flutter/flow/layers/display_list_layer.cc index 5fb06d57e4e..5039feae31c 100644 --- a/engine/src/flutter/flow/layers/display_list_layer.cc +++ b/engine/src/flutter/flow/layers/display_list_layer.cc @@ -114,7 +114,8 @@ void DisplayListLayer::Paint(PaintContext& context) const { if (context.raster_cache && display_list_raster_cache_item_) { AutoCachePaint cache_paint(context); - if (display_list_raster_cache_item_->Draw(context, cache_paint.paint())) { + if (display_list_raster_cache_item_->Draw(context, + cache_paint.sk_paint())) { TRACE_EVENT_INSTANT0("flutter", "raster cache hit"); return; } @@ -151,9 +152,9 @@ void DisplayListLayer::Paint(PaintContext& context) const { if (context.leaf_nodes_builder) { AutoCachePaint save_paint(context); int restore_count = context.leaf_nodes_builder->getSaveCount(); - if (save_paint.paint() != nullptr) { - DlPaint paint = DlPaint().setAlpha(save_paint.paint()->getAlpha()); - context.leaf_nodes_builder->saveLayer(&paint_bounds(), &paint); + if (save_paint.dl_paint() != nullptr) { + context.leaf_nodes_builder->saveLayer(&paint_bounds(), + save_paint.dl_paint()); } context.leaf_nodes_builder->drawDisplayList(display_list_.skia_object()); context.leaf_nodes_builder->restoreToCount(restore_count); diff --git a/engine/src/flutter/flow/layers/image_filter_layer.cc b/engine/src/flutter/flow/layers/image_filter_layer.cc index e210309ac95..4793bfd8f0d 100644 --- a/engine/src/flutter/flow/layers/image_filter_layer.cc +++ b/engine/src/flutter/flow/layers/image_filter_layer.cc @@ -87,7 +87,7 @@ void ImageFilterLayer::Paint(PaintContext& context) const { if (layer_raster_cache_item_->IsCacheChildren()) { cache_paint.setImageFilter(transformed_filter_); } - if (layer_raster_cache_item_->Draw(context, cache_paint.paint())) { + if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { return; } @@ -98,7 +98,7 @@ void ImageFilterLayer::Paint(PaintContext& context) const { // so we use the bounds of the child container which do not include any // modifications that the filter might apply. Layer::AutoSaveLayer save_layer = Layer::AutoSaveLayer::Create( - context, child_paint_bounds(), cache_paint.paint()); + context, child_paint_bounds(), cache_paint.sk_paint()); PaintChildren(context); } diff --git a/engine/src/flutter/flow/layers/layer.h b/engine/src/flutter/flow/layers/layer.h index f36c13200e3..dbc08d4998e 100644 --- a/engine/src/flutter/flow/layers/layer.h +++ b/engine/src/flutter/flow/layers/layer.h @@ -209,34 +209,46 @@ class Layer { explicit AutoCachePaint(PaintContext& context) : context_(context) { needs_paint_ = context.inherited_opacity < SK_Scalar1; if (needs_paint_) { - paint_.setAlphaf(context.inherited_opacity); + sk_paint_.setAlphaf(context.inherited_opacity); + dl_paint_.setAlpha(SkScalarRoundToInt(context.inherited_opacity * 255)); context.inherited_opacity = SK_Scalar1; } } - ~AutoCachePaint() { context_.inherited_opacity = paint_.getAlphaf(); } + ~AutoCachePaint() { context_.inherited_opacity = sk_paint_.getAlphaf(); } void setImageFilter(sk_sp filter) { - paint_.setImageFilter(filter); + sk_paint_.setImageFilter(filter); + dl_paint_.setImageFilter(DlImageFilter::From(filter)); update_needs_paint(); } void setColorFilter(sk_sp filter) { - paint_.setColorFilter(filter); + sk_paint_.setColorFilter(filter); + dl_paint_.setColorFilter(DlColorFilter::From(filter)); update_needs_paint(); } - const SkPaint* paint() { return needs_paint_ ? &paint_ : nullptr; } + void setBlendMode(DlBlendMode mode) { + sk_paint_.setBlendMode(ToSk(mode)); + dl_paint_.setBlendMode(mode); + update_needs_paint(); + } + + const SkPaint* sk_paint() { return needs_paint_ ? &sk_paint_ : nullptr; } + const DlPaint* dl_paint() { return needs_paint_ ? &dl_paint_ : nullptr; } private: PaintContext& context_; - SkPaint paint_; + SkPaint sk_paint_; + DlPaint dl_paint_; bool needs_paint_; void update_needs_paint() { - needs_paint_ = paint_.getImageFilter() != nullptr || - paint_.getColorFilter() != nullptr || - paint_.getAlphaf() < SK_Scalar1; + needs_paint_ = sk_paint_.getImageFilter() != nullptr || + sk_paint_.getColorFilter() != nullptr || + !sk_paint_.isSrcOver() || + sk_paint_.getAlphaf() < SK_Scalar1; } }; diff --git a/engine/src/flutter/flow/layers/shader_mask_layer.cc b/engine/src/flutter/flow/layers/shader_mask_layer.cc index ee1dacae86c..d39d508734c 100644 --- a/engine/src/flutter/flow/layers/shader_mask_layer.cc +++ b/engine/src/flutter/flow/layers/shader_mask_layer.cc @@ -50,13 +50,13 @@ void ShaderMaskLayer::Paint(PaintContext& context) const { AutoCachePaint cache_paint(context); if (context.raster_cache) { - if (layer_raster_cache_item_->Draw(context, cache_paint.paint())) { + if (layer_raster_cache_item_->Draw(context, cache_paint.sk_paint())) { return; } } Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create( - context, paint_bounds(), cache_paint.paint()); + context, paint_bounds(), cache_paint.sk_paint()); PaintChildren(context); SkPaint paint; diff --git a/engine/src/flutter/flow/layers/texture_layer.cc b/engine/src/flutter/flow/layers/texture_layer.cc index f7878476f28..fa760fe786e 100644 --- a/engine/src/flutter/flow/layers/texture_layer.cc +++ b/engine/src/flutter/flow/layers/texture_layer.cc @@ -61,7 +61,7 @@ void TextureLayer::Paint(PaintContext& context) const { } AutoCachePaint cache_paint(context); texture->Paint(*context.leaf_nodes_canvas, paint_bounds(), freeze_, - context.gr_context, ToSk(sampling_), cache_paint.paint()); + context.gr_context, ToSk(sampling_), cache_paint.sk_paint()); } } // namespace flutter