1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/src/nsColor.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,307 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/ArrayUtils.h" // for ArrayLength 1.10 +#include "mozilla/mozalloc.h" // for operator delete, etc 1.11 + 1.12 +#include "nsColor.h" 1.13 +#include <sys/types.h> // for int32_t 1.14 +#include "nsColorNames.h" // for nsColorNames 1.15 +#include "nsDebug.h" // for NS_ASSERTION, etc 1.16 +#include "nsStaticNameTable.h" 1.17 +#include "nsString.h" // for nsAutoCString, nsString, etc 1.18 +#include "nscore.h" // for nsAString, etc 1.19 + 1.20 +using namespace mozilla; 1.21 + 1.22 +// define an array of all color names 1.23 +#define GFX_COLOR(_name, _value) #_name, 1.24 +static const char* const kColorNames[] = { 1.25 +#include "nsColorNameList.h" 1.26 +}; 1.27 +#undef GFX_COLOR 1.28 + 1.29 +// define an array of all color name values 1.30 +#define GFX_COLOR(_name, _value) _value, 1.31 +static const nscolor kColors[] = { 1.32 +#include "nsColorNameList.h" 1.33 +}; 1.34 +#undef GFX_COLOR 1.35 + 1.36 +#define eColorName_COUNT (ArrayLength(kColorNames)) 1.37 +#define eColorName_UNKNOWN (-1) 1.38 + 1.39 +static nsStaticCaseInsensitiveNameTable* gColorTable = nullptr; 1.40 + 1.41 +void nsColorNames::AddRefTable(void) 1.42 +{ 1.43 + NS_ASSERTION(!gColorTable, "pre existing array!"); 1.44 + if (!gColorTable) { 1.45 + gColorTable = new nsStaticCaseInsensitiveNameTable(); 1.46 + if (gColorTable) { 1.47 +#ifdef DEBUG 1.48 + { 1.49 + // let's verify the table... 1.50 + for (uint32_t index = 0; index < eColorName_COUNT; ++index) { 1.51 + nsAutoCString temp1(kColorNames[index]); 1.52 + nsAutoCString temp2(kColorNames[index]); 1.53 + ToLowerCase(temp1); 1.54 + NS_ASSERTION(temp1.Equals(temp2), "upper case char in table"); 1.55 + } 1.56 + } 1.57 +#endif 1.58 + gColorTable->Init(kColorNames, eColorName_COUNT); 1.59 + } 1.60 + } 1.61 +} 1.62 + 1.63 +void nsColorNames::ReleaseTable(void) 1.64 +{ 1.65 + if (gColorTable) { 1.66 + delete gColorTable; 1.67 + gColorTable = nullptr; 1.68 + } 1.69 +} 1.70 + 1.71 +static int ComponentValue(const char16_t* aColorSpec, int aLen, int color, int dpc) 1.72 +{ 1.73 + int component = 0; 1.74 + int index = (color * dpc); 1.75 + if (2 < dpc) { 1.76 + dpc = 2; 1.77 + } 1.78 + while (--dpc >= 0) { 1.79 + char16_t ch = ((index < aLen) ? aColorSpec[index++] : '0'); 1.80 + if (('0' <= ch) && (ch <= '9')) { 1.81 + component = (component * 16) + (ch - '0'); 1.82 + } else if ((('a' <= ch) && (ch <= 'f')) || 1.83 + (('A' <= ch) && (ch <= 'F'))) { 1.84 + // "ch&7" handles lower and uppercase hex alphabetics 1.85 + component = (component * 16) + (ch & 7) + 9; 1.86 + } 1.87 + else { // not a hex digit, treat it like 0 1.88 + component = (component * 16); 1.89 + } 1.90 + } 1.91 + return component; 1.92 +} 1.93 + 1.94 +NS_GFX_(bool) NS_HexToRGB(const nsAString& aColorSpec, 1.95 + nscolor* aResult) 1.96 +{ 1.97 + const char16_t* buffer = aColorSpec.BeginReading(); 1.98 + 1.99 + int nameLen = aColorSpec.Length(); 1.100 + if ((nameLen == 3) || (nameLen == 6)) { 1.101 + // Make sure the digits are legal 1.102 + for (int i = 0; i < nameLen; i++) { 1.103 + char16_t ch = buffer[i]; 1.104 + if (((ch >= '0') && (ch <= '9')) || 1.105 + ((ch >= 'a') && (ch <= 'f')) || 1.106 + ((ch >= 'A') && (ch <= 'F'))) { 1.107 + // Legal character 1.108 + continue; 1.109 + } 1.110 + // Whoops. Illegal character. 1.111 + return false; 1.112 + } 1.113 + 1.114 + // Convert the ascii to binary 1.115 + int dpc = ((3 == nameLen) ? 1 : 2); 1.116 + // Translate components from hex to binary 1.117 + int r = ComponentValue(buffer, nameLen, 0, dpc); 1.118 + int g = ComponentValue(buffer, nameLen, 1, dpc); 1.119 + int b = ComponentValue(buffer, nameLen, 2, dpc); 1.120 + if (dpc == 1) { 1.121 + // Scale single digit component to an 8 bit value. Replicate the 1.122 + // single digit to compute the new value. 1.123 + r = (r << 4) | r; 1.124 + g = (g << 4) | g; 1.125 + b = (b << 4) | b; 1.126 + } 1.127 + NS_ASSERTION((r >= 0) && (r <= 255), "bad r"); 1.128 + NS_ASSERTION((g >= 0) && (g <= 255), "bad g"); 1.129 + NS_ASSERTION((b >= 0) && (b <= 255), "bad b"); 1.130 + *aResult = NS_RGB(r, g, b); 1.131 + return true; 1.132 + } 1.133 + 1.134 + // Improperly formatted color value 1.135 + return false; 1.136 +} 1.137 + 1.138 +// This implements part of the algorithm for legacy behavior described in 1.139 +// http://www.whatwg.org/specs/web-apps/current-work/complete/common-microsyntaxes.html#rules-for-parsing-a-legacy-color-value 1.140 +NS_GFX_(bool) NS_LooseHexToRGB(const nsString& aColorSpec, nscolor* aResult) 1.141 +{ 1.142 + if (aColorSpec.EqualsLiteral("transparent")) { 1.143 + return false; 1.144 + } 1.145 + 1.146 + int nameLen = aColorSpec.Length(); 1.147 + const char16_t* colorSpec = aColorSpec.get(); 1.148 + if (nameLen > 128) { 1.149 + nameLen = 128; 1.150 + } 1.151 + 1.152 + if ('#' == colorSpec[0]) { 1.153 + ++colorSpec; 1.154 + --nameLen; 1.155 + } 1.156 + 1.157 + // digits per component 1.158 + int dpc = (nameLen + 2) / 3; 1.159 + int newdpc = dpc; 1.160 + 1.161 + // Use only the rightmost 8 characters of each component. 1.162 + if (newdpc > 8) { 1.163 + nameLen -= newdpc - 8; 1.164 + colorSpec += newdpc - 8; 1.165 + newdpc = 8; 1.166 + } 1.167 + 1.168 + // And then keep trimming characters at the left until we'd trim one 1.169 + // that would leave a nonzero value, but not past 2 characters per 1.170 + // component. 1.171 + while (newdpc > 2) { 1.172 + bool haveNonzero = false; 1.173 + for (int c = 0; c < 3; ++c) { 1.174 + NS_ABORT_IF_FALSE(c * dpc < nameLen, 1.175 + "should not pass end of string while newdpc > 2"); 1.176 + char16_t ch = colorSpec[c * dpc]; 1.177 + if (('1' <= ch && ch <= '9') || 1.178 + ('A' <= ch && ch <= 'F') || 1.179 + ('a' <= ch && ch <= 'f')) { 1.180 + haveNonzero = true; 1.181 + break; 1.182 + } 1.183 + } 1.184 + if (haveNonzero) { 1.185 + break; 1.186 + } 1.187 + --newdpc; 1.188 + --nameLen; 1.189 + ++colorSpec; 1.190 + } 1.191 + 1.192 + // Translate components from hex to binary 1.193 + int r = ComponentValue(colorSpec, nameLen, 0, dpc); 1.194 + int g = ComponentValue(colorSpec, nameLen, 1, dpc); 1.195 + int b = ComponentValue(colorSpec, nameLen, 2, dpc); 1.196 + NS_ASSERTION((r >= 0) && (r <= 255), "bad r"); 1.197 + NS_ASSERTION((g >= 0) && (g <= 255), "bad g"); 1.198 + NS_ASSERTION((b >= 0) && (b <= 255), "bad b"); 1.199 + 1.200 + *aResult = NS_RGB(r, g, b); 1.201 + return true; 1.202 +} 1.203 + 1.204 +NS_GFX_(bool) NS_ColorNameToRGB(const nsAString& aColorName, nscolor* aResult) 1.205 +{ 1.206 + if (!gColorTable) return false; 1.207 + 1.208 + int32_t id = gColorTable->Lookup(aColorName); 1.209 + if (eColorName_UNKNOWN < id) { 1.210 + NS_ASSERTION(uint32_t(id) < eColorName_COUNT, 1.211 + "gColorTable->Lookup messed up"); 1.212 + if (aResult) { 1.213 + *aResult = kColors[id]; 1.214 + } 1.215 + return true; 1.216 + } 1.217 + return false; 1.218 +} 1.219 + 1.220 +// Returns kColorNames, an array of all possible color names, and sets 1.221 +// *aSizeArray to the size of that array. Do NOT call free() on this array. 1.222 +NS_GFX_(const char * const *) NS_AllColorNames(size_t *aSizeArray) 1.223 +{ 1.224 + *aSizeArray = ArrayLength(kColorNames); 1.225 + return kColorNames; 1.226 +} 1.227 + 1.228 +// Macro to blend two colors 1.229 +// 1.230 +// equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255 1.231 +#define MOZ_BLEND(target, bg, fg, fgalpha) \ 1.232 + FAST_DIVIDE_BY_255(target, (bg)*(255-fgalpha) + (fg)*(fgalpha)) 1.233 + 1.234 +NS_GFX_(nscolor) 1.235 +NS_ComposeColors(nscolor aBG, nscolor aFG) 1.236 +{ 1.237 + // This function uses colors that are non premultiplied alpha. 1.238 + int r, g, b, a; 1.239 + 1.240 + int bgAlpha = NS_GET_A(aBG); 1.241 + int fgAlpha = NS_GET_A(aFG); 1.242 + 1.243 + // Compute the final alpha of the blended color 1.244 + // a = fgAlpha + bgAlpha*(255 - fgAlpha)/255; 1.245 + FAST_DIVIDE_BY_255(a, bgAlpha*(255-fgAlpha)); 1.246 + a = fgAlpha + a; 1.247 + int blendAlpha; 1.248 + if (a == 0) { 1.249 + // In this case the blended color is totally trasparent, 1.250 + // we preserve the color information of the foreground color. 1.251 + blendAlpha = 255; 1.252 + } else { 1.253 + blendAlpha = (fgAlpha*255)/a; 1.254 + } 1.255 + MOZ_BLEND(r, NS_GET_R(aBG), NS_GET_R(aFG), blendAlpha); 1.256 + MOZ_BLEND(g, NS_GET_G(aBG), NS_GET_G(aFG), blendAlpha); 1.257 + MOZ_BLEND(b, NS_GET_B(aBG), NS_GET_B(aFG), blendAlpha); 1.258 + 1.259 + return NS_RGBA(r, g, b, a); 1.260 +} 1.261 + 1.262 +// Functions to convert from HSL color space to RGB color space. 1.263 +// This is the algorithm described in the CSS3 specification 1.264 + 1.265 +// helper 1.266 +static float 1.267 +HSL_HueToRGB(float m1, float m2, float h) 1.268 +{ 1.269 + if (h < 0.0f) 1.270 + h += 1.0f; 1.271 + if (h > 1.0f) 1.272 + h -= 1.0f; 1.273 + if (h < (float)(1.0/6.0)) 1.274 + return m1 + (m2 - m1)*h*6.0f; 1.275 + if (h < (float)(1.0/2.0)) 1.276 + return m2; 1.277 + if (h < (float)(2.0/3.0)) 1.278 + return m1 + (m2 - m1)*((float)(2.0/3.0) - h)*6.0f; 1.279 + return m1; 1.280 +} 1.281 + 1.282 +// The float parameters are all expected to be in the range 0-1 1.283 +NS_GFX_(nscolor) 1.284 +NS_HSL2RGB(float h, float s, float l) 1.285 +{ 1.286 + uint8_t r, g, b; 1.287 + float m1, m2; 1.288 + if (l <= 0.5f) { 1.289 + m2 = l*(s+1); 1.290 + } else { 1.291 + m2 = l + s - l*s; 1.292 + } 1.293 + m1 = l*2 - m2; 1.294 + r = uint8_t(255 * HSL_HueToRGB(m1, m2, h + 1.0f/3.0f)); 1.295 + g = uint8_t(255 * HSL_HueToRGB(m1, m2, h)); 1.296 + b = uint8_t(255 * HSL_HueToRGB(m1, m2, h - 1.0f/3.0f)); 1.297 + return NS_RGB(r, g, b); 1.298 +} 1.299 + 1.300 +NS_GFX_(const char*) 1.301 +NS_RGBToColorName(nscolor aColor) 1.302 +{ 1.303 + for (size_t idx = 0; idx < ArrayLength(kColors); ++idx) { 1.304 + if (kColors[idx] == aColor) { 1.305 + return kColorNames[idx]; 1.306 + } 1.307 + } 1.308 + 1.309 + return nullptr; 1.310 +}