///
/// Copyright © 2003-2008 JetBrains s.r.o.
/// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder).
///
using System;
using System.Drawing;
namespace JetBrains.Omea.GUIControls
{
///
/// Color Management routines.
/// Contains a few simple static functions for working with RGB, BGR and HLS colors.
/// Based in parts on ancient MSDN C++ samples (HLS-RGB conversion).
///
public class ColorManagement
{
///
/// Background for MaxHLS.
///
protected static byte c_nMaxHLS = 252;
///
/// H, L, and S vary over 0-c_nMaxHLS.
/// Best if divisible by 6.
/// Must fit in a System.Byte.
///
public static byte MaxHLS
{
get { return c_nMaxHLS; }
}
///
/// Background for MaxRGB.
///
protected static byte c_nMaxRGB = 255;
///
/// R, G, and B vary over 0-c_nMaxRGB.
/// Must fit in a .
///
public static byte MaxRGB
{
get { return c_nMaxRGB; }
}
///
/// Background for UndefinedHue.
///
protected static byte c_nUndefinedHue = 168;
///
/// Hue is undefined if Saturation is 0 (grey-scale). This value determines where the Hue scrollbar is initially set for achromatic colors.
/// Set tot 2/3 of MaxHLS by default.
///
public static byte UndefinedHue
{
get { return c_nUndefinedHue; }
}
///
/// RGBtoHLS() takes a DWORD RGB value and translates it to HLS BYTEs.
///
/// A point of reference for the algorithms is Foley and Van Dam, "Fundamentals of Interactive Computer Graphics," Pages 618-19. Their algorithm is in floating point. CHART implements a less general (hardwired ranges) integral algorithm.
///
/// There are potential round-off errors throughout this sample. ((0.5 + x)/y) without floating point is phrased ((x + (y/2))/y), yielding a very small round-off error. This makes many of the following divisions look strange.
///
/// The source RGB color.
/// Resulting Hue value.
/// Resulting Luminance value.
/// Resulting Saturation value.
public static void RGBtoHLS( UInt32 lRGBColor, out Byte H, out Byte L, out Byte S )
{
UInt16 R, G, B; /* input RGB values */
Byte cMax, cMin; /* Math.Max and Math.Min RGB values */
UInt16 Rdelta, Gdelta, Bdelta; /* intermediate value: % of spread from Math.Max */
/* get R, G, and B out of System.UInt32 */
R = GetRValue( lRGBColor );
G = GetGValue( lRGBColor );
B = GetBValue( lRGBColor );
/* calculate lightness */
cMax = (byte) Math.Max( Math.Max( R, G ), B );
cMin = (byte) Math.Min( Math.Min( R, G ), B );
L = (byte) ((((cMax + cMin) * c_nMaxHLS) + c_nMaxRGB) / (2 * c_nMaxRGB));
if( cMax == cMin )
{ /* r=g=b --> achromatic case */
S = 0; /* saturation */
H = c_nUndefinedHue; /* hue */
}
else
{ /* chromatic case */
/* saturation */
if( L <= (c_nMaxHLS / 2) )
S = (byte) ((((cMax - cMin) * c_nMaxHLS) + ((cMax + cMin) / 2)) / (cMax + cMin));
else
S = (byte) ((((cMax - cMin) * c_nMaxHLS) + ((2 * c_nMaxRGB - cMax - cMin) / 2)) / (2 * c_nMaxRGB - cMax - cMin));
/* hue */
Rdelta = (byte) ((((cMax - R) * (c_nMaxHLS / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin));
Gdelta = (byte) ((((cMax - G) * (c_nMaxHLS / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin));
Bdelta = (byte) ((((cMax - B) * (c_nMaxHLS / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin));
if( R == cMax )
H = (byte) (Bdelta - Gdelta);
else if( G == cMax )
H = (byte) ((c_nMaxHLS / 3) + Rdelta - Bdelta);
else /* B == cMax */
H = (byte) (((2 * c_nMaxHLS) / 3) + Gdelta - Rdelta);
if( H < 0 )
H += c_nMaxHLS;
if( H > c_nMaxHLS )
H -= c_nMaxHLS;
}
}
///
/// Utility routine for HLStoRGB.
///
public static UInt16 HueToRGB( UInt16 n1, UInt16 n2, UInt16 hue )
{
/* range check: note values passed add/subtract thirds of range */
if( hue < 0 )
hue += (ushort) c_nMaxHLS;
if( hue > c_nMaxHLS )
hue -= (ushort) c_nMaxHLS;
/* return r,g, or b value from this tridrant */
if( hue < (c_nMaxHLS / 6) )
return (ushort) (n1 + (((n2 - n1) * hue + (c_nMaxHLS / 12)) / (c_nMaxHLS / 6)));
if( hue < (c_nMaxHLS / 2) )
return (n2);
if( hue < ((c_nMaxHLS * 2) / 3) )
return (ushort) (n1 + (((n2 - n1) * (((c_nMaxHLS * 2) / 3) - hue) + (c_nMaxHLS / 12)) / (c_nMaxHLS / 6)));
else
return (n1);
}
///
/// Converts an HLS color to an RGB color and returns three byte components.
///
public static void HLStoRGB( UInt16 H, UInt16 L, UInt16 S, out Byte R, out Byte G, out Byte B )
{
UInt16 Magic1, Magic2; /* calculated magic numbers (really!) */
if( S == 0 )
{ /* achromatic case */
R = G = B = (byte) ((L * c_nMaxRGB) / c_nMaxHLS);
if( H != c_nUndefinedHue )
{
/* ERROR */
}
}
else
{ /* chromatic case */
/* set up magic numbers */
if( L <= (c_nMaxHLS / 2) )
Magic2 = (ushort) ((L * (c_nMaxHLS + S) + (c_nMaxHLS / 2)) / c_nMaxHLS);
else
Magic2 = (ushort) (L + S - ((L * S) + (c_nMaxHLS / 2)) / c_nMaxHLS);
Magic1 = (ushort) (2 * L - Magic2);
/* get RGB, change units from c_nMaxHLS to c_nMaxRGB */
R = (byte) ((HueToRGB( Magic1, Magic2, (ushort) (H + (c_nMaxHLS / 3)) ) * c_nMaxRGB + (c_nMaxHLS / 2)) / c_nMaxHLS);
G = (byte) ((HueToRGB( Magic1, Magic2, H ) * c_nMaxRGB + (c_nMaxHLS / 2)) / c_nMaxHLS);
B = (byte) ((HueToRGB( Magic1, Magic2, (ushort) (H - (c_nMaxHLS / 3)) ) * c_nMaxRGB + (c_nMaxHLS / 2)) / c_nMaxHLS);
}
}
///
/// Converts HLS to a object.
///
public static Color HLStoRGB( UInt16 H, UInt16 L, UInt16 S )
{
byte R, G, B;
HLStoRGB( H, L, S, out R, out G, out B );
return Color.FromArgb( R, G, B );
}
///
/// Works just like HLStoRGB, but reverses the return value (BGR DWORD instead of an RGB one).
/// This is useful in case of Windows bitmaps which use BGR colors.
///
public static UInt32 HLStoBGR( UInt16 hue, UInt16 lum, UInt16 sat )
{
UInt16 R, G, B; /* RGB component values */
UInt16 Magic1, Magic2; /* calculated magic numbers (really!) */
if( sat == 0 )
{ /* achromatic case */
R = G = B = (ushort) ((lum * c_nMaxRGB) / c_nMaxHLS);
if( hue != c_nUndefinedHue )
{
/* ERROR */
}
}
else
{ /* chromatic case */
/* set up magic numbers */
if( lum <= (c_nMaxHLS / 2) )
Magic2 = (ushort) ((lum * (c_nMaxHLS + sat) + (c_nMaxHLS / 2)) / c_nMaxHLS);
else
Magic2 = (ushort) (lum + sat - ((lum * sat) + (c_nMaxHLS / 2)) / c_nMaxHLS);
Magic1 = (ushort) (2 * lum - Magic2);
/* get RGB, change units from c_nMaxHLS to c_nMaxRGB */
R = (ushort) ((HueToRGB( Magic1, Magic2, (ushort) (hue + (c_nMaxHLS / 3)) ) * c_nMaxRGB + (c_nMaxHLS / 2)) / c_nMaxHLS);
G = (ushort) ((HueToRGB( Magic1, Magic2, hue ) * c_nMaxRGB + (c_nMaxHLS / 2)) / c_nMaxHLS);
B = (ushort) ((HueToRGB( Magic1, Magic2, (ushort) (hue - (c_nMaxHLS / 3)) ) * c_nMaxRGB + (c_nMaxHLS / 2)) / c_nMaxHLS);
}
return (RGB( (byte) B, (byte) G, (byte) R ));
}
///
/// Mixes two colors together in the proportion specified.
///
public static UInt32 Mix( UInt32 colorA, UInt32 colorB, double fA )
{
double fB = 1.0 - fA;
return
(((UInt32) (((colorA & 0xFF) >> 0) * fA + ((colorB & 0xFF) >> 0) * fB)) & 0xFF) << 0 |
(((UInt32) (((colorA & 0xFF00) >> 8) * fA + ((colorB & 0xFF00) >> 8) * fB)) & 0xFF) << 8 |
(((UInt32) (((colorA & 0xFF0000) >> 16) * fA + ((colorB & 0xFF0000) >> 16) * fB)) & 0xFF) << 16;
}
///
/// Mixes two colors together in the proportion specified.
///
/// First color.
/// Second color.
/// A number in between 0.0 and 1.0.
/// The new color.
public static Color Mix( Color colorA, Color colorB, double proportion )
{
if( (proportion < 0) || (proportion > 1) )
throw new ArgumentOutOfRangeException( "proportion", proportion, "Must be in between 0.0 and 1.0." );
double back = 1.0 - proportion;
return Color.FromArgb(
(int) (colorA.R * proportion + colorB.R * back),
(int) (colorA.G * proportion + colorB.G * back),
(int) (colorA.B * proportion + colorB.B * back) );
}
///
/// Converts a DWORD RGB color to BGR, or vice versa as it's symmetrical.
///
public static UInt32 RGB2BGR( UInt32 color )
{
return RGB( GetBValue( color ), GetGValue( color ), GetRValue( color ) );
}
///
/// Mixes two colors
/// Does the same as Mix but for a fixed 1:1 proportion.
///
///
///
///
public static UInt32 BlendTwo( UInt32 rgbA, UInt32 rgbB )
{
return (RGB( (byte) ((GetRValue( rgbA ) + GetRValue( rgbB )) >> 1), (byte) ((GetGValue( rgbA ) + GetGValue( rgbB )) >> 1), (byte) ((GetBValue( rgbA ) + GetBValue( rgbB )) >> 1) ));
}
///
/// Extracts the R value from the RGB color (R is less significant).
///
public static Byte GetRValue( UInt32 RGB )
{
return (Byte) (RGB & 0xFF);
}
///
/// Extracts the G value from the RGB color (R is less significant).
///
public static Byte GetGValue( UInt32 RGB )
{
return (Byte) ((RGB >> 8) & 0xFF);
}
///
/// Extracts the B value from the RGB color (R is less significant).
///
public static Byte GetBValue( UInt32 RGB )
{
return (Byte) ((RGB >> 16) & 0xFF);
}
///
/// Produces an RGB color out of R, G and B values (R is less significant).
///
public static UInt32 RGB( Byte R, Byte G, Byte B )
{
return (UInt32) R | ((UInt32) G << 8) | ((UInt32) B << 16);
}
///
/// Produces an RGB color out of R, G and B values (R is less significant).
///
public static UInt32 RGB( Color color )
{
return (UInt32) color.R | ((UInt32) color.G << 8) | ((UInt32) color.B << 16);
}
///
/// Converts a color value into an HTML-compatible hex-string.
///
public static string Hex(Color color)
{
return String.Format( "#{0,2:X}{1,2:X}{2,2:X}", color.R, color.G, color.B ).Replace( ' ', '0' );
}
}
}