Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(282)

Unified Diff: third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp

Issue 2641173008: [SPv2] Add CSS mask support (Closed)
Patch Set: rebase Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index c66dac08edcc4e31027ec51fc7a9f4c7f5150e6b..a49b7bb83613e7839c0ae576233a4c363a7b99b8 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -410,6 +410,38 @@ void PaintPropertyTreeBuilder::updateTransform(
}
}
+static bool computeMaskParameters(IntRect& maskClip,
+ const LayoutObject& object,
+ const LayoutPoint& paintOffset) {
+ DCHECK(object.isBoxModelObject() || object.isSVGChild());
+ const ComputedStyle& style = object.styleRef();
+
+ if (object.isSVGChild()) {
+ // TODO(trchen): Implement SVG masks.
+ return false;
+ }
+ if (!style.hasMask())
+ return false;
+
+ LayoutRect maximumMaskRegion;
+ // For HTML/CSS objects, the extent of the mask is known as "mask
+ // painting area", which is determined by CSS mask-clip property.
+ // We don't implement mask-clip:margin-box or no-clip currently,
+ // so the maximum we can get is border-box.
+ if (object.isBox()) {
+ maximumMaskRegion = toLayoutBox(object).borderBoxRect();
+ } else {
+ // For inline elements, depends on the value of box-decoration-break
+ // there could be one box in multiple fragments or multiple boxes.
+ // Either way here we are only interested in the bounding box of them.
+ DCHECK(object.isLayoutInline());
+ maximumMaskRegion = toLayoutInline(object).linesBoundingBox();
+ }
+ maximumMaskRegion.moveBy(paintOffset);
+ maskClip = enclosingIntRect(maximumMaskRegion);
+ return true;
+}
+
void PaintPropertyTreeBuilder::updateEffect(
const LayoutObject& object,
PaintPropertyTreeBuilderContext& context) {
@@ -419,14 +451,19 @@ void PaintPropertyTreeBuilder::updateEffect(
object.isBoxModelObject() && style.isStackingContext();
if (!isCSSIsolatedGroup && !object.isSVGChild()) {
if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) {
- if (auto* properties = object.getMutableForPainting().paintProperties())
+ if (auto* properties = object.getMutableForPainting().paintProperties()) {
context.forceSubtreeUpdate |= properties->clearEffect();
+ context.forceSubtreeUpdate |= properties->clearMask();
+ context.forceSubtreeUpdate |= properties->clearMaskClip();
+ }
}
return;
}
// TODO(trchen): Can't omit effect node if we have 3D children.
if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) {
+ const ClipPaintPropertyNode* outputClip = context.inputClipOfCurrentEffect;
+
bool effectNodeNeeded = false;
// Can't omit effect node if we have paint children with exotic blending.
@@ -452,6 +489,85 @@ void PaintPropertyTreeBuilder::updateEffect(
if (opacity != 1.0f)
effectNodeNeeded = true;
+ // We may begin to composite our subtree prior to an animation starts,
+ // but a compositor element ID is only needed when an animation is current.
+ CompositorElementId compositorElementId =
+ style.hasCurrentOpacityAnimation()
+ ? createDomNodeBasedCompositorElementId(object)
+ : CompositorElementId();
+ CompositingReasons compositingReasons = CompositingReasonNone;
+ if (CompositingReasonFinder::requiresCompositingForOpacityAnimation(
+ style)) {
+ compositingReasons = CompositingReasonActiveAnimation;
+ effectNodeNeeded = true;
+ }
+ DCHECK(!style.hasCurrentOpacityAnimation() ||
+ compositingReasons != CompositingReasonNone);
+
+ IntRect maskClip;
+ bool hasMask =
+ computeMaskParameters(maskClip, object, context.current.paintOffset);
+ if (hasMask) {
+ effectNodeNeeded = true;
+
+ auto& properties = object.getMutableForPainting().ensurePaintProperties();
+ context.forceSubtreeUpdate |= properties.updateMaskClip(
+ context.current.clip, context.current.transform,
+ FloatRoundedRect(maskClip));
+ outputClip = properties.maskClip();
+
+ // TODO(crbug.com/683425): PaintArtifactCompositor does not handle
+ // grouping (i.e. descendant-dependent compositing reason) properly yet.
+ // This forces masked subtree always create a layer for now.
+ compositingReasons |= CompositingReasonIsolateCompositedDescendants;
+ } else {
+ if (auto* properties = object.getMutableForPainting().paintProperties())
+ context.forceSubtreeUpdate |= properties->clearMaskClip();
+ }
+
+ if (effectNodeNeeded) {
+ auto& properties = object.getMutableForPainting().ensurePaintProperties();
+ context.forceSubtreeUpdate |= properties.updateEffect(
+ context.currentEffect, context.current.transform, outputClip,
+ CompositorFilterOperations(), opacity, blendMode, compositingReasons,
+ compositorElementId);
+ if (hasMask) {
+ // TODO(crbug.com/683425): PaintArtifactCompositor does not handle
+ // grouping (i.e. descendant-dependent compositing reason) properly yet.
+ // Adding CompositingReasonSquashingDisallowed forces mask not getting
+ // squashed into a child effect. Have no compositing reason otherwise.
+ context.forceSubtreeUpdate |= properties.updateMask(
+ properties.effect(), context.current.transform, outputClip,
+ CompositorFilterOperations(), 1.f, SkBlendMode::kDstIn,
+ CompositingReasonSquashingDisallowed, CompositorElementId());
+ } else {
+ context.forceSubtreeUpdate |= properties.clearMask();
+ }
+ } else {
+ if (auto* properties = object.getMutableForPainting().paintProperties()) {
+ context.forceSubtreeUpdate |= properties->clearEffect();
+ context.forceSubtreeUpdate |= properties->clearMask();
+ }
+ }
+ }
+
+ const auto* properties = object.paintProperties();
+ if (properties && properties->effect()) {
+ context.currentEffect = properties->effect();
+ if (properties->maskClip()) {
+ context.inputClipOfCurrentEffect = context.current.clip =
+ context.absolutePosition.clip = context.fixedPosition.clip =
+ properties->maskClip();
+ }
+ }
+}
+
+void PaintPropertyTreeBuilder::updateFilter(
+ const LayoutObject& object,
+ PaintPropertyTreeBuilderContext& context) {
+ const ComputedStyle& style = object.styleRef();
+
+ if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) {
CompositorFilterOperations filter;
if (object.isSVGChild()) {
// TODO(trchen): SVG caches filters in SVGResources. Implement it.
@@ -460,7 +576,6 @@ void PaintPropertyTreeBuilder::updateEffect(
filter = layer->createCompositorFilterOperationsForFilter(style);
}
- const ClipPaintPropertyNode* outputClip = context.inputClipOfCurrentEffect;
// The CSS filter spec didn't specify how filters interact with overflow
// clips. The implementation here mimics the old Blink/WebKit behavior for
// backward compatibility.
@@ -479,51 +594,44 @@ void PaintPropertyTreeBuilder::updateEffect(
// On the other hand, "B" should not be clipped because the overflow clip is
// not in its containing block chain, but as the filter output will be
// clipped, so a blurred "B" may still be invisible.
- if (!filter.isEmpty()) {
- effectNodeNeeded = true;
- outputClip = context.current.clip;
+ const ClipPaintPropertyNode* outputClip = context.current.clip;
- // TODO(trchen): A filter may contain spatial operations such that an
- // output pixel may depend on an input pixel outside of the output clip.
- // We should generate a special clip node to represent this expansion.
- }
-
- CompositingReasons compositingReasons = CompositingReasonNone;
- if (CompositingReasonFinder::requiresCompositingForEffectAnimation(style)) {
- compositingReasons = CompositingReasonActiveAnimation;
- effectNodeNeeded = true;
- }
+ // TODO(trchen): A filter may contain spatial operations such that an
+ // output pixel may depend on an input pixel outside of the output clip.
+ // We should generate a special clip node to represent this expansion.
+ // We may begin to composite our subtree prior to an animation starts,
+ // but a compositor element ID is only needed when an animation is current.
CompositorElementId compositorElementId =
- (style.hasCurrentOpacityAnimation() ||
- style.hasCurrentFilterAnimation() ||
- style.hasCurrentBackdropFilterAnimation())
+ style.hasCurrentFilterAnimation()
? createDomNodeBasedCompositorElementId(object)
: CompositorElementId();
-
- if (effectNodeNeeded) {
+ CompositingReasons compositingReasons =
+ CompositingReasonFinder::requiresCompositingForFilterAnimation(style)
+ ? CompositingReasonActiveAnimation
+ : CompositingReasonNone;
+ DCHECK(!style.hasCurrentFilterAnimation() ||
+ compositingReasons != CompositingReasonNone);
+
+ if (compositingReasons == CompositingReasonNone && filter.isEmpty()) {
+ if (auto* properties = object.getMutableForPainting().paintProperties())
+ context.forceSubtreeUpdate |= properties->clearFilter();
+ } else {
auto& properties = object.getMutableForPainting().ensurePaintProperties();
- context.forceSubtreeUpdate |= properties.updateEffect(
+ context.forceSubtreeUpdate |= properties.updateFilter(
context.currentEffect, context.current.transform, outputClip,
- std::move(filter), opacity, blendMode, compositingReasons,
+ std::move(filter), 1.f, SkBlendMode::kSrcOver, compositingReasons,
compositorElementId);
- } else {
- if (auto* properties = object.getMutableForPainting().paintProperties())
- context.forceSubtreeUpdate |= properties->clearEffect();
}
}
const auto* properties = object.paintProperties();
- if (properties && properties->effect()) {
- context.currentEffect = properties->effect();
- if (!properties->effect()->filter().isEmpty()) {
- // TODO(trchen): Change input clip to expansion hint once implemented.
- const ClipPaintPropertyNode* inputClip =
- properties->effect()->outputClip();
- context.inputClipOfCurrentEffect = context.current.clip =
- context.absolutePosition.clip = context.fixedPosition.clip =
- inputClip;
- }
+ if (properties && properties->filter()) {
+ context.currentEffect = properties->filter();
+ // TODO(trchen): Change input clip to expansion hint once implemented.
+ const ClipPaintPropertyNode* inputClip = properties->filter()->outputClip();
+ context.inputClipOfCurrentEffect = context.current.clip =
+ context.absolutePosition.clip = context.fixedPosition.clip = inputClip;
}
}
@@ -953,8 +1061,10 @@ void PaintPropertyTreeBuilder::updatePropertiesForSelf(
if (object.isBoxModelObject() || object.isSVG()) {
updateTransform(object, context);
- if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
+ if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
updateEffect(object, context);
+ updateFilter(object, context);
+ }
updateCssClip(object, context);
updateLocalBorderBoxContext(object, context);
if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())

Powered by Google App Engine
This is Rietveld 408576698