Changeset 259137 in webkit
- Timestamp:
- Mar 27, 2020, 1:57:41 PM (7 years ago)
- Location:
- trunk
- Files:
-
- 4 added
- 7 edited
-
LayoutTests/ChangeLog (modified) (1 diff)
-
LayoutTests/svg/clip-path/clip-path-on-overflowing-expected.html (added)
-
LayoutTests/svg/clip-path/clip-path-on-overflowing.html (added)
-
LayoutTests/svg/clip-path/mask-nested-clip-path-010-expected.svg (modified) (1 diff)
-
LayoutTests/svg/clip-path/mask-nested-clip-path-010.svg (modified) (1 diff)
-
LayoutTests/svg/clip-path/resource-clipper-multiple-repaints-expected.html (added)
-
LayoutTests/svg/clip-path/resource-clipper-multiple-repaints.html (added)
-
Source/WebCore/ChangeLog (modified) (1 diff)
-
Source/WebCore/rendering/RenderLayer.cpp (modified) (2 diffs)
-
Source/WebCore/rendering/svg/RenderSVGResourceClipper.cpp (modified) (9 diffs)
-
Source/WebCore/rendering/svg/RenderSVGResourceClipper.h (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r259132 r259137 1 2020-03-27 Simon Fraser <simon.fraser@apple.com> 2 3 Hovering over countries at https://covidinc.io/ shows bizarre rendering artifacts 4 https://bugs.webkit.org/show_bug.cgi?id=209635 5 <rdar://problem/60935010> 6 7 Reviewed by Said Abou-Hallawa. 8 9 Ref test that exercises the code path by painting into a tiled compositing 10 layer. 11 12 * svg/clip-path/clip-path-on-overflowing-expected.html: Added. 13 * svg/clip-path/clip-path-on-overflowing.html: Added. 14 * svg/clip-path/mask-nested-clip-path-010-expected.svg: 15 * svg/clip-path/mask-nested-clip-path-010.svg: Copied from imported/mozilla/svg/svg-integration/clipPath-html-03.xhtml, 16 and modified to have a non-zero offset for better testing of the clipping bounds computation. 17 * svg/clip-path/resource-clipper-multiple-repaints-expected.html: Added. 18 * svg/clip-path/resource-clipper-multiple-repaints.html: Added. 19 1 20 2020-03-27 Jason Lawrence <lawrence.j@apple.com> 2 21 -
trunk/LayoutTests/svg/clip-path/mask-nested-clip-path-010-expected.svg
r164592 r259137 1 1 <svg xmlns="http://www.w3.org/2000/svg" width="10000" height="400"> 2 2 <rect x="99" width="101" height="100" fill="green"/> 3 <rect x="29 9" width="101" height="100" fill="green"/>3 <rect x="295" width="105" height="100" fill="green"/> 4 4 </svg> -
trunk/LayoutTests/svg/clip-path/mask-nested-clip-path-010.svg
r164592 r259137 29 29 <g mask="url(#crop)" transform="translate(100, 0)"> 30 30 <rect width="10000" height="400" fill="red" clip-path="url(#clip2)"/> 31 <rect x="19 9" width="101" height="100" fill="green"/>31 <rect x="195" width="105" height="100" fill="green"/> 32 32 </g> 33 33 </svg> -
trunk/Source/WebCore/ChangeLog
r259135 r259137 1 2020-03-27 Simon Fraser <simon.fraser@apple.com> 2 3 Hovering over countries at https://covidinc.io/ shows bizarre rendering artifacts 4 https://bugs.webkit.org/show_bug.cgi?id=209635 5 <rdar://problem/60935010> 6 7 Reviewed by Said Abou-Hallawa. 8 9 RenderSVGResourceClipper::applyClippingToContext() cached an ImageBuffer per RenderObject 10 when using a image buffer mask. However, the function created and rendered into this image buffer 11 using repaintRect, which can change between invocations. Painting with different repaintRects 12 is very common when rendering into page tiles. 13 14 The buffer can only be re-used if the inputs used to create the buffer (objectBoundingBox, absoluteTransform) 15 are the same, so store those and compare them when determining when to use the cached buffer, and 16 don't use repaintRect when setting up the buffer. 17 18 This revealed another problem where renderers with visual overflow could be truncated by 19 the clipping, tested by imported/mozilla/svg/svg-integration/clipPath-html-03.xhtml, which occurred 20 because RenderLayer::setupClipPath() used the 'svgReferenceBox' for the clipping bounds, which 21 is the content box of the renderer excluding overflow. Fix this by using the bounds of the layer, 22 which includes the bounds of descendants. 23 24 Tests: svg/clip-path/clip-path-on-overflowing.html 25 svg/clip-path/resource-clipper-multiple-repaints.html 26 27 * rendering/RenderLayer.cpp: 28 (WebCore::RenderLayer::setupClipPath): 29 * rendering/svg/RenderSVGResourceClipper.cpp: 30 (WebCore::RenderSVGResourceClipper::removeAllClientsFromCache): 31 (WebCore::RenderSVGResourceClipper::applyClippingToContext): 32 (WebCore::RenderSVGResourceClipper::drawContentIntoMaskImage): 33 (WebCore::RenderSVGResourceClipper::addRendererToClipper): 34 (WebCore::RenderSVGResourceClipper::resourceBoundingBox): 35 * rendering/svg/RenderSVGResourceClipper.h: 36 1 37 2020-03-27 Chris Dumez <cdumez@apple.com> 2 38 -
trunk/Source/WebCore/rendering/RenderLayer.cpp
r259128 r259137 4469 4469 bool RenderLayer::setupClipPath(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const LayoutSize& offsetFromRoot, Optional<LayoutRect>& rootRelativeBounds) 4470 4470 { 4471 if (!renderer().hasClipPath() || context.paintingDisabled() )4471 if (!renderer().hasClipPath() || context.paintingDisabled() || paintingInfo.paintDirtyRect.isEmpty()) 4472 4472 return false; 4473 4473 … … 4495 4495 if (element && element->renderer() && is<RenderSVGResourceClipper>(element->renderer())) { 4496 4496 context.save(); 4497 float deviceSaleFactor = renderer().document().deviceScaleFactor(); 4498 FloatRect referenceBox = snapRectToDevicePixels(computeReferenceBox(renderer(), CSSBoxType::ContentBox, paintingOffsetFromRoot, rootRelativeBounds.value()), deviceSaleFactor); 4499 FloatPoint offset {referenceBox.location()}; 4497 auto referenceBox = snapRectToDevicePixels(rootRelativeBounds.value(), renderer().document().deviceScaleFactor()); 4498 auto offset = referenceBox.location(); 4500 4499 context.translate(offset); 4501 FloatRect svgReferenceBox { FloatPoint(), referenceBox.size()};4502 downcast<RenderSVGResourceClipper>(*element->renderer()).applyClippingToContext(renderer(), svgReferenceBox, paintingInfo.paintDirtyRect,context);4503 context.translate( FloatPoint(-offset.x(), -offset.y()));4500 FloatRect svgReferenceBox { {}, referenceBox.size() }; 4501 downcast<RenderSVGResourceClipper>(*element->renderer()).applyClippingToContext(renderer(), svgReferenceBox, context); 4502 context.translate(-offset); 4504 4503 return true; 4505 4504 } -
trunk/Source/WebCore/rendering/svg/RenderSVGResourceClipper.cpp
r257616 r259137 30 30 #include "HitTestResult.h" 31 31 #include "IntRect.h" 32 #include "Logging.h" 32 33 #include "RenderSVGText.h" 33 34 #include "RenderStyle.h" … … 39 40 #include "SVGUseElement.h" 40 41 #include <wtf/IsoMallocInlines.h> 42 #include <wtf/text/TextStream.h> 41 43 42 44 namespace WebCore { … … 53 55 void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidation) 54 56 { 55 m_clipBoundaries = FloatRect();57 m_clipBoundaries = { }; 56 58 m_clipper.clear(); 57 59 … … 71 73 ASSERT_UNUSED(resourceMode, !resourceMode); 72 74 73 return applyClippingToContext(renderer, renderer.objectBoundingBox(), renderer.repaintRectInLocalCoordinates(), *context); 75 auto repaintRect = renderer.repaintRectInLocalCoordinates(); 76 if (repaintRect.isEmpty()) 77 return true; 78 79 return applyClippingToContext(renderer, renderer.objectBoundingBox(), *context); 74 80 } 75 81 … … 127 133 } 128 134 129 bool RenderSVGResourceClipper::applyClippingToContext(RenderElement& renderer, const FloatRect& objectBoundingBox, const FloatRect& repaintRect, GraphicsContext& context) 130 { 131 ClipperMaskImage& clipperMaskImage = addRendererToClipper(renderer); 132 bool shouldCreateClipperMaskImage = !clipperMaskImage; 135 bool RenderSVGResourceClipper::applyClippingToContext(RenderElement& renderer, const FloatRect& objectBoundingBox, GraphicsContext& context) 136 { 137 ClipperData& clipperData = addRendererToClipper(renderer); 138 139 LOG_WITH_STREAM(SVG, stream << "RenderSVGResourceClipper " << this << " applyClippingToContext: renderer " << &renderer << " objectBoundingBox " << objectBoundingBox << " (existing image buffer " << clipperData.imageBuffer.get() << ")"); 133 140 134 141 AffineTransform animatedLocalTransform = clipPathElement().animatedLocalTransform(); 135 142 136 if ( shouldCreateClipperMaskImage&& pathOnlyClipping(context, animatedLocalTransform, objectBoundingBox))143 if (!clipperData.imageBuffer && pathOnlyClipping(context, animatedLocalTransform, objectBoundingBox)) 137 144 return true; 138 145 139 146 AffineTransform absoluteTransform = SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer); 140 141 if (shouldCreateClipperMaskImage && !repaintRect.isEmpty()) { 147 if (!clipperData.isValidForGeometry(objectBoundingBox, absoluteTransform)) { 142 148 // FIXME (149469): This image buffer should not be unconditionally unaccelerated. Making it match the context breaks nested clipping, though. 143 clipperMaskImage = SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, ColorSpace::SRGB, RenderingMode::Unaccelerated, &context); 144 if (!clipperMaskImage) 145 return false; 146 147 GraphicsContext& maskContext = clipperMaskImage->context(); 149 auto maskImage = SVGRenderingContext::createImageBuffer(objectBoundingBox, absoluteTransform, ColorSpace::SRGB, RenderingMode::Unaccelerated, &context); 150 if (!maskImage) 151 return false; 152 153 clipperData = { WTFMove(maskImage), objectBoundingBox, absoluteTransform }; 154 155 GraphicsContext& maskContext = clipperData.imageBuffer->context(); 148 156 maskContext.concatCTM(animatedLocalTransform); 149 157 … … 155 163 GraphicsContextStateSaver stateSaver(maskContext); 156 164 157 if (!clipper->applyClippingToContext(*this, objectBoundingBox, repaintRect,maskContext))165 if (!clipper->applyClippingToContext(*this, objectBoundingBox, maskContext)) 158 166 return false; 159 167 160 succeeded = drawContentIntoMaskImage( clipperMaskImage, objectBoundingBox);168 succeeded = drawContentIntoMaskImage(*clipperData.imageBuffer, objectBoundingBox); 161 169 // The context restore applies the clipping on non-CG platforms. 162 170 } else 163 succeeded = drawContentIntoMaskImage( clipperMaskImage, objectBoundingBox);171 succeeded = drawContentIntoMaskImage(*clipperData.imageBuffer, objectBoundingBox); 164 172 165 173 if (!succeeded) 166 clipper MaskImage.reset();167 } 168 169 if (!clipper MaskImage)174 clipperData = { }; 175 } 176 177 if (!clipperData.imageBuffer) 170 178 return false; 171 179 172 SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, repaintRect, clipperMaskImage, shouldCreateClipperMaskImage);180 SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, objectBoundingBox, clipperData.imageBuffer, true); 173 181 return true; 174 182 } 175 183 176 bool RenderSVGResourceClipper::drawContentIntoMaskImage(const ClipperMaskImage& clipperMaskImage, const FloatRect& objectBoundingBox) 177 { 178 ASSERT(clipperMaskImage); 179 180 GraphicsContext& maskContext = clipperMaskImage->context(); 184 bool RenderSVGResourceClipper::drawContentIntoMaskImage(ImageBuffer& maskImageBuffer, const FloatRect& objectBoundingBox) 185 { 186 GraphicsContext& maskContext = maskImageBuffer.context(); 181 187 182 188 AffineTransform maskContentTransformation; … … 228 234 // We have to pass the <use> renderer itself to renderSubtreeToImageBuffer() to apply it's x/y/transform/etc. values when rendering. 229 235 // So if isUseElement is true, refetch the childNode->renderer(), as renderer got overridden above. 230 SVGRenderingContext::renderSubtreeToImageBuffer( clipperMaskImage.get(), isUseElement ? *child.renderer() : *renderer, maskContentTransformation);236 SVGRenderingContext::renderSubtreeToImageBuffer(&maskImageBuffer, isUseElement ? *child.renderer() : *renderer, maskContentTransformation); 231 237 } 232 238 … … 252 258 } 253 259 254 ClipperMaskImage& RenderSVGResourceClipper::addRendererToClipper(const RenderObject& object)255 { 256 return m_clipper.add(&object, Clipper MaskImage()).iterator->value;260 RenderSVGResourceClipper::ClipperData& RenderSVGResourceClipper::addRendererToClipper(const RenderObject& object) 261 { 262 return m_clipper.add(&object, ClipperData()).iterator->value; 257 263 } 258 264 … … 302 308 // Resource was not layouted yet. Give back the boundingBox of the object. 303 309 if (selfNeedsLayout()) { 304 addRendererToClipper(object); 310 addRendererToClipper(object); // For selfNeedsClientInvalidation(). 305 311 return object.objectBoundingBox(); 306 312 } -
trunk/Source/WebCore/rendering/svg/RenderSVGResourceClipper.h
r224537 r259137 31 31 class ImageBuffer; 32 32 33 typedef std::unique_ptr<ImageBuffer> ClipperMaskImage;34 35 33 class RenderSVGResourceClipper final : public RenderSVGResourceContainer { 36 34 WTF_MAKE_ISO_ALLOCATED(RenderSVGResourceClipper); … … 48 46 // applyResource directly and use the rects from the object, since they are empty for RenderSVGResources 49 47 // FIXME: We made applyClippingToContext public because we cannot call applyResource on HTML elements (it asserts on RenderObject::objectBoundingBox) 50 bool applyClippingToContext(RenderElement&, const FloatRect&, const FloatRect&,GraphicsContext&);48 bool applyClippingToContext(RenderElement&, const FloatRect&, GraphicsContext&); 51 49 FloatRect resourceBoundingBox(const RenderObject&) override; 52 50 … … 61 59 62 60 private: 61 struct ClipperData { 62 FloatRect objectBoundingBox; 63 AffineTransform absoluteTransform; 64 std::unique_ptr<ImageBuffer> imageBuffer; 65 66 ClipperData() = default; 67 ClipperData(std::unique_ptr<ImageBuffer>&& buffer, const FloatRect& boundingBox, const AffineTransform& transform) 68 : objectBoundingBox(boundingBox) 69 , absoluteTransform(transform) 70 , imageBuffer(WTFMove(buffer)) 71 { 72 } 73 74 bool isValidForGeometry(const FloatRect& boundingBox, const AffineTransform& transform) const 75 { 76 return imageBuffer && objectBoundingBox == boundingBox && absoluteTransform == transform; 77 } 78 }; 79 63 80 void element() const = delete; 64 81 … … 67 84 68 85 bool pathOnlyClipping(GraphicsContext&, const AffineTransform&, const FloatRect&); 69 bool drawContentIntoMaskImage( const ClipperMaskImage&, const FloatRect& objectBoundingBox);86 bool drawContentIntoMaskImage(ImageBuffer&, const FloatRect& objectBoundingBox); 70 87 void calculateClipContentRepaintRect(); 71 Clipper MaskImage& addRendererToClipper(const RenderObject&);88 ClipperData& addRendererToClipper(const RenderObject&); 72 89 73 90 FloatRect m_clipBoundaries; 74 HashMap<const RenderObject*, Clipper MaskImage> m_clipper;91 HashMap<const RenderObject*, ClipperData> m_clipper; 75 92 }; 76 93
Note:
See TracChangeset
for help on using the changeset viewer.