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 (at) atoker.com> 5 * Copyright (C) 2008 Eric Seidel <eric (at) 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 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 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 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 94 CanvasStyle::CanvasStyle(Type type, float overrideAlpha) 95 : m_type(type) 96 , m_overrideAlpha(overrideAlpha) 97 { 98 } 99 100 CanvasStyle::CanvasStyle(RGBA32 rgba) 101 : m_type(RGBA) 102 , m_rgba(rgba) 103 { 104 } 105 106 CanvasStyle::CanvasStyle(float grayLevel, float alpha) 107 : m_type(RGBA) 108 , m_rgba(makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha)) 109 { 110 } 111 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 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 125 CanvasStyle::CanvasStyle(PassRefPtr<CanvasGradient> gradient) 126 : m_type(Gradient) 127 , m_gradient(gradient) 128 { 129 } 130 131 CanvasStyle::CanvasStyle(PassRefPtr<CanvasPattern> pattern) 132 : m_type(ImagePattern) 133 , m_pattern(pattern) 134 { 135 } 136 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 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 172 PassRefPtr<CanvasStyle> CanvasStyle::createFromGradient(PassRefPtr<CanvasGradient> gradient) 173 { 174 if (!gradient) 175 return 0; 176 return adoptRef(new CanvasStyle(gradient)); 177 } 178 PassRefPtr<CanvasStyle> CanvasStyle::createFromPattern(PassRefPtr<CanvasPattern> pattern) 179 { 180 if (!pattern) 181 return 0; 182 return adoptRef(new CanvasStyle(pattern)); 183 } 184 185 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 210 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 218 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 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 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