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