Chromium Code Reviews| 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 445c595b44494fdbe577d5a7e7666be82f5e91ef..c45c26c77a6b0a6b268d00bb04d83c58aa9e9fdf 100644 |
| --- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp |
| +++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp |
| @@ -415,6 +415,40 @@ void PaintPropertyTreeBuilder::updateTransform( |
| } |
| } |
| +static bool computeMaskParameters(IntRect& maskClip, |
|
chrishtr
2017/02/11 23:55:11
The bounds of the mask requires the bounds of chil
trchen
2017/02/13 21:12:02
Only for SVG. For HTML/CSS we only use the current
|
| + const LayoutObject& object, |
| + const LayoutPoint& paintOffset) { |
| + DCHECK(object.isBoxModelObject() || object.isSVGChild()); |
| + const ComputedStyle& style = object.styleRef(); |
| + |
| + if (object.isSVGChild()) { |
| + // TODO(trchen): Figure out what to do with SVG. SVG may use object |
|
chrishtr
2017/02/13 23:23:49
Remove this comment, per discussion.
trchen
2017/02/14 01:37:30
Alright. Changed it to just "Implement SVG masks."
|
| + // bounding box for mask sizing, but computing it requires property |
| + // tree to be built. |
| + 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(); |
|
chrishtr
2017/02/13 23:23:49
Can you point me to the legacy code which implemen
trchen
2017/02/14 01:37:30
The legacy code doesn't clip inline overflow corre
chrishtr
2017/02/14 02:11:33
I wonder if this has been filed as a bug before. D
trchen
2017/02/14 02:22:04
I'm not aware of one either.
|
| + } |
| + maximumMaskRegion.moveBy(paintOffset); |
| + maskClip = enclosingIntRect(maximumMaskRegion); |
| + return true; |
| +} |
| + |
| void PaintPropertyTreeBuilder::updateEffect( |
| const LayoutObject& object, |
| PaintPropertyTreeBuilderContext& context) { |
| @@ -424,14 +458,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. |
| @@ -457,6 +496,84 @@ 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 |
|
chrishtr
2017/02/13 23:23:49
Please adjust the comment to make it clear that th
trchen
2017/02/14 01:37:30
Done.
|
| + // grouping (i.e. descendant-dependent compositing reason) properly yet. |
| + // This forces mask won't get squashed into a child effect. |
| + 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. |
| @@ -465,7 +582,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. |
| @@ -484,51 +600,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; |
| } |
| } |
| @@ -946,8 +1055,10 @@ void PaintPropertyTreeBuilder::updatePropertiesForSelf( |
| if (object.isBoxModelObject() || object.isSVG()) { |
| updatePaintOffsetTranslation(object, context); |
| updateTransform(object, context); |
| - if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) |
| + if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { |
| updateEffect(object, context); |
| + updateFilter(object, context); |
| + } |
| updateCssClip(object, context); |
| updateLocalBorderBoxContext(object, context); |
| if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) |