Replace WTF::move with WTFMove
[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 "LayoutRepainter.h"
32 #include "PointerEventsHitRules.h"
33 #include "RenderImageResource.h"
34 #include "RenderLayer.h"
35 #include "RenderSVGResource.h"
36 #include "RenderSVGResourceFilter.h"
37 #include "SVGImageElement.h"
38 #include "SVGLength.h"
39 #include "SVGRenderingContext.h"
40 #include "SVGResources.h"
41 #include "SVGResourcesCache.h"
42 #include <wtf/StackStats.h>
43
44 namespace WebCore {
45
46 RenderSVGImage::RenderSVGImage(SVGImageElement& element, Ref<RenderStyle>&& style)
47     : RenderSVGModelObject(element, WTFMove(style))
48     , m_needsBoundariesUpdate(true)
49     , m_needsTransformUpdate(true)
50     , m_imageResource(std::make_unique<RenderImageResource>())
51 {
52     imageResource().initialize(this);
53 }
54
55 RenderSVGImage::~RenderSVGImage()
56 {
57     imageResource().shutdown();
58 }
59
60 SVGImageElement& RenderSVGImage::imageElement() const
61 {
62     return downcast<SVGImageElement>(RenderSVGModelObject::element());
63 }
64
65 bool RenderSVGImage::updateImageViewport()
66 {
67     FloatRect oldBoundaries = m_objectBoundingBox;
68     bool updatedViewport = false;
69
70     SVGLengthContext lengthContext(&imageElement());
71     m_objectBoundingBox = FloatRect(imageElement().x().value(lengthContext), imageElement().y().value(lengthContext), imageElement().width().value(lengthContext), imageElement().height().value(lengthContext));
72
73     // Images with preserveAspectRatio=none should force non-uniform scaling. This can be achieved
74     // by setting the image's container size to its intrinsic size.
75     // See: http://www.w3.org/TR/SVG/single-page.html, 7.8 The â€˜preserveAspectRatio’ attribute.
76     if (imageElement().preserveAspectRatio().align() == SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) {
77         if (CachedImage* cachedImage = imageResource().cachedImage()) {
78             LayoutSize intrinsicSize = cachedImage->imageSizeForRenderer(0, style().effectiveZoom());
79             if (intrinsicSize != imageResource().imageSize(style().effectiveZoom())) {
80                 imageResource().setContainerSizeForRenderer(roundedIntSize(intrinsicSize));
81                 updatedViewport = true;
82             }
83         }
84     }
85
86     if (oldBoundaries != m_objectBoundingBox) {
87         if (!updatedViewport)
88             imageResource().setContainerSizeForRenderer(enclosingIntRect(m_objectBoundingBox).size());
89         updatedViewport = true;
90         m_needsBoundariesUpdate = true;
91     }
92
93     return updatedViewport;
94 }
95
96 void RenderSVGImage::layout()
97 {
98     StackStats::LayoutCheckPoint layoutCheckPoint;
99     ASSERT(needsLayout());
100
101     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(*this) && selfNeedsLayout());
102     updateImageViewport();
103
104     bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_needsBoundariesUpdate;
105     if (m_needsTransformUpdate) {
106         m_localTransform = imageElement().animatedLocalTransform();
107         m_needsTransformUpdate = false;
108     }
109
110     if (m_needsBoundariesUpdate) {
111         m_repaintBoundingBoxExcludingShadow = m_objectBoundingBox;
112         SVGRenderSupport::intersectRepaintRectWithResources(*this, m_repaintBoundingBoxExcludingShadow);
113
114         m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow;
115         SVGRenderSupport::intersectRepaintRectWithShadows(*this, m_repaintBoundingBox);
116
117         m_needsBoundariesUpdate = false;
118     }
119
120     // Invalidate all resources of this client if our layout changed.
121     if (everHadLayout() && selfNeedsLayout())
122         SVGResourcesCache::clientLayoutChanged(*this);
123
124     // If our bounds changed, notify the parents.
125     if (transformOrBoundariesUpdate)
126         RenderSVGModelObject::setNeedsBoundariesUpdate();
127
128     repainter.repaintAfterLayout();
129     clearNeedsLayout();
130 }
131
132 void RenderSVGImage::paint(PaintInfo& paintInfo, const LayoutPoint&)
133 {
134     if (paintInfo.context().paintingDisabled() || paintInfo.phase != PaintPhaseForeground
135         || style().visibility() == HIDDEN || !imageResource().hasImage())
136         return;
137
138     FloatRect boundingBox = repaintRectInLocalCoordinates();
139     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
140         return;
141
142     PaintInfo childPaintInfo(paintInfo);
143     GraphicsContextStateSaver stateSaver(childPaintInfo.context());
144     childPaintInfo.applyTransform(m_localTransform);
145
146     if (childPaintInfo.phase == PaintPhaseForeground) {
147         SVGRenderingContext renderingContext(*this, childPaintInfo);
148
149         if (renderingContext.isRenderingPrepared()) {
150             if (style().svgStyle().bufferedRendering() == BR_STATIC && renderingContext.bufferForeground(m_bufferedForeground))
151                 return;
152
153             paintForeground(childPaintInfo);
154         }
155     }
156
157     if (style().outlineWidth())
158         paintOutline(childPaintInfo, IntRect(boundingBox));
159 }
160
161 void RenderSVGImage::paintForeground(PaintInfo& paintInfo)
162 {
163     RefPtr<Image> image = imageResource().image();
164     if (!image)
165         return;
166
167     FloatRect destRect = m_objectBoundingBox;
168     FloatRect srcRect(0, 0, image->width(), image->height());
169
170     imageElement().preserveAspectRatio().transformRect(destRect, srcRect);
171
172     paintInfo.context().drawImage(*image, destRect, srcRect);
173 }
174
175 void RenderSVGImage::invalidateBufferedForeground()
176 {
177     m_bufferedForeground.reset();
178 }
179
180 bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
181 {
182     // We only draw in the forground phase, so we only hit-test then.
183     if (hitTestAction != HitTestForeground)
184         return false;
185
186     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style().pointerEvents());
187     bool isVisible = (style().visibility() == VISIBLE);
188     if (isVisible || !hitRules.requireVisible) {
189         FloatPoint localPoint = localToParentTransform().inverse().valueOr(AffineTransform()).mapPoint(pointInParent);
190             
191         if (!SVGRenderSupport::pointInClippingArea(*this, localPoint))
192             return false;
193
194         if (hitRules.canHitFill) {
195             if (m_objectBoundingBox.contains(localPoint)) {
196                 updateHitTestResult(result, LayoutPoint(localPoint));
197                 return true;
198             }
199         }
200     }
201
202     return false;
203 }
204
205 void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*)
206 {
207     // The image resource defaults to nullImage until the resource arrives.
208     // This empty image may be cached by SVG resources which must be invalidated.
209     if (auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this))
210         resources->removeClientFromCache(*this);
211
212     // Eventually notify parent resources, that we've changed.
213     RenderSVGResource::markForLayoutAndParentResourceInvalidation(*this, false);
214
215     // Update the SVGImageCache sizeAndScales entry in case image loading finished after layout.
216     // (https://bugs.webkit.org/show_bug.cgi?id=99489)
217     m_objectBoundingBox = FloatRect();
218     updateImageViewport();
219
220     invalidateBufferedForeground();
221
222     repaint();
223 }
224
225 void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
226 {
227     // this is called from paint() after the localTransform has already been applied
228     IntRect contentRect = enclosingIntRect(repaintRectInLocalCoordinates());
229     if (!contentRect.isEmpty())
230         rects.append(contentRect);
231 }
232
233 } // namespace WebCore