• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 #include "Color.h"
28 
29 #include "PlatformString.h"
30 #include <math.h>
31 #include <wtf/Assertions.h>
32 #include <wtf/MathExtras.h>
33 
34 #include "ColorData.c"
35 
36 using namespace std;
37 using namespace WTF;
38 
39 namespace WebCore {
40 
41 #if !COMPILER(MSVC)
42 const RGBA32 Color::black;
43 const RGBA32 Color::white;
44 const RGBA32 Color::darkGray;
45 const RGBA32 Color::gray;
46 const RGBA32 Color::lightGray;
47 const RGBA32 Color::transparent;
48 #endif
49 
50 static const RGBA32 lightenedBlack = 0xFF545454;
51 static const RGBA32 darkenedWhite = 0xFFABABAB;
52 
makeRGB(int r,int g,int b)53 RGBA32 makeRGB(int r, int g, int b)
54 {
55     return 0xFF000000 | max(0, min(r, 255)) << 16 | max(0, min(g, 255)) << 8 | max(0, min(b, 255));
56 }
57 
makeRGBA(int r,int g,int b,int a)58 RGBA32 makeRGBA(int r, int g, int b, int a)
59 {
60     return max(0, min(a, 255)) << 24 | max(0, min(r, 255)) << 16 | max(0, min(g, 255)) << 8 | max(0, min(b, 255));
61 }
62 
colorFloatToRGBAByte(float f)63 static int colorFloatToRGBAByte(float f)
64 {
65     // We use lroundf and 255 instead of nextafterf(256, 0) to match CG's rounding
66     return max(0, min(static_cast<int>(lroundf(255.0f * f)), 255));
67 }
68 
makeRGBA32FromFloats(float r,float g,float b,float a)69 RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a)
70 {
71     return colorFloatToRGBAByte(a) << 24 | colorFloatToRGBAByte(r) << 16 | colorFloatToRGBAByte(g) << 8 | colorFloatToRGBAByte(b);
72 }
73 
colorWithOverrideAlpha(RGBA32 color,float overrideAlpha)74 RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha)
75 {
76     RGBA32 rgbOnly = color & 0x00FFFFFF;
77     RGBA32 rgba = rgbOnly | colorFloatToRGBAByte(overrideAlpha) << 24;
78     return rgba;
79 }
80 
calcHue(double temp1,double temp2,double hueVal)81 static double calcHue(double temp1, double temp2, double hueVal)
82 {
83     if (hueVal < 0.0)
84         hueVal++;
85     else if (hueVal > 1.0)
86         hueVal--;
87     if (hueVal * 6.0 < 1.0)
88         return temp1 + (temp2 - temp1) * hueVal * 6.0;
89     if (hueVal * 2.0 < 1.0)
90         return temp2;
91     if (hueVal * 3.0 < 2.0)
92         return temp1 + (temp2 - temp1) * (2.0 / 3.0 - hueVal) * 6.0;
93     return temp1;
94 }
95 
96 // Explanation of this algorithm can be found in the CSS3 Color Module
97 // specification at http://www.w3.org/TR/css3-color/#hsl-color with further
98 // explanation available at http://en.wikipedia.org/wiki/HSL_color_space
99 
100 // all values are in the range of 0 to 1.0
makeRGBAFromHSLA(double hue,double saturation,double lightness,double alpha)101 RGBA32 makeRGBAFromHSLA(double hue, double saturation, double lightness, double alpha)
102 {
103     const double scaleFactor = nextafter(256.0, 0.0);
104 
105     if (!saturation) {
106         int greyValue = static_cast<int>(lightness * scaleFactor);
107         return makeRGBA(greyValue, greyValue, greyValue, static_cast<int>(alpha * scaleFactor));
108     }
109 
110     double temp2 = lightness < 0.5 ? lightness * (1.0 + saturation) : lightness + saturation - lightness * saturation;
111     double temp1 = 2.0 * lightness - temp2;
112 
113     return makeRGBA(static_cast<int>(calcHue(temp1, temp2, hue + 1.0 / 3.0) * scaleFactor),
114                     static_cast<int>(calcHue(temp1, temp2, hue) * scaleFactor),
115                     static_cast<int>(calcHue(temp1, temp2, hue - 1.0 / 3.0) * scaleFactor),
116                     static_cast<int>(alpha * scaleFactor));
117 }
118 
119 // originally moved here from the CSS parser
parseHexColor(const String & name,RGBA32 & rgb)120 bool Color::parseHexColor(const String& name, RGBA32& rgb)
121 {
122     unsigned length = name.length();
123     if (length != 3 && length != 6)
124         return false;
125     unsigned value = 0;
126     for (unsigned i = 0; i < length; ++i) {
127         if (!isASCIIHexDigit(name[i]))
128             return false;
129         value <<= 4;
130         value |= toASCIIHexValue(name[i]);
131     }
132     if (length == 6) {
133         rgb = 0xFF000000 | value;
134         return true;
135     }
136     // #abc converts to #aabbcc
137     rgb = 0xFF000000
138         | (value & 0xF00) << 12 | (value & 0xF00) << 8
139         | (value & 0xF0) << 8 | (value & 0xF0) << 4
140         | (value & 0xF) << 4 | (value & 0xF);
141     return true;
142 }
143 
differenceSquared(const Color & c1,const Color & c2)144 int differenceSquared(const Color& c1, const Color& c2)
145 {
146     int dR = c1.red() - c2.red();
147     int dG = c1.green() - c2.green();
148     int dB = c1.blue() - c2.blue();
149     return dR * dR + dG * dG + dB * dB;
150 }
151 
Color(const String & name)152 Color::Color(const String& name)
153 {
154     if (name.startsWith("#"))
155         m_valid = parseHexColor(name.substring(1), m_color);
156     else
157         setNamedColor(name);
158 }
159 
Color(const char * name)160 Color::Color(const char* name)
161 {
162     if (name[0] == '#')
163         m_valid = parseHexColor(&name[1], m_color);
164     else {
165         const NamedColor* foundColor = findColor(name, strlen(name));
166         m_color = foundColor ? foundColor->RGBValue : 0;
167         m_color |= 0xFF000000;
168         m_valid = foundColor;
169     }
170 }
171 
name() const172 String Color::name() const
173 {
174     if (alpha() < 0xFF)
175         return String::format("#%02X%02X%02X%02X", red(), green(), blue(), alpha());
176     return String::format("#%02X%02X%02X", red(), green(), blue());
177 }
178 
findNamedColor(const String & name)179 static inline const NamedColor* findNamedColor(const String& name)
180 {
181     char buffer[64]; // easily big enough for the longest color name
182     unsigned length = name.length();
183     if (length > sizeof(buffer) - 1)
184         return 0;
185     for (unsigned i = 0; i < length; ++i) {
186         UChar c = name[i];
187         if (!c || c > 0x7F)
188             return 0;
189         buffer[i] = toASCIILower(static_cast<char>(c));
190     }
191     buffer[length] = '\0';
192     return findColor(buffer, length);
193 }
194 
setNamedColor(const String & name)195 void Color::setNamedColor(const String& name)
196 {
197     const NamedColor* foundColor = findNamedColor(name);
198     m_color = foundColor ? foundColor->RGBValue : 0;
199     m_color |= 0xFF000000;
200     m_valid = foundColor;
201 }
202 
light() const203 Color Color::light() const
204 {
205     // Hardcode this common case for speed.
206     if (m_color == black)
207         return lightenedBlack;
208 
209     const float scaleFactor = nextafterf(256.0f, 0.0f);
210 
211     float r, g, b, a;
212     getRGBA(r, g, b, a);
213 
214     float v = max(r, max(g, b));
215 
216     if (v == 0.0f)
217         // Lightened black with alpha.
218         return Color(0x54, 0x54, 0x54, alpha());
219 
220     float multiplier = min(1.0f, v + 0.33f) / v;
221 
222     return Color(static_cast<int>(multiplier * r * scaleFactor),
223                  static_cast<int>(multiplier * g * scaleFactor),
224                  static_cast<int>(multiplier * b * scaleFactor),
225                  alpha());
226 }
227 
dark() const228 Color Color::dark() const
229 {
230     // Hardcode this common case for speed.
231     if (m_color == white)
232         return darkenedWhite;
233 
234     const float scaleFactor = nextafterf(256.0f, 0.0f);
235 
236     float r, g, b, a;
237     getRGBA(r, g, b, a);
238 
239     float v = max(r, max(g, b));
240     float multiplier = max(0.0f, (v - 0.33f) / v);
241 
242     return Color(static_cast<int>(multiplier * r * scaleFactor),
243                  static_cast<int>(multiplier * g * scaleFactor),
244                  static_cast<int>(multiplier * b * scaleFactor),
245                  alpha());
246 }
247 
blendComponent(int c,int a)248 static int blendComponent(int c, int a)
249 {
250     // We use white.
251     float alpha = a / 255.0f;
252     int whiteBlend = 255 - a;
253     c -= whiteBlend;
254     return static_cast<int>(c / alpha);
255 }
256 
257 const int cStartAlpha = 153; // 60%
258 const int cEndAlpha = 204; // 80%;
259 const int cAlphaIncrement = 17; // Increments in between.
260 
blend(const Color & source) const261 Color Color::blend(const Color& source) const
262 {
263     if (!alpha() || !source.hasAlpha())
264         return source;
265 
266     if (!source.alpha())
267         return *this;
268 
269     int d = 255 * (alpha() + source.alpha()) - alpha() * source.alpha();
270     int a = d / 255;
271     int r = (red() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.red()) / d;
272     int g = (green() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.green()) / d;
273     int b = (blue() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.blue()) / d;
274     return Color(r, g, b, a);
275 }
276 
blendWithWhite() const277 Color Color::blendWithWhite() const
278 {
279     // If the color contains alpha already, we leave it alone.
280     if (hasAlpha())
281         return *this;
282 
283     Color newColor;
284     for (int alpha = cStartAlpha; alpha <= cEndAlpha; alpha += cAlphaIncrement) {
285         // We have a solid color.  Convert to an equivalent color that looks the same when blended with white
286         // at the current alpha.  Try using less transparency if the numbers end up being negative.
287         int r = blendComponent(red(), alpha);
288         int g = blendComponent(green(), alpha);
289         int b = blendComponent(blue(), alpha);
290 
291         newColor = Color(r, g, b, alpha);
292 
293         if (r >= 0 && g >= 0 && b >= 0)
294             break;
295     }
296     return newColor;
297 }
298 
getRGBA(float & r,float & g,float & b,float & a) const299 void Color::getRGBA(float& r, float& g, float& b, float& a) const
300 {
301     r = red() / 255.0f;
302     g = green() / 255.0f;
303     b = blue() / 255.0f;
304     a = alpha() / 255.0f;
305 }
306 
getRGBA(double & r,double & g,double & b,double & a) const307 void Color::getRGBA(double& r, double& g, double& b, double& a) const
308 {
309     r = red() / 255.0;
310     g = green() / 255.0;
311     b = blue() / 255.0;
312     a = alpha() / 255.0;
313 }
314 
315 } // namespace WebCore
316