summaryrefslogtreecommitdiffstats
path: root/gfx/src/nsColor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/src/nsColor.cpp')
-rw-r--r--gfx/src/nsColor.cpp361
1 files changed, 361 insertions, 0 deletions
diff --git a/gfx/src/nsColor.cpp b/gfx/src/nsColor.cpp
new file mode 100644
index 000000000..359f9fde4
--- /dev/null
+++ b/gfx/src/nsColor.cpp
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h" // for ArrayLength
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/MathAlgorithms.h"
+
+#include "nsColor.h"
+#include <sys/types.h> // for int32_t
+#include "nsColorNames.h" // for nsColorNames
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsStaticNameTable.h"
+#include "nsString.h" // for nsAutoCString, nsString, etc
+#include "nscore.h" // for nsAString, etc
+
+using namespace mozilla;
+
+// define an array of all color names
+#define GFX_COLOR(_name, _value) #_name,
+static const char* const kColorNames[] = {
+#include "nsColorNameList.h"
+};
+#undef GFX_COLOR
+
+// define an array of all color name values
+#define GFX_COLOR(_name, _value) _value,
+static const nscolor kColors[] = {
+#include "nsColorNameList.h"
+};
+#undef GFX_COLOR
+
+#define eColorName_COUNT (ArrayLength(kColorNames))
+#define eColorName_UNKNOWN (-1)
+
+static nsStaticCaseInsensitiveNameTable* gColorTable = nullptr;
+
+void nsColorNames::AddRefTable(void)
+{
+ NS_ASSERTION(!gColorTable, "pre existing array!");
+ if (!gColorTable) {
+ gColorTable =
+ new nsStaticCaseInsensitiveNameTable(kColorNames, eColorName_COUNT);
+ }
+}
+
+void nsColorNames::ReleaseTable(void)
+{
+ if (gColorTable) {
+ delete gColorTable;
+ gColorTable = nullptr;
+ }
+}
+
+static int ComponentValue(const char16_t* aColorSpec, int aLen, int color, int dpc)
+{
+ int component = 0;
+ int index = (color * dpc);
+ if (2 < dpc) {
+ dpc = 2;
+ }
+ while (--dpc >= 0) {
+ char16_t ch = ((index < aLen) ? aColorSpec[index++] : '0');
+ if (('0' <= ch) && (ch <= '9')) {
+ component = (component * 16) + (ch - '0');
+ } else if ((('a' <= ch) && (ch <= 'f')) ||
+ (('A' <= ch) && (ch <= 'F'))) {
+ // "ch&7" handles lower and uppercase hex alphabetics
+ component = (component * 16) + (ch & 7) + 9;
+ }
+ else { // not a hex digit, treat it like 0
+ component = (component * 16);
+ }
+ }
+ return component;
+}
+
+bool
+NS_HexToRGBA(const nsAString& aColorSpec, nsHexColorType aType,
+ nscolor* aResult)
+{
+ const char16_t* buffer = aColorSpec.BeginReading();
+
+ int nameLen = aColorSpec.Length();
+ bool hasAlpha = false;
+ if (nameLen != 3 && nameLen != 6) {
+ if ((nameLen != 4 && nameLen != 8) || aType == nsHexColorType::NoAlpha) {
+ // Improperly formatted color value
+ return false;
+ }
+ hasAlpha = true;
+ }
+
+ // Make sure the digits are legal
+ for (int i = 0; i < nameLen; i++) {
+ char16_t ch = buffer[i];
+ if (((ch >= '0') && (ch <= '9')) ||
+ ((ch >= 'a') && (ch <= 'f')) ||
+ ((ch >= 'A') && (ch <= 'F'))) {
+ // Legal character
+ continue;
+ }
+ // Whoops. Illegal character.
+ return false;
+ }
+
+ // Convert the ascii to binary
+ int dpc = ((nameLen <= 4) ? 1 : 2);
+ // Translate components from hex to binary
+ int r = ComponentValue(buffer, nameLen, 0, dpc);
+ int g = ComponentValue(buffer, nameLen, 1, dpc);
+ int b = ComponentValue(buffer, nameLen, 2, dpc);
+ int a;
+ if (hasAlpha) {
+ a = ComponentValue(buffer, nameLen, 3, dpc);
+ } else {
+ a = (dpc == 1) ? 0xf : 0xff;
+ }
+ if (dpc == 1) {
+ // Scale single digit component to an 8 bit value. Replicate the
+ // single digit to compute the new value.
+ r = (r << 4) | r;
+ g = (g << 4) | g;
+ b = (b << 4) | b;
+ a = (a << 4) | a;
+ }
+ NS_ASSERTION((r >= 0) && (r <= 255), "bad r");
+ NS_ASSERTION((g >= 0) && (g <= 255), "bad g");
+ NS_ASSERTION((b >= 0) && (b <= 255), "bad b");
+ NS_ASSERTION((a >= 0) && (a <= 255), "bad a");
+ *aResult = NS_RGBA(r, g, b, a);
+ return true;
+}
+
+// This implements part of the algorithm for legacy behavior described in
+// http://www.whatwg.org/specs/web-apps/current-work/complete/common-microsyntaxes.html#rules-for-parsing-a-legacy-color-value
+bool NS_LooseHexToRGB(const nsString& aColorSpec, nscolor* aResult)
+{
+ if (aColorSpec.EqualsLiteral("transparent")) {
+ return false;
+ }
+
+ int nameLen = aColorSpec.Length();
+ const char16_t* colorSpec = aColorSpec.get();
+ if (nameLen > 128) {
+ nameLen = 128;
+ }
+
+ if ('#' == colorSpec[0]) {
+ ++colorSpec;
+ --nameLen;
+ }
+
+ // digits per component
+ int dpc = (nameLen + 2) / 3;
+ int newdpc = dpc;
+
+ // Use only the rightmost 8 characters of each component.
+ if (newdpc > 8) {
+ nameLen -= newdpc - 8;
+ colorSpec += newdpc - 8;
+ newdpc = 8;
+ }
+
+ // And then keep trimming characters at the left until we'd trim one
+ // that would leave a nonzero value, but not past 2 characters per
+ // component.
+ while (newdpc > 2) {
+ bool haveNonzero = false;
+ for (int c = 0; c < 3; ++c) {
+ MOZ_ASSERT(c * dpc < nameLen,
+ "should not pass end of string while newdpc > 2");
+ char16_t ch = colorSpec[c * dpc];
+ if (('1' <= ch && ch <= '9') ||
+ ('A' <= ch && ch <= 'F') ||
+ ('a' <= ch && ch <= 'f')) {
+ haveNonzero = true;
+ break;
+ }
+ }
+ if (haveNonzero) {
+ break;
+ }
+ --newdpc;
+ --nameLen;
+ ++colorSpec;
+ }
+
+ // Translate components from hex to binary
+ int r = ComponentValue(colorSpec, nameLen, 0, dpc);
+ int g = ComponentValue(colorSpec, nameLen, 1, dpc);
+ int b = ComponentValue(colorSpec, nameLen, 2, dpc);
+ NS_ASSERTION((r >= 0) && (r <= 255), "bad r");
+ NS_ASSERTION((g >= 0) && (g <= 255), "bad g");
+ NS_ASSERTION((b >= 0) && (b <= 255), "bad b");
+
+ *aResult = NS_RGB(r, g, b);
+ return true;
+}
+
+bool NS_ColorNameToRGB(const nsAString& aColorName, nscolor* aResult)
+{
+ if (!gColorTable) return false;
+
+ int32_t id = gColorTable->Lookup(aColorName);
+ if (eColorName_UNKNOWN < id) {
+ NS_ASSERTION(uint32_t(id) < eColorName_COUNT,
+ "gColorTable->Lookup messed up");
+ if (aResult) {
+ *aResult = kColors[id];
+ }
+ return true;
+ }
+ return false;
+}
+
+// Returns kColorNames, an array of all possible color names, and sets
+// *aSizeArray to the size of that array. Do NOT call free() on this array.
+const char * const * NS_AllColorNames(size_t *aSizeArray)
+{
+ *aSizeArray = ArrayLength(kColorNames);
+ return kColorNames;
+}
+
+// Macro to blend two colors
+//
+// equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255
+#define MOZ_BLEND(target, bg, fg, fgalpha) \
+ FAST_DIVIDE_BY_255(target, (bg)*(255-fgalpha) + (fg)*(fgalpha))
+
+nscolor
+NS_ComposeColors(nscolor aBG, nscolor aFG)
+{
+ // This function uses colors that are non premultiplied alpha.
+ int r, g, b, a;
+
+ int bgAlpha = NS_GET_A(aBG);
+ int fgAlpha = NS_GET_A(aFG);
+
+ // Compute the final alpha of the blended color
+ // a = fgAlpha + bgAlpha*(255 - fgAlpha)/255;
+ FAST_DIVIDE_BY_255(a, bgAlpha*(255-fgAlpha));
+ a = fgAlpha + a;
+ int blendAlpha;
+ if (a == 0) {
+ // In this case the blended color is totally trasparent,
+ // we preserve the color information of the foreground color.
+ blendAlpha = 255;
+ } else {
+ blendAlpha = (fgAlpha*255)/a;
+ }
+ MOZ_BLEND(r, NS_GET_R(aBG), NS_GET_R(aFG), blendAlpha);
+ MOZ_BLEND(g, NS_GET_G(aBG), NS_GET_G(aFG), blendAlpha);
+ MOZ_BLEND(b, NS_GET_B(aBG), NS_GET_B(aFG), blendAlpha);
+
+ return NS_RGBA(r, g, b, a);
+}
+
+namespace mozilla {
+
+static uint32_t
+BlendColorComponent(uint32_t aBg, uint32_t aFg, uint32_t aFgAlpha)
+{
+ return RoundingDivideBy255(aBg * (255 - aFgAlpha) + aFg * aFgAlpha);
+}
+
+nscolor
+LinearBlendColors(nscolor aBg, nscolor aFg, uint_fast8_t aFgRatio)
+{
+ // Common case that either pure background or pure foreground
+ if (aFgRatio == 0) {
+ return aBg;
+ }
+ if (aFgRatio == 255) {
+ return aFg;
+ }
+ // Common case that alpha channel is equal (usually both are opaque)
+ if (NS_GET_A(aBg) == NS_GET_A(aFg)) {
+ auto r = BlendColorComponent(NS_GET_R(aBg), NS_GET_R(aFg), aFgRatio);
+ auto g = BlendColorComponent(NS_GET_G(aBg), NS_GET_G(aFg), aFgRatio);
+ auto b = BlendColorComponent(NS_GET_B(aBg), NS_GET_B(aFg), aFgRatio);
+ return NS_RGBA(r, g, b, NS_GET_A(aFg));
+ }
+
+ constexpr float kFactor = 1.0f / 255.0f;
+
+ float p1 = kFactor * (255 - aFgRatio);
+ float a1 = kFactor * NS_GET_A(aBg);
+ float r1 = a1 * NS_GET_R(aBg);
+ float g1 = a1 * NS_GET_G(aBg);
+ float b1 = a1 * NS_GET_B(aBg);
+
+ float p2 = 1.0f - p1;
+ float a2 = kFactor * NS_GET_A(aFg);
+ float r2 = a2 * NS_GET_R(aFg);
+ float g2 = a2 * NS_GET_G(aFg);
+ float b2 = a2 * NS_GET_B(aFg);
+
+ float a = p1 * a1 + p2 * a2;
+ if (a == 0.0) {
+ return NS_RGBA(0, 0, 0, 0);
+ }
+
+ auto r = ClampColor((p1 * r1 + p2 * r2) / a);
+ auto g = ClampColor((p1 * g1 + p2 * g2) / a);
+ auto b = ClampColor((p1 * b1 + p2 * b2) / a);
+ return NS_RGBA(r, g, b, NSToIntRound(a * 255));
+}
+
+} // namespace mozilla
+
+// Functions to convert from HSL color space to RGB color space.
+// This is the algorithm described in the CSS3 specification
+
+// helper
+static float
+HSL_HueToRGB(float m1, float m2, float h)
+{
+ if (h < 0.0f)
+ h += 1.0f;
+ if (h > 1.0f)
+ h -= 1.0f;
+ if (h < (float)(1.0/6.0))
+ return m1 + (m2 - m1)*h*6.0f;
+ if (h < (float)(1.0/2.0))
+ return m2;
+ if (h < (float)(2.0/3.0))
+ return m1 + (m2 - m1)*((float)(2.0/3.0) - h)*6.0f;
+ return m1;
+}
+
+// The float parameters are all expected to be in the range 0-1
+nscolor
+NS_HSL2RGB(float h, float s, float l)
+{
+ uint8_t r, g, b;
+ float m1, m2;
+ if (l <= 0.5f) {
+ m2 = l*(s+1);
+ } else {
+ m2 = l + s - l*s;
+ }
+ m1 = l*2 - m2;
+ r = uint8_t(255 * HSL_HueToRGB(m1, m2, h + 1.0f/3.0f));
+ g = uint8_t(255 * HSL_HueToRGB(m1, m2, h));
+ b = uint8_t(255 * HSL_HueToRGB(m1, m2, h - 1.0f/3.0f));
+ return NS_RGB(r, g, b);
+}
+
+const char*
+NS_RGBToColorName(nscolor aColor)
+{
+ for (size_t idx = 0; idx < ArrayLength(kColors); ++idx) {
+ if (kColors[idx] == aColor) {
+ return kColorNames[idx];
+ }
+ }
+
+ return nullptr;
+}