a4ab348924bf39371bf3c61098741a23e904d8f9
[WebKit-https.git] / Source / WebCore / html / canvas / CanvasStyle.cpp
1 /*
2  * Copyright (C) 2006, 2008, 2013 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 "StylePropertySet.h"
39 #include <wtf/Assertions.h>
40 #include <wtf/PassRefPtr.h>
41
42 #if USE(CG)
43 #include <CoreGraphics/CGContext.h>
44 #endif
45
46 #if PLATFORM(QT)
47 #include <QPainter>
48 #include <QBrush>
49 #include <QPen>
50 #include <QColor>
51 #endif
52
53 namespace WebCore {
54
55 enum ColorParseResult { ParsedRGBA, ParsedCurrentColor, ParsedSystemColor, ParseFailed };
56
57 static ColorParseResult parseColor(RGBA32& parsedColor, const String& colorString, Document* document = 0)
58 {
59     if (equalIgnoringCase(colorString, "currentcolor"))
60         return ParsedCurrentColor;
61     if (CSSParser::parseColor(parsedColor, colorString))
62         return ParsedRGBA;
63     if (CSSParser::parseSystemColor(parsedColor, colorString, document))
64         return ParsedSystemColor;
65     return ParseFailed;
66 }
67
68 RGBA32 currentColor(HTMLCanvasElement* canvas)
69 {
70     if (!canvas || !canvas->inDocument() || !canvas->inlineStyle())
71         return Color::black;
72     RGBA32 rgba = Color::black;
73     CSSParser::parseColor(rgba, canvas->inlineStyle()->getPropertyValue(CSSPropertyColor));
74     return rgba;
75 }
76
77 bool parseColorOrCurrentColor(RGBA32& parsedColor, const String& colorString, HTMLCanvasElement* canvas)
78 {
79     ColorParseResult parseResult = parseColor(parsedColor, colorString, canvas ? &canvas->document() : 0);
80     switch (parseResult) {
81     case ParsedRGBA:
82     case ParsedSystemColor:
83         return true;
84     case ParsedCurrentColor:
85         parsedColor = currentColor(canvas);
86         return true;
87     case ParseFailed:
88         return false;
89     default:
90         ASSERT_NOT_REACHED();
91         return false;
92     }
93 }
94
95 CanvasStyle::CanvasStyle(RGBA32 rgba)
96     : m_rgba(rgba)
97     , m_type(RGBA)
98 {
99 }
100
101 CanvasStyle::CanvasStyle(float grayLevel, float alpha)
102     : m_rgba(makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha))
103     , m_type(RGBA)
104 {
105 }
106
107 CanvasStyle::CanvasStyle(float r, float g, float b, float a)
108     : m_rgba(makeRGBA32FromFloats(r, g, b, a))
109     , m_type(RGBA)
110 {
111 }
112
113 CanvasStyle::CanvasStyle(float c, float m, float y, float k, float a)
114     : m_cmyka(new CMYKAValues(makeRGBAFromCMYKA(c, m, y, k, a), c, m, y, k, a))
115     , m_type(CMYKA)
116 {
117 }
118
119 CanvasStyle::CanvasStyle(PassRefPtr<CanvasGradient> gradient)
120     : m_gradient(gradient.leakRef())
121     , m_type(Gradient)
122 {
123     if (!m_gradient)
124         m_type = Invalid;
125 }
126
127 CanvasStyle::CanvasStyle(PassRefPtr<CanvasPattern> pattern)
128     : m_pattern(pattern.leakRef())
129     , m_type(ImagePattern)
130 {
131     if (!m_pattern)
132         m_type = Invalid;
133 }
134
135 CanvasStyle::~CanvasStyle()
136 {
137     if (m_type == Gradient)
138         m_gradient->deref();
139     else if (m_type == ImagePattern)
140         m_pattern->deref();
141     else if (m_type == CMYKA)
142         delete m_cmyka;
143 }
144
145 CanvasStyle CanvasStyle::createFromString(const String& color, Document* document)
146 {
147     RGBA32 rgba;
148     ColorParseResult parseResult = parseColor(rgba, color, document);
149     switch (parseResult) {
150     case ParsedRGBA:
151     case ParsedSystemColor:
152         return CanvasStyle(rgba);
153     case ParsedCurrentColor:
154         return CanvasStyle(ConstructCurrentColor);
155     case ParseFailed:
156         return CanvasStyle();
157     default:
158         ASSERT_NOT_REACHED();
159         return CanvasStyle();
160     }
161 }
162
163 CanvasStyle CanvasStyle::createFromStringWithOverrideAlpha(const String& color, float alpha)
164 {
165     RGBA32 rgba;
166     ColorParseResult parseResult = parseColor(rgba, color);
167     switch (parseResult) {
168     case ParsedRGBA:
169         return CanvasStyle(colorWithOverrideAlpha(rgba, alpha));
170     case ParsedCurrentColor:
171         return CanvasStyle(CurrentColorWithOverrideAlpha, alpha);
172     case ParseFailed:
173         return CanvasStyle();
174     default:
175         ASSERT_NOT_REACHED();
176         return CanvasStyle();
177     }
178 }
179
180 bool CanvasStyle::isEquivalentColor(const CanvasStyle& other) const
181 {
182     if (m_type != other.m_type)
183         return false;
184
185     switch (m_type) {
186     case RGBA:
187         return m_rgba == other.m_rgba;
188     case CMYKA:
189         return m_cmyka->c == other.m_cmyka->c
190             && m_cmyka->m == other.m_cmyka->m
191             && m_cmyka->y == other.m_cmyka->y
192             && m_cmyka->k == other.m_cmyka->k
193             && m_cmyka->a == other.m_cmyka->a;
194     case Gradient:
195     case ImagePattern:
196     case CurrentColor:
197     case CurrentColorWithOverrideAlpha:
198         return false;
199     case Invalid:
200         break;
201     }
202
203     ASSERT_NOT_REACHED();
204     return false;
205 }
206
207 bool CanvasStyle::isEquivalentRGBA(float r, float g, float b, float a) const
208 {
209     if (m_type != RGBA)
210         return false;
211
212     return m_rgba == makeRGBA32FromFloats(r, g, b, a);
213 }
214
215 bool CanvasStyle::isEquivalentCMYKA(float c, float m, float y, float k, float a) const
216 {
217     if (m_type != CMYKA)
218         return false;
219
220     return c == m_cmyka->c
221         && m == m_cmyka->m
222         && y == m_cmyka->y
223         && k == m_cmyka->k
224         && a == m_cmyka->a;
225 }
226
227 CanvasStyle::CanvasStyle(const CanvasStyle& other)
228 {
229     memcpy(this, &other, sizeof(CanvasStyle));
230     if (m_type == Gradient)
231         m_gradient->ref();
232     else if (m_type == ImagePattern)
233         m_pattern->ref();
234     else if (m_type == CMYKA)
235         m_cmyka = new CMYKAValues(other.m_cmyka->rgba, other.m_cmyka->c, other.m_cmyka->m, other.m_cmyka->y, other.m_cmyka->k, other.m_cmyka->a);
236 }
237
238 CanvasStyle& CanvasStyle::operator=(const CanvasStyle& other)
239 {
240     if (this != &other) {
241         memcpy(this, &other, sizeof(CanvasStyle));
242         if (m_type == Gradient)
243             m_gradient->ref();
244         else if (m_type == ImagePattern)
245             m_pattern->ref();
246         else if (m_type == CMYKA)
247             m_cmyka = new CMYKAValues(other.m_cmyka->rgba, other.m_cmyka->c, other.m_cmyka->m, other.m_cmyka->y, other.m_cmyka->k, other.m_cmyka->a);
248     }
249     return *this;
250 }
251
252 void CanvasStyle::applyStrokeColor(GraphicsContext* context) const
253 {
254     if (!context)
255         return;
256     switch (m_type) {
257     case RGBA:
258         context->setStrokeColor(m_rgba, ColorSpaceDeviceRGB);
259         break;
260     case CMYKA: {
261         // FIXME: Do this through platform-independent GraphicsContext API.
262         // We'll need a fancier Color abstraction to support CMYKA correctly
263 #if USE(CG)
264         CGContextSetCMYKStrokeColor(context->platformContext(), m_cmyka->c, m_cmyka->m, m_cmyka->y, m_cmyka->k, m_cmyka->a);
265 #elif PLATFORM(QT)
266         QPen currentPen = context->platformContext()->pen();
267         QColor clr;
268         clr.setCmykF(m_cmyka->c, m_cmyka->m, m_cmyka->y, m_cmyka->k, m_cmyka->a);
269         currentPen.setColor(clr);
270         context->platformContext()->setPen(currentPen);
271 #else
272         context->setStrokeColor(m_cmyka->rgba, ColorSpaceDeviceRGB);
273 #endif
274         break;
275     }
276     case Gradient:
277         context->setStrokeGradient(canvasGradient()->gradient());
278         break;
279     case ImagePattern:
280         context->setStrokePattern(canvasPattern()->pattern());
281         break;
282     case CurrentColor:
283     case CurrentColorWithOverrideAlpha:
284     case Invalid:
285         ASSERT_NOT_REACHED();
286         break;
287     }
288 }
289
290 void CanvasStyle::applyFillColor(GraphicsContext* context) const
291 {
292     if (!context)
293         return;
294     switch (m_type) {
295     case RGBA:
296         context->setFillColor(m_rgba, ColorSpaceDeviceRGB);
297         break;
298     case CMYKA: {
299         // FIXME: Do this through platform-independent GraphicsContext API.
300         // We'll need a fancier Color abstraction to support CMYKA correctly
301 #if USE(CG)
302         CGContextSetCMYKFillColor(context->platformContext(), m_cmyka->c, m_cmyka->m, m_cmyka->y, m_cmyka->k, m_cmyka->a);
303 #elif PLATFORM(QT)
304         QBrush currentBrush = context->platformContext()->brush();
305         QColor clr;
306         clr.setCmykF(m_cmyka->c, m_cmyka->m, m_cmyka->y, m_cmyka->k, m_cmyka->a);
307         currentBrush.setColor(clr);
308         context->platformContext()->setBrush(currentBrush);
309 #else
310         context->setFillColor(m_cmyka->rgba, ColorSpaceDeviceRGB);
311 #endif
312         break;
313     }
314     case Gradient:
315         context->setFillGradient(canvasGradient()->gradient());
316         break;
317     case ImagePattern:
318         context->setFillPattern(canvasPattern()->pattern());
319         break;
320     case CurrentColor:
321     case CurrentColorWithOverrideAlpha:
322     case Invalid:
323         ASSERT_NOT_REACHED();
324         break;
325     }
326 }
327
328 }