a65f0bc0bbe1f1f99a542966fccb83463254318f
[WebKit-https.git] / WebCore / kcanvas / RenderSVGImage.cpp
1 /*
2     Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
3     Copyright (C) 2006 Apple Computer, Inc.
4
5     This file is part of the WebKit project
6
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Library General Public
9     License as published by the Free Software Foundation; either
10     version 2 of the License, or (at your option) any later version.
11
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Library General Public License for more details.
16
17     You should have received a copy of the GNU Library General Public License
18     along with this library; see the file COPYING.LIB.  If not, write to
19     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20     Boston, MA 02111-1307, USA.
21 */
22
23 #include "config.h"
24 #if SVG_SUPPORT
25 #include "RenderSVGImage.h"
26
27 #include "Attr.h"
28 #include "GraphicsContext.h"
29 #include "KCanvasMaskerQuartz.h"
30 #include "KCanvasRenderingStyle.h"
31 #include "KCanvasResourcesQuartz.h"
32 #include "KRenderingDevice.h"
33 #include "SVGAnimatedLength.h"
34 #include "SVGAnimatedPreserveAspectRatio.h"
35 #include "SVGImageElement.h"
36 #include "SVGImageElement.h"
37 #include "ksvg.h"
38 #include <wtf/OwnPtr.h>
39
40 namespace WebCore {
41
42 RenderSVGImage::RenderSVGImage(SVGImageElement *impl)
43 : RenderImage(impl)
44 {
45 }
46
47 RenderSVGImage::~RenderSVGImage()
48 {
49 }
50
51 void RenderSVGImage::adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& srcRect, SVGPreserveAspectRatio *aspectRatio)
52 {
53     float origDestWidth = destRect.width();
54     float origDestHeight = destRect.height();
55     if (aspectRatio->meetOrSlice() == SVG_MEETORSLICE_MEET) {
56         float widthToHeightMultiplier = srcRect.height() / srcRect.width();
57         if (origDestHeight > (origDestWidth * widthToHeightMultiplier)) {
58             destRect.setHeight(origDestWidth * widthToHeightMultiplier);
59             switch(aspectRatio->align()) {
60                 case SVG_PRESERVEASPECTRATIO_XMINYMID:
61                 case SVG_PRESERVEASPECTRATIO_XMIDYMID:
62                 case SVG_PRESERVEASPECTRATIO_XMAXYMID:
63                     destRect.setY(origDestHeight / 2 - destRect.height() / 2);
64                     break;
65                 case SVG_PRESERVEASPECTRATIO_XMINYMAX:
66                 case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
67                 case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
68                     destRect.setY(origDestHeight - destRect.height());
69                     break;
70             }
71         }
72         if (origDestWidth > (origDestHeight / widthToHeightMultiplier)) {
73             destRect.setWidth(origDestHeight / widthToHeightMultiplier);
74             switch(aspectRatio->align()) {
75                 case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
76                 case SVG_PRESERVEASPECTRATIO_XMIDYMID:
77                 case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
78                     destRect.setX(origDestWidth / 2 - destRect.width() / 2);
79                     break;
80                 case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
81                 case SVG_PRESERVEASPECTRATIO_XMAXYMID:
82                 case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
83                     destRect.setX(origDestWidth - destRect.width());
84                     break;
85             }
86         }
87     } else if (aspectRatio->meetOrSlice() == SVG_MEETORSLICE_SLICE) {
88         float widthToHeightMultiplier = srcRect.height() / srcRect.width();
89         // if the destination height is less than the height of the image we'll be drawing
90         if (origDestHeight < (origDestWidth * widthToHeightMultiplier)) {
91             float destToSrcMultiplier = srcRect.width() / destRect.width();
92             srcRect.setHeight(destRect.height() * destToSrcMultiplier);
93             switch(aspectRatio->align()) {
94                 case SVG_PRESERVEASPECTRATIO_XMINYMID:
95                 case SVG_PRESERVEASPECTRATIO_XMIDYMID:
96                 case SVG_PRESERVEASPECTRATIO_XMAXYMID:
97                     srcRect.setY(image()->height() / 2 - srcRect.height() / 2);
98                     break;
99                 case SVG_PRESERVEASPECTRATIO_XMINYMAX:
100                 case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
101                 case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
102                     srcRect.setY(image()->height() - srcRect.height());
103                     break;
104             }
105         }
106         // if the destination width is less than the width of the image we'll be drawing
107         if (origDestWidth < (origDestHeight / widthToHeightMultiplier)) {
108             float destToSrcMultiplier = srcRect.height() / destRect.height();
109             srcRect.setWidth(destRect.width() * destToSrcMultiplier);
110             switch(aspectRatio->align()) {
111                 case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
112                 case SVG_PRESERVEASPECTRATIO_XMIDYMID:
113                 case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
114                     srcRect.setX(image()->width() / 2 - srcRect.width() / 2);
115                     break;
116                 case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
117                 case SVG_PRESERVEASPECTRATIO_XMAXYMID:
118                 case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
119                     srcRect.setX(image()->width() - srcRect.width());
120                     break;
121             }
122         }
123     }
124 }
125
126 void RenderSVGImage::paint(PaintInfo& paintInfo, int parentX, int parentY)
127 {
128     if (paintInfo.p->paintingDisabled() || (paintInfo.phase != PaintPhaseForeground) || style()->visibility() == HIDDEN)
129         return;
130     
131     KRenderingDevice* device = renderingDevice();
132     KRenderingDeviceContext* context = device->currentContext();
133     bool shouldPopContext = false;
134     if (context)
135         paintInfo.p->save();
136     else {
137         // Need to push a device context on the stack if empty.
138         context = paintInfo.p->createRenderingDeviceContext();
139         device->pushContext(context);
140         shouldPopContext = true;
141     }
142
143     context->concatCTM(QMatrix().translate(parentX, parentY));
144     context->concatCTM(localTransform());
145     translateForAttributes();
146     
147     FloatRect boundingBox = relativeBBox(true);
148     const SVGRenderStyle *svgStyle = style()->svgStyle();
149             
150     if (KCanvasClipper *clipper = getClipperById(document(), svgStyle->clipPath().mid(1)))
151         clipper->applyClip(boundingBox);
152
153     if (KCanvasMasker *masker = getMaskerById(document(), svgStyle->maskElement().mid(1)))
154         masker->applyMask(boundingBox);
155
156     KCanvasFilter *filter = getFilterById(document(), svgStyle->filter().mid(1));
157     if (filter)
158         filter->prepareFilter(boundingBox);
159     
160     OwnPtr<GraphicsContext> c(device->currentContext()->createGraphicsContext());
161     PaintInfo pi = paintInfo;
162     pi.p = c.get();
163
164     int x = 0, y = 0;
165     if (!shouldPaint(pi, x, y))
166         return;
167         
168     SVGImageElement *imageElt = static_cast<SVGImageElement *>(node());
169         
170     if (imageElt->preserveAspectRatio()->baseVal()->align() == SVG_PRESERVEASPECTRATIO_NONE)
171         RenderImage::paint(pi, 0, 0);
172     else {
173         FloatRect destRect(m_x, m_y, contentWidth(), contentHeight());
174         FloatRect srcRect(0, 0, image()->width(), image()->height());
175         adjustRectsForAspectRatio(destRect, srcRect, imageElt->preserveAspectRatio()->baseVal());
176         c->drawImage(image(), destRect, srcRect);
177     }
178
179     if (filter)
180         filter->applyFilter(boundingBox);
181     
182     // restore drawing state
183     if (!shouldPopContext)
184         paintInfo.p->restore();
185     else {
186         device->popContext();
187         delete context;
188     }
189 }
190
191 FloatRect RenderSVGImage::relativeBBox(bool includeStroke) const
192 {
193     return FloatRect(0, 0, width(), height());
194 }
195
196 void RenderSVGImage::imageChanged(CachedImage* image)
197 {
198     RenderImage::imageChanged(image);
199     // We override to invalidate a larger rect, since SVG images can draw outside their "bounds"
200     repaintRectangle(getAbsoluteRepaintRect());
201 }
202
203 IntRect RenderSVGImage::getAbsoluteRepaintRect()
204 {
205     SVGImageElement *image = static_cast<SVGImageElement *>(node());
206     float xOffset = image->x()->baseVal() ? image->x()->baseVal()->value() : 0;
207     float yOffset = image->y()->baseVal() ? image->y()->baseVal()->value() : 0;
208     FloatRect repaintRect = absoluteTransform().mapRect(FloatRect(xOffset, yOffset, width(), height()));
209
210     // Filters can expand the bounding box
211     KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().mid(1));
212     if (filter)
213         repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
214
215     return enclosingIntRect(repaintRect);
216 }
217
218 void RenderSVGImage::absoluteRects(DeprecatedValueList<IntRect>& rects, int _tx, int _ty)
219 {
220     rects.append(getAbsoluteRepaintRect());
221 }
222
223 void RenderSVGImage::translateForAttributes()
224 {
225     KRenderingDeviceContext *context = renderingDevice()->currentContext();
226     SVGImageElement *image = static_cast<SVGImageElement *>(node());
227     float xOffset = image->x()->baseVal() ? image->x()->baseVal()->value() : 0;
228     float yOffset = image->y()->baseVal() ? image->y()->baseVal()->value() : 0;
229     context->concatCTM(QMatrix().translate(xOffset, yOffset));
230 }
231
232 }
233
234 #endif // SVG_SUPPORT