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 "core/html/canvas/CanvasStyle.h"
31
32#include "CSSPropertyNames.h"
33#include "core/css/CSSParser.h"
34#include "core/css/StylePropertySet.h"
35#include "core/html/HTMLCanvasElement.h"
36#include "core/html/canvas/CanvasGradient.h"
37#include "core/html/canvas/CanvasPattern.h"
38#include "core/platform/graphics/GraphicsContext.h"
39#include "wtf/Assertions.h"
40#include "wtf/PassRefPtr.h"
41
42namespace WebCore {
43
44enum ColorParseResult { ParsedRGBA, ParsedCurrentColor, ParsedSystemColor, ParseFailed };
45
46static ColorParseResult parseColor(RGBA32& parsedColor, const String& colorString, Document* document = 0)
47{
48    if (equalIgnoringCase(colorString, "currentcolor"))
49        return ParsedCurrentColor;
50    if (CSSParser::parseColor(parsedColor, colorString))
51        return ParsedRGBA;
52    if (CSSParser::parseSystemColor(parsedColor, colorString, document))
53        return ParsedSystemColor;
54    return ParseFailed;
55}
56
57RGBA32 currentColor(HTMLCanvasElement* canvas)
58{
59    if (!canvas || !canvas->inDocument() || !canvas->inlineStyle())
60        return Color::black;
61    RGBA32 rgba = Color::black;
62    CSSParser::parseColor(rgba, canvas->inlineStyle()->getPropertyValue(CSSPropertyColor));
63    return rgba;
64}
65
66bool parseColorOrCurrentColor(RGBA32& parsedColor, const String& colorString, HTMLCanvasElement* canvas)
67{
68    ColorParseResult parseResult = parseColor(parsedColor, colorString, canvas ? canvas->document() : 0);
69    switch (parseResult) {
70    case ParsedRGBA:
71    case ParsedSystemColor:
72        return true;
73    case ParsedCurrentColor:
74        parsedColor = currentColor(canvas);
75        return true;
76    case ParseFailed:
77        return false;
78    default:
79        ASSERT_NOT_REACHED();
80        return false;
81    }
82}
83
84CanvasStyle::CanvasStyle(Type type, float overrideAlpha)
85    : m_type(type)
86    , m_overrideAlpha(overrideAlpha)
87{
88}
89
90CanvasStyle::CanvasStyle(RGBA32 rgba)
91    : m_type(RGBA)
92    , m_rgba(rgba)
93{
94}
95
96CanvasStyle::CanvasStyle(float grayLevel, float alpha)
97    : m_type(RGBA)
98    , m_rgba(makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha))
99{
100}
101
102CanvasStyle::CanvasStyle(float r, float g, float b, float a)
103    : m_type(RGBA)
104    , m_rgba(makeRGBA32FromFloats(r, g, b, a))
105{
106}
107
108CanvasStyle::CanvasStyle(float c, float m, float y, float k, float a)
109    : m_type(CMYKA)
110    , m_rgba(makeRGBAFromCMYKA(c, m, y, k, a))
111    , m_cmyka(c, m, y, k, a)
112{
113}
114
115CanvasStyle::CanvasStyle(PassRefPtr<CanvasGradient> gradient)
116    : m_type(Gradient)
117    , m_gradient(gradient)
118{
119}
120
121CanvasStyle::CanvasStyle(PassRefPtr<CanvasPattern> pattern)
122    : m_type(ImagePattern)
123    , m_pattern(pattern)
124{
125}
126
127PassRefPtr<CanvasStyle> CanvasStyle::createFromString(const String& color, Document* document)
128{
129    RGBA32 rgba;
130    ColorParseResult parseResult = parseColor(rgba, color, document);
131    switch (parseResult) {
132    case ParsedRGBA:
133    case ParsedSystemColor:
134        return adoptRef(new CanvasStyle(rgba));
135    case ParsedCurrentColor:
136        return adoptRef(new CanvasStyle(CurrentColor));
137    case ParseFailed:
138        return 0;
139    default:
140        ASSERT_NOT_REACHED();
141        return 0;
142    }
143}
144
145PassRefPtr<CanvasStyle> CanvasStyle::createFromStringWithOverrideAlpha(const String& color, float alpha)
146{
147    RGBA32 rgba;
148    ColorParseResult parseResult = parseColor(rgba, color);
149    switch (parseResult) {
150    case ParsedRGBA:
151        return adoptRef(new CanvasStyle(colorWithOverrideAlpha(rgba, alpha)));
152    case ParsedCurrentColor:
153        return adoptRef(new CanvasStyle(CurrentColorWithOverrideAlpha, alpha));
154    case ParseFailed:
155        return 0;
156    default:
157        ASSERT_NOT_REACHED();
158        return 0;
159    }
160}
161
162PassRefPtr<CanvasStyle> CanvasStyle::createFromGradient(PassRefPtr<CanvasGradient> gradient)
163{
164    if (!gradient)
165        return 0;
166    return adoptRef(new CanvasStyle(gradient));
167}
168PassRefPtr<CanvasStyle> CanvasStyle::createFromPattern(PassRefPtr<CanvasPattern> pattern)
169{
170    if (!pattern)
171        return 0;
172    return adoptRef(new CanvasStyle(pattern));
173}
174
175bool CanvasStyle::isEquivalentColor(const CanvasStyle& other) const
176{
177    if (m_type != other.m_type)
178        return false;
179
180    switch (m_type) {
181    case RGBA:
182        return m_rgba == other.m_rgba;
183    case CMYKA:
184        return m_cmyka.c == other.m_cmyka.c
185            && m_cmyka.m == other.m_cmyka.m
186            && m_cmyka.y == other.m_cmyka.y
187            && m_cmyka.k == other.m_cmyka.k
188            && m_cmyka.a == other.m_cmyka.a;
189    case Gradient:
190    case ImagePattern:
191    case CurrentColor:
192    case CurrentColorWithOverrideAlpha:
193        return false;
194    }
195
196    ASSERT_NOT_REACHED();
197    return false;
198}
199
200bool CanvasStyle::isEquivalentRGBA(float r, float g, float b, float a) const
201{
202    if (m_type != RGBA)
203        return false;
204
205    return m_rgba == makeRGBA32FromFloats(r, g, b, a);
206}
207
208bool CanvasStyle::isEquivalentCMYKA(float c, float m, float y, float k, float a) const
209{
210    if (m_type != CMYKA)
211        return false;
212
213    return c == m_cmyka.c
214        && m == m_cmyka.m
215        && y == m_cmyka.y
216        && k == m_cmyka.k
217        && a == m_cmyka.a;
218}
219
220void CanvasStyle::applyStrokeColor(GraphicsContext* context)
221{
222    if (!context)
223        return;
224    switch (m_type) {
225    case RGBA:
226        context->setStrokeColor(m_rgba);
227        break;
228    case CMYKA: {
229        // FIXME: Do this through platform-independent GraphicsContext API.
230        // We'll need a fancier Color abstraction to support CMYKA correctly
231        context->setStrokeColor(m_rgba);
232        break;
233    }
234    case Gradient:
235        context->setStrokeGradient(canvasGradient()->gradient());
236        break;
237    case ImagePattern:
238        context->setStrokePattern(canvasPattern()->pattern());
239        break;
240    case CurrentColor:
241    case CurrentColorWithOverrideAlpha:
242        ASSERT_NOT_REACHED();
243        break;
244    }
245}
246
247void CanvasStyle::applyFillColor(GraphicsContext* context)
248{
249    if (!context)
250        return;
251    switch (m_type) {
252    case RGBA:
253        context->setFillColor(m_rgba);
254        break;
255    case CMYKA: {
256        // FIXME: Do this through platform-independent GraphicsContext API.
257        // We'll need a fancier Color abstraction to support CMYKA correctly
258        context->setFillColor(m_rgba);
259        break;
260    }
261    case Gradient:
262        context->setFillGradient(canvasGradient()->gradient());
263        break;
264    case ImagePattern:
265        context->setFillPattern(canvasPattern()->pattern());
266        break;
267    case CurrentColor:
268    case CurrentColorWithOverrideAlpha:
269        ASSERT_NOT_REACHED();
270        break;
271    }
272}
273
274}
275