b424f9852fbe3a647bfece768dd8109e5d0b84e9
[WebKit-https.git] / WebCore / kcanvas / device / quartz / KCanvasResourcesQuartz.mm
1 /*
2  * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26
27 #include "config.h"
28 #import "KCanvasResourcesQuartz.h"
29
30 #import "kcanvas/KCanvas.h"
31 #import "SVGRenderStyle.h"
32 #import "KCanvasMatrix.h"
33
34 #import "KCanvasPathQuartz.h"
35 #import "KRenderingDeviceQuartz.h"
36 #import "KCanvasFilterQuartz.h"
37 #import "QuartzSupport.h"
38
39 #import <kxmlcore/Assertions.h>
40
41 void KCanvasContainerQuartz::calcMinMaxWidth()
42 {
43     KHTMLAssert( !minMaxKnown());
44     m_minWidth = m_maxWidth = 0;
45     setMinMaxKnown();
46 }
47
48 void KCanvasContainerQuartz::layout()
49 {
50     KHTMLAssert(needsLayout());
51     KHTMLAssert(minMaxKnown());
52
53     QRect oldBounds;
54     bool checkForRepaint = checkForRepaintDuringLayout();
55     if (checkForRepaint)
56         oldBounds = getAbsoluteRepaintRect();
57
58     calcWidth();
59     calcHeight();
60
61     if (checkForRepaint)
62         repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
63         
64     RenderContainer::layout();
65 }
66
67 void KCanvasContainerQuartz::paint(PaintInfo &paintInfo, int parentX, int parentY)
68 {
69     if (paintInfo.p->paintingDisabled())
70         return;
71     
72     // No one should be transforming us via these.
73     ASSERT(m_x == 0);
74     ASSERT(m_y == 0);
75         
76     if (shouldPaintBackgroundOrBorder() && paintInfo.phase != PaintActionOutline) 
77         paintBoxDecorations(paintInfo, parentX, parentY);
78     
79     if (paintInfo.phase == PaintActionOutline && style()->outlineWidth() && style()->visibility() == khtml::VISIBLE)
80         paintOutline(paintInfo.p, parentX, parentY, width(), height(), style());
81     
82     if (paintInfo.phase != PaintActionForeground || !drawsContents() || style()->visibility() == khtml::HIDDEN)
83         return;
84     
85     KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().mid(1));
86     if (!firstChild() && !filter)
87         return; // Spec: groups w/o children still may render filter content.
88     
89     KRenderingDeviceQuartz *quartzDevice = static_cast<KRenderingDeviceQuartz *>(QPainter::renderingDevice());
90     KRenderingDeviceContext *deviceContext = 0;
91     if (!parent()->isKCanvasContainer()) {
92         // I only need to setup for KCanvas rendering if it hasn't already been done.
93         deviceContext = paintInfo.p->createRenderingDeviceContext();
94         quartzDevice->pushContext(deviceContext);
95     }
96     paintInfo.p->save();
97     
98     CGContextRef context = paintInfo.p->currentContext();
99     
100     if (parentX != 0 || parentY != 0) {
101         // Translate from parent offsets (khtml) to a relative transform (ksvg2/kcanvas)
102         CGContextConcatCTM(context, CGAffineTransformMakeTranslation(parentX, parentY));
103         parentX = parentY = 0;
104     }
105     
106     if (viewport().isValid())
107         CGContextConcatCTM(context, CGAffineTransformMakeTranslation(viewport().x(), viewport().y()));
108     
109     if (!localTransform().isIdentity())
110         CGContextConcatCTM(context, CGAffineTransform(localTransform()));
111     
112     QRect dirtyRect = paintInfo.r;
113     
114     QString clipname = style()->svgStyle()->clipPath().mid(1);
115     KCanvasClipperQuartz *clipper = static_cast<KCanvasClipperQuartz *>(getClipperById(document(), clipname));
116     if (clipper)
117         clipper->applyClip(context, CGRect(relativeBBox(true)));
118     
119     float opacity = style()->opacity();
120     if (opacity < 1.0f)
121         paintInfo.p->beginTransparencyLayer(opacity);
122
123     if (filter)
124         filter->prepareFilter(quartzDevice, relativeBBox(true));
125     
126     if (!viewBox().isNull()) {
127         QRectF viewportRect = viewport();
128         if (!parent()->isKCanvasContainer())
129             viewportRect = QRectF(viewport().x(), viewport().y(), width(), height());
130         CGContextConcatCTM(paintInfo.p->currentContext(), CGAffineTransform(getAspectRatio(viewBox(), viewportRect).qmatrix()));
131     }
132     
133     RenderContainer::paint(paintInfo, 0, 0);
134     
135     if (filter)
136         filter->applyFilter(quartzDevice, relativeBBox(true));
137     
138     if (opacity < 1.0f)
139         paintInfo.p->endTransparencyLayer();
140     
141     // restore drawing state
142     paintInfo.p->restore();
143     if (!parent()->isKCanvasContainer()) {
144         quartzDevice->popContext();
145         delete deviceContext;
146     }
147 }
148
149 void KCanvasContainerQuartz::setViewport(const QRectF& viewport)
150 {
151     m_viewport = viewport;
152 }
153
154 QRectF KCanvasContainerQuartz::viewport() const
155 {
156    return m_viewport;
157 }
158
159 void KCanvasContainerQuartz::setViewBox(const QRectF& viewBox)
160 {
161     m_viewBox = viewBox;
162 }
163
164 QRectF KCanvasContainerQuartz::viewBox() const
165 {
166     return m_viewBox;
167 }
168
169 void KCanvasContainerQuartz::setAlign(KCAlign align)
170 {
171     m_align = align;
172 }
173
174 KCAlign KCanvasContainerQuartz::align() const
175 {
176     return m_align;
177 }
178
179 QMatrix KCanvasContainerQuartz::absoluteTransform() const
180 {
181     QMatrix transform = KCanvasContainer::absoluteTransform();
182     if (!viewBox().isNull()) {
183         QRectF viewportRect = viewport();
184         if (!parent()->isKCanvasContainer())
185             viewportRect = QRectF(viewport().x(), viewport().y(), width(), height());
186         transform *= getAspectRatio(viewBox(), viewportRect).qmatrix();
187     }
188     return transform;
189 }
190
191 void KCanvasClipperQuartz::applyClip(CGContextRef context, CGRect relativeBBox) const
192 {
193     if (m_clipData.count() < 1)
194         return;
195
196     BOOL heterogenousClipRules = NO;
197     KCWindRule clipRule = m_clipData[0].windRule;
198
199     CGContextBeginPath(context);
200
201     CGAffineTransform bboxTransform = CGAffineTransformMakeMapBetweenRects(CGRectMake(0,0,1,1), relativeBBox); // could be done lazily.
202
203     for (unsigned int x = 0; x < m_clipData.count(); x++) {
204         KCClipData data = m_clipData[x];
205         if (data.windRule != clipRule)
206             heterogenousClipRules = YES;
207         
208         KCanvasPathQuartz *path = static_cast<KCanvasPathQuartz*>(data.path.get());        
209         CGPathRef clipPath = static_cast<KCanvasPathQuartz*>(path)->cgPath();
210
211         if (data.bboxUnits) {
212             CGMutablePathRef transformedPath = CGPathCreateMutable();
213             CGPathAddPath(transformedPath, &bboxTransform, clipPath);
214             CGContextAddPath(context, transformedPath);
215             CGPathRelease(transformedPath);
216         } else
217             CGContextAddPath(context, clipPath);
218     }
219
220     if (m_clipData.count()) {
221         // FIXME!
222         // We don't currently allow for heterogenous clip rules.
223         // we would have to detect such, draw to a mask, and then clip
224         // to that mask                
225         if (!CGContextIsPathEmpty(context)) {
226             if (clipRule == RULE_EVENODD)
227                 CGContextEOClip(context);
228             else
229                 CGContextClip(context);
230         }
231     }
232 }
233
234
235 KCanvasImageQuartz::~KCanvasImageQuartz()
236 {
237         CGLayerRelease(m_cgLayer);
238 }
239
240 CGLayerRef KCanvasImageQuartz::cgLayer()
241 {
242         return m_cgLayer;
243 }
244
245 void KCanvasImageQuartz::setCGLayer(CGLayerRef layer)
246 {
247         if (m_cgLayer != layer) {
248                 CGLayerRelease(m_cgLayer);
249                 m_cgLayer = CGLayerRetain(layer);
250         }
251 }
252