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 "nsDebug.h" // for NS_ASSERTION, etc
14 #include "nsStaticNameTable.h"
15 #include "nsString.h" // for nsAutoCString, nsString, etc
16 #include "nscore.h" // for nsAString, etc
17 #include "prtypes.h" // for PR_BEGIN_MACRO, etc
19 using namespace mozilla
;
21 static int ComponentValue(const char16_t
* aColorSpec
, int aLen
, int color
,
24 int index
= (color
* dpc
);
29 char16_t ch
= ((index
< aLen
) ? aColorSpec
[index
++] : '0');
30 if (('0' <= ch
) && (ch
<= '9')) {
31 component
= (component
* 16) + (ch
- '0');
32 } else if ((('a' <= ch
) && (ch
<= 'f')) || (('A' <= ch
) && (ch
<= 'F'))) {
33 // "ch&7" handles lower and uppercase hex alphabetics
34 component
= (component
* 16) + (ch
& 7) + 9;
35 } else { // not a hex digit, treat it like 0
36 component
= (component
* 16);
42 bool NS_HexToRGBA(const nsAString
& aColorSpec
, nsHexColorType aType
,
44 const char16_t
* buffer
= aColorSpec
.BeginReading();
46 int nameLen
= aColorSpec
.Length();
47 bool hasAlpha
= false;
48 if (nameLen
!= 3 && nameLen
!= 6) {
49 if ((nameLen
!= 4 && nameLen
!= 8) || aType
== nsHexColorType::NoAlpha
) {
50 // Improperly formatted color value
56 // Make sure the digits are legal
57 for (int i
= 0; i
< nameLen
; i
++) {
58 char16_t ch
= buffer
[i
];
59 if (((ch
>= '0') && (ch
<= '9')) || ((ch
>= 'a') && (ch
<= 'f')) ||
60 ((ch
>= 'A') && (ch
<= 'F'))) {
64 // Whoops. Illegal character.
68 // Convert the ascii to binary
69 int dpc
= ((nameLen
<= 4) ? 1 : 2);
70 // Translate components from hex to binary
71 int r
= ComponentValue(buffer
, nameLen
, 0, dpc
);
72 int g
= ComponentValue(buffer
, nameLen
, 1, dpc
);
73 int b
= ComponentValue(buffer
, nameLen
, 2, dpc
);
76 a
= ComponentValue(buffer
, nameLen
, 3, dpc
);
78 a
= (dpc
== 1) ? 0xf : 0xff;
81 // Scale single digit component to an 8 bit value. Replicate the
82 // single digit to compute the new value.
88 NS_ASSERTION((r
>= 0) && (r
<= 255), "bad r");
89 NS_ASSERTION((g
>= 0) && (g
<= 255), "bad g");
90 NS_ASSERTION((b
>= 0) && (b
<= 255), "bad b");
91 NS_ASSERTION((a
>= 0) && (a
<= 255), "bad a");
92 *aResult
= NS_RGBA(r
, g
, b
, a
);
96 // This implements part of the algorithm for legacy behavior described in
97 // http://www.whatwg.org/specs/web-apps/current-work/complete/common-microsyntaxes.html#rules-for-parsing-a-legacy-color-value
98 bool NS_LooseHexToRGB(const nsString
& aColorSpec
, nscolor
* aResult
) {
99 if (aColorSpec
.EqualsLiteral("transparent")) {
103 int nameLen
= aColorSpec
.Length();
104 const char16_t
* colorSpec
= aColorSpec
.get();
109 if ('#' == colorSpec
[0]) {
114 // digits per component
115 int dpc
= (nameLen
+ 2) / 3;
118 // Use only the rightmost 8 characters of each component.
120 nameLen
-= newdpc
- 8;
121 colorSpec
+= newdpc
- 8;
125 // And then keep trimming characters at the left until we'd trim one
126 // that would leave a nonzero value, but not past 2 characters per
129 bool haveNonzero
= false;
130 for (int c
= 0; c
< 3; ++c
) {
131 MOZ_ASSERT(c
* dpc
< nameLen
,
132 "should not pass end of string while newdpc > 2");
133 char16_t ch
= colorSpec
[c
* dpc
];
134 if (('1' <= ch
&& ch
<= '9') || ('A' <= ch
&& ch
<= 'F') ||
135 ('a' <= ch
&& ch
<= 'f')) {
148 // Translate components from hex to binary
149 int r
= ComponentValue(colorSpec
, nameLen
, 0, dpc
);
150 int g
= ComponentValue(colorSpec
, nameLen
, 1, dpc
);
151 int b
= ComponentValue(colorSpec
, nameLen
, 2, dpc
);
152 NS_ASSERTION((r
>= 0) && (r
<= 255), "bad r");
153 NS_ASSERTION((g
>= 0) && (g
<= 255), "bad g");
154 NS_ASSERTION((b
>= 0) && (b
<= 255), "bad b");
156 *aResult
= NS_RGB(r
, g
, b
);
160 // Fast approximate division by 255. It has the property that
161 // for all 0 <= n <= 255*255, FAST_DIVIDE_BY_255(n) == n/255.
162 // But it only uses two adds and two shifts instead of an
163 // integer division (which is expensive on many processors).
165 // equivalent to target=v/255
166 #define FAST_DIVIDE_BY_255(target, v) \
169 target = ((tmp_ << 8) + tmp_ + 255) >> 16; \
172 // Macro to blend two colors
174 // equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255
175 #define MOZ_BLEND(target, bg, fg, fgalpha) \
176 FAST_DIVIDE_BY_255(target, (bg) * (255 - fgalpha) + (fg) * (fgalpha))
178 nscolor
NS_ComposeColors(nscolor aBG
, nscolor aFG
) {
179 // This function uses colors that are non premultiplied alpha.
182 int bgAlpha
= NS_GET_A(aBG
);
183 int fgAlpha
= NS_GET_A(aFG
);
185 // Compute the final alpha of the blended color
186 // a = fgAlpha + bgAlpha*(255 - fgAlpha)/255;
187 FAST_DIVIDE_BY_255(a
, bgAlpha
* (255 - fgAlpha
));
191 // In this case the blended color is totally trasparent,
192 // we preserve the color information of the foreground color.
195 blendAlpha
= (fgAlpha
* 255) / a
;
197 MOZ_BLEND(r
, NS_GET_R(aBG
), NS_GET_R(aFG
), blendAlpha
);
198 MOZ_BLEND(g
, NS_GET_G(aBG
), NS_GET_G(aFG
), blendAlpha
);
199 MOZ_BLEND(b
, NS_GET_B(aBG
), NS_GET_B(aFG
), blendAlpha
);
201 return NS_RGBA(r
, g
, b
, a
);