[PE] Properly handle subpixel accumulation across isolation
For pre-CompositeAfterPaint, we let subpixel accumulation propagate
through isolation to keep consistent with legacy compositing code.
For ComposteAfterPaint, we discard subpixel accumulation at
isolation.
Change-Id: Id9460dbb3c7c89931c38119e75f044b9079f3de8
Reviewed-on: https://chromium-review.googlesource.com/c/1488020
Reviewed-by: vmpstr <vmpstr@chromium.org>
Reviewed-by: Chris Harrelson <chrishtr@chromium.org>
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#636642}
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 f76c0f3..a03828a 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
@@ -443,28 +443,34 @@
full_context_.direct_compositing_reasons))
return;
- // We should use the same subpixel paint offset values for snapping
- // regardless of whether a transform is present. If there is a transform
- // we round the paint offset but keep around the residual fractional
- // component for the transformed content to paint with. In spv1 this was
- // called "subpixel accumulation". For more information, see
- // PaintLayer::subpixelAccumulation() and
- // PaintLayerPainter::paintFragmentByApplyingTransform.
+ // We should use the same subpixel paint offset values for snapping regardless
+ // of paint offset translation. If we create a paint offset translation we
+ // round the paint offset but keep around the residual fractional component
+ // (i.e. subpixel accumulation) for the transformed content to paint with.
+ // In pre-CompositeAfterPaint, if the object has layer, this corresponds to
+ // PaintLayer::SubpixelAccumulation().
paint_offset_translation = RoundedIntPoint(context_.current.paint_offset);
- LayoutPoint fractional_paint_offset =
- LayoutPoint(context_.current.paint_offset - *paint_offset_translation);
- if (fractional_paint_offset != LayoutPoint()) {
- // If the object has a non-translation transform, discard the fractional
- // paint offset which can't be transformed by the transform.
- TransformationMatrix matrix;
- object_.StyleRef().ApplyTransform(
- matrix, LayoutSize(), ComputedStyle::kExcludeTransformOrigin,
- ComputedStyle::kIncludeMotionPath,
- ComputedStyle::kIncludeIndependentTransformProperties);
- if (!matrix.IsIdentityOrTranslation())
- fractional_paint_offset = LayoutPoint();
+ LayoutPoint subpixel_accumulation;
+ // Don't propagate subpixel accumulation through paint isolation. In
+ // pre-CompositeAfterPaint we still need to keep consistence with the legacy
+ // compositing code.
+ if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
+ !NeedsIsolationNodes(object_)) {
+ subpixel_accumulation =
+ LayoutPoint(context_.current.paint_offset - *paint_offset_translation);
+ if (subpixel_accumulation != LayoutPoint()) {
+ // If the object has a non-translation transform, discard the fractional
+ // paint offset which can't be transformed by the transform.
+ TransformationMatrix matrix;
+ object_.StyleRef().ApplyTransform(
+ matrix, LayoutSize(), ComputedStyle::kExcludeTransformOrigin,
+ ComputedStyle::kIncludeMotionPath,
+ ComputedStyle::kIncludeIndependentTransformProperties);
+ if (!matrix.IsIdentityOrTranslation())
+ subpixel_accumulation = LayoutPoint();
+ }
}
- context_.current.paint_offset = fractional_paint_offset;
+ context_.current.paint_offset = subpixel_accumulation;
}
void FragmentPaintPropertyTreeBuilder::UpdatePaintOffsetTranslation(
@@ -2220,12 +2226,21 @@
UpdatePaintOffset();
UpdateForPaintOffsetTranslation(paint_offset_translation);
- if (fragment_data_.PaintOffset() != context_.current.paint_offset) {
+ LayoutSize paint_offset_delta =
+ fragment_data_.PaintOffset() - context_.current.paint_offset;
+ if (!paint_offset_delta.IsZero()) {
// Many paint properties depend on paint offset so we force an update of
- // the entire subtree on paint offset changes.
- // However, they are blocked by isolation.
- full_context_.force_subtree_update_reasons |=
- PaintPropertyTreeBuilderContext::kSubtreeUpdateIsolationBlocked;
+ // the entire subtree on paint offset changes. However, they are blocked by
+ // isolation if subpixel accumulation doesn't change or CompositeAfterPaint
+ // is enabled.
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
+ paint_offset_delta == RoundedIntSize(paint_offset_delta)) {
+ full_context_.force_subtree_update_reasons |=
+ PaintPropertyTreeBuilderContext::kSubtreeUpdateIsolationBlocked;
+ } else {
+ full_context_.force_subtree_update_reasons |=
+ PaintPropertyTreeBuilderContext::kSubtreeUpdateIsolationPiercing;
+ }
object_.GetMutableForPainting().SetShouldCheckForPaintInvalidation();
fragment_data_.SetPaintOffset(context_.current.paint_offset);
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 ad7a27b..ac41904 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
@@ -1578,4 +1578,45 @@
// Pass if no crash.
}
+TEST_P(PaintPropertyTreeUpdateTest, SubpixelAccumulationAcrossIsolation) {
+ SetBodyInnerHTML(R"HTML(
+ <style>body { margin: 0 }</style>
+ <div id="parent" style="margin-left: 10.25px">
+ <div id="isolation" style="contain: paint">
+ <div id="child"><div>
+ </div>
+ </div>
+ )HTML");
+ auto* parent_element = GetDocument().getElementById("parent");
+ auto* parent = parent_element->GetLayoutObject();
+ auto* isolation_properties = PaintPropertiesForElement("isolation");
+ auto* child = GetLayoutObjectByElementId("child");
+ EXPECT_EQ(LayoutPoint(LayoutUnit(10.25), LayoutUnit()),
+ parent->FirstFragment().PaintOffset());
+ EXPECT_EQ(FloatSize(10, 0), isolation_properties->PaintOffsetTranslation()
+ ->Matrix()
+ .To2DTranslation());
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+ EXPECT_EQ(LayoutPoint(), child->FirstFragment().PaintOffset());
+ } else {
+ EXPECT_EQ(LayoutPoint(LayoutUnit(0.25), LayoutUnit()),
+ child->FirstFragment().PaintOffset());
+ }
+
+ parent_element->setAttribute(html_names::kStyleAttr, "margin-left: 12.75px");
+ UpdateAllLifecyclePhasesForTest();
+
+ EXPECT_EQ(LayoutPoint(LayoutUnit(12.75), LayoutUnit()),
+ parent->FirstFragment().PaintOffset());
+ EXPECT_EQ(FloatSize(13, 0), isolation_properties->PaintOffsetTranslation()
+ ->Matrix()
+ .To2DTranslation());
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+ EXPECT_EQ(LayoutPoint(), child->FirstFragment().PaintOffset());
+ } else {
+ EXPECT_EQ(LayoutPoint(LayoutUnit(-0.25), LayoutUnit()),
+ child->FirstFragment().PaintOffset());
+ }
+}
+
} // namespace blink