1/*
2 * Copyright (C) 2006-2017 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 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 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 "StyleProperties.h"
39
40#if USE(CG)
41#include <CoreGraphics/CGContext.h>
42#endif
43
44namespace WebCore {
45
46static bool isCurrentColorString(const String& colorString)
47{
48 return equalLettersIgnoringASCIICase(colorString, "currentcolor");
49}
50
51static Color parseColor(const String& colorString)
52{
53 Color color = CSSParser::parseColor(colorString);
54 if (color.isValid())
55 return color;
56 return CSSParser::parseSystemColor(colorString, nullptr);
57}
58
59Color currentColor(HTMLCanvasElement* canvas)
60{
61 if (!canvas || !canvas->isConnected() || !canvas->inlineStyle())
62 return Color::black;
63 Color color = CSSParser::parseColor(canvas->inlineStyle()->getPropertyValue(CSSPropertyColor));
64 if (!color.isValid())
65 return Color::black;
66 return color;
67}
68
69Color parseColorOrCurrentColor(const String& colorString, HTMLCanvasElement* canvas)
70{
71 if (isCurrentColorString(colorString))
72 return currentColor(canvas);
73
74 return parseColor(colorString);
75}
76
77CanvasStyle::CanvasStyle(Color color)
78 : m_style(color)
79{
80}
81
82CanvasStyle::CanvasStyle(float grayLevel, float alpha)
83 : m_style(Color { grayLevel, grayLevel, grayLevel, alpha })
84{
85}
86
87CanvasStyle::CanvasStyle(float r, float g, float b, float a)
88 : m_style(Color { r, g, b, a })
89{
90}
91
92CanvasStyle::CanvasStyle(float c, float m, float y, float k, float a)
93 : m_style(CMYKAColor { Color { c, m, y, k, a }, c, m, y, k, a })
94{
95}
96
97CanvasStyle::CanvasStyle(CanvasGradient& gradient)
98 : m_style(makeRefPtr(gradient))
99{
100}
101
102CanvasStyle::CanvasStyle(CanvasPattern& pattern)
103 : m_style(makeRefPtr(pattern))
104{
105}
106
107inline CanvasStyle::CanvasStyle(CurrentColor color)
108 : m_style(color)
109{
110}
111
112CanvasStyle CanvasStyle::createFromString(const String& colorString)
113{
114 if (isCurrentColorString(colorString))
115 return CurrentColor { WTF::nullopt };
116
117 Color color = parseColor(colorString);
118 if (!color.isValid())
119 return { };
120
121 return color;
122}
123
124CanvasStyle CanvasStyle::createFromStringWithOverrideAlpha(const String& colorString, float alpha)
125{
126 if (isCurrentColorString(colorString))
127 return CurrentColor { alpha };
128
129 Color color = parseColor(colorString);
130 if (!color.isValid())
131 return { };
132
133 return Color { colorWithOverrideAlpha(color.rgb(), alpha) };
134}
135
136bool CanvasStyle::isEquivalentColor(const CanvasStyle& other) const
137{
138 if (WTF::holds_alternative<Color>(m_style) && WTF::holds_alternative<Color>(other.m_style))
139 return WTF::get<Color>(m_style) == WTF::get<Color>(other.m_style);
140
141 if (WTF::holds_alternative<CMYKAColor>(m_style) && WTF::holds_alternative<CMYKAColor>(other.m_style)) {
142 auto& a = WTF::get<CMYKAColor>(m_style);
143 auto& b = WTF::get<CMYKAColor>(other.m_style);
144 return a.c == b.c && a.m == b.m && a.y == b.y && a.k == b.k && a.a == b.a;
145 }
146
147 return false;
148}
149
150bool CanvasStyle::isEquivalentRGBA(float r, float g, float b, float a) const
151{
152 return WTF::holds_alternative<Color>(m_style) && WTF::get<Color>(m_style) == Color { r, g, b, a };
153}
154
155bool CanvasStyle::isEquivalentCMYKA(float c, float m, float y, float k, float a) const
156{
157 if (!WTF::holds_alternative<CMYKAColor>(m_style))
158 return false;
159
160 auto& channels = WTF::get<CMYKAColor>(m_style);
161 return c == channels.c && m == channels.m && y == channels.y && k == channels.k && a == channels.a;
162}
163
164void CanvasStyle::applyStrokeColor(GraphicsContext& context) const
165{
166 WTF::switchOn(m_style,
167 [&context] (const Color& color) {
168 context.setStrokeColor(color);
169 },
170 [&context] (const CMYKAColor& color) {
171 // FIXME: Do this through platform-independent GraphicsContext API.
172 // We'll need a fancier Color abstraction to support CMYKA correctly
173#if USE(CG)
174 CGContextSetCMYKStrokeColor(context.platformContext(), color.c, color.m, color.y, color.k, color.a);
175#else
176 context.setStrokeColor(color.color);
177#endif
178 },
179 [&context] (const RefPtr<CanvasGradient>& gradient) {
180 context.setStrokeGradient(gradient->gradient());
181 },
182 [&context] (const RefPtr<CanvasPattern>& pattern) {
183 context.setStrokePattern(pattern->pattern());
184 },
185 [] (const CurrentColor&) {
186 ASSERT_NOT_REACHED();
187 },
188 [] (const Invalid&) {
189 ASSERT_NOT_REACHED();
190 }
191 );
192}
193
194void CanvasStyle::applyFillColor(GraphicsContext& context) const
195{
196 WTF::switchOn(m_style,
197 [&context] (const Color& color) {
198 context.setFillColor(color);
199 },
200 [&context] (const CMYKAColor& color) {
201 // FIXME: Do this through platform-independent GraphicsContext API.
202 // We'll need a fancier Color abstraction to support CMYKA correctly
203#if USE(CG)
204 CGContextSetCMYKFillColor(context.platformContext(), color.c, color.m, color.y, color.k, color.a);
205#else
206 context.setFillColor(color.color);
207#endif
208 },
209 [&context] (const RefPtr<CanvasGradient>& gradient) {
210 context.setFillGradient(gradient->gradient());
211 },
212 [&context] (const RefPtr<CanvasPattern>& pattern) {
213 context.setFillPattern(pattern->pattern());
214 },
215 [] (const CurrentColor&) {
216 ASSERT_NOT_REACHED();
217 },
218 [] (const Invalid&) {
219 ASSERT_NOT_REACHED();
220 }
221 );
222}
223
224}
225