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