RenderSVGImage.cpp revision 635860845790a19bf50bbc51ba8fb66a96dde068
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 "FloatQuad.h"
33#include "GraphicsContext.h"
34#include "PointerEventsHitRules.h"
35#include "SVGImageElement.h"
36#include "SVGLength.h"
37#include "SVGPreserveAspectRatio.h"
38#include "SVGRenderSupport.h"
39#include "SVGResourceClipper.h"
40#include "SVGResourceFilter.h"
41#include "SVGResourceMasker.h"
42
43namespace WebCore {
44
45RenderSVGImage::RenderSVGImage(SVGImageElement* impl)
46    : RenderImage(impl)
47{
48}
49
50RenderSVGImage::~RenderSVGImage()
51{
52}
53
54void RenderSVGImage::adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& srcRect, SVGPreserveAspectRatio* aspectRatio)
55{
56    float origDestWidth = destRect.width();
57    float origDestHeight = destRect.height();
58    if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET) {
59        float widthToHeightMultiplier = srcRect.height() / srcRect.width();
60        if (origDestHeight > (origDestWidth * widthToHeightMultiplier)) {
61            destRect.setHeight(origDestWidth * widthToHeightMultiplier);
62            switch(aspectRatio->align()) {
63                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
64                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
65                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
66                    destRect.setY(destRect.y() + origDestHeight / 2.0f - destRect.height() / 2.0f);
67                    break;
68                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
69                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
70                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
71                    destRect.setY(destRect.y() + origDestHeight - destRect.height());
72                    break;
73            }
74        }
75        if (origDestWidth > (origDestHeight / widthToHeightMultiplier)) {
76            destRect.setWidth(origDestHeight / widthToHeightMultiplier);
77            switch(aspectRatio->align()) {
78                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
79                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
80                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
81                    destRect.setX(destRect.x() + origDestWidth / 2.0f - destRect.width() / 2.0f);
82                    break;
83                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
84                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
85                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
86                    destRect.setX(destRect.x() + origDestWidth - destRect.width());
87                    break;
88            }
89        }
90    } else if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE) {
91        float widthToHeightMultiplier = srcRect.height() / srcRect.width();
92        // if the destination height is less than the height of the image we'll be drawing
93        if (origDestHeight < (origDestWidth * widthToHeightMultiplier)) {
94            float destToSrcMultiplier = srcRect.width() / destRect.width();
95            srcRect.setHeight(destRect.height() * destToSrcMultiplier);
96            switch(aspectRatio->align()) {
97                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
98                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
99                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
100                    srcRect.setY(destRect.y() + image()->height() / 2.0f - srcRect.height() / 2.0f);
101                    break;
102                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
103                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
104                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
105                    srcRect.setY(destRect.y() + image()->height() - srcRect.height());
106                    break;
107            }
108        }
109        // if the destination width is less than the width of the image we'll be drawing
110        if (origDestWidth < (origDestHeight / widthToHeightMultiplier)) {
111            float destToSrcMultiplier = srcRect.height() / destRect.height();
112            srcRect.setWidth(destRect.width() * destToSrcMultiplier);
113            switch(aspectRatio->align()) {
114                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
115                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
116                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
117                    srcRect.setX(destRect.x() + image()->width() / 2.0f - srcRect.width() / 2.0f);
118                    break;
119                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
120                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
121                case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
122                    srcRect.setX(destRect.x() + image()->width() - srcRect.width());
123                    break;
124            }
125        }
126    }
127}
128
129bool RenderSVGImage::calculateLocalTransform()
130{
131    TransformationMatrix oldTransform = m_localTransform;
132    m_localTransform = static_cast<SVGStyledTransformableElement*>(element())->animatedLocalTransform();
133    return (m_localTransform != oldTransform);
134}
135
136void RenderSVGImage::layout()
137{
138    ASSERT(needsLayout());
139
140    IntRect oldBounds;
141    IntRect oldOutlineBox;
142    bool checkForRepaint = checkForRepaintDuringLayout();
143    if (checkForRepaint) {
144        oldBounds = absoluteClippedOverflowRect();
145        oldOutlineBox = absoluteOutlineBounds();
146    }
147
148    calculateLocalTransform();
149
150    // minimum height
151    setHeight(errorOccurred() ? intrinsicSize().height() : 0);
152
153    calcWidth();
154    calcHeight();
155
156    SVGImageElement* image = static_cast<SVGImageElement*>(node());
157    m_localBounds = FloatRect(image->x().value(image), image->y().value(image), image->width().value(image), image->height().value(image));
158
159    calculateAbsoluteBounds();
160
161    if (checkForRepaint)
162        repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox);
163
164    setNeedsLayout(false);
165}
166
167void RenderSVGImage::paint(PaintInfo& paintInfo, int, int)
168{
169    if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN)
170        return;
171
172    paintInfo.context->save();
173    paintInfo.context->concatCTM(localTransform());
174
175    if (paintInfo.phase == PaintPhaseForeground) {
176        SVGResourceFilter* filter = 0;
177
178        PaintInfo savedInfo(paintInfo);
179
180        prepareToRenderSVGContent(this, paintInfo, m_localBounds, filter);
181
182        FloatRect destRect = m_localBounds;
183        FloatRect srcRect(0, 0, image()->width(), image()->height());
184
185        SVGImageElement* imageElt = static_cast<SVGImageElement*>(node());
186        if (imageElt->preserveAspectRatio()->align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE)
187            adjustRectsForAspectRatio(destRect, srcRect, imageElt->preserveAspectRatio());
188
189        paintInfo.context->drawImage(image(), destRect, srcRect);
190
191        finishRenderSVGContent(this, paintInfo, m_localBounds, filter, savedInfo.context);
192    }
193
194    paintInfo.context->restore();
195}
196
197bool RenderSVGImage::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int _x, int _y, int, int, HitTestAction hitTestAction)
198{
199    // We only draw in the forground phase, so we only hit-test then.
200    if (hitTestAction != HitTestForeground)
201        return false;
202
203    PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, style()->pointerEvents());
204
205    bool isVisible = (style()->visibility() == VISIBLE);
206    if (isVisible || !hitRules.requireVisible) {
207        double localX, localY;
208        absoluteTransform().inverse().map(_x, _y, &localX, &localY);
209
210        if (hitRules.canHitFill) {
211            if (m_localBounds.contains(narrowPrecisionToFloat(localX), narrowPrecisionToFloat(localY))) {
212                updateHitTestResult(result, IntPoint(_x, _y));
213                return true;
214            }
215        }
216    }
217
218    return false;
219}
220
221FloatRect RenderSVGImage::relativeBBox(bool) const
222{
223    return m_localBounds;
224}
225
226void RenderSVGImage::imageChanged(WrappedImagePtr image, const IntRect* rect)
227{
228    RenderImage::imageChanged(image, rect);
229
230    // We override to invalidate a larger rect, since SVG images can draw outside their "bounds"
231    repaintRectangle(absoluteClippedOverflowRect());
232}
233
234void RenderSVGImage::calculateAbsoluteBounds()
235{
236    // FIXME: broken with CSS transforms
237    FloatRect absoluteRect = absoluteTransform().mapRect(relativeBBox(true));
238
239#if ENABLE(SVG_FILTERS)
240    // Filters can expand the bounding box
241    SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter());
242    if (filter)
243        absoluteRect.unite(filter->filterBBoxForItemBBox(absoluteRect));
244#endif
245
246    if (!absoluteRect.isEmpty())
247        absoluteRect.inflate(1); // inflate 1 pixel for antialiasing
248
249    m_absoluteBounds = enclosingIntRect(absoluteRect);
250}
251
252IntRect RenderSVGImage::clippedOverflowRectForRepaint(RenderBox* /*repaintContainer*/)
253{
254    // FIXME: handle non-root repaintContainer
255    return m_absoluteBounds;
256}
257
258void RenderSVGImage::addFocusRingRects(GraphicsContext* graphicsContext, int, int)
259{
260    // this is called from paint() after the localTransform has already been applied
261    IntRect contentRect = enclosingIntRect(relativeBBox());
262    graphicsContext->addFocusRingRect(contentRect);
263}
264
265void RenderSVGImage::absoluteRects(Vector<IntRect>& rects, int, int, bool)
266{
267    rects.append(absoluteClippedOverflowRect());
268}
269
270void RenderSVGImage::absoluteQuads(Vector<FloatQuad>& quads, bool)
271{
272    quads.append(FloatRect(absoluteClippedOverflowRect()));
273}
274
275}
276
277#endif // ENABLE(SVG)
278