|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "mozilla/ArrayUtils.h" // for ArrayLength |
|
7 #include "mozilla/mozalloc.h" // for operator delete, etc |
|
8 |
|
9 #include "nsColor.h" |
|
10 #include <sys/types.h> // for int32_t |
|
11 #include "nsColorNames.h" // for nsColorNames |
|
12 #include "nsDebug.h" // for NS_ASSERTION, etc |
|
13 #include "nsStaticNameTable.h" |
|
14 #include "nsString.h" // for nsAutoCString, nsString, etc |
|
15 #include "nscore.h" // for nsAString, etc |
|
16 |
|
17 using namespace mozilla; |
|
18 |
|
19 // define an array of all color names |
|
20 #define GFX_COLOR(_name, _value) #_name, |
|
21 static const char* const kColorNames[] = { |
|
22 #include "nsColorNameList.h" |
|
23 }; |
|
24 #undef GFX_COLOR |
|
25 |
|
26 // define an array of all color name values |
|
27 #define GFX_COLOR(_name, _value) _value, |
|
28 static const nscolor kColors[] = { |
|
29 #include "nsColorNameList.h" |
|
30 }; |
|
31 #undef GFX_COLOR |
|
32 |
|
33 #define eColorName_COUNT (ArrayLength(kColorNames)) |
|
34 #define eColorName_UNKNOWN (-1) |
|
35 |
|
36 static nsStaticCaseInsensitiveNameTable* gColorTable = nullptr; |
|
37 |
|
38 void nsColorNames::AddRefTable(void) |
|
39 { |
|
40 NS_ASSERTION(!gColorTable, "pre existing array!"); |
|
41 if (!gColorTable) { |
|
42 gColorTable = new nsStaticCaseInsensitiveNameTable(); |
|
43 if (gColorTable) { |
|
44 #ifdef DEBUG |
|
45 { |
|
46 // let's verify the table... |
|
47 for (uint32_t index = 0; index < eColorName_COUNT; ++index) { |
|
48 nsAutoCString temp1(kColorNames[index]); |
|
49 nsAutoCString temp2(kColorNames[index]); |
|
50 ToLowerCase(temp1); |
|
51 NS_ASSERTION(temp1.Equals(temp2), "upper case char in table"); |
|
52 } |
|
53 } |
|
54 #endif |
|
55 gColorTable->Init(kColorNames, eColorName_COUNT); |
|
56 } |
|
57 } |
|
58 } |
|
59 |
|
60 void nsColorNames::ReleaseTable(void) |
|
61 { |
|
62 if (gColorTable) { |
|
63 delete gColorTable; |
|
64 gColorTable = nullptr; |
|
65 } |
|
66 } |
|
67 |
|
68 static int ComponentValue(const char16_t* aColorSpec, int aLen, int color, int dpc) |
|
69 { |
|
70 int component = 0; |
|
71 int index = (color * dpc); |
|
72 if (2 < dpc) { |
|
73 dpc = 2; |
|
74 } |
|
75 while (--dpc >= 0) { |
|
76 char16_t ch = ((index < aLen) ? aColorSpec[index++] : '0'); |
|
77 if (('0' <= ch) && (ch <= '9')) { |
|
78 component = (component * 16) + (ch - '0'); |
|
79 } else if ((('a' <= ch) && (ch <= 'f')) || |
|
80 (('A' <= ch) && (ch <= 'F'))) { |
|
81 // "ch&7" handles lower and uppercase hex alphabetics |
|
82 component = (component * 16) + (ch & 7) + 9; |
|
83 } |
|
84 else { // not a hex digit, treat it like 0 |
|
85 component = (component * 16); |
|
86 } |
|
87 } |
|
88 return component; |
|
89 } |
|
90 |
|
91 NS_GFX_(bool) NS_HexToRGB(const nsAString& aColorSpec, |
|
92 nscolor* aResult) |
|
93 { |
|
94 const char16_t* buffer = aColorSpec.BeginReading(); |
|
95 |
|
96 int nameLen = aColorSpec.Length(); |
|
97 if ((nameLen == 3) || (nameLen == 6)) { |
|
98 // Make sure the digits are legal |
|
99 for (int i = 0; i < nameLen; i++) { |
|
100 char16_t ch = buffer[i]; |
|
101 if (((ch >= '0') && (ch <= '9')) || |
|
102 ((ch >= 'a') && (ch <= 'f')) || |
|
103 ((ch >= 'A') && (ch <= 'F'))) { |
|
104 // Legal character |
|
105 continue; |
|
106 } |
|
107 // Whoops. Illegal character. |
|
108 return false; |
|
109 } |
|
110 |
|
111 // Convert the ascii to binary |
|
112 int dpc = ((3 == nameLen) ? 1 : 2); |
|
113 // Translate components from hex to binary |
|
114 int r = ComponentValue(buffer, nameLen, 0, dpc); |
|
115 int g = ComponentValue(buffer, nameLen, 1, dpc); |
|
116 int b = ComponentValue(buffer, nameLen, 2, dpc); |
|
117 if (dpc == 1) { |
|
118 // Scale single digit component to an 8 bit value. Replicate the |
|
119 // single digit to compute the new value. |
|
120 r = (r << 4) | r; |
|
121 g = (g << 4) | g; |
|
122 b = (b << 4) | b; |
|
123 } |
|
124 NS_ASSERTION((r >= 0) && (r <= 255), "bad r"); |
|
125 NS_ASSERTION((g >= 0) && (g <= 255), "bad g"); |
|
126 NS_ASSERTION((b >= 0) && (b <= 255), "bad b"); |
|
127 *aResult = NS_RGB(r, g, b); |
|
128 return true; |
|
129 } |
|
130 |
|
131 // Improperly formatted color value |
|
132 return false; |
|
133 } |
|
134 |
|
135 // This implements part of the algorithm for legacy behavior described in |
|
136 // http://www.whatwg.org/specs/web-apps/current-work/complete/common-microsyntaxes.html#rules-for-parsing-a-legacy-color-value |
|
137 NS_GFX_(bool) NS_LooseHexToRGB(const nsString& aColorSpec, nscolor* aResult) |
|
138 { |
|
139 if (aColorSpec.EqualsLiteral("transparent")) { |
|
140 return false; |
|
141 } |
|
142 |
|
143 int nameLen = aColorSpec.Length(); |
|
144 const char16_t* colorSpec = aColorSpec.get(); |
|
145 if (nameLen > 128) { |
|
146 nameLen = 128; |
|
147 } |
|
148 |
|
149 if ('#' == colorSpec[0]) { |
|
150 ++colorSpec; |
|
151 --nameLen; |
|
152 } |
|
153 |
|
154 // digits per component |
|
155 int dpc = (nameLen + 2) / 3; |
|
156 int newdpc = dpc; |
|
157 |
|
158 // Use only the rightmost 8 characters of each component. |
|
159 if (newdpc > 8) { |
|
160 nameLen -= newdpc - 8; |
|
161 colorSpec += newdpc - 8; |
|
162 newdpc = 8; |
|
163 } |
|
164 |
|
165 // And then keep trimming characters at the left until we'd trim one |
|
166 // that would leave a nonzero value, but not past 2 characters per |
|
167 // component. |
|
168 while (newdpc > 2) { |
|
169 bool haveNonzero = false; |
|
170 for (int c = 0; c < 3; ++c) { |
|
171 NS_ABORT_IF_FALSE(c * dpc < nameLen, |
|
172 "should not pass end of string while newdpc > 2"); |
|
173 char16_t ch = colorSpec[c * dpc]; |
|
174 if (('1' <= ch && ch <= '9') || |
|
175 ('A' <= ch && ch <= 'F') || |
|
176 ('a' <= ch && ch <= 'f')) { |
|
177 haveNonzero = true; |
|
178 break; |
|
179 } |
|
180 } |
|
181 if (haveNonzero) { |
|
182 break; |
|
183 } |
|
184 --newdpc; |
|
185 --nameLen; |
|
186 ++colorSpec; |
|
187 } |
|
188 |
|
189 // Translate components from hex to binary |
|
190 int r = ComponentValue(colorSpec, nameLen, 0, dpc); |
|
191 int g = ComponentValue(colorSpec, nameLen, 1, dpc); |
|
192 int b = ComponentValue(colorSpec, nameLen, 2, dpc); |
|
193 NS_ASSERTION((r >= 0) && (r <= 255), "bad r"); |
|
194 NS_ASSERTION((g >= 0) && (g <= 255), "bad g"); |
|
195 NS_ASSERTION((b >= 0) && (b <= 255), "bad b"); |
|
196 |
|
197 *aResult = NS_RGB(r, g, b); |
|
198 return true; |
|
199 } |
|
200 |
|
201 NS_GFX_(bool) NS_ColorNameToRGB(const nsAString& aColorName, nscolor* aResult) |
|
202 { |
|
203 if (!gColorTable) return false; |
|
204 |
|
205 int32_t id = gColorTable->Lookup(aColorName); |
|
206 if (eColorName_UNKNOWN < id) { |
|
207 NS_ASSERTION(uint32_t(id) < eColorName_COUNT, |
|
208 "gColorTable->Lookup messed up"); |
|
209 if (aResult) { |
|
210 *aResult = kColors[id]; |
|
211 } |
|
212 return true; |
|
213 } |
|
214 return false; |
|
215 } |
|
216 |
|
217 // Returns kColorNames, an array of all possible color names, and sets |
|
218 // *aSizeArray to the size of that array. Do NOT call free() on this array. |
|
219 NS_GFX_(const char * const *) NS_AllColorNames(size_t *aSizeArray) |
|
220 { |
|
221 *aSizeArray = ArrayLength(kColorNames); |
|
222 return kColorNames; |
|
223 } |
|
224 |
|
225 // Macro to blend two colors |
|
226 // |
|
227 // equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255 |
|
228 #define MOZ_BLEND(target, bg, fg, fgalpha) \ |
|
229 FAST_DIVIDE_BY_255(target, (bg)*(255-fgalpha) + (fg)*(fgalpha)) |
|
230 |
|
231 NS_GFX_(nscolor) |
|
232 NS_ComposeColors(nscolor aBG, nscolor aFG) |
|
233 { |
|
234 // This function uses colors that are non premultiplied alpha. |
|
235 int r, g, b, a; |
|
236 |
|
237 int bgAlpha = NS_GET_A(aBG); |
|
238 int fgAlpha = NS_GET_A(aFG); |
|
239 |
|
240 // Compute the final alpha of the blended color |
|
241 // a = fgAlpha + bgAlpha*(255 - fgAlpha)/255; |
|
242 FAST_DIVIDE_BY_255(a, bgAlpha*(255-fgAlpha)); |
|
243 a = fgAlpha + a; |
|
244 int blendAlpha; |
|
245 if (a == 0) { |
|
246 // In this case the blended color is totally trasparent, |
|
247 // we preserve the color information of the foreground color. |
|
248 blendAlpha = 255; |
|
249 } else { |
|
250 blendAlpha = (fgAlpha*255)/a; |
|
251 } |
|
252 MOZ_BLEND(r, NS_GET_R(aBG), NS_GET_R(aFG), blendAlpha); |
|
253 MOZ_BLEND(g, NS_GET_G(aBG), NS_GET_G(aFG), blendAlpha); |
|
254 MOZ_BLEND(b, NS_GET_B(aBG), NS_GET_B(aFG), blendAlpha); |
|
255 |
|
256 return NS_RGBA(r, g, b, a); |
|
257 } |
|
258 |
|
259 // Functions to convert from HSL color space to RGB color space. |
|
260 // This is the algorithm described in the CSS3 specification |
|
261 |
|
262 // helper |
|
263 static float |
|
264 HSL_HueToRGB(float m1, float m2, float h) |
|
265 { |
|
266 if (h < 0.0f) |
|
267 h += 1.0f; |
|
268 if (h > 1.0f) |
|
269 h -= 1.0f; |
|
270 if (h < (float)(1.0/6.0)) |
|
271 return m1 + (m2 - m1)*h*6.0f; |
|
272 if (h < (float)(1.0/2.0)) |
|
273 return m2; |
|
274 if (h < (float)(2.0/3.0)) |
|
275 return m1 + (m2 - m1)*((float)(2.0/3.0) - h)*6.0f; |
|
276 return m1; |
|
277 } |
|
278 |
|
279 // The float parameters are all expected to be in the range 0-1 |
|
280 NS_GFX_(nscolor) |
|
281 NS_HSL2RGB(float h, float s, float l) |
|
282 { |
|
283 uint8_t r, g, b; |
|
284 float m1, m2; |
|
285 if (l <= 0.5f) { |
|
286 m2 = l*(s+1); |
|
287 } else { |
|
288 m2 = l + s - l*s; |
|
289 } |
|
290 m1 = l*2 - m2; |
|
291 r = uint8_t(255 * HSL_HueToRGB(m1, m2, h + 1.0f/3.0f)); |
|
292 g = uint8_t(255 * HSL_HueToRGB(m1, m2, h)); |
|
293 b = uint8_t(255 * HSL_HueToRGB(m1, m2, h - 1.0f/3.0f)); |
|
294 return NS_RGB(r, g, b); |
|
295 } |
|
296 |
|
297 NS_GFX_(const char*) |
|
298 NS_RGBToColorName(nscolor aColor) |
|
299 { |
|
300 for (size_t idx = 0; idx < ArrayLength(kColors); ++idx) { |
|
301 if (kColors[idx] == aColor) { |
|
302 return kColorNames[idx]; |
|
303 } |
|
304 } |
|
305 |
|
306 return nullptr; |
|
307 } |