2009-01-23 David Hyatt <hyatt@apple.com>
[WebKit-https.git] / WebCore / rendering / 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 Rob Buis <buis@kde.org>
6
7     This file is part of the WebKit project
8
9     This library is free software; you can redistribute it and/or
10     modify it under the terms of the GNU Library General Public
11     License as published by the Free Software Foundation; either
12     version 2 of the License, or (at your option) any later version.
13
14     This library is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17     Library General Public License for more details.
18
19     You should have received a copy of the GNU Library General Public License
20     along with this library; see the file COPYING.LIB.  If not, write to
21     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22     Boston, MA 02110-1301, USA.
23 */
24
25 #include "config.h"
26
27 #if ENABLE(SVG)
28 #include "RenderSVGImage.h"
29
30 #include "Attr.h"
31 #include "FloatConversion.h"
32 #include "GraphicsContext.h"
33 #include "PointerEventsHitRules.h"
34 #include "SVGImageElement.h"
35 #include "SVGLength.h"
36 #include "SVGPreserveAspectRatio.h"
37 #include "SVGRenderSupport.h"
38 #include "SVGResourceClipper.h"
39 #include "SVGResourceFilter.h"
40 #include "SVGResourceMasker.h"
41
42 namespace WebCore {
43
44 RenderSVGImage::RenderSVGImage(SVGImageElement* impl)
45     : RenderImage(impl)
46 {
47 }
48
49 RenderSVGImage::~RenderSVGImage()
50 {
51 }
52
53 void RenderSVGImage::adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& srcRect, SVGPreserveAspectRatio* aspectRatio)
54 {
55     float origDestWidth = destRect.width();
56     float origDestHeight = destRect.height();
57     if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET) {
58         float widthToHeightMultiplier = srcRect.height() / srcRect.width();
59         if (origDestHeight > (origDestWidth * widthToHeightMultiplier)) {
60             destRect.setHeight(origDestWidth * widthToHeightMultiplier);
61             switch(aspectRatio->align()) {
62                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
63                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
64                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
65                     destRect.setY(destRect.y() + origDestHeight / 2.0f - destRect.height() / 2.0f);
66                     break;
67                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
68                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
69                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
70                     destRect.setY(destRect.y() + origDestHeight - destRect.height());
71                     break;
72             }
73         }
74         if (origDestWidth > (origDestHeight / widthToHeightMultiplier)) {
75             destRect.setWidth(origDestHeight / widthToHeightMultiplier);
76             switch(aspectRatio->align()) {
77                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
78                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
79                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
80                     destRect.setX(destRect.x() + origDestWidth / 2.0f - destRect.width() / 2.0f);
81                     break;
82                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
83                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
84                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
85                     destRect.setX(destRect.x() + origDestWidth - destRect.width());
86                     break;
87             }
88         }
89     } else if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE) {
90         float widthToHeightMultiplier = srcRect.height() / srcRect.width();
91         // if the destination height is less than the height of the image we'll be drawing
92         if (origDestHeight < (origDestWidth * widthToHeightMultiplier)) {
93             float destToSrcMultiplier = srcRect.width() / destRect.width();
94             srcRect.setHeight(destRect.height() * destToSrcMultiplier);
95             switch(aspectRatio->align()) {
96                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
97                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
98                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
99                     srcRect.setY(destRect.y() + image()->height() / 2.0f - srcRect.height() / 2.0f);
100                     break;
101                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
102                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
103                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
104                     srcRect.setY(destRect.y() + image()->height() - srcRect.height());
105                     break;
106             }
107         }
108         // if the destination width is less than the width of the image we'll be drawing
109         if (origDestWidth < (origDestHeight / widthToHeightMultiplier)) {
110             float destToSrcMultiplier = srcRect.height() / destRect.height();
111             srcRect.setWidth(destRect.width() * destToSrcMultiplier);
112             switch(aspectRatio->align()) {
113                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
114                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
115                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
116                     srcRect.setX(destRect.x() + image()->width() / 2.0f - srcRect.width() / 2.0f);
117                     break;
118                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
119                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
120                 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
121                     srcRect.setX(destRect.x() + image()->width() - srcRect.width());
122                     break;
123             }
124         }
125     }
126 }
127
128 bool RenderSVGImage::calculateLocalTransform()
129 {
130     TransformationMatrix oldTransform = m_localTransform;
131     m_localTransform = static_cast<SVGStyledTransformableElement*>(element())->animatedLocalTransform();
132     return (m_localTransform != oldTransform);
133 }
134
135 void RenderSVGImage::layout()
136 {
137     ASSERT(needsLayout());
138     
139     IntRect oldBounds;
140     IntRect oldOutlineBox;
141     bool checkForRepaint = checkForRepaintDuringLayout();
142     if (checkForRepaint) {
143         oldBounds = absoluteClippedOverflowRect();
144         oldOutlineBox = absoluteOutlineBounds();
145     }
146     
147     calculateLocalTransform();
148     
149     // minimum height
150     setHeight(errorOccurred() ? intrinsicSize().height() : 0);
151
152     calcWidth();
153     calcHeight();
154
155     SVGImageElement* image = static_cast<SVGImageElement*>(node());
156     m_localBounds = FloatRect(image->x().value(image), image->y().value(image), image->width().value(image), image->height().value(image));
157
158     calculateAbsoluteBounds();
159
160     if (checkForRepaint)
161         repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox);
162
163     setNeedsLayout(false);
164 }
165
166 void RenderSVGImage::paint(PaintInfo& paintInfo, int, int)
167 {
168     if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN)
169         return;
170
171     paintInfo.context->save();
172     paintInfo.context->concatCTM(localTransform());
173
174     if (paintInfo.phase == PaintPhaseForeground) {
175         SVGResourceFilter* filter = 0;
176
177         PaintInfo savedInfo(paintInfo);
178
179         prepareToRenderSVGContent(this, paintInfo, m_localBounds, filter);
180
181         FloatRect destRect = m_localBounds;
182         FloatRect srcRect(0, 0, image()->width(), image()->height());
183
184         SVGImageElement* imageElt = static_cast<SVGImageElement*>(node());
185         if (imageElt->preserveAspectRatio()->align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE)
186             adjustRectsForAspectRatio(destRect, srcRect, imageElt->preserveAspectRatio());
187
188         paintInfo.context->drawImage(image(), destRect, srcRect);
189
190         finishRenderSVGContent(this, paintInfo, m_localBounds, filter, savedInfo.context);
191     }
192     
193     paintInfo.context->restore();
194 }
195
196 bool RenderSVGImage::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int _x, int _y, int, int, HitTestAction hitTestAction)
197 {
198     // We only draw in the forground phase, so we only hit-test then.
199     if (hitTestAction != HitTestForeground)
200         return false;
201
202     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, style()->pointerEvents());
203     
204     bool isVisible = (style()->visibility() == VISIBLE);
205     if (isVisible || !hitRules.requireVisible) {
206         double localX, localY;
207         absoluteTransform().inverse().map(_x, _y, &localX, &localY);
208
209         if (hitRules.canHitFill) {
210             if (m_localBounds.contains(narrowPrecisionToFloat(localX), narrowPrecisionToFloat(localY))) {
211                 updateHitTestResult(result, IntPoint(_x, _y));
212                 return true;
213             }
214         }
215     }
216
217     return false;
218 }
219
220 FloatRect RenderSVGImage::relativeBBox(bool) const
221 {
222     return m_localBounds;
223 }
224
225 void RenderSVGImage::imageChanged(WrappedImagePtr image, const IntRect* rect)
226 {
227     RenderImage::imageChanged(image, rect);
228
229     // We override to invalidate a larger rect, since SVG images can draw outside their "bounds"
230     repaintRectangle(absoluteClippedOverflowRect());
231 }
232
233 void RenderSVGImage::calculateAbsoluteBounds()
234 {
235     // FIXME: broken with CSS transforms
236     FloatRect absoluteRect = absoluteTransform().mapRect(relativeBBox(true));
237
238 #if ENABLE(SVG_FILTERS)
239     // Filters can expand the bounding box
240     SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter());
241     if (filter)
242         absoluteRect.unite(filter->filterBBoxForItemBBox(absoluteRect));
243 #endif
244
245     if (!absoluteRect.isEmpty())
246         absoluteRect.inflate(1); // inflate 1 pixel for antialiasing
247
248     m_absoluteBounds = enclosingIntRect(absoluteRect);
249 }
250
251 IntRect RenderSVGImage::absoluteClippedOverflowRect()
252 {
253     return m_absoluteBounds;
254 }
255
256 void RenderSVGImage::addFocusRingRects(GraphicsContext* graphicsContext, int, int)
257 {
258     // this is called from paint() after the localTransform has already been applied
259     IntRect contentRect = enclosingIntRect(relativeBBox());
260     graphicsContext->addFocusRingRect(contentRect);
261 }
262
263 void RenderSVGImage::absoluteRects(Vector<IntRect>& rects, int, int, bool)
264 {
265     rects.append(absoluteClippedOverflowRect());
266 }
267
268 void RenderSVGImage::absoluteQuads(Vector<FloatQuad>& quads, bool)
269 {
270     quads.append(FloatRect(absoluteClippedOverflowRect()));
271 }
272
273 }
274
275 #endif // ENABLE(SVG)