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