c902e3e19278d59ef7d240ffbce741a03419f87d
[WebKit-https.git] / WebCore / kcanvas / KCanvasContainer.cpp
1 /*
2     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
3                   2004, 2005 Rob Buis <buis@kde.org>
4
5     This file is part of the KDE 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     aint 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 "KCanvasRenderingStyle.h"
26 #include "KRenderingDevice.h"
27 #include "KCanvasContainer.h"
28 #include "SVGStyledElement.h"
29 #include "GraphicsContext.h"
30
31 namespace WebCore {
32
33 class KCanvasContainer::Private
34 {
35 public:
36     Private() : drawsContents(true), slice(false) { }    
37     ~Private() { }
38
39     bool drawsContents : 1;
40     bool slice : 1;
41     QMatrix matrix;
42     
43     FloatRect viewport;
44     FloatRect viewBox;
45     KCAlign align;
46 };
47
48 KCanvasContainer::KCanvasContainer(SVGStyledElement *node)
49 : RenderContainer(node), d(new Private())
50 {
51     setReplaced(true);
52 }
53
54 KCanvasContainer::~KCanvasContainer()
55 {
56     delete d;
57 }
58
59 bool KCanvasContainer::drawsContents() const
60 {
61     return d->drawsContents;
62 }
63
64 void KCanvasContainer::setDrawsContents(bool drawsContents)
65 {
66     d->drawsContents = drawsContents;
67 }
68
69 QMatrix KCanvasContainer::localTransform() const
70 {
71     return d->matrix;
72 }
73
74 void KCanvasContainer::setLocalTransform(const QMatrix &matrix)
75 {
76     d->matrix = matrix;
77 }
78
79 bool KCanvasContainer::canHaveChildren() const
80 {
81     return true;
82 }
83     
84 bool KCanvasContainer::requiresLayer()
85 {
86     return false;
87 }
88
89 short KCanvasContainer::lineHeight(bool b, bool isRootLineBox) const
90 {
91     return height() + marginTop() + marginBottom();
92 }
93
94 short KCanvasContainer::baselinePosition(bool b, bool isRootLineBox) const
95 {
96     return height() + marginTop() + marginBottom();
97 }
98
99 void KCanvasContainer::calcMinMaxWidth()
100 {
101     KHTMLAssert(!minMaxKnown());
102     m_minWidth = m_maxWidth = 0;
103     setMinMaxKnown();
104 }
105
106 void KCanvasContainer::layout()
107 {
108     KHTMLAssert(needsLayout());
109     KHTMLAssert(minMaxKnown());
110
111     IntRect oldBounds;
112     bool checkForRepaint = checkForRepaintDuringLayout();
113     if (checkForRepaint)
114         oldBounds = getAbsoluteRepaintRect();
115
116     calcWidth();
117     calcHeight();
118
119     if (checkForRepaint)
120         repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
121         
122     RenderContainer::layout();
123 }
124
125 void KCanvasContainer::paint(PaintInfo &paintInfo, int parentX, int parentY)
126 {
127     if (paintInfo.p->paintingDisabled())
128         return;
129     
130     // No one should be transforming us via these.
131     //ASSERT(d->x == 0);
132     //ASSERT(d->y == 0);
133         
134     if (shouldPaintBackgroundOrBorder() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) 
135         paintBoxDecorations(paintInfo, parentX, parentY);
136     
137     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE)
138         paintOutline(paintInfo.p, parentX, parentY, width(), height(), style());
139     
140     if (paintInfo.phase != WebCore::PaintPhaseForeground || !drawsContents() || style()->visibility() == HIDDEN)
141         return;
142     
143     KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().mid(1));
144     if (!firstChild() && !filter)
145         return; // Spec: groups w/o children still may render filter content.
146     
147     KRenderingDevice* device = renderingDevice();
148     KRenderingDeviceContext *deviceContext = device->currentContext();
149     bool shouldPopContext = false;
150     if (!deviceContext) {
151         // I only need to setup for KCanvas rendering if it hasn't already been done.
152         deviceContext = paintInfo.p->createRenderingDeviceContext();
153         device->pushContext(deviceContext);
154         shouldPopContext = true;
155     } else
156         paintInfo.p->save();
157     
158     if (parentX != 0 || parentY != 0) {
159         // Translate from parent offsets (khtml) to a relative transform (ksvg2/kcanvas)
160         deviceContext->concatCTM(QMatrix().translate(parentX, parentY));
161         parentX = parentY = 0;
162     }
163     
164     if (!viewport().isEmpty())
165         deviceContext->concatCTM(QMatrix().translate(viewport().x(), viewport().y()));
166     
167     if (!localTransform().isIdentity())
168         deviceContext->concatCTM(localTransform());
169     
170     if (KCanvasClipper *clipper = getClipperById(document(), style()->svgStyle()->clipPath().mid(1)))
171         clipper->applyClip(relativeBBox(true));
172
173     if (KCanvasMasker *masker = getMaskerById(document(), style()->svgStyle()->maskElement().mid(1)))
174         masker->applyMask(relativeBBox(true));
175
176     float opacity = style()->opacity();
177     if (opacity < 1.0f)
178         paintInfo.p->beginTransparencyLayer(opacity);
179
180     if (filter)
181         filter->prepareFilter(relativeBBox(true));
182     
183     if (!viewBox().isEmpty())
184         deviceContext->concatCTM(viewportTransform());
185     
186     RenderContainer::paint(paintInfo, 0, 0);
187     
188     if (filter)
189         filter->applyFilter(relativeBBox(true));
190     
191     if (opacity < 1.0f)
192         paintInfo.p->endTransparencyLayer();
193     
194     // restore drawing state
195     if (shouldPopContext) {
196         device->popContext();
197         delete deviceContext;
198     } else
199         paintInfo.p->restore();
200 }
201
202 void KCanvasContainer::setViewport(const FloatRect& viewport)
203 {
204     d->viewport = viewport;
205 }
206
207 FloatRect KCanvasContainer::viewport() const
208 {
209    return d->viewport;
210 }
211
212 void KCanvasContainer::setViewBox(const FloatRect& viewBox)
213 {
214     d->viewBox = viewBox;
215 }
216
217 FloatRect KCanvasContainer::viewBox() const
218 {
219     return d->viewBox;
220 }
221
222 void KCanvasContainer::setAlign(KCAlign align)
223 {
224     d->align = align;
225 }
226
227 KCAlign KCanvasContainer::align() const
228 {
229     return d->align;
230 }
231
232 QMatrix KCanvasContainer::viewportTransform() const
233 {
234     if (!viewBox().isEmpty()) {
235         FloatRect viewportRect = viewport();
236         if (!parent()->isKCanvasContainer())
237             viewportRect = FloatRect(viewport().x(), viewport().y(), width(), height());
238         return getAspectRatio(viewBox(), viewportRect).qmatrix();
239     }
240     return QMatrix();
241 }
242
243 IntRect KCanvasContainer::getAbsoluteRepaintRect()
244 {
245     IntRect repaintRect;
246     
247     for (RenderObject *current = firstChild(); current != 0; current = current->nextSibling())
248         repaintRect.unite(current->getAbsoluteRepaintRect());
249     
250     // Filters can expand the bounding box
251     KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().mid(1));
252     if (filter)
253         repaintRect.unite(enclosingIntRect(filter->filterBBoxForItemBBox(repaintRect)));
254
255     return repaintRect;
256 }
257
258 QMatrix KCanvasContainer::absoluteTransform() const
259 {
260     return viewportTransform() * RenderContainer::absoluteTransform();
261 }
262
263 bool KCanvasContainer::fillContains(const FloatPoint &p) const
264 {
265     RenderObject *current = firstChild();
266     while (current != 0) {
267         if (current->isRenderPath() && static_cast<RenderPath *>(current)->fillContains(p))
268             return true;
269         current = current->nextSibling();
270     }
271
272     return false;
273 }
274
275 bool KCanvasContainer::strokeContains(const FloatPoint &p) const
276 {
277     RenderObject *current = firstChild();
278     while (current != 0) {
279         if (current->isRenderPath() && static_cast<RenderPath *>(current)->strokeContains(p))
280             return true;
281         current = current->nextSibling();
282     }
283
284     return false;
285 }
286
287 FloatRect KCanvasContainer::relativeBBox(bool includeStroke) const
288 {
289     FloatRect rect;
290     
291     RenderObject *current = firstChild();
292     for (; current != 0; current = current->nextSibling()) {
293         FloatRect childBBox = current->relativeBBox(includeStroke);
294         FloatRect mappedBBox = current->localTransform().mapRect(childBBox);
295         rect.unite(mappedBBox);
296     }
297
298     return rect;
299 }
300
301 void KCanvasContainer::setSlice(bool slice)
302 {
303     d->slice = slice;
304 }
305
306 bool KCanvasContainer::slice() const
307 {
308     return d->slice;
309 }
310
311 KCanvasMatrix KCanvasContainer::getAspectRatio(const FloatRect& logical, const FloatRect& physical) const
312 {
313     KCanvasMatrix temp;
314
315     float logicX = logical.x();
316     float logicY = logical.y();
317     float logicWidth = logical.width();
318     float logicHeight = logical.height();
319     float physWidth = physical.width();
320     float physHeight = physical.height();
321
322     float vpar = logicWidth / logicHeight;
323     float svgar = physWidth / physHeight;
324
325     if (align() == ALIGN_NONE) {
326         temp.scale(physWidth / logicWidth, physHeight / logicHeight);
327         temp.translate(-logicX, -logicY);
328     } else if ((vpar < svgar && !slice()) || (vpar >= svgar && slice())) {
329         temp.scale(physHeight / logicHeight, physHeight / logicHeight);
330
331         if (align() == ALIGN_XMINYMIN || align() == ALIGN_XMINYMID || align() == ALIGN_XMINYMAX)
332             temp.translate(-logicX, -logicY);
333         else if (align() == ALIGN_XMIDYMIN || align() == ALIGN_XMIDYMID || align() == ALIGN_XMIDYMAX)
334             temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight) / 2, -logicY);
335         else
336             temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight), -logicY);
337     } else {
338         temp.scale(physWidth / logicWidth, physWidth / logicWidth);
339
340         if (align() == ALIGN_XMINYMIN || align() == ALIGN_XMIDYMIN || align() == ALIGN_XMAXYMIN)
341             temp.translate(-logicX, -logicY);
342         else if (align() == ALIGN_XMINYMID || align() == ALIGN_XMIDYMID || align() == ALIGN_XMAXYMID)
343             temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth) / 2);
344         else
345             temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth));
346     }
347
348     return temp;
349 }
350
351 }
352
353 // vim:ts=4:noet
354 #endif // SVG_SUPPORT
355