1 /*
2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies)
4 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
5 * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30 #include "CanvasStyle.h"
31
32 #include "CSSParser.h"
33 #include "CSSPropertyNames.h"
34 #include "CanvasGradient.h"
35 #include "CanvasPattern.h"
36 #include "GraphicsContext.h"
37 #include "HTMLCanvasElement.h"
38 #include <wtf/Assertions.h>
39 #include <wtf/PassRefPtr.h>
40
41 #if USE(CG)
42 #include <CoreGraphics/CGContext.h>
43 #endif
44
45 #if PLATFORM(QT)
46 #include <QPainter>
47 #include <QBrush>
48 #include <QPen>
49 #include <QColor>
50 #endif
51
52 namespace WebCore {
53
54 enum ColorParseResult { ParsedRGBA, ParsedCurrentColor, ParsedSystemColor, ParseFailed };
55
parseColor(RGBA32 & parsedColor,const String & colorString,Document * document=0)56 static ColorParseResult parseColor(RGBA32& parsedColor, const String& colorString, Document* document = 0)
57 {
58 if (equalIgnoringCase(colorString, "currentcolor"))
59 return ParsedCurrentColor;
60 if (CSSParser::parseColor(parsedColor, colorString))
61 return ParsedRGBA;
62 if (CSSParser::parseSystemColor(parsedColor, colorString, document))
63 return ParsedSystemColor;
64 return ParseFailed;
65 }
66
currentColor(HTMLCanvasElement * canvas)67 RGBA32 currentColor(HTMLCanvasElement* canvas)
68 {
69 if (!canvas || !canvas->inDocument())
70 return Color::black;
71 RGBA32 rgba = Color::black;
72 CSSParser::parseColor(rgba, canvas->style()->getPropertyValue(CSSPropertyColor));
73 return rgba;
74 }
75
parseColorOrCurrentColor(RGBA32 & parsedColor,const String & colorString,HTMLCanvasElement * canvas)76 bool parseColorOrCurrentColor(RGBA32& parsedColor, const String& colorString, HTMLCanvasElement* canvas)
77 {
78 ColorParseResult parseResult = parseColor(parsedColor, colorString, canvas ? canvas->document() : 0);
79 switch (parseResult) {
80 case ParsedRGBA:
81 case ParsedSystemColor:
82 return true;
83 case ParsedCurrentColor:
84 parsedColor = currentColor(canvas);
85 return true;
86 case ParseFailed:
87 return false;
88 default:
89 ASSERT_NOT_REACHED();
90 return false;
91 }
92 }
93
CanvasStyle(Type type,float overrideAlpha)94 CanvasStyle::CanvasStyle(Type type, float overrideAlpha)
95 : m_type(type)
96 , m_overrideAlpha(overrideAlpha)
97 {
98 }
99
CanvasStyle(RGBA32 rgba)100 CanvasStyle::CanvasStyle(RGBA32 rgba)
101 : m_type(RGBA)
102 , m_rgba(rgba)
103 {
104 }
105
CanvasStyle(float grayLevel,float alpha)106 CanvasStyle::CanvasStyle(float grayLevel, float alpha)
107 : m_type(RGBA)
108 , m_rgba(makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha))
109 {
110 }
111
CanvasStyle(float r,float g,float b,float a)112 CanvasStyle::CanvasStyle(float r, float g, float b, float a)
113 : m_type(RGBA)
114 , m_rgba(makeRGBA32FromFloats(r, g, b, a))
115 {
116 }
117
CanvasStyle(float c,float m,float y,float k,float a)118 CanvasStyle::CanvasStyle(float c, float m, float y, float k, float a)
119 : m_type(CMYKA)
120 , m_rgba(makeRGBAFromCMYKA(c, m, y, k, a))
121 , m_cmyka(c, m, y, k, a)
122 {
123 }
124
CanvasStyle(PassRefPtr<CanvasGradient> gradient)125 CanvasStyle::CanvasStyle(PassRefPtr<CanvasGradient> gradient)
126 : m_type(Gradient)
127 , m_gradient(gradient)
128 {
129 }
130
CanvasStyle(PassRefPtr<CanvasPattern> pattern)131 CanvasStyle::CanvasStyle(PassRefPtr<CanvasPattern> pattern)
132 : m_type(ImagePattern)
133 , m_pattern(pattern)
134 {
135 }
136
createFromString(const String & color,Document * document)137 PassRefPtr<CanvasStyle> CanvasStyle::createFromString(const String& color, Document* document)
138 {
139 RGBA32 rgba;
140 ColorParseResult parseResult = parseColor(rgba, color, document);
141 switch (parseResult) {
142 case ParsedRGBA:
143 case ParsedSystemColor:
144 return adoptRef(new CanvasStyle(rgba));
145 case ParsedCurrentColor:
146 return adoptRef(new CanvasStyle(CurrentColor));
147 case ParseFailed:
148 return 0;
149 default:
150 ASSERT_NOT_REACHED();
151 return 0;
152 }
153 }
154
createFromStringWithOverrideAlpha(const String & color,float alpha)155 PassRefPtr<CanvasStyle> CanvasStyle::createFromStringWithOverrideAlpha(const String& color, float alpha)
156 {
157 RGBA32 rgba;
158 ColorParseResult parseResult = parseColor(rgba, color);
159 switch (parseResult) {
160 case ParsedRGBA:
161 return adoptRef(new CanvasStyle(colorWithOverrideAlpha(rgba, alpha)));
162 case ParsedCurrentColor:
163 return adoptRef(new CanvasStyle(CurrentColorWithOverrideAlpha, alpha));
164 case ParseFailed:
165 return 0;
166 default:
167 ASSERT_NOT_REACHED();
168 return 0;
169 }
170 }
171
createFromGradient(PassRefPtr<CanvasGradient> gradient)172 PassRefPtr<CanvasStyle> CanvasStyle::createFromGradient(PassRefPtr<CanvasGradient> gradient)
173 {
174 if (!gradient)
175 return 0;
176 return adoptRef(new CanvasStyle(gradient));
177 }
createFromPattern(PassRefPtr<CanvasPattern> pattern)178 PassRefPtr<CanvasStyle> CanvasStyle::createFromPattern(PassRefPtr<CanvasPattern> pattern)
179 {
180 if (!pattern)
181 return 0;
182 return adoptRef(new CanvasStyle(pattern));
183 }
184
isEquivalentColor(const CanvasStyle & other) const185 bool CanvasStyle::isEquivalentColor(const CanvasStyle& other) const
186 {
187 if (m_type != other.m_type)
188 return false;
189
190 switch (m_type) {
191 case RGBA:
192 return m_rgba == other.m_rgba;
193 case CMYKA:
194 return m_cmyka.c == other.m_cmyka.c
195 && m_cmyka.m == other.m_cmyka.m
196 && m_cmyka.y == other.m_cmyka.y
197 && m_cmyka.k == other.m_cmyka.k
198 && m_cmyka.a == other.m_cmyka.a;
199 case Gradient:
200 case ImagePattern:
201 case CurrentColor:
202 case CurrentColorWithOverrideAlpha:
203 return false;
204 }
205
206 ASSERT_NOT_REACHED();
207 return false;
208 }
209
isEquivalentRGBA(float r,float g,float b,float a) const210 bool CanvasStyle::isEquivalentRGBA(float r, float g, float b, float a) const
211 {
212 if (m_type != RGBA)
213 return false;
214
215 return m_rgba == makeRGBA32FromFloats(r, g, b, a);
216 }
217
isEquivalentCMYKA(float c,float m,float y,float k,float a) const218 bool CanvasStyle::isEquivalentCMYKA(float c, float m, float y, float k, float a) const
219 {
220 if (m_type != CMYKA)
221 return false;
222
223 return c == m_cmyka.c
224 && m == m_cmyka.m
225 && y == m_cmyka.y
226 && k == m_cmyka.k
227 && a == m_cmyka.a;
228 }
229
applyStrokeColor(GraphicsContext * context)230 void CanvasStyle::applyStrokeColor(GraphicsContext* context)
231 {
232 if (!context)
233 return;
234 switch (m_type) {
235 case RGBA:
236 context->setStrokeColor(m_rgba, ColorSpaceDeviceRGB);
237 break;
238 case CMYKA: {
239 // FIXME: Do this through platform-independent GraphicsContext API.
240 // We'll need a fancier Color abstraction to support CMYKA correctly
241 #if USE(CG)
242 CGContextSetCMYKStrokeColor(context->platformContext(), m_cmyka.c, m_cmyka.m, m_cmyka.y, m_cmyka.k, m_cmyka.a);
243 #elif PLATFORM(QT)
244 QPen currentPen = context->platformContext()->pen();
245 QColor clr;
246 clr.setCmykF(m_cmyka.c, m_cmyka.m, m_cmyka.y, m_cmyka.k, m_cmyka.a);
247 currentPen.setColor(clr);
248 context->platformContext()->setPen(currentPen);
249 #else
250 context->setStrokeColor(m_rgba, ColorSpaceDeviceRGB);
251 #endif
252 break;
253 }
254 case Gradient:
255 context->setStrokeGradient(canvasGradient()->gradient());
256 break;
257 case ImagePattern:
258 context->setStrokePattern(canvasPattern()->pattern());
259 break;
260 case CurrentColor:
261 case CurrentColorWithOverrideAlpha:
262 ASSERT_NOT_REACHED();
263 break;
264 }
265 }
266
applyFillColor(GraphicsContext * context)267 void CanvasStyle::applyFillColor(GraphicsContext* context)
268 {
269 if (!context)
270 return;
271 switch (m_type) {
272 case RGBA:
273 context->setFillColor(m_rgba, ColorSpaceDeviceRGB);
274 break;
275 case CMYKA: {
276 // FIXME: Do this through platform-independent GraphicsContext API.
277 // We'll need a fancier Color abstraction to support CMYKA correctly
278 #if USE(CG)
279 CGContextSetCMYKFillColor(context->platformContext(), m_cmyka.c, m_cmyka.m, m_cmyka.y, m_cmyka.k, m_cmyka.a);
280 #elif PLATFORM(QT)
281 QBrush currentBrush = context->platformContext()->brush();
282 QColor clr;
283 clr.setCmykF(m_cmyka.c, m_cmyka.m, m_cmyka.y, m_cmyka.k, m_cmyka.a);
284 currentBrush.setColor(clr);
285 context->platformContext()->setBrush(currentBrush);
286 #else
287 context->setFillColor(m_rgba, ColorSpaceDeviceRGB);
288 #endif
289 break;
290 }
291 case Gradient:
292 context->setFillGradient(canvasGradient()->gradient());
293 break;
294 case ImagePattern:
295 context->setFillPattern(canvasPattern()->pattern());
296 break;
297 case CurrentColor:
298 case CurrentColorWithOverrideAlpha:
299 ASSERT_NOT_REACHED();
300 break;
301 }
302 }
303
304 }
305