Track output bounds of filters in EffectPaintPropertyNode
This basically replaces crrev.com/c/6354923, as a more complete fix to
the bug.
crrev.com/c/6354923 worked in case that the reference box contains all
inputs. That's true for HTML elements because the reference box is the
bounding box of visual rects of all descendants, but that's not always
true for SVG elements.
Now track the output bounds (calculated from the bounding box of
visual rects of all descendants) and use it in EffectPaintPropertyNode
::MapRect as the result if there are any reference filters.
Bug: 373759987
Change-Id: I5f8ddc7d61e3320b4416fa3c5921c24d29e8d649
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6374076
Reviewed-by: Fredrik Söderquist <fs@opera.com>
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1436900}
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.cc b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
index ed1d50d..7119ab0 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
@@ -396,7 +396,7 @@
// by the layer (see `SVGContainerPainter::Paint`).
auto* properties = object.FirstFragment().PaintProperties();
if (properties && properties->Filter() &&
- properties->Filter()->Filter().HasReferenceFilter()) {
+ properties->Filter()->HasReferenceFilter()) {
context.GetPaintController().EnsureChunk();
}
}
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index be0d133..95596fd8 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -2062,13 +2062,14 @@
return false;
}
-static void UpdateFilterEffect(const LayoutObject& object,
- const EffectPaintPropertyNode* effect_node,
- CompositorFilterOperations& filter) {
+static void UpdateFilterEffect(
+ const LayoutObject& object,
+ const EffectPaintPropertyNode* effect_node,
+ EffectPaintPropertyNode::FilterInfo& filter_info) {
if (object.HasLayer()) {
// Try to use the cached filter.
- if (effect_node) {
- filter = effect_node->Filter();
+ if (effect_node && effect_node->Filter()) {
+ filter_info.operations = *effect_node->Filter();
}
PaintLayer* layer = To<LayoutBoxModelObject>(object).Layer();
#if DCHECK_IS_ON()
@@ -2077,7 +2078,9 @@
layer->UpdateFilterReferenceBox();
DCHECK_EQ(reference_box, layer->FilterReferenceBox());
#endif
- layer->UpdateCompositorFilterOperationsForFilter(filter);
+ layer->UpdateCompositorFilterOperationsForFilter(filter_info.operations);
+ filter_info.output_bounds = filter_info.operations.MapRect(
+ ToEnclosingRect(layer->FilterReferenceBox()));
return;
}
if (object.IsSVGChild() && !object.IsText()) {
@@ -2087,9 +2090,12 @@
if (!object.StyleRef().HasFilter())
return;
// Try to use the cached filter.
- if (effect_node)
- filter = effect_node->Filter();
- client->UpdateFilterData(filter);
+ if (effect_node && effect_node->Filter()) {
+ filter_info.operations = *effect_node->Filter();
+ }
+ client->UpdateFilterData(filter_info.operations);
+ filter_info.output_bounds = filter_info.operations.MapRect(
+ gfx::ToEnclosingRect(object.VisualRectInLocalSVGCoordinates()));
}
}
@@ -2099,8 +2105,13 @@
if (NeedsFilter(object_, full_context_)) {
EffectPaintPropertyNode::State state;
state.local_transform_space = context_.current.transform;
-
- UpdateFilterEffect(object_, properties_->Filter(), state.filter);
+ EffectPaintPropertyNode::FilterInfo filter_info;
+ UpdateFilterEffect(object_, properties_->Filter(), filter_info);
+ if (!filter_info.operations.IsEmpty()) {
+ state.filter_info =
+ std::make_unique<EffectPaintPropertyNode::FilterInfo>(
+ std::move(filter_info));
+ }
// The CSS filter spec didn't specify how filters interact with overflow
// clips. The implementation here mimics the old Blink/WebKit behavior for
@@ -2140,20 +2151,13 @@
state.self_or_ancestor_participates_in_view_transition =
context_.self_or_ancestor_participates_in_view_transition;
- // This must be computed before std::move(state) below.
- bool needs_pixel_moving_filter_clip_expander =
- (state.direct_compositing_reasons &
- (CompositingReason::kWillChangeFilter |
- CompositingReason::kActiveFilterAnimation)) ||
- state.filter.HasFilterThatMovesPixels();
-
EffectPaintPropertyNode::AnimationState animation_state;
animation_state.is_running_filter_animation_on_compositor =
object_.StyleRef().IsRunningFilterAnimationOnCompositor();
OnUpdateEffect(properties_->UpdateFilter(
*context_.current_effect, std::move(state), animation_state));
- if (needs_pixel_moving_filter_clip_expander) {
+ if (properties_->Filter()->NeedsPixelMovingFilterClipExpander()) {
OnUpdateClip(properties_->UpdatePixelMovingFilterClipExpander(
*context_.current.clip,
ClipPaintPropertyNode::State(*context_.current.transform,
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index 6d27f0a..a11e1cd9 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -7159,7 +7159,7 @@
auto* properties = PaintPropertiesForElement("target");
ASSERT_TRUE(properties);
ASSERT_TRUE(properties->Filter());
- EXPECT_TRUE(properties->Filter()->Filter().IsEmpty());
+ EXPECT_FALSE(properties->Filter()->Filter());
EXPECT_TRUE(properties->Filter()->RequiresCompositingForWillChangeFilter());
// will-change:filter should not cause transform or effect node.
@@ -7176,7 +7176,7 @@
auto* properties = PaintPropertiesForElement("target");
ASSERT_TRUE(properties);
ASSERT_TRUE(properties->Filter());
- EXPECT_TRUE(properties->Filter()->Filter().IsEmpty());
+ EXPECT_FALSE(properties->Filter()->Filter());
EXPECT_TRUE(properties->Filter()->RequiresCompositingForWillChangeFilter());
// will-change:filter should not add compositing reason for the transform or
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
index 05f60f8b..e5a0c06 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
@@ -1770,8 +1770,11 @@
const auto* properties = PaintPropertiesForElement("span");
ASSERT_TRUE(properties);
ASSERT_TRUE(properties->Filter());
+ ASSERT_TRUE(properties->Filter()->Filter());
EXPECT_EQ(gfx::PointF(0, 20),
- properties->Filter()->Filter().ReferenceBox().origin());
+ properties->Filter()->Filter()->ReferenceBox().origin());
+ EXPECT_EQ(gfx::Point(-3, 17),
+ properties->Filter()->FilterOutputBounds().origin());
GetDocument()
.getElementById(AtomicString("spacer"))
@@ -1780,7 +1783,9 @@
UpdateAllLifecyclePhasesForTest();
ASSERT_EQ(properties, PaintPropertiesForElement("span"));
EXPECT_EQ(gfx::PointF(0, 100),
- properties->Filter()->Filter().ReferenceBox().origin());
+ properties->Filter()->Filter()->ReferenceBox().origin());
+ EXPECT_EQ(gfx::Point(-3, 97),
+ properties->Filter()->FilterOutputBounds().origin());
}
TEST_P(PaintPropertyTreeUpdateTest, StartSVGAnimation) {
diff --git a/third_party/blink/renderer/core/paint/svg_container_painter.cc b/third_party/blink/renderer/core/paint/svg_container_painter.cc
index 08db50028..bce8e737 100644
--- a/third_party/blink/renderer/core/paint/svg_container_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_container_painter.cc
@@ -25,8 +25,7 @@
namespace {
bool HasReferenceFilterEffect(const ObjectPaintProperties& properties) {
- return properties.Filter() &&
- properties.Filter()->Filter().HasReferenceFilter();
+ return properties.Filter() && properties.Filter()->HasReferenceFilter();
}
} // namespace
diff --git a/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc b/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc
index b062519..7601e1d 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc
@@ -37,9 +37,9 @@
layer_clip_ =
CreateClip(c0(), *layer_transform_, FloatRoundedRect(12, 34, 56, 78));
layer_effect_ = EffectPaintPropertyNode::Create(
- e0(), EffectPaintPropertyNode::State{
- layer_transform_, layer_clip_, CompositorFilterOperations(),
- nullptr, 0.789f, SkBlendMode::kSrcIn});
+ e0(),
+ EffectPaintPropertyNode::State{layer_transform_, layer_clip_, nullptr,
+ nullptr, 0.789f, SkBlendMode::kSrcIn});
}
return PropertyTreeState(*layer_transform_, *layer_clip_, *layer_effect_);
}
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
index 400e84e..f6f5c28 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
@@ -673,7 +673,7 @@
return action;
}
- bool has_filter = !effect.Filter().IsEmpty();
+ bool has_filter = !!effect.Filter();
bool has_opacity = effect.Opacity() != 1.f;
// TODO(crbug.com/1334293): Normally backdrop filters should be composited and
// effect.BackdropFilter() should be null, but compositing can be disabled in
@@ -702,7 +702,7 @@
// bounds, which we never generate.
cc::PaintFlags filter_flags;
filter_flags.setImageFilter(cc::RenderSurfaceFilters::BuildImageFilter(
- effect.Filter().AsCcFilterOperations()));
+ effect.Filter()->AsCcFilterOperations()));
save_layer_id = push<cc::SaveLayerOp>(filter_flags);
}
result_.EndPaintOfPairedBegin();
@@ -718,27 +718,17 @@
current_clip_ = input_clip;
current_effect_ = &effect;
- if (effect.Filter().HasReferenceFilter()) {
- // Map the input rect through the filter to determine the bounds of the
- // effect on an empty source. For empty chunks, or chunks with empty bounds,
- // with a filter applied that produces output even when there's no input
- // this will expand the bounds to match.
- gfx::RectF input_rect;
- if (RuntimeEnabledFeatures::ReferenceFilterMapsReferenceBoxEnabled()) {
- // Use the reference box as the input rect.
- input_rect = effect.Filter().ReferenceBox();
- } else {
- // Use a random point as the input rect.
- input_rect = gfx::RectF(effect.Filter().ReferenceBox().CenterPoint(),
- gfx::SizeF());
- }
- gfx::RectF filtered_bounds = current_effect_->MapRect(input_rect);
- effect_bounds_stack_.back().bounds = filtered_bounds;
+ if (effect.HasReferenceFilter()) {
+ // For empty chunks, or chunks with empty bounds, with a filter applied
+ // that produces output even when there's no input this will expand the
+ // bounds to match.
+ gfx::Rect filtered_bounds = effect.FilterOutputBounds();
+ effect_bounds_stack_.back().bounds = gfx::RectF(filtered_bounds);
// Emit an empty paint operation to add the filtered bounds (mapped to layer
// space) to the visual rect of the filter's SaveLayerOp.
result_.StartPaint();
- result_.EndPaintOfUnpaired(chunk_to_layer_mapper_.MapVisualRect(
- gfx::ToEnclosingRect(filtered_bounds)));
+ result_.EndPaintOfUnpaired(
+ chunk_to_layer_mapper_.MapVisualRect(filtered_bounds));
}
return {};
}
@@ -771,18 +761,18 @@
DCHECK(effect_bounds_stack_.size());
const auto& bounds_info = effect_bounds_stack_.back();
gfx::RectF bounds = bounds_info.bounds;
- if (current_effect_->Filter().IsEmpty()) {
+ if (!current_effect_->Filter()) {
if (!bounds.IsEmpty()) {
result_.UpdateSaveLayerBounds(bounds_info.save_layer_id,
gfx::RectFToSkRect(bounds));
}
} else {
- // We need an empty bounds for empty filter to avoid performance issue of
- // PDF renderer. See crbug.com/740824.
+ // Don't check bounds.IsEmpty() because we need an empty bounds for empty
+ // filter to avoid performance issue of PDF renderer. See crbug.com/740824.
result_.UpdateSaveLayerBounds(bounds_info.save_layer_id,
gfx::RectFToSkRect(bounds));
// We need to propagate the filtered bounds to the parent.
- bounds = current_effect_->MapRect(bounds);
+ bounds = gfx::RectF(current_effect_->MapRect(gfx::ToEnclosingRect(bounds)));
}
effect_bounds_stack_.pop_back();
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index 0869065..9828719 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -1263,8 +1263,7 @@
static cc::RenderSurfaceReason RenderSurfaceReasonForEffect(
const EffectPaintPropertyNode& effect) {
- if (!effect.Filter().IsEmpty() ||
- effect.RequiresCompositingForWillChangeFilter()) {
+ if (effect.Filter() || effect.RequiresCompositingForWillChangeFilter()) {
return cc::RenderSurfaceReason::kFilter;
}
if (effect.HasActiveFilterAnimation())
@@ -1313,15 +1312,15 @@
if (effect.MayHaveBackdropEffect()) {
effect_node.may_have_backdrop_effect = true;
// We never have backdrop effect and filter on the same effect node.
- DCHECK(effect.Filter().IsEmpty());
+ DCHECK(!effect.Filter());
if (auto* backdrop_filter = effect.BackdropFilter()) {
effect_node.backdrop_filters = backdrop_filter->AsCcFilterOperations();
effect_node.backdrop_filter_bounds = effect.BackdropFilterBounds();
effect_node.backdrop_mask_element_id = effect.BackdropMaskElementId();
}
effect_node.blend_mode = effect.BlendMode();
- } else {
- effect_node.filters = effect.Filter().AsCcFilterOperations();
+ } else if (auto* filter = effect.Filter()) {
+ effect_node.filters = filter->AsCcFilterOperations();
}
effect_node.double_sided = !transform.IsBackfaceHidden();
effect_node.effect_changed = effect.NodeChangeAffectsRaster();
diff --git a/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc b/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc
index 550f2ba..3820e6a 100644
--- a/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc
+++ b/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc
@@ -7,7 +7,6 @@
#include "third_party/blink/renderer/platform/graphics/color.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/rect_conversions.h"
namespace blink {
@@ -108,10 +107,9 @@
return filter_operations_.IsEmpty();
}
-gfx::RectF CompositorFilterOperations::MapRect(
- const gfx::RectF& input_rect) const {
- return gfx::RectF(
- filter_operations_.MapRect(gfx::ToEnclosingRect(input_rect)));
+gfx::Rect CompositorFilterOperations::MapRect(
+ const gfx::Rect& input_rect) const {
+ return filter_operations_.MapRect(input_rect);
}
bool CompositorFilterOperations::HasFilterThatMovesPixels() const {
diff --git a/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h b/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h
index f58dbdd..1407ba3 100644
--- a/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h
+++ b/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h
@@ -48,8 +48,8 @@
size_t size() const { return filter_operations_.size(); }
// Returns a rect covering the destination pixels that can be affected by
- // source pixels in |inputRect|.
- gfx::RectF MapRect(const gfx::RectF& input_rect) const;
+ // source pixels in `input_rect`.
+ gfx::Rect MapRect(const gfx::Rect& input_rect) const;
bool HasFilterThatMovesPixels() const;
bool HasReferenceFilter() const;
diff --git a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
index 0ce6c3e..d2d2778 100644
--- a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
@@ -13,6 +13,24 @@
namespace {
+PaintPropertyChangeType ComputeFilterChange(
+ const EffectPaintPropertyNode::FilterInfo* a,
+ const EffectPaintPropertyNode::FilterInfo* b,
+ bool is_running_filter_animation_on_compositor) {
+ if (!a && !b) {
+ return PaintPropertyChangeType::kUnchanged;
+ }
+ if (!a || !b || a->output_bounds != b->output_bounds) {
+ return PaintPropertyChangeType::kChangedOnlyValues;
+ }
+ if (a->operations != b->operations) {
+ return is_running_filter_animation_on_compositor
+ ? PaintPropertyChangeType::kChangedOnlyCompositedValues
+ : PaintPropertyChangeType::kChangedOnlyValues;
+ }
+ return PaintPropertyChangeType::kUnchanged;
+}
+
PaintPropertyChangeType ComputeBackdropFilterChange(
const EffectPaintPropertyNode::BackdropFilterInfo* a,
const EffectPaintPropertyNode::BackdropFilterInfo* b,
@@ -51,17 +69,21 @@
DCHECK(!animation_state.is_running_opacity_animation_on_compositor);
return PaintPropertyChangeType::kChangedOnlyValues;
}
- bool filter_changed = filter != other.filter;
- if (filter_changed &&
- !animation_state.is_running_filter_animation_on_compositor) {
+
+ auto filter_changed = ComputeFilterChange(
+ filter_info.get(), other.filter_info.get(),
+ animation_state.is_running_filter_animation_on_compositor);
+ if (filter_changed == PaintPropertyChangeType::kChangedOnlyValues) {
return PaintPropertyChangeType::kChangedOnlyValues;
}
+
auto backdrop_filter_changed = ComputeBackdropFilterChange(
backdrop_filter_info.get(), other.backdrop_filter_info.get(),
animation_state.is_running_backdrop_filter_animation_on_compositor);
if (backdrop_filter_changed == PaintPropertyChangeType::kChangedOnlyValues) {
return PaintPropertyChangeType::kChangedOnlyValues;
}
+
bool non_reraster_values_changed =
direct_compositing_reasons != other.direct_compositing_reasons ||
compositor_element_id != other.compositor_element_id;
@@ -80,7 +102,8 @@
if (simple_values_changed)
return PaintPropertyChangeType::kChangedOnlySimpleValues;
- if (opacity_changed || filter_changed ||
+ if (opacity_changed ||
+ filter_changed != PaintPropertyChangeType::kUnchanged ||
backdrop_filter_changed != PaintPropertyChangeType::kUnchanged) {
return PaintPropertyChangeType::kChangedOnlyCompositedValues;
}
@@ -197,17 +220,15 @@
return change;
}
-gfx::RectF EffectPaintPropertyNode::MapRect(
- const gfx::RectF& input_rect) const {
- if (state_.filter.IsEmpty()) {
+gfx::Rect EffectPaintPropertyNode::MapRect(const gfx::Rect& input_rect) const {
+ if (!state_.filter_info) {
return input_rect;
}
- gfx::RectF rect = input_rect;
- if (RuntimeEnabledFeatures::ReferenceFilterMapsReferenceBoxEnabled() &&
- state_.filter.HasReferenceFilter()) {
- rect.UnionEvenIfEmpty(state_.filter.ReferenceBox());
+ if (RuntimeEnabledFeatures::ReferenceFilterOutputBoundsEnabled() &&
+ state_.filter_info->operations.HasReferenceFilter()) {
+ return state_.filter_info->output_bounds;
}
- return state_.filter.MapRect(rect);
+ return state_.filter_info->operations.MapRect(input_rect);
}
std::unique_ptr<JSONObject> EffectPaintPropertyNode::ToJSON() const {
@@ -215,8 +236,9 @@
json->SetString("localTransformSpace",
String::Format("%p", state_.local_transform_space.Get()));
json->SetString("outputClip", String::Format("%p", state_.output_clip.Get()));
- if (!state_.filter.IsEmpty())
- json->SetString("filter", state_.filter.ToString());
+ if (state_.filter_info) {
+ json->SetString("filter", state_.filter_info->operations.ToString());
+ }
if (auto* backdrop_filter = BackdropFilter())
json->SetString("backdrop_filter", backdrop_filter->ToString());
if (state_.opacity != 1.0f)
diff --git a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
index 94c8df8..a7acf74 100644
--- a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
@@ -83,6 +83,13 @@
STACK_ALLOCATED();
};
+ struct FilterInfo {
+ CompositorFilterOperations operations;
+ gfx::Rect output_bounds;
+
+ USING_FAST_MALLOC(FilterInfo);
+ };
+
struct BackdropFilterInfo {
CompositorFilterOperations operations;
gfx::RRectF bounds;
@@ -111,7 +118,7 @@
// Optionally a number of effects can be applied to the composited output.
// The chain of effects will be applied in the following order:
// === Begin of effects ===
- CompositorFilterOperations filter;
+ std::unique_ptr<FilterInfo> filter_info;
std::unique_ptr<BackdropFilterInfo> backdrop_filter_info;
float opacity = 1;
SkBlendMode blend_mode = SkBlendMode::kSrcOver;
@@ -204,7 +211,13 @@
SkBlendMode BlendMode() const { return state_.blend_mode; }
float Opacity() const { return state_.opacity; }
- const CompositorFilterOperations& Filter() const { return state_.filter; }
+ const CompositorFilterOperations* Filter() const {
+ return state_.filter_info ? &state_.filter_info->operations : nullptr;
+ }
+ const gfx::Rect& FilterOutputBounds() const {
+ CHECK(state_.filter_info);
+ return state_.filter_info->output_bounds;
+ }
const CompositorFilterOperations* BackdropFilter() const {
if (!state_.backdrop_filter_info) {
@@ -224,23 +237,28 @@
return state_.backdrop_filter_info->mask_element_id;
}
+ bool HasReferenceFilter() const {
+ return state_.filter_info &&
+ state_.filter_info->operations.HasReferenceFilter();
+ }
bool HasFilterThatMovesPixels() const {
- return state_.filter.HasFilterThatMovesPixels();
+ return state_.filter_info &&
+ state_.filter_info->operations.HasFilterThatMovesPixels();
}
bool HasRealEffects() const {
return Opacity() != 1.0f || BlendMode() != SkBlendMode::kSrcOver ||
- !Filter().IsEmpty() || BackdropFilter();
+ Filter() || BackdropFilter();
}
bool IsOpacityOnly() const {
- return BlendMode() == SkBlendMode::kSrcOver && Filter().IsEmpty() &&
+ return BlendMode() == SkBlendMode::kSrcOver && !Filter() &&
!BackdropFilter();
}
// Returns a rect covering the pixels that can be affected by pixels in
// `input_rect`. The rects are in the space of `LocalTransformSpace`.
- gfx::RectF MapRect(const gfx::RectF& input_rect) const;
+ gfx::Rect MapRect(const gfx::Rect& input_rect) const;
bool HasDirectCompositingReasons() const {
return state_.direct_compositing_reasons != CompositingReason::kNone;
@@ -290,7 +308,7 @@
// True if the filter is not empty, or could become non-empty without a
// compositing update via a compositor animation or direct update.
bool MayHaveFilter() const {
- return !Filter().IsEmpty() || HasActiveFilterAnimation() ||
+ return Filter() || HasActiveFilterAnimation() ||
RequiresCompositingForWillChangeFilter();
}
// True if the backdrop filter is not empty, or could become non-empty
@@ -300,6 +318,12 @@
RequiresCompositingForWillChangeBackdropFilter();
}
+ bool NeedsPixelMovingFilterClipExpander() const {
+ return HasActiveFilterAnimation() ||
+ RequiresCompositingForWillChangeFilter() ||
+ HasFilterThatMovesPixels();
+ }
+
// Whether the effect node uses the backdrop as an input. This includes
// exotic blending modes and backdrop filters.
bool MayHaveBackdropEffect() const {
diff --git a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc
index bcada61d..5a1f1a2 100644
--- a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc
@@ -9,6 +9,7 @@
#include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "ui/gfx/geometry/rect_conversions.h"
namespace blink {
@@ -325,8 +326,10 @@
rect_to_map = FloatClipRect(gfx::RectF());
return false;
}
- if (!rect_to_map.IsInfinite())
- rect_to_map.Rect() = filter->MapRect(rect_to_map.Rect());
+ if (!rect_to_map.IsInfinite()) {
+ rect_to_map.Rect() = gfx::RectF(
+ filter->MapRect(gfx::ToEnclosingRect(rect_to_map.Rect())));
+ }
}
last_state = new_state;
diff --git a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc
index b05d97c..34b49ee 100644
--- a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc
@@ -930,7 +930,7 @@
output.Intersect(clip_below_effect->LayoutClipRect().Rect());
EXPECT_EQ(gfx::RectF(20, 30, 90, 80), output);
// 3. effect (the outset is 3 times of blur amount).
- output = filters.MapRect(output);
+ output = gfx::RectF(filters.MapRect(gfx::ToEnclosingRect(output)));
EXPECT_EQ(gfx::RectF(-40, -30, 210, 200), output);
// 4. clip_above_effect
output.Intersect(clip_above_effect->LayoutClipRect().Rect());
@@ -981,7 +981,7 @@
output.Intersect(clip_below_effect->LayoutClipRect().Rect());
EXPECT_EQ(gfx::RectF(20, 30, 90, 80), output);
// 3. effect (the outset is 3 times of blur amount).
- output = filters.MapRect(output);
+ output = gfx::RectF(filters.MapRect(gfx::ToEnclosingRect(output)));
EXPECT_EQ(gfx::RectF(-40, -30, 210, 200), output);
// 4. clipAboveEffect
output.Intersect(clip_above_effect->LayoutClipRect().Rect());
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc b/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc
index 45b2eac..e1d69ad 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc
@@ -749,7 +749,8 @@
TEST_F(PaintPropertyNodeTest, EffectLocalTransformSpaceChange) {
// Let effect.child1 have pixel-moving filter.
EffectPaintPropertyNode::State state{transform.child1, clip.child1};
- state.filter.AppendBlurFilter(20);
+ state.filter_info = std::make_unique<EffectPaintPropertyNode::FilterInfo>();
+ state.filter_info->operations.AppendBlurFilter(20);
effect.child1->Update(*effect.ancestor, std::move(state));
ResetAllChanged();
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 4fdc7454..910fcb4f 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -3596,7 +3596,7 @@
},
{
// Killswitch M136.
- name: "ReferenceFilterMapsReferenceBox",
+ name: "ReferenceFilterOutputBounds",
status: "stable",
},
{
diff --git a/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h b/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h
index 75f73c8..61105a6e 100644
--- a/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h
+++ b/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h
@@ -10,6 +10,7 @@
#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
#include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/transform.h"
namespace blink {
@@ -114,7 +115,8 @@
EffectPaintPropertyNode::State state;
state.local_transform_space = &local_transform_space;
state.output_clip = output_clip;
- state.filter = std::move(filter);
+ state.filter_info = std::make_unique<EffectPaintPropertyNode::FilterInfo>(
+ filter, filter.MapRect(gfx::ToEnclosingRect(filter.ReferenceBox())));
state.direct_compositing_reasons = compositing_reasons;
state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
NewUniqueObjectId(), CompositorElementIdNamespace::kEffectFilter);
@@ -137,7 +139,8 @@
EffectPaintPropertyNode::State state;
state.local_transform_space = &parent.Unalias().LocalTransformSpace();
state.output_clip = output_clip;
- state.filter = std::move(filter);
+ state.filter_info = std::make_unique<EffectPaintPropertyNode::FilterInfo>(
+ filter, filter.MapRect(gfx::ToEnclosingRect(filter.ReferenceBox())));
state.direct_compositing_reasons = CompositingReason::kActiveFilterAnimation;
state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
NewUniqueObjectId(), CompositorElementIdNamespace::kEffectFilter);
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/feComposite-intersection-feTile-input-svg.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/feComposite-intersection-feTile-input-svg.html
new file mode 100644
index 0000000..7c9fd6a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/feComposite-intersection-feTile-input-svg.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://crbug.com/373759987">
+<link rel="match" href="feComposite-intersection-feTile-input-ref.html">
+<style>body { margin: 0; }</style>
+<svg>
+ <filter id="test" filterUnits="userSpaceOnUse"
+ color-interpolation-filters="sRGB" x="0" y="0">
+ <feColorMatrix width="5" height="5"></feColorMatrix>
+ <feTile result="fill"></feTile>
+ <feColorMatrix in="SourceGraphic" values="0 0 0 0 0
+ 0 0 0 0 0
+ 0 0 0 0 0
+ 0 1 0 0 0">
+ </feColorMatrix>
+ <feComposite in="fill" operator="in"></feComposite>
+ </filter>
+ <g filter="url(#test)">
+ <rect fill="none" stroke="black" stroke-width="20" x="10" y="10" width="120" height="120"/>
+ <rect fill="#0f0" x="20" y="20" width="100" height="100" opacity="0.5"/>
+ </g>
+</svg>
diff --git a/third_party/blink/web_tests/fast/reflections/reflection-with-zoom-expected.png b/third_party/blink/web_tests/fast/reflections/reflection-with-zoom-expected.png
index 707881a..b4e3e61 100644
--- a/third_party/blink/web_tests/fast/reflections/reflection-with-zoom-expected.png
+++ b/third_party/blink/web_tests/fast/reflections/reflection-with-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/reflections/reflection-with-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/reflections/reflection-with-zoom-expected.png
index 09e9a133..0e0b5dd 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/reflections/reflection-with-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/reflections/reflection-with-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/reflections/reflection-with-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/reflections/reflection-with-zoom-expected.png
index 09e9a133..0e0b5dd 100644
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/reflections/reflection-with-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/reflections/reflection-with-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/reflections/reflection-with-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/reflections/reflection-with-zoom-expected.png
index 09e9a133..0e0b5dd 100644
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/reflections/reflection-with-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/reflections/reflection-with-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/reflections/reflection-with-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/reflections/reflection-with-zoom-expected.png
index 09e9a133..0e0b5dd 100644
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/reflections/reflection-with-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/reflections/reflection-with-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/reflections/reflection-with-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/reflections/reflection-with-zoom-expected.png
index 09e9a133..0e0b5dd 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/reflections/reflection-with-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/reflections/reflection-with-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/svg/batik/text/textFeatures-expected.png b/third_party/blink/web_tests/platform/mac/svg/batik/text/textFeatures-expected.png
index 42d1349..4266baf 100644
--- a/third_party/blink/web_tests/platform/mac/svg/batik/text/textFeatures-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/batik/text/textFeatures-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/fast/reflections/reflection-with-zoom-expected.png b/third_party/blink/web_tests/platform/win11-arm64/fast/reflections/reflection-with-zoom-expected.png
index 09e9a133..0e0b5dd 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/fast/reflections/reflection-with-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/fast/reflections/reflection-with-zoom-expected.png
Binary files differ