2006-10-05 Eric Seidel <eric@eseidel.com>
[WebKit-https.git] / WebCore / kcanvas / device / quartz / KRenderingPaintServerQuartz.mm
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *               2006 Alexander Kellett <lypanov@kde.org>
4  *               2006 Rob Buis <buis@kde.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
26  */
27
28
29 #include "config.h"
30 #ifdef SVG_SUPPORT
31 #import "KRenderingPaintServerQuartz.h"
32 #import "QuartzSupport.h"
33 #import "KCanvasResourcesQuartz.h"
34 #import "KRenderingDeviceQuartz.h"
35
36 #import "KCanvasRenderingStyle.h"
37 #import "KRenderingPaintServer.h"
38 #import "KRenderingFillPainter.h"
39 #import "KRenderingStrokePainter.h"
40 #import "KRenderingDevice.h"
41
42 #import "Logging.h"
43
44 namespace WebCore {
45
46 void KRenderingPaintServerQuartzHelper::strokePath(CGContextRef context, const RenderPath* renderPath)
47 {
48     CGContextStrokePath(context);
49 }
50
51 void KRenderingPaintServerQuartzHelper::clipToStrokePath(CGContextRef context, const RenderPath* renderPath)
52 {
53     CGContextReplacePathWithStrokedPath(context);
54     CGContextClip(context);
55 }    
56
57 void KRenderingPaintServerQuartzHelper::fillPath(CGContextRef context, const RenderPath* renderPath)
58 {
59     if (KSVGPainterFactory::fillPainter(renderPath->style(), renderPath).fillRule() == RULE_EVENODD)
60         CGContextEOFillPath(context);
61     else
62         CGContextFillPath(context);
63 }
64
65 void KRenderingPaintServerQuartzHelper::clipToFillPath(CGContextRef context, const RenderPath* renderPath)
66 {
67     if (KSVGPainterFactory::fillPainter(renderPath->style(), renderPath).fillRule() == RULE_EVENODD)
68         CGContextEOClip(context);
69     else
70         CGContextClip(context);
71 }    
72
73 void KRenderingPaintServerSolidQuartz::draw(KRenderingDeviceContext* renderingContext, const RenderPath* path, KCPaintTargetType type) const
74 {
75     if (!setup(renderingContext, path, type))
76         return;
77     renderPath(renderingContext, path, type);
78     teardown(renderingContext, path, type);
79 }
80
81 bool KRenderingPaintServerSolidQuartz::setup(KRenderingDeviceContext* renderingContext, const RenderObject* renderObject, KCPaintTargetType type) const
82 {
83     KRenderingDeviceContextQuartz* quartzContext = static_cast<KRenderingDeviceContextQuartz*>(renderingContext);
84     CGContextRef context = quartzContext->cgContext();
85     RenderStyle* renderStyle = renderObject->style();
86
87     CGContextSetAlpha(context, renderStyle->opacity());
88     
89     static CGColorSpaceRef deviceRGBColorSpace = CGColorSpaceCreateDeviceRGB(); // This should be shared from GraphicsContext, or some other central location
90
91     if ((type & APPLY_TO_FILL) && renderStyle->svgStyle()->hasFill()) {
92         CGFloat colorComponents[4];
93         color().getRGBA(colorComponents[0], colorComponents[1], colorComponents[2], colorComponents[3]);
94         ASSERT(!color().hasAlpha());
95         colorComponents[3] = renderStyle->svgStyle()->fillOpacity(); // SVG/CSS colors are not specified w/o alpha
96         CGContextSetFillColorSpace(context, deviceRGBColorSpace);
97         CGContextSetFillColor(context, colorComponents);
98         if (isPaintingText()) {
99             const_cast<RenderObject*>(renderObject)->style()->setColor(color());
100             CGContextSetTextDrawingMode(context, kCGTextFill);
101         }
102     }
103
104     if ((type & APPLY_TO_STROKE) && renderStyle->svgStyle()->hasStroke()) {
105         CGFloat colorComponents[4];
106         color().getRGBA(colorComponents[0], colorComponents[1], colorComponents[2], colorComponents[3]);
107         ASSERT(!color().hasAlpha());
108         colorComponents[3] = renderStyle->svgStyle()->strokeOpacity(); // SVG/CSS colors are not specified w/o alpha
109         CGContextSetStrokeColorSpace(context, deviceRGBColorSpace);
110         CGContextSetStrokeColor(context, colorComponents);
111         applyStrokeStyleToContext(context, renderStyle, renderObject);
112         if (isPaintingText()) {
113             const_cast<RenderObject*>(renderObject)->style()->setColor(color());
114             CGContextSetTextDrawingMode(context, kCGTextStroke);
115         }
116     }
117     
118     return true;
119 }
120
121 void KRenderingPaintServerSolidQuartz::renderPath(KRenderingDeviceContext* renderingContext, const RenderPath* renderPath, KCPaintTargetType type) const
122 {
123     RenderStyle* renderStyle = renderPath->style();
124     KRenderingDeviceContextQuartz* quartzContext = static_cast<KRenderingDeviceContextQuartz*>(renderingContext);
125     CGContextRef context = quartzContext->cgContext();    
126     if ((type & APPLY_TO_FILL) && renderStyle->svgStyle()->hasFill())
127         KRenderingPaintServerQuartzHelper::fillPath(context, renderPath);
128     if ((type & APPLY_TO_STROKE) && renderStyle->svgStyle()->hasStroke())
129         KRenderingPaintServerQuartzHelper::strokePath(context, renderPath);
130 }
131
132 void KRenderingPaintServerSolidQuartz::teardown(KRenderingDeviceContext* renderingContext, const RenderObject* renderObject, KCPaintTargetType type) const
133 {
134 }
135
136 void patternCallback(void *info, CGContextRef context)
137 {
138     CGLayerRef layer = reinterpret_cast<KCanvasImageQuartz*>(info)->cgLayer();
139     CGContextDrawLayerAtPoint(context, CGPointZero, layer);
140 }
141
142 void KRenderingPaintServerPatternQuartz::draw(KRenderingDeviceContext* renderingContext, const RenderPath* path, KCPaintTargetType type) const
143 {
144     if (!setup(renderingContext, path, type))
145         return;
146     renderPath(renderingContext, path, type);
147     teardown(renderingContext, path, type);
148 }
149
150 bool KRenderingPaintServerPatternQuartz::setup(KRenderingDeviceContext* renderingContext, const RenderObject* renderObject, KCPaintTargetType type) const
151 {
152     if(listener()) // this seems like bad design to me, should be in a common baseclass. -- ecs 8/6/05
153         listener()->resourceNotification();
154
155     RenderStyle* renderStyle = renderObject->style();
156
157     KRenderingDeviceContextQuartz* quartzContext = static_cast<KRenderingDeviceContextQuartz*>(renderingContext);
158     CGContextRef context = quartzContext->cgContext();
159
160     KCanvasImage* cell = tile();
161     if (!cell)
162         return false;
163
164     CGContextSaveGState(context);
165
166     CGSize cellSize = CGSize(cell->size());
167
168     CGFloat alpha = 1; // canvasStyle->opacity(); //which?
169             
170     // Patterns don't seem to resepect the CTM unless we make them...
171     CGAffineTransform ctm = CGContextGetCTM(context);
172     CGAffineTransform transform = patternTransform();
173     transform = CGAffineTransformConcat(transform, ctm);
174
175     CGSize phase = CGSizeMake(bbox().x(), -bbox().y()); // Pattern space seems to start in the lower-left, so we flip the Y here.
176     CGContextSetPatternPhase(context, phase);
177
178     CGPatternCallbacks callbacks = {0, patternCallback, NULL};
179     m_pattern = CGPatternCreate(
180         tile(),
181         CGRectMake(0, 0, cellSize.width, cellSize.height),
182         transform,
183         bbox().width(), //cellSize.width,
184         bbox().height(), //cellSize.height,
185         kCGPatternTilingConstantSpacing,  // FIXME: should ask CG guys.
186         true, // has color
187         &callbacks);
188
189     CGContextSetAlpha(context, renderStyle->opacity()); // or do I set the alpha above?
190
191     m_patternSpace = CGColorSpaceCreatePattern(NULL);
192
193     if ((type & APPLY_TO_FILL) && renderStyle->svgStyle()->hasFill()) {
194         CGContextSetFillColorSpace(context, m_patternSpace);
195         CGContextSetFillPattern(context, m_pattern, &alpha);
196         if (isPaintingText()) {
197             const_cast<RenderObject*>(renderObject)->style()->setColor(Color());
198             CGContextSetTextDrawingMode(context, kCGTextFill);
199         }
200     }
201     
202     if ((type & APPLY_TO_STROKE) && renderStyle->svgStyle()->hasStroke()) {
203         CGContextSetStrokeColorSpace(context, m_patternSpace);
204         CGContextSetStrokePattern(context, m_pattern, &alpha);
205         applyStrokeStyleToContext(context, renderStyle, renderObject);
206         if (isPaintingText()) {
207             const_cast<RenderObject*>(renderObject)->style()->setColor(Color());
208             CGContextSetTextDrawingMode(context, kCGTextStroke);
209         }
210     }
211     
212     return true;
213 }
214
215 void KRenderingPaintServerPatternQuartz::renderPath(KRenderingDeviceContext* renderingContext, const RenderPath* renderPath, KCPaintTargetType type) const
216 {
217     RenderStyle* renderStyle = renderPath->style();
218
219     KRenderingDeviceContextQuartz* quartzContext = static_cast<KRenderingDeviceContextQuartz*>(renderingContext);
220     CGContextRef context = quartzContext->cgContext();
221
222     if ((type & APPLY_TO_FILL) && renderStyle->svgStyle()->hasFill())
223         KRenderingPaintServerQuartzHelper::fillPath(context, renderPath);
224     
225     if ((type & APPLY_TO_STROKE) && renderStyle->svgStyle()->hasStroke())
226         KRenderingPaintServerQuartzHelper::strokePath(context, renderPath);
227 }
228
229 void KRenderingPaintServerPatternQuartz::teardown(KRenderingDeviceContext* renderingContext, const RenderObject* renderObject, KCPaintTargetType type) const
230 {
231     KRenderingDeviceContextQuartz* quartzContext = static_cast<KRenderingDeviceContextQuartz*>(renderingContext);
232     CGContextRef context = quartzContext->cgContext();
233     CGPatternRelease(m_pattern);
234     CGColorSpaceRelease(m_patternSpace);
235     CGContextRestoreGState(context);
236 }
237
238 }
239
240 #endif // SVG_SUPPORT