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