michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" // for ArrayLength michael@0: #include "mozilla/mozalloc.h" // for operator delete, etc michael@0: michael@0: #include "nsColor.h" michael@0: #include // for int32_t michael@0: #include "nsColorNames.h" // for nsColorNames michael@0: #include "nsDebug.h" // for NS_ASSERTION, etc michael@0: #include "nsStaticNameTable.h" michael@0: #include "nsString.h" // for nsAutoCString, nsString, etc michael@0: #include "nscore.h" // for nsAString, etc michael@0: michael@0: using namespace mozilla; michael@0: michael@0: // define an array of all color names michael@0: #define GFX_COLOR(_name, _value) #_name, michael@0: static const char* const kColorNames[] = { michael@0: #include "nsColorNameList.h" michael@0: }; michael@0: #undef GFX_COLOR michael@0: michael@0: // define an array of all color name values michael@0: #define GFX_COLOR(_name, _value) _value, michael@0: static const nscolor kColors[] = { michael@0: #include "nsColorNameList.h" michael@0: }; michael@0: #undef GFX_COLOR michael@0: michael@0: #define eColorName_COUNT (ArrayLength(kColorNames)) michael@0: #define eColorName_UNKNOWN (-1) michael@0: michael@0: static nsStaticCaseInsensitiveNameTable* gColorTable = nullptr; michael@0: michael@0: void nsColorNames::AddRefTable(void) michael@0: { michael@0: NS_ASSERTION(!gColorTable, "pre existing array!"); michael@0: if (!gColorTable) { michael@0: gColorTable = new nsStaticCaseInsensitiveNameTable(); michael@0: if (gColorTable) { michael@0: #ifdef DEBUG michael@0: { michael@0: // let's verify the table... michael@0: for (uint32_t index = 0; index < eColorName_COUNT; ++index) { michael@0: nsAutoCString temp1(kColorNames[index]); michael@0: nsAutoCString temp2(kColorNames[index]); michael@0: ToLowerCase(temp1); michael@0: NS_ASSERTION(temp1.Equals(temp2), "upper case char in table"); michael@0: } michael@0: } michael@0: #endif michael@0: gColorTable->Init(kColorNames, eColorName_COUNT); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void nsColorNames::ReleaseTable(void) michael@0: { michael@0: if (gColorTable) { michael@0: delete gColorTable; michael@0: gColorTable = nullptr; michael@0: } michael@0: } michael@0: michael@0: static int ComponentValue(const char16_t* aColorSpec, int aLen, int color, int dpc) michael@0: { michael@0: int component = 0; michael@0: int index = (color * dpc); michael@0: if (2 < dpc) { michael@0: dpc = 2; michael@0: } michael@0: while (--dpc >= 0) { michael@0: char16_t ch = ((index < aLen) ? aColorSpec[index++] : '0'); michael@0: if (('0' <= ch) && (ch <= '9')) { michael@0: component = (component * 16) + (ch - '0'); michael@0: } else if ((('a' <= ch) && (ch <= 'f')) || michael@0: (('A' <= ch) && (ch <= 'F'))) { michael@0: // "ch&7" handles lower and uppercase hex alphabetics michael@0: component = (component * 16) + (ch & 7) + 9; michael@0: } michael@0: else { // not a hex digit, treat it like 0 michael@0: component = (component * 16); michael@0: } michael@0: } michael@0: return component; michael@0: } michael@0: michael@0: NS_GFX_(bool) NS_HexToRGB(const nsAString& aColorSpec, michael@0: nscolor* aResult) michael@0: { michael@0: const char16_t* buffer = aColorSpec.BeginReading(); michael@0: michael@0: int nameLen = aColorSpec.Length(); michael@0: if ((nameLen == 3) || (nameLen == 6)) { michael@0: // Make sure the digits are legal michael@0: for (int i = 0; i < nameLen; i++) { michael@0: char16_t ch = buffer[i]; michael@0: if (((ch >= '0') && (ch <= '9')) || michael@0: ((ch >= 'a') && (ch <= 'f')) || michael@0: ((ch >= 'A') && (ch <= 'F'))) { michael@0: // Legal character michael@0: continue; michael@0: } michael@0: // Whoops. Illegal character. michael@0: return false; michael@0: } michael@0: michael@0: // Convert the ascii to binary michael@0: int dpc = ((3 == nameLen) ? 1 : 2); michael@0: // Translate components from hex to binary michael@0: int r = ComponentValue(buffer, nameLen, 0, dpc); michael@0: int g = ComponentValue(buffer, nameLen, 1, dpc); michael@0: int b = ComponentValue(buffer, nameLen, 2, dpc); michael@0: if (dpc == 1) { michael@0: // Scale single digit component to an 8 bit value. Replicate the michael@0: // single digit to compute the new value. michael@0: r = (r << 4) | r; michael@0: g = (g << 4) | g; michael@0: b = (b << 4) | b; michael@0: } michael@0: NS_ASSERTION((r >= 0) && (r <= 255), "bad r"); michael@0: NS_ASSERTION((g >= 0) && (g <= 255), "bad g"); michael@0: NS_ASSERTION((b >= 0) && (b <= 255), "bad b"); michael@0: *aResult = NS_RGB(r, g, b); michael@0: return true; michael@0: } michael@0: michael@0: // Improperly formatted color value michael@0: return false; michael@0: } michael@0: michael@0: // This implements part of the algorithm for legacy behavior described in michael@0: // http://www.whatwg.org/specs/web-apps/current-work/complete/common-microsyntaxes.html#rules-for-parsing-a-legacy-color-value michael@0: NS_GFX_(bool) NS_LooseHexToRGB(const nsString& aColorSpec, nscolor* aResult) michael@0: { michael@0: if (aColorSpec.EqualsLiteral("transparent")) { michael@0: return false; michael@0: } michael@0: michael@0: int nameLen = aColorSpec.Length(); michael@0: const char16_t* colorSpec = aColorSpec.get(); michael@0: if (nameLen > 128) { michael@0: nameLen = 128; michael@0: } michael@0: michael@0: if ('#' == colorSpec[0]) { michael@0: ++colorSpec; michael@0: --nameLen; michael@0: } michael@0: michael@0: // digits per component michael@0: int dpc = (nameLen + 2) / 3; michael@0: int newdpc = dpc; michael@0: michael@0: // Use only the rightmost 8 characters of each component. michael@0: if (newdpc > 8) { michael@0: nameLen -= newdpc - 8; michael@0: colorSpec += newdpc - 8; michael@0: newdpc = 8; michael@0: } michael@0: michael@0: // And then keep trimming characters at the left until we'd trim one michael@0: // that would leave a nonzero value, but not past 2 characters per michael@0: // component. michael@0: while (newdpc > 2) { michael@0: bool haveNonzero = false; michael@0: for (int c = 0; c < 3; ++c) { michael@0: NS_ABORT_IF_FALSE(c * dpc < nameLen, michael@0: "should not pass end of string while newdpc > 2"); michael@0: char16_t ch = colorSpec[c * dpc]; michael@0: if (('1' <= ch && ch <= '9') || michael@0: ('A' <= ch && ch <= 'F') || michael@0: ('a' <= ch && ch <= 'f')) { michael@0: haveNonzero = true; michael@0: break; michael@0: } michael@0: } michael@0: if (haveNonzero) { michael@0: break; michael@0: } michael@0: --newdpc; michael@0: --nameLen; michael@0: ++colorSpec; michael@0: } michael@0: michael@0: // Translate components from hex to binary michael@0: int r = ComponentValue(colorSpec, nameLen, 0, dpc); michael@0: int g = ComponentValue(colorSpec, nameLen, 1, dpc); michael@0: int b = ComponentValue(colorSpec, nameLen, 2, dpc); michael@0: NS_ASSERTION((r >= 0) && (r <= 255), "bad r"); michael@0: NS_ASSERTION((g >= 0) && (g <= 255), "bad g"); michael@0: NS_ASSERTION((b >= 0) && (b <= 255), "bad b"); michael@0: michael@0: *aResult = NS_RGB(r, g, b); michael@0: return true; michael@0: } michael@0: michael@0: NS_GFX_(bool) NS_ColorNameToRGB(const nsAString& aColorName, nscolor* aResult) michael@0: { michael@0: if (!gColorTable) return false; michael@0: michael@0: int32_t id = gColorTable->Lookup(aColorName); michael@0: if (eColorName_UNKNOWN < id) { michael@0: NS_ASSERTION(uint32_t(id) < eColorName_COUNT, michael@0: "gColorTable->Lookup messed up"); michael@0: if (aResult) { michael@0: *aResult = kColors[id]; michael@0: } michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // Returns kColorNames, an array of all possible color names, and sets michael@0: // *aSizeArray to the size of that array. Do NOT call free() on this array. michael@0: NS_GFX_(const char * const *) NS_AllColorNames(size_t *aSizeArray) michael@0: { michael@0: *aSizeArray = ArrayLength(kColorNames); michael@0: return kColorNames; michael@0: } michael@0: michael@0: // Macro to blend two colors michael@0: // michael@0: // equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255 michael@0: #define MOZ_BLEND(target, bg, fg, fgalpha) \ michael@0: FAST_DIVIDE_BY_255(target, (bg)*(255-fgalpha) + (fg)*(fgalpha)) michael@0: michael@0: NS_GFX_(nscolor) michael@0: NS_ComposeColors(nscolor aBG, nscolor aFG) michael@0: { michael@0: // This function uses colors that are non premultiplied alpha. michael@0: int r, g, b, a; michael@0: michael@0: int bgAlpha = NS_GET_A(aBG); michael@0: int fgAlpha = NS_GET_A(aFG); michael@0: michael@0: // Compute the final alpha of the blended color michael@0: // a = fgAlpha + bgAlpha*(255 - fgAlpha)/255; michael@0: FAST_DIVIDE_BY_255(a, bgAlpha*(255-fgAlpha)); michael@0: a = fgAlpha + a; michael@0: int blendAlpha; michael@0: if (a == 0) { michael@0: // In this case the blended color is totally trasparent, michael@0: // we preserve the color information of the foreground color. michael@0: blendAlpha = 255; michael@0: } else { michael@0: blendAlpha = (fgAlpha*255)/a; michael@0: } michael@0: MOZ_BLEND(r, NS_GET_R(aBG), NS_GET_R(aFG), blendAlpha); michael@0: MOZ_BLEND(g, NS_GET_G(aBG), NS_GET_G(aFG), blendAlpha); michael@0: MOZ_BLEND(b, NS_GET_B(aBG), NS_GET_B(aFG), blendAlpha); michael@0: michael@0: return NS_RGBA(r, g, b, a); michael@0: } michael@0: michael@0: // Functions to convert from HSL color space to RGB color space. michael@0: // This is the algorithm described in the CSS3 specification michael@0: michael@0: // helper michael@0: static float michael@0: HSL_HueToRGB(float m1, float m2, float h) michael@0: { michael@0: if (h < 0.0f) michael@0: h += 1.0f; michael@0: if (h > 1.0f) michael@0: h -= 1.0f; michael@0: if (h < (float)(1.0/6.0)) michael@0: return m1 + (m2 - m1)*h*6.0f; michael@0: if (h < (float)(1.0/2.0)) michael@0: return m2; michael@0: if (h < (float)(2.0/3.0)) michael@0: return m1 + (m2 - m1)*((float)(2.0/3.0) - h)*6.0f; michael@0: return m1; michael@0: } michael@0: michael@0: // The float parameters are all expected to be in the range 0-1 michael@0: NS_GFX_(nscolor) michael@0: NS_HSL2RGB(float h, float s, float l) michael@0: { michael@0: uint8_t r, g, b; michael@0: float m1, m2; michael@0: if (l <= 0.5f) { michael@0: m2 = l*(s+1); michael@0: } else { michael@0: m2 = l + s - l*s; michael@0: } michael@0: m1 = l*2 - m2; michael@0: r = uint8_t(255 * HSL_HueToRGB(m1, m2, h + 1.0f/3.0f)); michael@0: g = uint8_t(255 * HSL_HueToRGB(m1, m2, h)); michael@0: b = uint8_t(255 * HSL_HueToRGB(m1, m2, h - 1.0f/3.0f)); michael@0: return NS_RGB(r, g, b); michael@0: } michael@0: michael@0: NS_GFX_(const char*) michael@0: NS_RGBToColorName(nscolor aColor) michael@0: { michael@0: for (size_t idx = 0; idx < ArrayLength(kColors); ++idx) { michael@0: if (kColors[idx] == aColor) { michael@0: return kColorNames[idx]; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: }