1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ArrayUtils.h" // for ArrayLength
8 #include "mozilla/mozalloc.h" // for operator delete, etc
9 #include "mozilla/MathAlgorithms.h"
12 #include <sys/types.h> // for int32_t
13 #include "nsColorNames.h" // for nsColorNames
14 #include "nsDebug.h" // for NS_ASSERTION, etc
15 #include "nsStaticNameTable.h"
16 #include "nsString.h" // for nsAutoCString, nsString, etc
17 #include "nscore.h" // for nsAString, etc
18 #include "prtypes.h" // for PR_BEGIN_MACRO, etc
20 using namespace mozilla
;
22 // define an array of all color names
23 #define GFX_COLOR(_name, _value) #_name,
24 static const char* const kColorNames
[] = {
25 #include "nsColorNameList.h"
29 // define an array of all color name values
30 #define GFX_COLOR(_name, _value) _value,
31 static const nscolor kColors
[] = {
32 #include "nsColorNameList.h"
36 #define eColorName_COUNT (ArrayLength(kColorNames))
37 #define eColorName_UNKNOWN (-1)
39 static nsStaticCaseInsensitiveNameTable
* gColorTable
= nullptr;
41 void nsColorNames::AddRefTable(void) {
42 NS_ASSERTION(!gColorTable
, "pre existing array!");
45 new nsStaticCaseInsensitiveNameTable(kColorNames
, eColorName_COUNT
);
49 void nsColorNames::ReleaseTable(void) {
52 gColorTable
= nullptr;
56 static int ComponentValue(const char16_t
* aColorSpec
, int aLen
, int color
,
59 int index
= (color
* dpc
);
64 char16_t ch
= ((index
< aLen
) ? aColorSpec
[index
++] : '0');
65 if (('0' <= ch
) && (ch
<= '9')) {
66 component
= (component
* 16) + (ch
- '0');
67 } else if ((('a' <= ch
) && (ch
<= 'f')) || (('A' <= ch
) && (ch
<= 'F'))) {
68 // "ch&7" handles lower and uppercase hex alphabetics
69 component
= (component
* 16) + (ch
& 7) + 9;
70 } else { // not a hex digit, treat it like 0
71 component
= (component
* 16);
77 bool NS_HexToRGBA(const nsAString
& aColorSpec
, nsHexColorType aType
,
79 const char16_t
* buffer
= aColorSpec
.BeginReading();
81 int nameLen
= aColorSpec
.Length();
82 bool hasAlpha
= false;
83 if (nameLen
!= 3 && nameLen
!= 6) {
84 if ((nameLen
!= 4 && nameLen
!= 8) || aType
== nsHexColorType::NoAlpha
) {
85 // Improperly formatted color value
91 // Make sure the digits are legal
92 for (int i
= 0; i
< nameLen
; i
++) {
93 char16_t ch
= buffer
[i
];
94 if (((ch
>= '0') && (ch
<= '9')) || ((ch
>= 'a') && (ch
<= 'f')) ||
95 ((ch
>= 'A') && (ch
<= 'F'))) {
99 // Whoops. Illegal character.
103 // Convert the ascii to binary
104 int dpc
= ((nameLen
<= 4) ? 1 : 2);
105 // Translate components from hex to binary
106 int r
= ComponentValue(buffer
, nameLen
, 0, dpc
);
107 int g
= ComponentValue(buffer
, nameLen
, 1, dpc
);
108 int b
= ComponentValue(buffer
, nameLen
, 2, dpc
);
111 a
= ComponentValue(buffer
, nameLen
, 3, dpc
);
113 a
= (dpc
== 1) ? 0xf : 0xff;
116 // Scale single digit component to an 8 bit value. Replicate the
117 // single digit to compute the new value.
123 NS_ASSERTION((r
>= 0) && (r
<= 255), "bad r");
124 NS_ASSERTION((g
>= 0) && (g
<= 255), "bad g");
125 NS_ASSERTION((b
>= 0) && (b
<= 255), "bad b");
126 NS_ASSERTION((a
>= 0) && (a
<= 255), "bad a");
127 *aResult
= NS_RGBA(r
, g
, b
, a
);
131 // This implements part of the algorithm for legacy behavior described in
132 // http://www.whatwg.org/specs/web-apps/current-work/complete/common-microsyntaxes.html#rules-for-parsing-a-legacy-color-value
133 bool NS_LooseHexToRGB(const nsString
& aColorSpec
, nscolor
* aResult
) {
134 if (aColorSpec
.EqualsLiteral("transparent")) {
138 int nameLen
= aColorSpec
.Length();
139 const char16_t
* colorSpec
= aColorSpec
.get();
144 if ('#' == colorSpec
[0]) {
149 // digits per component
150 int dpc
= (nameLen
+ 2) / 3;
153 // Use only the rightmost 8 characters of each component.
155 nameLen
-= newdpc
- 8;
156 colorSpec
+= newdpc
- 8;
160 // And then keep trimming characters at the left until we'd trim one
161 // that would leave a nonzero value, but not past 2 characters per
164 bool haveNonzero
= false;
165 for (int c
= 0; c
< 3; ++c
) {
166 MOZ_ASSERT(c
* dpc
< nameLen
,
167 "should not pass end of string while newdpc > 2");
168 char16_t ch
= colorSpec
[c
* dpc
];
169 if (('1' <= ch
&& ch
<= '9') || ('A' <= ch
&& ch
<= 'F') ||
170 ('a' <= ch
&& ch
<= 'f')) {
183 // Translate components from hex to binary
184 int r
= ComponentValue(colorSpec
, nameLen
, 0, dpc
);
185 int g
= ComponentValue(colorSpec
, nameLen
, 1, dpc
);
186 int b
= ComponentValue(colorSpec
, nameLen
, 2, dpc
);
187 NS_ASSERTION((r
>= 0) && (r
<= 255), "bad r");
188 NS_ASSERTION((g
>= 0) && (g
<= 255), "bad g");
189 NS_ASSERTION((b
>= 0) && (b
<= 255), "bad b");
191 *aResult
= NS_RGB(r
, g
, b
);
195 bool NS_ColorNameToRGB(const nsAString
& aColorName
, nscolor
* aResult
) {
196 if (!gColorTable
) return false;
198 int32_t id
= gColorTable
->Lookup(aColorName
);
199 if (eColorName_UNKNOWN
< id
) {
200 NS_ASSERTION(uint32_t(id
) < eColorName_COUNT
,
201 "gColorTable->Lookup messed up");
203 *aResult
= kColors
[id
];
210 // Fast approximate division by 255. It has the property that
211 // for all 0 <= n <= 255*255, FAST_DIVIDE_BY_255(n) == n/255.
212 // But it only uses two adds and two shifts instead of an
213 // integer division (which is expensive on many processors).
215 // equivalent to target=v/255
216 #define FAST_DIVIDE_BY_255(target, v) \
219 target = ((tmp_ << 8) + tmp_ + 255) >> 16; \
222 // Macro to blend two colors
224 // equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255
225 #define MOZ_BLEND(target, bg, fg, fgalpha) \
226 FAST_DIVIDE_BY_255(target, (bg) * (255 - fgalpha) + (fg) * (fgalpha))
228 nscolor
NS_ComposeColors(nscolor aBG
, nscolor aFG
) {
229 // This function uses colors that are non premultiplied alpha.
232 int bgAlpha
= NS_GET_A(aBG
);
233 int fgAlpha
= NS_GET_A(aFG
);
235 // Compute the final alpha of the blended color
236 // a = fgAlpha + bgAlpha*(255 - fgAlpha)/255;
237 FAST_DIVIDE_BY_255(a
, bgAlpha
* (255 - fgAlpha
));
241 // In this case the blended color is totally trasparent,
242 // we preserve the color information of the foreground color.
245 blendAlpha
= (fgAlpha
* 255) / a
;
247 MOZ_BLEND(r
, NS_GET_R(aBG
), NS_GET_R(aFG
), blendAlpha
);
248 MOZ_BLEND(g
, NS_GET_G(aBG
), NS_GET_G(aFG
), blendAlpha
);
249 MOZ_BLEND(b
, NS_GET_B(aBG
), NS_GET_B(aFG
), blendAlpha
);
251 return NS_RGBA(r
, g
, b
, a
);
254 const char* NS_RGBToColorName(nscolor aColor
) {
255 for (size_t idx
= 0; idx
< ArrayLength(kColors
); ++idx
) {
256 if (kColors
[idx
] == aColor
) {
257 return kColorNames
[idx
];