Back to index

lightning-sunbird  0.9+nobinonly
nsColor.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "plstr.h"
00039 #include "nsColor.h"
00040 #include "nsColorNames.h"
00041 #include "nsString.h"
00042 #include "nscore.h"
00043 #include "nsCoord.h"
00044 #include "nsUnitConversion.h"
00045 #include "nsCOMPtr.h"
00046 #include "nsIServiceManager.h"
00047 #include "nsIScreen.h"
00048 #include "nsIScreenManager.h"
00049 #include <math.h>
00050 #include "prprf.h"
00051 
00052 static int ComponentValue(const char* aColorSpec, int aLen, int color, int dpc)
00053 {
00054   int component = 0;
00055   int index = (color * dpc);
00056   if (2 < dpc) {
00057     dpc = 2;
00058   }
00059   while (--dpc >= 0) {
00060     char ch = ((index < aLen) ? aColorSpec[index++] : '0');
00061     if (('0' <= ch) && (ch <= '9')) {
00062       component = (component * 16) + (ch - '0');
00063     } else if ((('a' <= ch) && (ch <= 'f')) || 
00064                (('A' <= ch) && (ch <= 'F'))) {
00065       // "ch&7" handles lower and uppercase hex alphabetics
00066       component = (component * 16) + (ch & 7) + 9;
00067     }
00068     else {  // not a hex digit, treat it like 0
00069       component = (component * 16);
00070     }
00071   }
00072   return component;
00073 }
00074 
00075 extern "C" NS_GFX_(PRBool) NS_HexToRGB(const nsString& aColorSpec,
00076                                        nscolor* aResult)
00077 {
00078   // XXXldb nsStackString<10>
00079   NS_LossyConvertUCS2toASCII bufferStr(aColorSpec);
00080   return NS_ASCIIHexToRGB(bufferStr, aResult);
00081 }
00082 
00083 extern "C" NS_GFX_(PRBool) NS_ASCIIHexToRGB(const nsCString& aColorSpec,
00084                                             nscolor* aResult)
00085 {
00086   const char* buffer = aColorSpec.get();
00087 
00088   int nameLen = aColorSpec.Length();
00089   if ((nameLen == 3) || (nameLen == 6)) {
00090     // Make sure the digits are legal
00091     for (int i = 0; i < nameLen; i++) {
00092       char ch = buffer[i];
00093       if (((ch >= '0') && (ch <= '9')) ||
00094           ((ch >= 'a') && (ch <= 'f')) ||
00095           ((ch >= 'A') && (ch <= 'F'))) {
00096         // Legal character
00097         continue;
00098       }
00099       // Whoops. Illegal character.
00100       return PR_FALSE;
00101     }
00102 
00103     // Convert the ascii to binary
00104     int dpc = ((3 == nameLen) ? 1 : 2);
00105     // Translate components from hex to binary
00106     int r = ComponentValue(buffer, nameLen, 0, dpc);
00107     int g = ComponentValue(buffer, nameLen, 1, dpc);
00108     int b = ComponentValue(buffer, nameLen, 2, dpc);
00109     if (dpc == 1) {
00110       // Scale single digit component to an 8 bit value. Replicate the
00111       // single digit to compute the new value.
00112       r = (r << 4) | r;
00113       g = (g << 4) | g;
00114       b = (b << 4) | b;
00115     }
00116     NS_ASSERTION((r >= 0) && (r <= 255), "bad r");
00117     NS_ASSERTION((g >= 0) && (g <= 255), "bad g");
00118     NS_ASSERTION((b >= 0) && (b <= 255), "bad b");
00119     if (nsnull != aResult) {
00120       *aResult = NS_RGB(r, g, b);
00121     }
00122     return PR_TRUE;
00123   }
00124 
00125   // Improperly formatted color value
00126   return PR_FALSE;
00127 }
00128 
00129 // compatible with legacy Nav behavior
00130 extern "C" NS_GFX_(PRBool) NS_LooseHexToRGB(const nsString& aColorSpec, nscolor* aResult)
00131 {
00132   // XXXldb nsStackString<30>
00133   NS_LossyConvertUCS2toASCII buffer(aColorSpec);
00134 
00135   int nameLen = buffer.Length();
00136   const char* colorSpec = buffer.get();
00137   if ('#' == colorSpec[0]) {
00138     ++colorSpec;
00139     --nameLen;
00140   }
00141 
00142   if (3 < nameLen) {
00143     // Convert the ascii to binary
00144     int dpc = (nameLen / 3) + (((nameLen % 3) != 0) ? 1 : 0);
00145     if (4 < dpc) {
00146       dpc = 4;
00147     }
00148 
00149     // Translate components from hex to binary
00150     int r = ComponentValue(colorSpec, nameLen, 0, dpc);
00151     int g = ComponentValue(colorSpec, nameLen, 1, dpc);
00152     int b = ComponentValue(colorSpec, nameLen, 2, dpc);
00153     NS_ASSERTION((r >= 0) && (r <= 255), "bad r");
00154     NS_ASSERTION((g >= 0) && (g <= 255), "bad g");
00155     NS_ASSERTION((b >= 0) && (b <= 255), "bad b");
00156     if (nsnull != aResult) {
00157       *aResult = NS_RGB(r, g, b);
00158     }
00159   }
00160   else {
00161     if (nsnull != aResult) {
00162       *aResult = NS_RGB(0, 0, 0);
00163     }
00164   }
00165   return PR_TRUE;
00166 }
00167 
00168 extern "C" NS_GFX_(void) NS_RGBToHex(nscolor aColor, nsAString& aResult)
00169 {
00170   char buf[10];
00171   PR_snprintf(buf, sizeof(buf), "#%02x%02x%02x",
00172               NS_GET_R(aColor), NS_GET_G(aColor), NS_GET_B(aColor));
00173   CopyASCIItoUTF16(buf, aResult);
00174 }
00175 
00176 extern "C" NS_GFX_(void) NS_RGBToASCIIHex(nscolor aColor,
00177                                           nsAFlatCString& aResult)
00178 {
00179   aResult.SetLength(7);
00180   NS_ASSERTION(aResult.Length() == 7, "small SetLength failed, use an autostring instead!");
00181   char *buf = aResult.BeginWriting();
00182   PR_snprintf(buf, 8, "#%02x%02x%02x",
00183               NS_GET_R(aColor), NS_GET_G(aColor), NS_GET_B(aColor));
00184 }
00185 
00186 extern "C" NS_GFX_(PRBool) NS_ColorNameToRGB(const nsAString& aColorName, nscolor* aResult)
00187 {
00188   nsColorName id = nsColorNames::LookupName(aColorName);
00189   if (eColorName_UNKNOWN < id) {
00190     NS_ASSERTION(id < eColorName_COUNT, "LookupName mess up");
00191     if (nsnull != aResult) {
00192       *aResult = nsColorNames::kColors[id];
00193     }
00194     return PR_TRUE;
00195   }
00196   return PR_FALSE;
00197 }
00198 
00199 extern "C" NS_GFX_(nscolor) NS_BrightenColor(nscolor inColor)
00200 {
00201   PRIntn r, g, b, max, over;
00202 
00203   r = NS_GET_R(inColor);
00204   g = NS_GET_G(inColor);
00205   b = NS_GET_B(inColor);
00206 
00207   //10% of max color increase across the board
00208   r += 25;
00209   g += 25;
00210   b += 25;
00211 
00212   //figure out which color is largest
00213   if (r > g)
00214   {
00215     if (b > r)
00216       max = b;
00217     else
00218       max = r;
00219   }
00220   else
00221   {
00222     if (b > g)
00223       max = b;
00224     else
00225       max = g;
00226   }
00227 
00228   //if we overflowed on this max color, increase
00229   //other components by the overflow amount
00230   if (max > 255)
00231   {
00232     over = max - 255;
00233 
00234     if (max == r)
00235     {
00236       g += over;
00237       b += over;
00238     }
00239     else if (max == g)
00240     {
00241       r += over;
00242       b += over;
00243     }
00244     else
00245     {
00246       r += over;
00247       g += over;
00248     }
00249   }
00250 
00251   //clamp
00252   if (r > 255)
00253     r = 255;
00254   if (g > 255)
00255     g = 255;
00256   if (b > 255)
00257     b = 255;
00258 
00259   return NS_RGBA(r, g, b, NS_GET_A(inColor));
00260 }
00261 
00262 extern "C" NS_GFX_(nscolor) NS_DarkenColor(nscolor inColor)
00263 {
00264   PRIntn r, g, b, max;
00265 
00266   r = NS_GET_R(inColor);
00267   g = NS_GET_G(inColor);
00268   b = NS_GET_B(inColor);
00269 
00270   //10% of max color decrease across the board
00271   r -= 25;
00272   g -= 25;
00273   b -= 25;
00274 
00275   //figure out which color is largest
00276   if (r > g)
00277   {
00278     if (b > r)
00279       max = b;
00280     else
00281       max = r;
00282   }
00283   else
00284   {
00285     if (b > g)
00286       max = b;
00287     else
00288       max = g;
00289   }
00290 
00291   //if we underflowed on this max color, decrease
00292   //other components by the underflow amount
00293   if (max < 0)
00294   {
00295     if (max == r)
00296     {
00297       g += max;
00298       b += max;
00299     }
00300     else if (max == g)
00301     {
00302       r += max;
00303       b += max;
00304     }
00305     else
00306     {
00307       r += max;
00308       g += max;
00309     }
00310   }
00311 
00312   //clamp
00313   if (r < 0)
00314     r = 0;
00315   if (g < 0)
00316     g = 0;
00317   if (b < 0)
00318     b = 0;
00319 
00320   return NS_RGBA(r, g, b, NS_GET_A(inColor));
00321 }
00322 
00323 extern "C" NS_GFX_(nscolor)
00324 NS_ComposeColors(nscolor aBG, nscolor aFG)
00325 {
00326   PRIntn bgAlpha = NS_GET_A(aBG);
00327   PRIntn r, g, b, a;
00328 
00329   // First compute what we get drawing aBG onto RGBA(0,0,0,0)
00330   MOZ_BLEND(r, 0, NS_GET_R(aBG), bgAlpha);
00331   MOZ_BLEND(g, 0, NS_GET_G(aBG), bgAlpha);
00332   MOZ_BLEND(b, 0, NS_GET_B(aBG), bgAlpha);
00333   a = bgAlpha;
00334 
00335   // Now draw aFG on top of that
00336   PRIntn fgAlpha = NS_GET_A(aFG);
00337   MOZ_BLEND(r, r, NS_GET_R(aFG), fgAlpha);
00338   MOZ_BLEND(g, g, NS_GET_G(aFG), fgAlpha);
00339   MOZ_BLEND(b, b, NS_GET_B(aFG), fgAlpha);
00340   MOZ_BLEND(a, a, 255, fgAlpha);
00341   
00342   return NS_RGBA(r, g, b, a);
00343 }
00344 
00345 // Functions to convert from HSL color space to RGB color space.
00346 // This is the algorithm described in the CSS3 specification
00347 
00348 // helper
00349 static float
00350 HSL_HueToRGB(float m1, float m2, float h)
00351 {
00352   if (h < 0.0f)
00353     h += 1.0f;
00354   if (h > 1.0f)
00355     h -= 1.0f;
00356   if (h < (float)(1.0/6.0))
00357     return m1 + (m2 - m1)*h*6.0f;
00358   if (h < (float)(1.0/2.0))
00359     return m2;
00360   if (h < (float)(2.0/3.0))
00361     return m1 + (m2 - m1)*((float)(2.0/3.0) - h)*6.0f;
00362   return m1;      
00363 }
00364 
00365 // The float parameters are all expected to be in the range 0-1
00366 extern "C" NS_GFX_(nscolor)
00367 NS_HSL2RGB(float h, float s, float l)
00368 {
00369   PRUint8 r, g, b;
00370   float m1, m2;
00371   if (l <= 0.5f) {
00372     m2 = l*(s+1);
00373   } else {
00374     m2 = l + s - l*s;
00375   }
00376   m1 = l*2 - m2;
00377   r = PRUint8(255 * HSL_HueToRGB(m1, m2, h + 1.0f/3.0f));
00378   g = PRUint8(255 * HSL_HueToRGB(m1, m2, h));
00379   b = PRUint8(255 * HSL_HueToRGB(m1, m2, h - 1.0f/3.0f));
00380   return NS_RGB(r, g, b);  
00381 }