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
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25
26 #if ENABLE(SVG)
27 #include "RenderSVGImage.h"
28
29 #include "Attr.h"
30 #include "FloatConversion.h"
31 #include "FloatQuad.h"
32 #include "GraphicsContext.h"
33 #include "PointerEventsHitRules.h"
34 #include "RenderLayer.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
43 namespace WebCore {
44
RenderSVGImage(SVGImageElement * impl)45 RenderSVGImage::RenderSVGImage(SVGImageElement* impl)
46 : RenderImage(impl)
47 {
48 }
49
adjustRectsForAspectRatio(FloatRect & destRect,FloatRect & srcRect,SVGPreserveAspectRatio * aspectRatio)50 void RenderSVGImage::adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& srcRect, SVGPreserveAspectRatio* aspectRatio)
51 {
52 float origDestWidth = destRect.width();
53 float origDestHeight = destRect.height();
54 if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET) {
55 float widthToHeightMultiplier = srcRect.height() / srcRect.width();
56 if (origDestHeight > (origDestWidth * widthToHeightMultiplier)) {
57 destRect.setHeight(origDestWidth * widthToHeightMultiplier);
58 switch (aspectRatio->align()) {
59 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
60 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
61 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
62 destRect.setY(destRect.y() + origDestHeight / 2.0f - destRect.height() / 2.0f);
63 break;
64 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
65 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
66 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
67 destRect.setY(destRect.y() + origDestHeight - destRect.height());
68 break;
69 }
70 }
71 if (origDestWidth > (origDestHeight / widthToHeightMultiplier)) {
72 destRect.setWidth(origDestHeight / widthToHeightMultiplier);
73 switch (aspectRatio->align()) {
74 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
75 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
76 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
77 destRect.setX(destRect.x() + origDestWidth / 2.0f - destRect.width() / 2.0f);
78 break;
79 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
80 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
81 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
82 destRect.setX(destRect.x() + origDestWidth - destRect.width());
83 break;
84 }
85 }
86 } else if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE) {
87 float widthToHeightMultiplier = srcRect.height() / srcRect.width();
88 // if the destination height is less than the height of the image we'll be drawing
89 if (origDestHeight < (origDestWidth * widthToHeightMultiplier)) {
90 float destToSrcMultiplier = srcRect.width() / destRect.width();
91 srcRect.setHeight(destRect.height() * destToSrcMultiplier);
92 switch (aspectRatio->align()) {
93 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
94 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
95 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
96 srcRect.setY(destRect.y() + image()->height() / 2.0f - srcRect.height() / 2.0f);
97 break;
98 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
99 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
100 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
101 srcRect.setY(destRect.y() + image()->height() - srcRect.height());
102 break;
103 }
104 }
105 // if the destination width is less than the width of the image we'll be drawing
106 if (origDestWidth < (origDestHeight / widthToHeightMultiplier)) {
107 float destToSrcMultiplier = srcRect.height() / destRect.height();
108 srcRect.setWidth(destRect.width() * destToSrcMultiplier);
109 switch (aspectRatio->align()) {
110 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
111 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
112 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
113 srcRect.setX(destRect.x() + image()->width() / 2.0f - srcRect.width() / 2.0f);
114 break;
115 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
116 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
117 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
118 srcRect.setX(destRect.x() + image()->width() - srcRect.width());
119 break;
120 }
121 }
122 }
123 }
124
layout()125 void RenderSVGImage::layout()
126 {
127 ASSERT(needsLayout());
128
129 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
130
131 SVGImageElement* image = static_cast<SVGImageElement*>(node());
132 m_localTransform = image->animatedLocalTransform();
133
134 // minimum height
135 setHeight(errorOccurred() ? intrinsicSize().height() : 0);
136
137 calcWidth();
138 calcHeight();
139
140 m_localBounds = FloatRect(image->x().value(image), image->y().value(image), image->width().value(image), image->height().value(image));
141
142 repainter.repaintAfterLayout();
143
144 setNeedsLayout(false);
145 }
146
paint(PaintInfo & paintInfo,int,int)147 void RenderSVGImage::paint(PaintInfo& paintInfo, int, int)
148 {
149 if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN)
150 return;
151
152 paintInfo.context->save();
153 paintInfo.context->concatCTM(localToParentTransform());
154
155 if (paintInfo.phase == PaintPhaseForeground) {
156 SVGResourceFilter* filter = 0;
157
158 PaintInfo savedInfo(paintInfo);
159
160 prepareToRenderSVGContent(this, paintInfo, m_localBounds, filter);
161
162 FloatRect destRect = m_localBounds;
163 FloatRect srcRect(0, 0, image()->width(), image()->height());
164
165 SVGImageElement* imageElt = static_cast<SVGImageElement*>(node());
166 if (imageElt->preserveAspectRatio()->align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE)
167 adjustRectsForAspectRatio(destRect, srcRect, imageElt->preserveAspectRatio());
168
169 paintInfo.context->drawImage(image(), destRect, srcRect);
170 finishRenderSVGContent(this, paintInfo, filter, savedInfo.context);
171 }
172
173 if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
174 paintOutline(paintInfo.context, 0, 0, width(), height(), style());
175
176 paintInfo.context->restore();
177 }
178
nodeAtFloatPoint(const HitTestRequest &,HitTestResult & result,const FloatPoint & pointInParent,HitTestAction hitTestAction)179 bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest&, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
180 {
181 // We only draw in the forground phase, so we only hit-test then.
182 if (hitTestAction != HitTestForeground)
183 return false;
184
185 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, style()->pointerEvents());
186
187 bool isVisible = (style()->visibility() == VISIBLE);
188 if (isVisible || !hitRules.requireVisible) {
189 FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
190
191 if (hitRules.canHitFill) {
192 if (m_localBounds.contains(localPoint)) {
193 updateHitTestResult(result, roundedIntPoint(localPoint));
194 return true;
195 }
196 }
197 }
198
199 return false;
200 }
201
nodeAtPoint(const HitTestRequest &,HitTestResult &,int,int,int,int,HitTestAction)202 bool RenderSVGImage::nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int, HitTestAction)
203 {
204 ASSERT_NOT_REACHED();
205 return false;
206 }
207
objectBoundingBox() const208 FloatRect RenderSVGImage::objectBoundingBox() const
209 {
210 return m_localBounds;
211 }
212
repaintRectInLocalCoordinates() const213 FloatRect RenderSVGImage::repaintRectInLocalCoordinates() const
214 {
215 FloatRect repaintRect = m_localBounds;
216
217 // Filters can paint outside the image content
218 repaintRect.unite(filterBoundingBoxForRenderer(this));
219
220 return repaintRect;
221 }
222
imageChanged(WrappedImagePtr image,const IntRect * rect)223 void RenderSVGImage::imageChanged(WrappedImagePtr image, const IntRect* rect)
224 {
225 RenderImage::imageChanged(image, rect);
226 repaint();
227 }
228
clippedOverflowRectForRepaint(RenderBoxModelObject * repaintContainer)229 IntRect RenderSVGImage::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
230 {
231 return SVGRenderBase::clippedOverflowRectForRepaint(this, repaintContainer);
232 }
233
computeRectForRepaint(RenderBoxModelObject * repaintContainer,IntRect & repaintRect,bool fixed)234 void RenderSVGImage::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
235 {
236 SVGRenderBase::computeRectForRepaint(this, repaintContainer, repaintRect, fixed);
237 }
238
mapLocalToContainer(RenderBoxModelObject * repaintContainer,bool fixed,bool useTransforms,TransformState & transformState) const239 void RenderSVGImage::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) const
240 {
241 SVGRenderBase::mapLocalToContainer(this, repaintContainer, fixed, useTransforms, transformState);
242 }
243
addFocusRingRects(GraphicsContext * graphicsContext,int,int)244 void RenderSVGImage::addFocusRingRects(GraphicsContext* graphicsContext, int, int)
245 {
246 // this is called from paint() after the localTransform has already been applied
247 IntRect contentRect = enclosingIntRect(repaintRectInLocalCoordinates());
248 graphicsContext->addFocusRingRect(contentRect);
249 }
250
absoluteRects(Vector<IntRect> & rects,int,int)251 void RenderSVGImage::absoluteRects(Vector<IntRect>& rects, int, int)
252 {
253 rects.append(absoluteClippedOverflowRect());
254 }
255
absoluteQuads(Vector<FloatQuad> & quads)256 void RenderSVGImage::absoluteQuads(Vector<FloatQuad>& quads)
257 {
258 quads.append(FloatRect(absoluteClippedOverflowRect()));
259 }
260
261 }
262
263 #endif // ENABLE(SVG)
264