Repaint issues with -webkit-svg-shadow used on a container
https://bugs.webkit.org/show_bug.cgi?id=65643
<rdar://problem/7600532>
Reviewed by Simon Fraser.
SVG renderer repaint rects are currently expanded only by the shadow of
the renderer itself; however, the area they need to repaint can be larger
than that, if their parents also have shadows. We need to take into account
parent's shadows (respecting transforms, as well).
clippedOverflowRectForRepaint already recurses upwards through the render tree,
and ends up with a rect in layout coordinates, so we manually apply the shadow
at each step (repaintRectInLocalCoordinatesExcludingSVGShadow was added to allow
us to get the raw repaint rect without the shadow baked-in).
repaintRectInLocalCoordinates now includes shadows from all parents.
Also, RenderSVGRoot was clipping repaint rects to the viewport before applying
shadows, so offscreen elements with on-screen shadows (applied by the root) would not paint the shadows.
We can just swap the order of these things to correct this.
Tests: svg/css/parent-shadow-offscreen.svg, svg/css/root-shadow-offscreen.svg, svg/repaint/repaint-webkit-svg-shadow.svg
* rendering/RenderObject.cpp:
(WebCore::RenderObject::addChild): Mark the child being added as having an SVG shadow if it is being added as a child of an element that does.
(WebCore::RenderObject::styleDidChange): Mark the child being added as having an SVG shadow if its new style has a shadow.
* rendering/svg/RenderSVGImage.cpp:
(WebCore::RenderSVGImage::layout): Cache the repaint rect before intersecting it with the shadow.
* rendering/svg/RenderSVGImage.h:
(WebCore::RenderSVGImage::repaintRectInLocalCoordinatesExcludingSVGShadow): Return the cached repaint rect for the renderer without the shadow included.
* rendering/svg/RenderSVGModelObject.cpp:
(WebCore::RenderSVGModelObject::RenderSVGModelObject): Renderers do not have a shadow by default.
* rendering/svg/RenderSVGModelObject.h:
(WebCore::RenderSVGModelObject::repaintRectInLocalCoordinatesExcludingSVGShadow): Return the cached repaint rect for the renderer without the shadow included.
(WebCore::RenderSVGModelObject::hasSVGShadow): Return whether or not the renderer has a shadow.
(WebCore::RenderSVGModelObject::setHasSVGShadow): Set whether or not the renderer has a shadow.
* rendering/svg/RenderSVGRoot.cpp:
(WebCore::RenderSVGRoot::RenderSVGRoot):
(WebCore::RenderSVGRoot::computeFloatRectForRepaint): Apply the shadow before clipping to the viewport, so we draw shadows for elements outside the viewport.
(WebCore::RenderSVGRoot::updateCachedBoundaries): Cache the repaint rect before intersecting it with the shadow.
* rendering/svg/RenderSVGRoot.h:
(WebCore::RenderSVGRoot::hasSVGShadow): Return whether or not the renderer has a shadow.
(WebCore::RenderSVGRoot::setHasSVGShadow): Set whether or not the renderer has a shadow.
(WebCore::RenderSVGRoot::repaintRectInLocalCoordinatesExcludingSVGShadow): Return the cached repaint rect for the renderer without the shadow included.
* rendering/svg/RenderSVGShape.cpp:
(WebCore::RenderSVGShape::updateRepaintBoundingBox): Cache the repaint rect before intersecting it with the shadow.
* rendering/svg/RenderSVGShape.h:
(WebCore::RenderSVGShape::repaintRectInLocalCoordinatesExcludingSVGShadow): Return the cached repaint rect for the renderer without the shadow included.
* rendering/svg/SVGRenderSupport.cpp:
(WebCore::SVGRenderSupport::repaintRectForRendererInLocalCoordinatesExcludingSVGShadow): Return the cached repaint rect for the renderer without the shadow included.
(WebCore::SVGRenderSupport::clippedOverflowRectForRepaint): Apply shadows as we walk through our parents, instead of only applying the renderer's own shadow.
(WebCore::SVGRenderSupport::rendererHasSVGShadow): Return whether or not the renderer has a shadow.
(WebCore::SVGRenderSupport::setRendererHasSVGShadow): Set whether or not the renderer has a shadow.
(WebCore::SVGRenderSupport::intersectRepaintRectWithShadows): Walk through the element's parents, adding shadows to the repaint rect as we go, eventually
transforming the repaint rect back into local coordinates.
(WebCore::SVGRenderSupport::intersectRepaintRectWithResources): Don't add shadows by default, just other resources, so that we can cache repaint rects with and without shadows.
* rendering/svg/SVGRenderSupport.h:
* platform/chromium/TestExpectations: Mark tests as needing rebaseline.
* platform/efl/TestExpectations: Mark tests as needing rebaseline.
* platform/gtk/TestExpectations: Mark tests as needing rebaseline.
* platform/qt/TestExpectations: Mark tests as needing rebaseline.
* platform/mac/fast/repaint/moving-shadow-on-container-expected.txt:
* platform/mac/svg/css/arrow-with-shadow-expected.txt:
* platform/mac/svg/css/clippath-with-shadow-expected.txt:
* platform/mac/svg/css/composite-shadow-example-expected.txt:
* platform/mac/svg/css/composite-shadow-with-opacity-expected.txt:
* platform/mac/svg/css/group-with-shadow-expected.txt:
* platform/mac/svg/css/shadow-changes-expected.txt:
* platform/mac/svg/custom/simple-text-double-shadow-expected.txt:
Rebaseline Mac results due to this change.
* svg/css/parent-shadow-offscreen-expected.svg: Added.
* svg/css/parent-shadow-offscreen.svg: Added.
Add a new test that ensures that <g> with -webkit-svg-shadow applied draws when children are offscreen but the shadow is not.
* svg/css/root-shadow-offscreen-expected.svg: Added.
* svg/css/root-shadow-offscreen.svg: Added.
Add a new test that ensures that <svg> with -webkit-svg-shadow applied draws when children are offscreen but the shadow is not.
* platform/mac/svg/repaint/repaint-webkit-svg-shadow-expected.png: Added.
* svg/repaint/repaint-webkit-svg-shadow-expected.txt: Added.
* svg/repaint/repaint-webkit-svg-shadow.svg: Added.
Add a new test that ensures that SVG elements with -webkit-svg-shadow are correctly invalidated.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@133834 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/rendering/svg/SVGRenderSupport.cpp b/Source/WebCore/rendering/svg/SVGRenderSupport.cpp
index 8bc8faa..2972689 100644
--- a/Source/WebCore/rendering/svg/SVGRenderSupport.cpp
+++ b/Source/WebCore/rendering/svg/SVGRenderSupport.cpp
@@ -46,6 +46,16 @@
namespace WebCore {
+FloatRect SVGRenderSupport::repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(const RenderObject* object)
+{
+ // FIXME: Add support for RenderSVGBlock.
+
+ if (object->isSVGShape() || object->isSVGImage() || object->isSVGContainer())
+ return static_cast<const RenderSVGModelObject*>(object)->repaintRectInLocalCoordinatesExcludingSVGShadow();
+
+ return object->repaintRectInLocalCoordinates();
+}
+
LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderObject* object, RenderLayerModelObject* repaintContainer)
{
// Return early for any cases where we don't actually paint
@@ -54,7 +64,10 @@
// Pass our local paint rect to computeRectForRepaint() which will
// map to parent coords and recurse up the parent chain.
- FloatRect repaintRect = object->repaintRectInLocalCoordinates();
+ FloatRect repaintRect = repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(object);
+ const SVGRenderStyle* svgStyle = object->style()->svgStyle();
+ if (const ShadowData* shadow = svgStyle->shadow())
+ shadow->adjustRectForShadow(repaintRect);
object->computeFloatRectForRepaint(repaintContainer, repaintRect);
return enclosingLayoutRect(repaintRect);
}
@@ -288,23 +301,73 @@
return object->style()->overflowX() == OHIDDEN;
}
+bool SVGRenderSupport::rendererHasSVGShadow(const RenderObject* object)
+{
+ // FIXME: Add support for RenderSVGBlock.
+
+ if (object->isSVGShape() || object->isSVGImage() || object->isSVGContainer())
+ return static_cast<const RenderSVGModelObject*>(object)->hasSVGShadow();
+
+ if (object->isSVGRoot())
+ return static_cast<const RenderSVGRoot*>(object)->hasSVGShadow();
+
+ return false;
+}
+
+void SVGRenderSupport::setRendererHasSVGShadow(RenderObject* object, bool hasShadow)
+{
+ // FIXME: Add support for RenderSVGBlock.
+
+ if (object->isSVGShape() || object->isSVGImage() || object->isSVGContainer())
+ return static_cast<RenderSVGModelObject*>(object)->setHasSVGShadow(hasShadow);
+
+ if (object->isSVGRoot())
+ return static_cast<RenderSVGRoot*>(object)->setHasSVGShadow(hasShadow);
+}
+
+void SVGRenderSupport::intersectRepaintRectWithShadows(const RenderObject* object, FloatRect& repaintRect)
+{
+ // Since -webkit-svg-shadow enables shadow drawing for its children, but its children
+ // don't inherit the shadow in their SVGRenderStyle, we need to search our parents for
+ // shadows in order to correctly compute our repaint rect.
+
+ ASSERT(object);
+
+ const RenderObject* currentObject = object;
+
+ AffineTransform localToRootTransform;
+
+ while (currentObject && rendererHasSVGShadow(currentObject)) {
+ RenderStyle* style = currentObject->style();
+ if (style) {
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ if (svgStyle) {
+ if (const ShadowData* shadow = svgStyle->shadow())
+ shadow->adjustRectForShadow(repaintRect);
+ }
+ }
+
+ repaintRect = currentObject->localToParentTransform().mapRect(repaintRect);
+ localToRootTransform *= currentObject->localToParentTransform();
+
+ currentObject = currentObject->parent();
+ };
+
+ if (localToRootTransform.isIdentity())
+ return;
+
+ AffineTransform rootToLocalTransform = localToRootTransform.inverse();
+ repaintRect = rootToLocalTransform.mapRect(repaintRect);
+}
+
void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect)
{
ASSERT(object);
- RenderStyle* style = object->style();
- ASSERT(style);
-
- const SVGRenderStyle* svgStyle = style->svgStyle();
- ASSERT(svgStyle);
-
RenderObject* renderer = const_cast<RenderObject*>(object);
SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
- if (!resources) {
- if (const ShadowData* shadow = svgStyle->shadow())
- shadow->adjustRectForShadow(repaintRect);
+ if (!resources)
return;
- }
#if ENABLE(FILTERS)
if (RenderSVGResourceFilter* filter = resources->filter())
@@ -316,9 +379,6 @@
if (RenderSVGResourceMasker* masker = resources->masker())
repaintRect.intersect(masker->resourceBoundingBox(renderer));
-
- if (const ShadowData* shadow = svgStyle->shadow())
- shadow->adjustRectForShadow(repaintRect);
}
bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
@@ -382,6 +442,17 @@
}
}
+void SVGRenderSupport::childAdded(RenderObject* parent, RenderObject* child)
+{
+ SVGRenderSupport::setRendererHasSVGShadow(child, SVGRenderSupport::rendererHasSVGShadow(parent) || SVGRenderSupport::rendererHasSVGShadow(child));
+}
+
+void SVGRenderSupport::styleChanged(RenderObject* object)
+{
+ RenderObject* parent = object->parent();
+ SVGRenderSupport::setRendererHasSVGShadow(object, (parent && SVGRenderSupport::rendererHasSVGShadow(parent)) || (object->style()->svgStyle() && object->style()->svgStyle()->shadow()));
+}
+
}
#endif