gfx/src/nsColor.cpp

changeset 0
6474c204b198
     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 +}

mercurial