94c56ef9a50962ea4d63540f291c8b05f0e5ed13
[WebKit-https.git] / Source / WebCore / rendering / svg / RenderSVGImage.cpp
1 /*
2  * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
3  * Copyright (C) 2006 Apple Inc.
4  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
5  * Copyright (C) 2007, 2008, 2009 Rob Buis <buis@kde.org>
6  * Copyright (C) 2009 Google, Inc.
7  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
8  * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25
26 #include "config.h"
27 #include "RenderSVGImage.h"
28
29 #include "FloatQuad.h"
30 #include "GraphicsContext.h"
31 #include "HitTestResult.h"
32 #include "LayoutRepainter.h"
33 #include "PointerEventsHitRules.h"
34 #include "RenderImageResource.h"
35 #include "RenderLayer.h"
36 #include "RenderSVGResource.h"
37 #include "RenderSVGResourceFilter.h"
38 #include "SVGImageElement.h"
39 #include "SVGRenderingContext.h"
40 #include "SVGResources.h"
41 #include "SVGResourcesCache.h"
42 #include <wtf/IsoMallocInlines.h>
43 #include <wtf/StackStats.h>
44
45 namespace WebCore {
46
47 WTF_MAKE_ISO_ALLOCATED_IMPL(RenderSVGImage);
48
49 RenderSVGImage::RenderSVGImage(SVGImageElement& element, RenderStyle&& style)
50     : RenderSVGModelObject(element, WTFMove(style))
51     , m_needsBoundariesUpdate(true)
52     , m_needsTransformUpdate(true)
53     , m_imageResource(std::make_unique<RenderImageResource>())
54 {
55     imageResource().initialize(*this);
56 }
57
58 RenderSVGImage::~RenderSVGImage() = default;
59
60 void RenderSVGImage::willBeDestroyed()
61 {
62     imageResource().shutdown();
63     RenderSVGModelObject::willBeDestroyed();
64 }
65
66 SVGImageElement& RenderSVGImage::imageElement() const
67 {
68     return downcast<SVGImageElement>(RenderSVGModelObject::element());
69 }
70
71 bool RenderSVGImage::updateImageViewport()
72 {
73     FloatRect oldBoundaries = m_objectBoundingBox;
74     bool updatedViewport = false;
75
76     SVGLengthContext lengthContext(&imageElement());
77     m_objectBoundingBox = FloatRect(imageElement().x().value(lengthContext), imageElement().y().value(lengthContext), imageElement().width().value(lengthContext), imageElement().height().value(lengthContext));
78
79     URL imageSourceURL = document().completeURL(imageElement().imageSourceURL());
80
81     // Images with preserveAspectRatio=none should force non-uniform scaling. This can be achieved
82     // by setting the image's container size to its intrinsic size.
83     // See: http://www.w3.org/TR/SVG/single-page.html, 7.8 The â€˜preserveAspectRatio’ attribute.
84     if (imageElement().preserveAspectRatio().align() == SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_NONE) {
85         if (CachedImage* cachedImage = imageResource().cachedImage()) {
86             LayoutSize intrinsicSize = cachedImage->imageSizeForRenderer(nullptr, style().effectiveZoom());
87             if (intrinsicSize != imageResource().imageSize(style().effectiveZoom())) {
88                 imageResource().setContainerContext(roundedIntSize(intrinsicSize), imageSourceURL);
89                 updatedViewport = true;
90             }
91         }
92     }
93
94     if (oldBoundaries != m_objectBoundingBox) {
95         if (!updatedViewport)
96             imageResource().setContainerContext(enclosingIntRect(m_objectBoundingBox).size(), imageSourceURL);
97         updatedViewport = true;
98         m_needsBoundariesUpdate = true;
99     }
100
101     return updatedViewport;
102 }
103
104 void RenderSVGImage::layout()
105 {
106     StackStats::LayoutCheckPoint layoutCheckPoint;
107     ASSERT(needsLayout());
108
109     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(*this) && selfNeedsLayout());
110     updateImageViewport();
111
112     bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_needsBoundariesUpdate;
113     if (m_needsTransformUpdate) {
114         m_localTransform = imageElement().animatedLocalTransform();
115         m_needsTransformUpdate = false;
116     }
117
118     if (m_needsBoundariesUpdate) {
119         m_repaintBoundingBoxExcludingShadow = m_objectBoundingBox;
120         SVGRenderSupport::intersectRepaintRectWithResources(*this, m_repaintBoundingBoxExcludingShadow);
121
122         m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow;
123         SVGRenderSupport::intersectRepaintRectWithShadows(*this, m_repaintBoundingBox);
124
125         m_needsBoundariesUpdate = false;
126     }
127
128     // Invalidate all resources of this client if our layout changed.
129     if (everHadLayout() && selfNeedsLayout())
130         SVGResourcesCache::clientLayoutChanged(*this);
131
132     // If our bounds changed, notify the parents.
133     if (transformOrBoundariesUpdate)
134         RenderSVGModelObject::setNeedsBoundariesUpdate();
135
136     repainter.repaintAfterLayout();
137     clearNeedsLayout();
138 }
139
140 void RenderSVGImage::paint(PaintInfo& paintInfo, const LayoutPoint&)
141 {
142     if (paintInfo.context().paintingDisabled() || paintInfo.phase != PaintPhaseForeground
143         || style().visibility() == Visibility::Hidden || !imageResource().cachedImage())
144         return;
145
146     FloatRect boundingBox = repaintRectInLocalCoordinates();
147     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
148         return;
149
150     PaintInfo childPaintInfo(paintInfo);
151     GraphicsContextStateSaver stateSaver(childPaintInfo.context());
152     childPaintInfo.applyTransform(m_localTransform);
153
154     if (childPaintInfo.phase == PaintPhaseForeground) {
155         SVGRenderingContext renderingContext(*this, childPaintInfo);
156
157         if (renderingContext.isRenderingPrepared()) {
158             if (style().svgStyle().bufferedRendering() == BufferedRendering::Static && renderingContext.bufferForeground(m_bufferedForeground))
159                 return;
160
161             paintForeground(childPaintInfo);
162         }
163     }
164
165     if (style().outlineWidth())
166         paintOutline(childPaintInfo, IntRect(boundingBox));
167 }
168
169 void RenderSVGImage::paintForeground(PaintInfo& paintInfo)
170 {
171     RefPtr<Image> image = imageResource().image();
172     if (!image)
173         return;
174
175     FloatRect destRect = m_objectBoundingBox;
176     FloatRect srcRect(0, 0, image->width(), image->height());
177
178     imageElement().preserveAspectRatio().transformRect(destRect, srcRect);
179
180     paintInfo.context().drawImage(*image, destRect, srcRect);
181 }
182
183 void RenderSVGImage::invalidateBufferedForeground()
184 {
185     m_bufferedForeground.reset();
186 }
187
188 bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
189 {
190     // We only draw in the forground phase, so we only hit-test then.
191     if (hitTestAction != HitTestForeground)
192         return false;
193
194     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style().pointerEvents());
195     bool isVisible = (style().visibility() == Visibility::Visible);
196     if (isVisible || !hitRules.requireVisible) {
197         FloatPoint localPoint = localToParentTransform().inverse().value_or(AffineTransform()).mapPoint(pointInParent);
198             
199         if (!SVGRenderSupport::pointInClippingArea(*this, localPoint))
200             return false;
201
202         if (hitRules.canHitFill) {
203             if (m_objectBoundingBox.contains(localPoint)) {
204                 updateHitTestResult(result, LayoutPoint(localPoint));
205                 if (result.addNodeToListBasedTestResult(&imageElement(), request, localPoint) == HitTestProgress::Stop)
206                     return true;
207             }
208         }
209     }
210
211     return false;
212 }
213
214 void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*)
215 {
216     // The image resource defaults to nullImage until the resource arrives.
217     // This empty image may be cached by SVG resources which must be invalidated.
218     if (auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this))
219         resources->removeClientFromCache(*this);
220
221     // Eventually notify parent resources, that we've changed.
222     RenderSVGResource::markForLayoutAndParentResourceInvalidation(*this, false);
223
224     // Update the SVGImageCache sizeAndScales entry in case image loading finished after layout.
225     // (https://bugs.webkit.org/show_bug.cgi?id=99489)
226     m_objectBoundingBox = FloatRect();
227     updateImageViewport();
228
229     invalidateBufferedForeground();
230
231     repaint();
232 }
233
234 void RenderSVGImage::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
235 {
236     // this is called from paint() after the localTransform has already been applied
237     LayoutRect contentRect = LayoutRect(repaintRectInLocalCoordinates());
238     if (!contentRect.isEmpty())
239         rects.append(contentRect);
240 }
241
242 } // namespace WebCore