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