https://bugs.webkit.org/show_bug.cgi?id=138551
Patch by Byungseon Shin <sun.shin@lge.com> on 2015-01-07
Reviewed by Simon Fraser.
Fix clipping compositing descendants of an accelerated layer having
border radius and clip overflow issue by using layer corner-radius
or a CAShapeLayer mask on Mac, and setting up a separate mask layer
on the childContainmentLayer on other platforms.
This patch is extracted from following Blink revisions:
<http://src.chromium.org/viewvc/blink?view=revision&revision=162463>
<http://src.chromium.org/viewvc/blink?view=revision&revision=158258>
by <rosca@adobe.com>
Clip accelerated descendants of an accelerated layer having border radius and clip overflow.
Large canvas does not honor containing div's border radius.
and <http://src.chromium.org/viewvc/blink?view=revision&revision=160578>
by <junov@chromium.org>
Fix for CSS clip-path with accelerated 2D canvas.
* WebCore.exp.in:
* platform/graphics/GraphicsLayer.cpp:
(WebCore::GraphicsLayer::dumpProperties):
* platform/graphics/GraphicsLayer.h:
(WebCore::GraphicsLayer::applyClippingBorder):
(WebCore::GraphicsLayer::clearClippingBorder):
(WebCore::GraphicsLayer::needsClippingMaskLayer):
* platform/graphics/GraphicsLayerClient.h:
* platform/graphics/ca/GraphicsLayerCA.cpp:
(WebCore::GraphicsLayerCA::applyClippingBorder):
(WebCore::GraphicsLayerCA::clearClippingBorder):
* platform/graphics/ca/GraphicsLayerCA.h:
* rendering/PaintPhase.h:
* rendering/RenderBlock.cpp:
(WebCore::RenderBlock::paintObject):
* rendering/RenderBox.cpp:
(WebCore::RenderBox::paintClippingMask):
* rendering/RenderBox.h:
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::paintLayerContents):
(WebCore::RenderLayer::paintChildClippingMaskForFragments):
* rendering/RenderLayer.h:
* rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::~RenderLayerBacking):
(WebCore::RenderLayerBacking::destroyGraphicsLayers):
(WebCore::RenderLayerBacking::updateConfiguration):
(WebCore::RenderLayerBacking::updateGeometry):
(WebCore::RenderLayerBacking::updateChildClippingStrategy):
(WebCore::RenderLayerBacking::setContentsNeedDisplay):
(WebCore::RenderLayerBacking::setContentsNeedDisplayInRect):
(WebCore::RenderLayerBacking::paintIntoLayer):
(WebCore::RenderLayerBacking::paintContents):
(WebCore::RenderLayerBacking::backingStoreMemoryEstimate):
* rendering/RenderLayerBacking.h:
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@178029
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2015-01-07 Byungseon Shin <sun.shin@lge.com>
+
+ Fix Border-radius clipping issue on a composited descendants
+ https://bugs.webkit.org/show_bug.cgi?id=138551
+
+ Reviewed by Simon Fraser.
+
+ Fix clipping compositing descendants of an accelerated layer having
+ border radius and clip overflow issue by using layer corner-radius
+ or a CAShapeLayer mask on Mac, and setting up a separate mask layer
+ on the childContainmentLayer on other platforms.
+
+ This patch is extracted from following Blink revisions:
+ <http://src.chromium.org/viewvc/blink?view=revision&revision=162463>
+ <http://src.chromium.org/viewvc/blink?view=revision&revision=158258>
+ by <rosca@adobe.com>
+
+ Clip accelerated descendants of an accelerated layer having border radius and clip overflow.
+ Large canvas does not honor containing div's border radius.
+
+ and <http://src.chromium.org/viewvc/blink?view=revision&revision=160578>
+ by <junov@chromium.org>
+
+ Fix for CSS clip-path with accelerated 2D canvas.
+
+ * WebCore.exp.in:
+ * platform/graphics/GraphicsLayer.cpp:
+ (WebCore::GraphicsLayer::dumpProperties):
+ * platform/graphics/GraphicsLayer.h:
+ (WebCore::GraphicsLayer::applyClippingBorder):
+ (WebCore::GraphicsLayer::clearClippingBorder):
+ (WebCore::GraphicsLayer::needsClippingMaskLayer):
+ * platform/graphics/GraphicsLayerClient.h:
+ * platform/graphics/ca/GraphicsLayerCA.cpp:
+ (WebCore::GraphicsLayerCA::applyClippingBorder):
+ (WebCore::GraphicsLayerCA::clearClippingBorder):
+ * platform/graphics/ca/GraphicsLayerCA.h:
+ * rendering/PaintPhase.h:
+ * rendering/RenderBlock.cpp:
+ (WebCore::RenderBlock::paintObject):
+ * rendering/RenderBox.cpp:
+ (WebCore::RenderBox::paintClippingMask):
+ * rendering/RenderBox.h:
+ * rendering/RenderLayer.cpp:
+ (WebCore::RenderLayer::paintLayerContents):
+ (WebCore::RenderLayer::paintChildClippingMaskForFragments):
+ * rendering/RenderLayer.h:
+ * rendering/RenderLayerBacking.cpp:
+ (WebCore::RenderLayerBacking::~RenderLayerBacking):
+ (WebCore::RenderLayerBacking::destroyGraphicsLayers):
+ (WebCore::RenderLayerBacking::updateConfiguration):
+ (WebCore::RenderLayerBacking::updateGeometry):
+ (WebCore::RenderLayerBacking::updateChildClippingStrategy):
+ (WebCore::RenderLayerBacking::setContentsNeedDisplay):
+ (WebCore::RenderLayerBacking::setContentsNeedDisplayInRect):
+ (WebCore::RenderLayerBacking::paintIntoLayer):
+ (WebCore::RenderLayerBacking::paintContents):
+ (WebCore::RenderLayerBacking::backingStoreMemoryEstimate):
+ * rendering/RenderLayerBacking.h:
+
2015-01-06 Antti Koivisto <antti@apple.com>
Use HashMap instead of CFDictionary for composite font reference map
__ZN7WebCore15GraphicsLayerCA18setOpacityInternalEf
__ZN7WebCore15GraphicsLayerCA18setReplicatedLayerEPNS_13GraphicsLayerE
__ZN7WebCore15GraphicsLayerCA18setShowDebugBorderEb
+__ZN7WebCore15GraphicsLayerCA19applyClippingBorderERKNS_16FloatRoundedRectE
+__ZN7WebCore15GraphicsLayerCA19clearClippingBorderEv
__ZN7WebCore15GraphicsLayerCA19setCustomAppearanceENS_13GraphicsLayer16CustomAppearanceE
__ZN7WebCore15GraphicsLayerCA20setChildrenTransformERKNS_20TransformationMatrixE
__ZN7WebCore15GraphicsLayerCA20setReplicatedByLayerEPNS_13GraphicsLayerE
writeIndent(ts, indent + 2);
ts << "GraphicsLayerPaintMask\n";
}
+ if (paintingPhase() & GraphicsLayerPaintChildClippingMask) {
+ writeIndent(ts, indent + 2);
+ ts << "GraphicsLayerPaintChildClippingMask\n";
+ }
if (paintingPhase() & GraphicsLayerPaintOverflowContents) {
writeIndent(ts, indent + 2);
ts << "GraphicsLayerPaintOverflowContents\n";
FloatRoundedRect contentsClippingRect() const { return m_contentsClippingRect; }
virtual void setContentsClippingRect(const FloatRoundedRect& roundedRect) { m_contentsClippingRect = roundedRect; }
+ virtual bool applyClippingBorder(const FloatRoundedRect&) { return false; }
+ virtual void clearClippingBorder() { return; }
+
// Transitions are identified by a special animation name that cannot clash with a keyframe identifier.
static String animationNameForTransition(AnimatedPropertyID);
virtual bool isGraphicsLayerCARemote() const { return false; }
virtual bool isGraphicsLayerTextureMapper() const { return false; }
+ virtual bool needsClippingMaskLayer() { return true; };
+
protected:
// Should be called from derived class destructors. Should call willBeDestroyed() on super.
WEBCORE_EXPORT virtual void willBeDestroyed();
GraphicsLayerPaintMask = (1 << 2),
GraphicsLayerPaintOverflowContents = (1 << 3),
GraphicsLayerPaintCompositedScroll = (1 << 4),
+ GraphicsLayerPaintChildClippingMask = (1 << 5),
GraphicsLayerPaintAllWithOverflowClip = (GraphicsLayerPaintBackground | GraphicsLayerPaintForeground | GraphicsLayerPaintMask)
};
typedef unsigned GraphicsLayerPaintingPhase;
}
}
+bool GraphicsLayerCA::applyClippingBorder(const FloatRoundedRect& roundedRect)
+{
+ if (roundedRect.radii().isUniformCornerRadius()) {
+ m_layer->setMask(nullptr);
+ m_layer->setMasksToBounds(true);
+ m_layer->setCornerRadius(roundedRect.radii().topLeft().width());
+ } else {
+ if (!m_shapeMaskLayer) {
+ m_shapeMaskLayer = createPlatformCALayer(PlatformCALayer::LayerTypeShapeLayer, this);
+ m_shapeMaskLayer->setAnchorPoint(FloatPoint3D());
+ }
+
+ m_shapeMaskLayer->setPosition(FloatPoint());
+ m_shapeMaskLayer->setBounds(m_layer->bounds());
+
+ m_layer->setCornerRadius(0);
+ m_layer->setMask(m_shapeMaskLayer.get());
+
+ FloatRoundedRect offsetClippingRoundedRect(m_layer->bounds(), roundedRect.radii());
+ m_shapeMaskLayer->setShapeRoundedRect(offsetClippingRoundedRect);
+ }
+
+ return true;
+}
+
+void GraphicsLayerCA::clearClippingBorder()
+{
+ m_layer->setCornerRadius(0);
+ m_layer->setMasksToBounds(false);
+ m_layer->setMask(nullptr);
+}
+
// The clipping strategy depends on whether the rounded rect has equal corner radii.
void GraphicsLayerCA::updateClippingStrategy(PlatformCALayer& clippingLayer, RefPtr<PlatformCALayer>& shapeMaskLayer, const FloatRoundedRect& roundedRect)
{
private:
virtual bool isGraphicsLayerCA() const override { return true; }
+ virtual bool applyClippingBorder(const FloatRoundedRect&) override;
+ virtual void clearClippingBorder() override;
+
WEBCORE_EXPORT virtual void willBeDestroyed() override;
// PlatformCALayerClient overrides
PaintPhaseSelection,
PaintPhaseCollapsedTableBorders,
PaintPhaseTextClip,
- PaintPhaseMask
+ PaintPhaseMask,
+ PaintPhaseClippingMask
};
enum PaintBehaviorFlags {
return;
}
+ if (paintPhase == PaintPhaseClippingMask && style().visibility() == VISIBLE) {
+ paintClippingMask(paintInfo, paintOffset);
+ return;
+ }
+
// If just painting the root background, then return.
if (paintInfo.paintRootBackgroundOnly())
return;
paintMaskImages(paintInfo, paintRect);
}
+void RenderBox::paintClippingMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
+{
+ if (!paintInfo.shouldPaintWithinRoot(*this) || style().visibility() != VISIBLE || paintInfo.phase != PaintPhaseClippingMask || paintInfo.context->paintingDisabled())
+ return;
+
+ LayoutRect paintRect = LayoutRect(paintOffset, size());
+ paintInfo.context->fillRect(snappedIntRect(paintRect), Color::black, style().colorSpace());
+}
+
void RenderBox::paintMaskImages(const PaintInfo& paintInfo, const LayoutRect& paintRect)
{
// Figure out if we need to push a transparency layer to render our mask.
virtual void paintObject(PaintInfo&, const LayoutPoint&) { ASSERT_NOT_REACHED(); }
virtual void paintBoxDecorations(PaintInfo&, const LayoutPoint&);
virtual void paintMask(PaintInfo&, const LayoutPoint&);
+ virtual void paintClippingMask(PaintInfo&, const LayoutPoint&);
virtual void imageChanged(WrappedImagePtr, const IntRect* = 0) override;
// Called when a positioned object moves but doesn't necessarily change size. A simplified layout is attempted
paintMaskForFragments(layerFragments, context, localPaintingInfo, subtreePaintRootForRenderer);
}
+ if ((localPaintFlags & PaintLayerPaintingChildClippingMaskPhase) && shouldPaintContent && !selectionOnly) {
+ // Paint the border radius mask for the fragments.
+ paintChildClippingMaskForFragments(layerFragments, context, localPaintingInfo, subtreePaintRootForRenderer);
+ }
+
// End our transparency layer
if (haveTransparency && m_usedTransparency && !m_paintingInsideReflection) {
context->endTransparencyLayer();
}
}
+void RenderLayer::paintChildClippingMaskForFragments(const LayerFragments& layerFragments, GraphicsContext* context, const LayerPaintingInfo& localPaintingInfo,
+ RenderObject* subtreePaintRootForRenderer)
+{
+ for (size_t i = 0; i < layerFragments.size(); ++i) {
+ const LayerFragment& fragment = layerFragments.at(i);
+ if (!fragment.shouldPaintContent)
+ continue;
+
+ if (localPaintingInfo.clipToDirtyRect)
+ clipToRect(localPaintingInfo, context, fragment.foregroundRect, IncludeSelfForBorderRadius); // Child clipping mask painting will handle clipping to self.
+
+ // Paint the clipped mask.
+ PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhaseClippingMask, PaintBehaviorNormal, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer());
+ renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelAccumulation));
+
+ if (localPaintingInfo.clipToDirtyRect)
+ restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.foregroundRect);
+ }
+}
+
void RenderLayer::paintOverflowControlsForFragments(const LayerFragments& layerFragments, GraphicsContext* context, const LayerPaintingInfo& localPaintingInfo)
{
for (size_t i = 0; i < layerFragments.size(); ++i) {
PaintLayerPaintingOverflowContents = 1 << 9,
PaintLayerPaintingRootBackgroundOnly = 1 << 10,
PaintLayerPaintingSkipRootBackground = 1 << 11,
+ PaintLayerPaintingChildClippingMaskPhase = 1 << 12,
PaintLayerPaintingCompositingAllPhases = (PaintLayerPaintingCompositingBackgroundPhase | PaintLayerPaintingCompositingForegroundPhase | PaintLayerPaintingCompositingMaskPhase)
};
void paintOutlineForFragments(const LayerFragments&, GraphicsContext*, const LayerPaintingInfo&, PaintBehavior, RenderObject* paintingRootForRenderer);
void paintOverflowControlsForFragments(const LayerFragments&, GraphicsContext*, const LayerPaintingInfo&);
void paintMaskForFragments(const LayerFragments&, GraphicsContext*, const LayerPaintingInfo&, RenderObject* paintingRootForRenderer);
+ void paintChildClippingMaskForFragments(const LayerFragments&, GraphicsContext*, const LayerPaintingInfo&, RenderObject* paintingRootForRenderer);
void paintTransformedLayerIntoFragments(GraphicsContext*, const LayerPaintingInfo&, PaintLayerFlags);
RenderLayer* transparentPaintingAncestor();
updateForegroundLayer(false);
updateBackgroundLayer(false);
updateMaskLayer(false);
+ updateChildClippingStrategy(false);
updateScrollingLayers(false);
detachFromScrollingCoordinator();
destroyGraphicsLayers();
m_backgroundLayer = nullptr;
m_childContainmentLayer = nullptr;
m_maskLayer = nullptr;
+ m_childClippingMaskLayer = nullptr;
m_scrollingLayer = nullptr;
m_scrollingContentsLayer = nullptr;
updateMaskLayer(renderer().hasMask());
+ updateChildClippingStrategy(needsDescendentsClippingLayer);
+
if (m_owningLayer.hasReflection()) {
if (m_owningLayer.reflectionLayer()->backing()) {
GraphicsLayer* reflectionLayer = m_owningLayer.reflectionLayer()->backing()->graphicsLayer();
clipLayer->setPosition(FloatPoint(clippingBox.location() - localCompositingBounds.location()));
clipLayer->setSize(clippingBox.size());
clipLayer->setOffsetFromRenderer(toFloatSize(clippingBox.location()));
+
+ if (m_childClippingMaskLayer && !m_scrollingLayer) {
+ m_childClippingMaskLayer->setSize(clipLayer->size());
+ m_childClippingMaskLayer->setPosition(FloatPoint());
+ m_childClippingMaskLayer->setOffsetFromRenderer(clipLayer->offsetFromRenderer());
+ }
}
if (m_maskLayer) {
FloatSize oldScrollingLayerOffset = m_scrollingLayer->offsetFromRenderer();
m_scrollingLayer->setOffsetFromRenderer(-toFloatSize(paddingBox.location()));
+ if (m_childClippingMaskLayer) {
+ m_childClippingMaskLayer->setPosition(m_scrollingLayer->position());
+ m_childClippingMaskLayer->setSize(m_scrollingLayer->size());
+ m_childClippingMaskLayer->setOffsetFromRenderer(toFloatSize(paddingBox.location()));
+ }
+
bool paddingBoxOffsetChanged = oldScrollingLayerOffset != m_scrollingLayer->offsetFromRenderer();
IntSize scrollSize(m_owningLayer.scrollWidth(), m_owningLayer.scrollHeight());
m_graphicsLayer->setPaintingPhase(paintingPhaseForPrimaryLayer());
}
+void RenderLayerBacking::updateChildClippingStrategy(bool needsDescendentsClippingLayer)
+{
+ if (hasClippingLayer() && needsDescendentsClippingLayer) {
+ if (is<RenderBox>(renderer())) {
+ LayoutRect boxRect(LayoutPoint(), downcast<RenderBox>(renderer()).size());
+ FloatRoundedRect contentsClippingRect = renderer().style().getRoundedInnerBorderFor(boxRect).pixelSnappedRoundedRectForPainting(deviceScaleFactor());
+ contentsClippingRect.move(contentOffsetInCompostingLayer());
+ if (clippingLayer()->applyClippingBorder(contentsClippingRect)) {
+ if (m_childClippingMaskLayer)
+ m_childClippingMaskLayer = nullptr;
+ return;
+ }
+ }
+
+ if (!m_childClippingMaskLayer) {
+ m_childClippingMaskLayer = createGraphicsLayer("Child Clipping Mask Layer");
+ m_childClippingMaskLayer->setDrawsContent(true);
+ m_childClippingMaskLayer->setPaintingPhase(GraphicsLayerPaintChildClippingMask);
+ clippingLayer()->setMaskLayer(m_childClippingMaskLayer.get());
+ }
+ } else {
+ if (m_childClippingMaskLayer) {
+ m_childClippingMaskLayer = nullptr;
+ if (hasClippingLayer())
+ clippingLayer()->setMaskLayer(nullptr);
+ } else
+ if (hasClippingLayer())
+ clippingLayer()->clearClippingBorder();
+ }
+}
+
bool RenderLayerBacking::updateScrollingLayers(bool needsScrollingLayers)
{
if (needsScrollingLayers == !!m_scrollingLayer)
if (m_maskLayer && m_maskLayer->drawsContent())
m_maskLayer->setNeedsDisplay();
+ if (m_childClippingMaskLayer && m_childClippingMaskLayer->drawsContent())
+ m_childClippingMaskLayer->setNeedsDisplay();
+
if (m_scrollingContentsLayer && m_scrollingContentsLayer->drawsContent())
m_scrollingContentsLayer->setNeedsDisplay();
}
m_maskLayer->setNeedsDisplayInRect(layerDirtyRect, shouldClip);
}
+ if (m_childClippingMaskLayer && m_childClippingMaskLayer->drawsContent()) {
+ FloatRect layerDirtyRect = r;
+ layerDirtyRect.move(-m_childClippingMaskLayer->offsetFromRenderer());
+ m_childClippingMaskLayer->setNeedsDisplayInRect(layerDirtyRect);
+ }
+
if (m_scrollingContentsLayer && m_scrollingContentsLayer->drawsContent()) {
FloatRect layerDirtyRect = pixelSnappedRectForPainting;
layerDirtyRect.move(-m_scrollingContentsLayer->offsetFromRenderer() + m_devicePixelFractionFromRenderer);
paintFlags |= RenderLayer::PaintLayerPaintingCompositingForegroundPhase;
if (paintingPhase & GraphicsLayerPaintMask)
paintFlags |= RenderLayer::PaintLayerPaintingCompositingMaskPhase;
+ if (paintingPhase & GraphicsLayerPaintChildClippingMask)
+ paintFlags |= RenderLayer::PaintLayerPaintingChildClippingMaskPhase;
if (paintingPhase & GraphicsLayerPaintOverflowContents)
paintFlags |= RenderLayer::PaintLayerPaintingOverflowContents;
if (paintingPhase & GraphicsLayerPaintCompositedScroll)
|| graphicsLayer == m_foregroundLayer.get()
|| graphicsLayer == m_backgroundLayer.get()
|| graphicsLayer == m_maskLayer.get()
+ || graphicsLayer == m_childClippingMaskLayer.get()
|| graphicsLayer == m_scrollingContentsLayer.get()) {
InspectorInstrumentation::willPaint(&renderer());
backingMemory += m_backgroundLayer->backingStoreMemoryEstimate();
if (m_maskLayer)
backingMemory += m_maskLayer->backingStoreMemoryEstimate();
+ if (m_childClippingMaskLayer)
+ backingMemory += m_childClippingMaskLayer->backingStoreMemoryEstimate();
if (m_scrollingContentsLayer)
backingMemory += m_scrollingContentsLayer->backingStoreMemoryEstimate();
ScrollingNodeID scrollingNodeIDForChildren() const { return m_scrollingNodeID ? m_scrollingNodeID : m_viewportConstrainedNodeID; }
bool hasMaskLayer() const { return m_maskLayer != 0; }
+ bool hasChildClippingMaskLayer() const { return m_childClippingMaskLayer != nullptr; }
GraphicsLayer* parentForSublayers() const;
GraphicsLayer* childForSuperlayers() const;
bool requiresScrollCornerLayer() const;
bool updateScrollingLayers(bool scrollingLayers);
void updateDrawsContent(bool isSimpleContainer);
+ void updateChildClippingStrategy(bool needsDescendentsClippingLayer);
void updateRootLayerConfiguration();
std::unique_ptr<GraphicsLayer> m_backgroundLayer; // Only used in cases where we need to draw the background separately.
std::unique_ptr<GraphicsLayer> m_childContainmentLayer; // Only used if we have clipping on a stacking context with compositing children, or if the layer has a tile cache.
std::unique_ptr<GraphicsLayer> m_maskLayer; // Only used if we have a mask.
+ std::unique_ptr<GraphicsLayer> m_childClippingMaskLayer; // Only used if we have to clip child layers or accelerated contents with border radius or clip-path.
std::unique_ptr<GraphicsLayer> m_layerForHorizontalScrollbar;
std::unique_ptr<GraphicsLayer> m_layerForVerticalScrollbar;