nsColor.cpp (6330B)
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/. */ 6 7 #include "mozilla/mozalloc.h" // for operator delete, etc 8 9 #include "nsColor.h" 10 #include <sys/types.h> // for int32_t 11 #include "nsDebug.h" // for NS_ASSERTION, etc 12 #include "nsStaticNameTable.h" 13 #include "nsString.h" // for nsAutoCString, nsString, etc 14 #include "nscore.h" // for nsAString, etc 15 #include "prtypes.h" // for PR_BEGIN_MACRO, etc 16 17 using namespace mozilla; 18 19 static int ComponentValue(const char16_t* aColorSpec, int aLen, int color, 20 int dpc) { 21 int component = 0; 22 int index = (color * dpc); 23 if (2 < dpc) { 24 dpc = 2; 25 } 26 while (--dpc >= 0) { 27 char16_t ch = ((index < aLen) ? aColorSpec[index++] : '0'); 28 if (('0' <= ch) && (ch <= '9')) { 29 component = (component * 16) + (ch - '0'); 30 } else if ((('a' <= ch) && (ch <= 'f')) || (('A' <= ch) && (ch <= 'F'))) { 31 // "ch&7" handles lower and uppercase hex alphabetics 32 component = (component * 16) + (ch & 7) + 9; 33 } else { // not a hex digit, treat it like 0 34 component = (component * 16); 35 } 36 } 37 return component; 38 } 39 40 bool NS_HexToRGBA(const nsAString& aColorSpec, nsHexColorType aType, 41 nscolor* aResult) { 42 const char16_t* buffer = aColorSpec.BeginReading(); 43 44 int nameLen = aColorSpec.Length(); 45 bool hasAlpha = false; 46 if (nameLen != 3 && nameLen != 6) { 47 if ((nameLen != 4 && nameLen != 8) || aType == nsHexColorType::NoAlpha) { 48 // Improperly formatted color value 49 return false; 50 } 51 hasAlpha = true; 52 } 53 54 // Make sure the digits are legal 55 for (int i = 0; i < nameLen; i++) { 56 char16_t ch = buffer[i]; 57 if (((ch >= '0') && (ch <= '9')) || ((ch >= 'a') && (ch <= 'f')) || 58 ((ch >= 'A') && (ch <= 'F'))) { 59 // Legal character 60 continue; 61 } 62 // Whoops. Illegal character. 63 return false; 64 } 65 66 // Convert the ascii to binary 67 int dpc = ((nameLen <= 4) ? 1 : 2); 68 // Translate components from hex to binary 69 int r = ComponentValue(buffer, nameLen, 0, dpc); 70 int g = ComponentValue(buffer, nameLen, 1, dpc); 71 int b = ComponentValue(buffer, nameLen, 2, dpc); 72 int a; 73 if (hasAlpha) { 74 a = ComponentValue(buffer, nameLen, 3, dpc); 75 } else { 76 a = (dpc == 1) ? 0xf : 0xff; 77 } 78 if (dpc == 1) { 79 // Scale single digit component to an 8 bit value. Replicate the 80 // single digit to compute the new value. 81 r = (r << 4) | r; 82 g = (g << 4) | g; 83 b = (b << 4) | b; 84 a = (a << 4) | a; 85 } 86 NS_ASSERTION((r >= 0) && (r <= 255), "bad r"); 87 NS_ASSERTION((g >= 0) && (g <= 255), "bad g"); 88 NS_ASSERTION((b >= 0) && (b <= 255), "bad b"); 89 NS_ASSERTION((a >= 0) && (a <= 255), "bad a"); 90 *aResult = NS_RGBA(r, g, b, a); 91 return true; 92 } 93 94 // This implements part of the algorithm for legacy behavior described in 95 // http://www.whatwg.org/specs/web-apps/current-work/complete/common-microsyntaxes.html#rules-for-parsing-a-legacy-color-value 96 bool NS_LooseHexToRGB(const nsString& aColorSpec, nscolor* aResult) { 97 if (aColorSpec.EqualsLiteral("transparent")) { 98 return false; 99 } 100 101 int nameLen = aColorSpec.Length(); 102 const char16_t* colorSpec = aColorSpec.get(); 103 if (nameLen > 128) { 104 nameLen = 128; 105 } 106 107 if ('#' == colorSpec[0]) { 108 ++colorSpec; 109 --nameLen; 110 } 111 112 // digits per component 113 int dpc = (nameLen + 2) / 3; 114 int newdpc = dpc; 115 116 // Use only the rightmost 8 characters of each component. 117 if (newdpc > 8) { 118 nameLen -= newdpc - 8; 119 colorSpec += newdpc - 8; 120 newdpc = 8; 121 } 122 123 // And then keep trimming characters at the left until we'd trim one 124 // that would leave a nonzero value, but not past 2 characters per 125 // component. 126 while (newdpc > 2) { 127 bool haveNonzero = false; 128 for (int c = 0; c < 3; ++c) { 129 MOZ_ASSERT(c * dpc < nameLen, 130 "should not pass end of string while newdpc > 2"); 131 char16_t ch = colorSpec[c * dpc]; 132 if (('1' <= ch && ch <= '9') || ('A' <= ch && ch <= 'F') || 133 ('a' <= ch && ch <= 'f')) { 134 haveNonzero = true; 135 break; 136 } 137 } 138 if (haveNonzero) { 139 break; 140 } 141 --newdpc; 142 --nameLen; 143 ++colorSpec; 144 } 145 146 // Translate components from hex to binary 147 int r = ComponentValue(colorSpec, nameLen, 0, dpc); 148 int g = ComponentValue(colorSpec, nameLen, 1, dpc); 149 int b = ComponentValue(colorSpec, nameLen, 2, dpc); 150 NS_ASSERTION((r >= 0) && (r <= 255), "bad r"); 151 NS_ASSERTION((g >= 0) && (g <= 255), "bad g"); 152 NS_ASSERTION((b >= 0) && (b <= 255), "bad b"); 153 154 *aResult = NS_RGB(r, g, b); 155 return true; 156 } 157 158 // Fast approximate division by 255. It has the property that 159 // for all 0 <= n <= 255*255, FAST_DIVIDE_BY_255(n) == n/255. 160 // But it only uses two adds and two shifts instead of an 161 // integer division (which is expensive on many processors). 162 // 163 // equivalent to target=v/255 164 #define FAST_DIVIDE_BY_255(target, v) \ 165 PR_BEGIN_MACRO \ 166 unsigned tmp_ = v; \ 167 target = ((tmp_ << 8) + tmp_ + 255) >> 16; \ 168 PR_END_MACRO 169 170 // Macro to blend two colors 171 // 172 // equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255 173 #define MOZ_BLEND(target, bg, fg, fgalpha) \ 174 FAST_DIVIDE_BY_255(target, (bg) * (255 - fgalpha) + (fg) * (fgalpha)) 175 176 nscolor NS_ComposeColors(nscolor aBG, nscolor aFG) { 177 // This function uses colors that are non premultiplied alpha. 178 int r, g, b, a; 179 180 int bgAlpha = NS_GET_A(aBG); 181 int fgAlpha = NS_GET_A(aFG); 182 183 // Compute the final alpha of the blended color 184 // a = fgAlpha + bgAlpha*(255 - fgAlpha)/255; 185 FAST_DIVIDE_BY_255(a, bgAlpha * (255 - fgAlpha)); 186 a = fgAlpha + a; 187 int blendAlpha; 188 if (a == 0) { 189 // In this case the blended color is totally trasparent, 190 // we preserve the color information of the foreground color. 191 blendAlpha = 255; 192 } else { 193 blendAlpha = (fgAlpha * 255) / a; 194 } 195 MOZ_BLEND(r, NS_GET_R(aBG), NS_GET_R(aFG), blendAlpha); 196 MOZ_BLEND(g, NS_GET_G(aBG), NS_GET_G(aFG), blendAlpha); 197 MOZ_BLEND(b, NS_GET_B(aBG), NS_GET_B(aFG), blendAlpha); 198 199 return NS_RGBA(r, g, b, a); 200 }