The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

package Graphics::ColorUtils;

use 5.008003;
use strict;
use warnings;

use Carp;

require Exporter;

our @ISA = qw(Exporter);

our %EXPORT_TAGS = ( 'gradients' => [ qw( gradient
					  grad2rgb
					  available_gradients
					  register_gradient) ],
		     'names' => [ qw( name2rgb
				      available_names
				      register_name
				      set_default_namespace
				      get_default_namespace ) ],
		     'all' => [ qw( rgb2yiq yiq2rgb
				    rgb2cmy cmy2rgb
				    rgb2hls hls2rgb
				    rgb2hsv hsv2rgb
				    gradient
				    grad2rgb
				    available_gradients
				    register_gradient
				    name2rgb
				    available_names
				    register_name
				    set_default_namespace
				    get_default_namespace ) ],
		   );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw( rgb2yiq yiq2rgb
		  rgb2cmy cmy2rgb
		  rgb2hls hls2rgb
		  rgb2hsv hsv2rgb );

our $VERSION = '0.17';

# ==================================================
# ++++++++++++++++++++++++++++++++++++++++++++++++++
# ==================================================

# ==================================================
# Utility

# Takes a (r,g,b) triple of numbers (possibly floats) and returns
# - a string like '#33FF21' in scalar context
# - a triple of corresponding integers in array context
sub _fmt {
  return wantarray ? map { int } @_ : sprintf( "#%02x%02x%02x", @_ );
}

# ==================================================
# YIQ

sub rgb2yiq {
  # $r, $g, $b : 0..255
  my ( $r, $g, $b ) = map { $_/255.0 } @_;          # Scale RGB to 0..1

  my $y = 0.299*$r + 0.587*$g + 0.114*$b;
  my $i = 0.596*$r - 0.275*$g - 0.321*$b;
  my $q = 0.212*$r - 0.523*$g + 0.311*$b;

  return ( $y, $i, $q );
}

sub yiq2rgb {
  # $y, $i, $q : 0..1
  my ( $y, $i, $q ) = @_;

  my $r = 255.0*( $y + 0.956*$i + 0.621*$q );
  my $g = 255.0*( $y - 0.272*$i - 0.647*$q );
  my $b = 255.0*( $y - 1.105*$i + 1.705*$q );

  return _fmt( $r, $g, $b );
}

# ==================================================
# CMY

sub rgb2cmy {
  # $r, $g, $b : 0..255
  my ( $r, $g, $b ) = map { $_/255.0 } @_;          # Scale RGB to 0..1

  return ( 1.0 - $r, 1.0 - $g, 1.0 - $b );
}

sub cmy2rgb {
  # $c, $m, $y : 0..1
  my ( $c, $m, $y ) = @_;

  return _fmt( 255*(1.0-$c), 255*(1.0-$m), 255*(1.0-$y) );
}

# ==================================================
# HLS

# Foley, van Dam, et al:
# Computer Grapics-Principles and Practice (1990) p595f

sub rgb2hls {
  # $r, $g, $b : 0..255
  #   Note special name '$bb' to avoid conflict with ($a,$b) in sort()
  my ( $r, $g, $bb ) = map { $_/255.0 } @_;         # Scale RGB to 0..1

  my ( $minc, $maxc ) = ( sort { $a <=> $b } ( $r, $g, $bb ) )[0,2];

  my $m = $minc + $maxc;                            # "Mean"

  if( $maxc == $minc ) { return ( 0, 0.5*$m, 0 ); } # Achromatic case

  my $d = $maxc - $minc;                            # "Delta"
  my $s = ( $m <= 1.0 ) ? $d/$m : $d/(2.0-$m );     # Saturation

  my $h = 0;                                        # Hue
  if(    $r  == $maxc ) { $h =     ( $g-$bb )/$d; }
  elsif( $g  == $maxc ) { $h = 2 + ( $bb-$r )/$d; }
  elsif( $bb == $maxc ) { $h = 4 + ( $r-$g )/$d; }
  else {
    # Never get here!
    croak "Internal Error: Unexpected value ,$maxc, in Graphics::ColorUtils::rgb2hls( $r, $g, $bb )";
  }

  $h *= 60;                                         # Convert to degrees
  if( $h < 0 ) { $h += 360; }                       # Ensure positive hue

  return ( $h, 0.5*$m, $s );
}

sub hls2rgb {
  # $h: 0..360 (red=0->yellow->green=120->cyan->blue=240->magenta steps of 60)
  # $l, $s : 0..1 (inclusive)
  my ( $h, $l, $s ) = @_;

  if( $s == 0.0 ) { return _fmt(255*$l, 255*$l, 255*$l); } # achromatic (grey)

# This is the INCORRECT line as it is in the book quoted above:
# my $m2 = ( $l <= 0.5 ) ? ($l*($l+$s)) : ($l - $l*$s + $s);
# This is the CORRECT line: (first alternative: 1 vs $l)
  my $m2 = ( $l <= 0.5 ) ? ($l*(1+$s)) : ($l - $l*$s + $s);
  my $m1 = 2.0*$l - $m2;

  my $r = 255 * _value( $m1, $m2, $h + 120 );
  my $g = 255 * _value( $m1, $m2, $h );
  my $b = 255 * _value( $m1, $m2, $h - 120 );

  return _fmt( $r, $g, $b );
}

sub _value {
  my ( $n1, $n2, $hue ) = @_;

  if(    $hue > 360 ) { $hue -= 360; }
  elsif( $hue <   0 ) { $hue += 360; }

  if(    $hue <  60 ) { return $n1 + $hue * ( $n2-$n1 )/60.0; }
  elsif( $hue < 180 ) { return $n2; }
  elsif( $hue < 240 ) { return $n1 + ( 240-$hue ) * ( $n2-$n1 )/60.0; }
  else                { return $n1; }
}

# ==================================================
# HSV

# Foley, van Dam, et al:
# Computer Grapics-Principles and Practice (1990) p592f

sub rgb2hsv {
  # $r, $g, $b : 0..25
  #   Note special name '$bb' to avoid conflict with ($a,$b) in sort()
  my ( $r, $g, $bb ) = map { $_/255.0 } @_; # Scale RGB to 0..1

  my ( $minc, $maxc ) = ( sort { $a <=> $b } ( $r, $g, $bb ) )[0,2];

  my $v = $maxc;                         # Value
  my $d = $maxc - $minc;                 # "Delta"
  my $s = ( $maxc == 0 ) ? 0 : $d/$maxc; # No saturation if R=G=B=0

  if( $s == 0 ) { return ( 0, 0, $v ); } # Achromatic case

  my $h = 0;                             # Hue
  if(    $r == $maxc ) { $h =     ( $g-$bb )/$d; }
  elsif( $g == $maxc ) { $h = 2 + ( $bb-$r )/$d; }
  elsif( $bb == $maxc ) { $h = 4 + ( $r-$g )/$d; }
  else {
    # Never get here!
    croak "Internal Error: Unexpected value ,$maxc, in Graphics::ColorUtils::rgb2hsv( $r, $g, $bb )";
  }

  $h *= 60;                              # Convert to degrees
  if( $h < 0 ) { $h += 360; }            # Ensure positive hue

  return ( $h, $s, $v );
}

sub hsv2rgb {
  # $h: 0..360 (red=0->yellow->green=120->cyan->blue=240->magenta steps of 60)
  #     (tolerates larger values of $h by reducing them to the standard circle)
  # $s, $v : 0..1 (inclusive)
  my ( $h, $s, $v ) = @_;

  $v *= 255;
  if( $s == 0 ) { return _fmt( $v, $v, $v ); } # achromatic (grey)

  my $i = int( $h/60 );  # sector 0 to 5
  my $f = ($h/60) - $i;  # fractional part of h/60

  my $p = $v * ( 1 - $s );
  my $q = $v * ( 1 - $s * $f );
  my $t = $v * ( 1 - $s * ( 1 - $f ) );

  $i %= 6;               # tolerate values of $h larger than 360
  if(    $i==0 ) { return _fmt( $v, $t, $p ); }
  elsif( $i==1 ) { return _fmt( $q, $v, $p ); }
  elsif( $i==2 ) { return _fmt( $p, $v, $t ); }
  elsif( $i==3 ) { return _fmt( $p, $q, $v ); }
  elsif( $i==4 ) { return _fmt( $t, $p, $v ); }
  elsif( $i==5 ) { return _fmt( $v, $p, $q ); }
  else { 
    # Never get here!
    croak "Internal Error: Unexpected value ,$i, in Graphics::ColorUtils::hsv2rgb( $h, $s, $v )";
  }
}

# ==================================================
# Gradients

# Gradients grey, heat, map, and rainbow have been inspired by similar 
# ideas in Yorick. 
# For Yorick, cf http://yorick.sourceforge.net 
# and also http://www.maumae.net/yorick/doc/index.php
# as well as http://www.mhatt.aps.anl.gov/dohn/software/yorick/

BEGIN {
my %_gradients = (
		  'grey' => [
	[  0,  0,  0],[  1,  1,  1],[  2,  2,  2],[  3,  3,  3],[  4,  4,  4],
	[  5,  5,  5],[  6,  6,  6],[  7,  7,  7],[  9,  9,  9],[ 10, 10, 10],
	[ 11, 11, 11],[ 12, 12, 12],[ 13, 13, 13],[ 14, 14, 14],[ 15, 15, 15],
	[ 16, 16, 16],[ 17, 17, 17],[ 18, 18, 18],[ 19, 19, 19],[ 20, 20, 20],
	[ 21, 21, 21],[ 22, 22, 22],[ 23, 23, 23],[ 25, 25, 25],[ 26, 26, 26],
	[ 27, 27, 27],[ 28, 28, 28],[ 29, 29, 29],[ 30, 30, 30],[ 31, 31, 31],
	[ 32, 32, 32],[ 33, 33, 33],[ 34, 34, 34],[ 35, 35, 35],[ 36, 36, 36],
	[ 37, 37, 37],[ 38, 38, 38],[ 39, 39, 39],[ 41, 41, 41],[ 42, 42, 42],
	[ 43, 43, 43],[ 44, 44, 44],[ 45, 45, 45],[ 46, 46, 46],[ 47, 47, 47],
	[ 48, 48, 48],[ 49, 49, 49],[ 50, 50, 50],[ 51, 51, 51],[ 52, 52, 52],
	[ 53, 53, 53],[ 54, 54, 54],[ 55, 55, 55],[ 57, 57, 57],[ 58, 58, 58],
	[ 59, 59, 59],[ 60, 60, 60],[ 61, 61, 61],[ 62, 62, 62],[ 63, 63, 63],
	[ 64, 64, 64],[ 65, 65, 65],[ 66, 66, 66],[ 67, 67, 67],[ 68, 68, 68],
	[ 69, 69, 69],[ 70, 70, 70],[ 71, 71, 71],[ 73, 73, 73],[ 74, 74, 74],
	[ 75, 75, 75],[ 76, 76, 76],[ 77, 77, 77],[ 78, 78, 78],[ 79, 79, 79],
	[ 80, 80, 80],[ 81, 81, 81],[ 82, 82, 82],[ 83, 83, 83],[ 84, 84, 84],
	[ 85, 85, 85],[ 86, 86, 86],[ 87, 87, 87],[ 89, 89, 89],[ 90, 90, 90],
	[ 91, 91, 91],[ 92, 92, 92],[ 93, 93, 93],[ 94, 94, 94],[ 95, 95, 95],
	[ 96, 96, 96],[ 97, 97, 97],[ 98, 98, 98],[ 99, 99, 99],[100,100,100],
	[101,101,101],[102,102,102],[103,103,103],[105,105,105],[106,106,106],
	[107,107,107],[108,108,108],[109,109,109],[110,110,110],[111,111,111],
	[112,112,112],[113,113,113],[114,114,114],[115,115,115],[116,116,116],
	[117,117,117],[118,118,118],[119,119,119],[121,121,121],[122,122,122],
	[123,123,123],[124,124,124],[125,125,125],[126,126,126],[127,127,127],
	[128,128,128],[129,129,129],[130,130,130],[131,131,131],[132,132,132],
	[133,133,133],[134,134,134],[135,135,135],[137,137,137],[138,138,138],
	[139,139,139],[140,140,140],[141,141,141],[142,142,142],[143,143,143],
	[144,144,144],[145,145,145],[146,146,146],[147,147,147],[148,148,148],
	[149,149,149],[150,150,150],[151,151,151],[153,153,153],[154,154,154],
	[155,155,155],[156,156,156],[157,157,157],[158,158,158],[159,159,159],
	[160,160,160],[161,161,161],[162,162,162],[163,163,163],[164,164,164],
	[165,165,165],[166,166,166],[167,167,167],[169,169,169],[170,170,170],
	[171,171,171],[172,172,172],[173,173,173],[174,174,174],[175,175,175],
	[176,176,176],[177,177,177],[178,178,178],[179,179,179],[180,180,180],
	[181,181,181],[182,182,182],[183,183,183],[185,185,185],[186,186,186],
	[187,187,187],[188,188,188],[189,189,189],[190,190,190],[191,191,191],
	[192,192,192],[193,193,193],[194,194,194],[195,195,195],[196,196,196],
	[197,197,197],[198,198,198],[199,199,199],[201,201,201],[202,202,202],
	[203,203,203],[204,204,204],[205,205,205],[206,206,206],[207,207,207],
	[208,208,208],[209,209,209],[210,210,210],[211,211,211],[212,212,212],
	[213,213,213],[214,214,214],[215,215,215],[217,217,217],[218,218,218],
	[219,219,219],[220,220,220],[221,221,221],[222,222,222],[223,223,223],
	[224,224,224],[225,225,225],[226,226,226],[227,227,227],[228,228,228],
	[229,229,229],[230,230,230],[231,231,231],[233,233,233],[234,234,234],
	[235,235,235],[236,236,236],[237,237,237],[238,238,238],[239,239,239],
	[240,240,240],[241,241,241],[242,242,242],[243,243,243],[244,244,244],
	[245,245,245],[246,246,246],[247,247,247],[249,249,249],[250,250,250],
	[251,251,251],[252,252,252],[253,253,253],[254,254,254],[255,255,255]
			    ],
		  'heat' => [
	[  0,  0,  0],[  1,  0,  0],[  2,  0,  0],[  4,  0,  0],[  5,  0,  0],
	[  7,  0,  0],[  8,  0,  0],[ 10,  0,  0],[ 11,  0,  0],[ 13,  0,  0],
	[ 15,  0,  0],[ 17,  0,  0],[ 18,  0,  0],[ 20,  0,  0],[ 21,  0,  0],
	[ 23,  0,  0],[ 24,  0,  0],[ 26,  0,  0],[ 27,  0,  0],[ 28,  0,  0],
	[ 30,  0,  0],[ 31,  0,  0],[ 33,  0,  0],[ 34,  0,  0],[ 36,  0,  0],
	[ 37,  0,  0],[ 39,  0,  0],[ 40,  0,  0],[ 42,  0,  0],[ 43,  0,  0],
	[ 46,  0,  0],[ 47,  0,  0],[ 49,  0,  0],[ 50,  0,  0],[ 52,  0,  0],
	[ 53,  0,  0],[ 55,  0,  0],[ 56,  0,  0],[ 57,  0,  0],[ 59,  0,  0],
	[ 60,  0,  0],[ 62,  0,  0],[ 63,  0,  0],[ 65,  0,  0],[ 66,  0,  0],
	[ 68,  0,  0],[ 69,  0,  0],[ 70,  0,  0],[ 72,  0,  0],[ 73,  0,  0],
	[ 76,  0,  0],[ 78,  0,  0],[ 79,  0,  0],[ 81,  0,  0],[ 82,  0,  0],
	[ 84,  0,  0],[ 85,  0,  0],[ 86,  0,  0],[ 88,  0,  0],[ 89,  0,  0],
	[ 92,  0,  0],[ 94,  0,  0],[ 95,  0,  0],[ 97,  0,  0],[ 98,  0,  0],
	[ 99,  0,  0],[101,  0,  0],[102,  0,  0],[104,  0,  0],[105,  0,  0],
	[108,  0,  0],[110,  0,  0],[111,  0,  0],[113,  0,  0],[114,  0,  0],
	[115,  0,  0],[117,  0,  0],[118,  0,  0],[120,  0,  0],[121,  0,  0],
	[123,  0,  0],[124,  0,  0],[126,  0,  0],[127,  0,  0],[128,  0,  0],
	[130,  0,  0],[131,  0,  0],[133,  0,  0],[134,  0,  0],[136,  0,  0],
	[139,  0,  0],[140,  0,  0],[141,  0,  0],[143,  0,  0],[144,  0,  0],
	[146,  0,  0],[147,  0,  0],[149,  0,  0],[150,  0,  0],[152,  0,  0],
	[153,  0,  0],[155,  0,  0],[156,  0,  0],[157,  0,  0],[159,  0,  0],
	[160,  0,  0],[162,  0,  0],[163,  0,  0],[165,  0,  0],[166,  0,  0],
	[169,  0,  0],[170,  0,  0],[172,  0,  0],[173,  0,  0],[175,  1,  0],
	[176,  3,  0],[178,  5,  0],[179,  7,  0],[181,  9,  0],[182, 11,  0],
	[185, 15,  0],[186, 17,  0],[188, 18,  0],[189, 20,  0],[191, 22,  0],
	[192, 24,  0],[194, 26,  0],[195, 28,  0],[197, 30,  0],[198, 32,  0],
	[201, 35,  0],[202, 37,  0],[204, 39,  0],[205, 41,  0],[207, 43,  0],
	[208, 45,  0],[210, 47,  0],[211, 49,  0],[212, 51,  0],[214, 52,  0],
	[215, 54,  0],[217, 56,  0],[218, 58,  0],[220, 60,  0],[221, 62,  0],
	[223, 64,  0],[224, 66,  0],[226, 68,  0],[227, 69,  0],[228, 71,  0],
	[231, 75,  0],[233, 77,  0],[234, 79,  0],[236, 81,  0],[237, 83,  0],
	[239, 85,  0],[240, 86,  0],[241, 88,  0],[243, 90,  0],[244, 92,  0],
	[246, 94,  0],[247, 96,  0],[249, 98,  0],[250,100,  0],[252,102,  0],
	[253,103,  0],[255,105,  0],[255,107,  0],[255,109,  0],[255,111,  0],
	[255,115,  0],[255,117,  0],[255,119,  0],[255,120,  0],[255,122,  0],
	[255,124,  0],[255,126,  0],[255,128,  0],[255,130,  0],[255,132,  0],
	[255,136,  7],[255,137, 11],[255,139, 15],[255,141, 19],[255,143, 23],
	[255,145, 27],[255,147, 31],[255,149, 35],[255,151, 39],[255,153, 43],
	[255,156, 51],[255,158, 54],[255,160, 58],[255,162, 62],[255,164, 66],
	[255,166, 70],[255,168, 74],[255,170, 78],[255,171, 82],[255,173, 86],
	[255,175, 90],[255,177, 94],[255,179, 98],[255,181,102],[255,183,105],
	[255,185,109],[255,187,113],[255,188,117],[255,190,121],[255,192,125],
	[255,196,133],[255,198,137],[255,200,141],[255,202,145],[255,204,149],
	[255,205,153],[255,207,156],[255,209,160],[255,211,164],[255,213,168],
	[255,215,172],[255,217,176],[255,219,180],[255,221,184],[255,222,188],
	[255,224,192],[255,226,196],[255,228,200],[255,230,204],[255,232,207],
	[255,236,215],[255,238,219],[255,239,223],[255,241,227],[255,243,231],
	[255,245,235],[255,247,239],[255,249,243],[255,251,247],[255,253,251]
			    ],
		  'map' =>  [
	[  0,  0,  0],[  0,  0, 46],[  0,  0, 58],[  0,  0, 69],[  0,  0, 81],
	[  0,  0, 92],[  0,  0,104],[  0,  0,116],[  0,  3,116],[  1,  6,116],
	[  2,  8,116],[  2, 11,116],[  3, 13,117],[  4, 16,117],[  5, 18,117],
	[  5, 21,117],[  6, 23,117],[  7, 26,118],[  8, 28,118],[  8, 31,118],
	[  9, 33,118],[ 10, 36,118],[ 11, 38,119],[ 11, 41,119],[ 12, 43,119],
	[ 13, 45,119],[ 14, 48,119],[ 15, 50,120],[ 15, 52,120],[ 16, 55,120],
	[ 17, 57,120],[ 18, 59,120],[ 18, 61,121],[ 19, 64,121],[ 20, 66,121],
	[ 21, 68,121],[ 22, 70,121],[ 22, 72,122],[ 23, 74,122],[ 24, 77,122],
	[ 25, 79,122],[ 26, 81,122],[ 26, 83,123],[ 27, 85,123],[ 28, 87,123],
	[ 29, 89,123],[ 30, 91,123],[ 31, 93,124],[ 31, 95,124],[ 32, 97,124],
	[ 33, 99,124],[ 34,100,124],[ 35,102,125],[ 36,104,125],[ 36,106,125],
	[ 37,108,125],[ 38,109,125],[ 39,111,126],[ 40,113,126],[ 41,115,126],
	[ 41,116,126],[ 42,118,126],[ 43,120,127],[ 44,121,127],[ 45,123,127],
	[ 46,125,127],[ 47,126,127],[ 48,128,128],[ 48,128,126],[ 48,129,125],
	[ 49,129,124],[ 49,130,123],[ 50,131,122],[ 50,131,120],[ 51,132,119],
	[ 51,133,118],[ 52,133,117],[ 52,134,115],[ 53,134,114],[ 53,135,113],
	[ 54,136,111],[ 54,136,110],[ 55,137,109],[ 55,138,108],[ 56,138,106],
	[ 56,139,105],[ 57,140,104],[ 57,140,102],[ 58,141,101],[ 58,141,100],
	[ 59,142, 98],[ 59,143, 97],[ 60,143, 96],[ 61,144, 94],[ 61,145, 93],
	[ 62,145, 92],[ 62,146, 90],[ 63,146, 89],[ 63,147, 88],[ 64,148, 86],
	[ 64,148, 85],[ 65,149, 84],[ 65,150, 82],[ 66,150, 81],[ 67,151, 80],
	[ 67,151, 78],[ 68,152, 77],[ 68,153, 76],[ 69,153, 74],[ 69,154, 73],
	[ 70,155, 71],[ 71,155, 70],[ 73,156, 71],[ 76,156, 72],[ 78,157, 72],
	[ 81,158, 73],[ 83,158, 73],[ 86,159, 74],[ 88,160, 75],[ 91,160, 75],
	[ 94,161, 76],[ 96,161, 76],[ 99,162, 77],[101,163, 77],[104,163, 78],
	[106,164, 79],[109,165, 79],[111,165, 80],[114,166, 80],[117,166, 81],
	[119,167, 82],[121,168, 82],[122,168, 82],[124,168, 83],[126,169, 83],
	[128,169, 83],[129,170, 84],[131,170, 84],[133,171, 84],[135,171, 85],
	[136,172, 85],[138,172, 85],[140,172, 86],[141,173, 86],[143,173, 86],
	[145,174, 87],[147,174, 87],[149,175, 87],[150,175, 88],[152,175, 88],
	[154,176, 88],[156,176, 89],[157,177, 89],[159,177, 89],[161,178, 90],
	[163,178, 90],[165,179, 90],[166,179, 91],[168,179, 91],[170,180, 91],
	[172,180, 92],[174,181, 92],[175,181, 92],[177,182, 93],[179,182, 93],
	[181,183, 93],[183,183, 94],[183,182, 94],[184,181, 94],[184,181, 95],
	[185,180, 95],[185,179, 95],[186,178, 96],[186,177, 96],[187,176, 97],
	[187,175, 97],[187,174, 97],[188,173, 98],[188,172, 98],[189,171, 98],
	[189,170, 99],[190,169, 99],[190,168, 99],[190,167,100],[191,166,100],
	[191,165,100],[192,164,101],[192,163,101],[193,163,104],[195,164,106],
	[196,164,108],[197,165,111],[198,165,113],[199,166,116],[201,167,118],
	[202,167,121],[203,168,123],[204,169,126],[205,170,129],[207,171,131],
	[208,172,134],[209,173,137],[210,174,139],[211,175,142],[213,176,145],
	[214,177,148],[215,178,150],[216,179,153],[217,181,156],[219,182,159],
	[220,184,162],[221,185,165],[222,187,168],[223,188,170],[225,190,173],
	[226,192,176],[227,194,179],[228,196,182],[229,198,185],[231,200,189],
	[232,202,192],[233,204,195],[234,206,198],[235,208,201],[237,211,204],
	[238,213,207],[239,215,211],[240,218,214],[241,221,217],[243,223,220],
	[244,226,224],[245,229,227],[246,232,230],[247,235,234],[249,238,237],
	[250,241,241],[251,244,244],[252,248,248],[253,251,251],[255,255,255]
				    ],
		  'rainbow' => [
	[255,  0, 42],[255,  0, 36],[255,  0, 31],[255,  0, 26],[255,  0, 20],
	[255,  0, 15],[255,  0, 10],[255,  0,  4],[255,  5,  0],[255, 11,  0],
	[255, 16,  0],[255, 22,  0],[255, 27,  0],[255, 32,  0],[255, 38,  0],
	[255, 43,  0],[255, 48,  0],[255, 54,  0],[255, 59,  0],[255, 65,  0],
	[255, 70,  0],[255, 75,  0],[255, 81,  0],[255, 91,  0],[255, 97,  0],
	[255,102,  0],[255,108,  0],[255,113,  0],[255,118,  0],[255,124,  0],
	[255,129,  0],[255,135,  0],[255,140,  0],[255,145,  0],[255,151,  0],
	[255,156,  0],[255,161,  0],[255,167,  0],[255,178,  0],[255,183,  0],
	[255,188,  0],[255,194,  0],[255,199,  0],[255,204,  0],[255,210,  0],
	[255,215,  0],[255,221,  0],[255,226,  0],[255,231,  0],[255,237,  0],
	[255,242,  0],[255,247,  0],[255,253,  0],[245,255,  0],[240,255,  0],
	[235,255,  0],[229,255,  0],[224,255,  0],[219,255,  0],[213,255,  0],
	[208,255,  0],[202,255,  0],[197,255,  0],[192,255,  0],[186,255,  0],
	[181,255,  0],[175,255,  0],[170,255,  0],[159,255,  0],[154,255,  0],
	[149,255,  0],[143,255,  0],[138,255,  0],[132,255,  0],[127,255,  0],
	[122,255,  0],[116,255,  0],[111,255,  0],[106,255,  0],[100,255,  0],
	[ 95,255,  0],[ 89,255,  0],[ 84,255,  0],[ 73,255,  0],[ 68,255,  0],
	[ 63,255,  0],[ 57,255,  0],[ 52,255,  0],[ 46,255,  0],[ 41,255,  0],
	[ 36,255,  0],[ 30,255,  0],[ 25,255,  0],[ 19,255,  0],[ 14,255,  0],
	[  9,255,  0],[  3,255,  0],[  0,255,  1],[  0,255, 12],[  0,255, 17],
	[  0,255, 23],[  0,255, 28],[  0,255, 33],[  0,255, 39],[  0,255, 44],
	[  0,255, 49],[  0,255, 55],[  0,255, 60],[  0,255, 66],[  0,255, 71],
	[  0,255, 76],[  0,255, 82],[  0,255, 87],[  0,255, 98],[  0,255,103],
	[  0,255,109],[  0,255,114],[  0,255,119],[  0,255,125],[  0,255,130],
	[  0,255,135],[  0,255,141],[  0,255,146],[  0,255,152],[  0,255,157],
	[  0,255,162],[  0,255,168],[  0,255,173],[  0,255,184],[  0,255,189],
	[  0,255,195],[  0,255,200],[  0,255,205],[  0,255,211],[  0,255,216],
	[  0,255,222],[  0,255,227],[  0,255,232],[  0,255,238],[  0,255,243],
	[  0,255,248],[  0,255,254],[  0,250,255],[  0,239,255],[  0,234,255],
	[  0,228,255],[  0,223,255],[  0,218,255],[  0,212,255],[  0,207,255],
	[  0,201,255],[  0,196,255],[  0,191,255],[  0,185,255],[  0,180,255],
	[  0,174,255],[  0,169,255],[  0,164,255],[  0,153,255],[  0,148,255],
	[  0,142,255],[  0,137,255],[  0,131,255],[  0,126,255],[  0,121,255],
	[  0,115,255],[  0,110,255],[  0,105,255],[  0, 99,255],[  0, 94,255],
	[  0, 88,255],[  0, 83,255],[  0, 78,255],[  0, 67,255],[  0, 62,255],
	[  0, 56,255],[  0, 51,255],[  0, 45,255],[  0, 40,255],[  0, 35,255],
	[  0, 29,255],[  0, 24,255],[  0, 18,255],[  0, 13,255],[  0,  8,255],
	[  0,  2,255],[  2,  0,255],[  7,  0,255],[ 18,  0,255],[ 24,  0,255],
	[ 29,  0,255],[ 34,  0,255],[ 40,  0,255],[ 45,  0,255],[ 50,  0,255],
	[ 56,  0,255],[ 61,  0,255],[ 67,  0,255],[ 72,  0,255],[ 77,  0,255],
	[ 83,  0,255],[ 88,  0,255],[ 93,  0,255],[104,  0,255],[110,  0,255],
	[115,  0,255],[120,  0,255],[126,  0,255],[131,  0,255],[136,  0,255],
	[142,  0,255],[147,  0,255],[153,  0,255],[158,  0,255],[163,  0,255],
	[169,  0,255],[174,  0,255],[180,  0,255],[190,  0,255],[196,  0,255],
	[201,  0,255],[206,  0,255],[212,  0,255],[217,  0,255],[223,  0,255],
	[228,  0,255],[233,  0,255],[239,  0,255],[244,  0,255],[249,  0,255],
	[255,  0,254],[255,  0,249],[255,  0,243],[255,  0,233],[255,  0,227],
	[255,  0,222],[255,  0,217],[255,  0,211],[255,  0,206],[255,  0,201]
			    ]
		 );

# Returns a hash: gradient-name => color-count
sub available_gradients {
  return map { $_, scalar( @{ $_gradients{$_} } ) } keys %_gradients;
}

# Returns array-ref of rgb-triples, undef if gradient-name not found
sub gradient {
  my ( $name ) = @_;

  unless( exists $_gradients{ $name } ) { return; }

  return $_gradients{$name};
}

# Returns the color corresponding to the position in the gradient given by f.
# Returns undef when gradient not found or f outside valid range.
sub grad2rgb {
  my ( $name, $frac ) = @_;

  unless( exists $_gradients{ $name } ) { return; }
  if( $frac < 0.0 || $frac >= 1.0 ) { return; }

  my $idx = int( $frac * scalar( @{$_gradients{$name}} ) );
  return _fmt( @{ $_gradients{$name}[$idx] } );
}

# Expects a gradient and and array-ref to an array of rgb triples.
# If the name already exists, the function returns the old array; undef otherws
sub register_gradient {
  my ( $name, $array_ref ) = @_;

  if( exists $_gradients{ $name } ) {
    my $old = $_gradients{ $name };
    $_gradients{ $name } = $array_ref;
    return $old;
  }

  $_gradients{ $name } = $array_ref;
  return undef;
}

} # end BEGIN (Gradients)

# ==================================================
# Names

BEGIN {

my $_default_namespace = 'x11';

my %_colors = (
  'www:aqua'    => [  0,255,255],'www:black'   => [  0,  0,  0],
  'www:blue'    => [  0,  0,255],'www:fuchsia' => [255,  0,255],
  'www:gray'    => [190,190,190],'www:green'   => [  0,128,  0],
  'www:lime'    => [  0,255,  0],'www:maroon'  => [128,  0,  0],
  'www:navy'    => [  0,  0,128],'www:olive'   => [128,128,  0],
  'www:purple'  => [128,  0,128],'www:red'     => [255,  0,  0],
  'www:silver'  => [192,192,192],'www:teal'    => [  0,128,128],
  'www:white'   => [255,255,255],'www:yellow'  => [255,255,  0],
  'www:orange'  => [255,165,  0],

'svg:palevioletred' => [219,112,147],'svg:mediumslateblue' => [123,104,238],
'svg:gold' => [255,215,0],'svg:gainsboro' => [220,220,220],
'svg:yellow' => [255,255,0],'svg:limegreen' => [50,205,50],
'svg:lightgoldenrodyellow' => [250,250,210],'svg:lavenderblush' => [255,240,245],
'svg:darkmagenta' => [139,0,139],'svg:darkgrey' => [169,169,169],
'svg:blanchedalmond' => [255,235,205],'svg:ghostwhite' => [248,248,255],
'svg:floralwhite' => [255,250,240],'svg:coral' => [255,127,80],
'svg:honeydew' => [240,255,240],'svg:mistyrose' => [255,228,225],
'svg:slateblue' => [106,90,205],'svg:goldenrod' => [218,165,32],
'svg:darkcyan' => [0,139,139],'svg:moccasin' => [255,228,181],
'svg:mediumvioletred' => [199,21,133],'svg:maroon' => [128,0,0],
'svg:lightpink' => [255,182,193],'svg:lightsalmon' => [255,160,122],
'svg:paleturquoise' => [175,238,238],'svg:darksalmon' => [233,150,122],
'svg:yellowgreen' => [154,205,50],'svg:mediumturquoise' => [72,209,204],
'svg:chartreuse' => [127,255,0],'svg:peru' => [205,133,63],
'svg:palegoldenrod' => [238,232,170],'svg:red' => [255,0,0],
'svg:lavender' => [230,230,250],'svg:lightseagreen' => [32,178,170],
'svg:powderblue' => [176,224,230],'svg:orchid' => [218,112,214],
'svg:cornsilk' => [255,248,220],'svg:seagreen' => [46,139,87],
'svg:royalblue' => [65,105,225],'svg:ivory' => [255,255,240],
'svg:tan' => [210,180,140],'svg:linen' => [250,240,230],
'svg:darkorchid' => [153,50,204],'svg:tomato' => [255,99,71],
'svg:lightcyan' => [224,255,255],'svg:darkolivegreen' => [85,107,47],
'svg:sienna' => [160,82,45],'svg:lightsteelblue' => [176,196,222],
'svg:indigo' => [75,0,130],'svg:peachpuff' => [255,218,185],
'svg:lime' => [0,255,0],'svg:mediumspringgreen' => [0,250,154],
'svg:silver' => [192,192,192],'svg:saddlebrown' => [139,69,19],
'svg:lightyellow' => [255,255,224],'svg:grey' => [128,128,128],
'svg:thistle' => [216,191,216],'svg:deepskyblue' => [0,191,255],
'svg:lightgreen' => [144,238,144],'svg:blueviolet' => [138,43,226],
'svg:aqua' => [0,255,255],'svg:cyan' => [0,255,255],
'svg:papayawhip' => [255,239,213],'svg:deeppink' => [255,20,147],
'svg:firebrick' => [178,34,34],'svg:navy' => [0,0,128],
'svg:hotpink' => [255,105,180],'svg:pink' => [255,192,203],
'svg:darkturquoise' => [0,206,209],'svg:navajowhite' => [255,222,173],
'svg:lightslategrey' => [119,136,153],'svg:lawngreen' => [124,252,0],
'svg:lightcoral' => [240,128,128],'svg:palegreen' => [152,251,152],
'svg:dodgerblue' => [30,144,255],'svg:greenyellow' => [173,255,47],
'svg:lightskyblue' => [135,206,250],'svg:brown' => [165,42,42],
'svg:dimgrey' => [105,105,105],'svg:aquamarine' => [127,255,212],
'svg:darkseagreen' => [143,188,143],'svg:fuchsia' => [255,0,255],
'svg:magenta' => [255,0,255],'svg:chocolate' => [210,105,30],
'svg:mediumseagreen' => [60,179,113],'svg:cadetblue' => [95,158,160],
'svg:purple' => [128,0,128],'svg:turquoise' => [64,224,208],
'svg:darkkhaki' => [189,183,107],'svg:antiquewhite' => [250,235,215],
'svg:skyblue' => [135,206,235],'svg:sandybrown' => [244,164,96],
'svg:mediumblue' => [0,0,205],'svg:steelblue' => [70,130,180],
'svg:indianred' => [205,92,92],'svg:khaki' => [240,230,140],
'svg:lightblue' => [173,216,230],'svg:green' => [0,128,0],
'svg:olive' => [128,128,0],'svg:mediumorchid' => [186,85,211],
'svg:blue' => [0,0,255],'svg:snow' => [255,250,250],
'svg:rosybrown' => [188,143,143],'svg:orange' => [255,165,0],
'svg:slategrey' => [112,128,144],'svg:darkorange' => [255,140,0],
'svg:violet' => [238,130,238],'svg:darkslategrey' => [47,79,79],
'svg:whitesmoke' => [245,245,245],'svg:burlywood' => [222,184,135],
'svg:darkgreen' => [0,100,0],'svg:lemonchiffon' => [255,250,205],
'svg:midnightblue' => [25,25,112],'svg:mintcream' => [245,255,250],
'svg:oldlace' => [253,245,230],'svg:black' => [0,0,0],
'svg:bisque' => [255,228,196],'svg:mediumaquamarine' => [102,205,170],
'svg:olivedrab' => [107,142,35],'svg:salmon' => [250,128,114],
'svg:teal' => [0,128,128],'svg:seashell' => [255,245,238],
'svg:springgreen' => [0,255,127],'svg:plum' => [221,160,221],
'svg:darkviolet' => [148,0,211],'svg:wheat' => [245,222,179],
'svg:mediumpurple' => [147,112,219],'svg:cornflowerblue' => [100,149,237],
'svg:forestgreen' => [34,139,34],'svg:darkgoldenrod' => [184,134,11],
'svg:aliceblue' => [240,248,255],'svg:white' => [255,255,255],
'svg:darkblue' => [0,0,139],'svg:azure' => [240,255,255],
'svg:darkred' => [139,0,0],'svg:orangered' => [255,69,0],
'svg:darkslateblue' => [72,61,139],'svg:crimson' => [220,20,60],
'svg:lightgrey' => [211,211,211],'svg:beige' => [245,245,220],

'x11:deepskyblue3' => [0,154,205],'x11:gold' => [255,215,0],
'x11:gold1' => [255,215,0],'x11:mediumpurple3' => [137,104,205],
'x11:royalblue3' => [58,95,205],'x11:lightgoldenrodyellow' => [250,250,210],
'x11:lavenderblush' => [255,240,245],'x11:lavenderblush1' => [255,240,245],
'x11:pink1' => [255,181,197],'x11:green3' => [0,205,0],
'x11:lightsteelblue1' => [202,225,255],'x11:blanchedalmond' => [255,235,205],
'x11:salmon1' => [255,140,105],'x11:ghostwhite' => [248,248,255],
'x11:floralwhite' => [255,250,240],'x11:dodgerblue4' => [16,78,139],
'x11:grey43' => [110,110,110],'x11:indianred4' => [139,58,58],
'x11:mistyrose1' => [255,228,225],'x11:mistyrose' => [255,228,225],
'x11:dodgerblue2' => [28,134,238],'x11:grey37' => [94,94,94],
'x11:grey9' => [23,23,23],'x11:purple4' => [85,26,139],
'x11:orchid2' => [238,122,233],'x11:cornsilk3' => [205,200,177],
'x11:goldenrod' => [218,165,32],'x11:hotpink4' => [139,58,98],
'x11:lightpink' => [255,182,193],'x11:coral2' => [238,106,80],
'x11:cyan2' => [0,238,238],'x11:grey87' => [222,222,222],
'x11:grey91' => [232,232,232],'x11:violetred4' => [139,34,82],
'x11:violetred2' => [238,58,140],'x11:indianred2' => [238,99,99],
'x11:lightyellow3' => [205,205,180],'x11:darkolivegreen2' => [188,238,104],
'x11:magenta3' => [205,0,205],'x11:grey64' => [163,163,163],
'x11:honeydew3' => [193,205,193],'x11:lightsalmon3' => [205,129,98],
'x11:springgreen4' => [0,139,69],'x11:grey57' => [145,145,145],
'x11:grey50' => [127,127,127],'x11:grey66' => [168,168,168],
'x11:antiquewhite1' => [255,239,219],'x11:paleturquoise' => [175,238,238],
'x11:navajowhite2' => [238,207,161],'x11:lightpink3' => [205,140,149],
'x11:darksalmon' => [233,150,122],'x11:grey52' => [133,133,133],
'x11:slategrey3' => [159,182,205],'x11:darkseagreen4' => [105,139,105],
'x11:chartreuse' => [127,255,0],'x11:chartreuse1' => [127,255,0],
'x11:grey42' => [107,107,107],'x11:peru' => [205,133,63],
'x11:tan3' => [205,133,63],'x11:grey19' => [48,48,48],
'x11:palegreen3' => [124,205,124],'x11:lavender' => [230,230,250],
'x11:red3' => [205,0,0],'x11:orchid' => [218,112,214],
'x11:powderblue' => [176,224,230],'x11:grey35' => [89,89,89],
'x11:plum4' => [139,102,139],'x11:cornsilk' => [255,248,220],
'x11:cornsilk1' => [255,248,220],'x11:royalblue' => [65,105,225],
'x11:darkgoldenrod2' => [238,173,14],'x11:lightpink4' => [139,95,101],
'x11:springgreen2' => [0,238,118],'x11:tan' => [210,180,140],
'x11:lightslateblue' => [132,112,255],'x11:darkorchid' => [153,50,204],
'x11:orangered2' => [238,64,0],'x11:palevioletred1' => [255,130,171],
'x11:grey63' => [161,161,161],'x11:maroon2' => [238,48,167],
'x11:blue2' => [0,0,238],'x11:turquoise4' => [0,134,139],
'x11:lightcyan1' => [224,255,255],'x11:lightcyan' => [224,255,255],
'x11:springgreen3' => [0,205,102],'x11:darkorchid4' => [104,34,139],
'x11:sienna' => [160,82,45],'x11:goldenrod2' => [238,180,34],
'x11:lightgoldenrod3' => [205,190,112],'x11:green' => [0,255,0],
'x11:green1' => [0,255,0],'x11:peachpuff1' => [255,218,185],
'x11:peachpuff' => [255,218,185],'x11:yellow3' => [205,205,0],
'x11:mediumspringgreen' => [0,250,154],'x11:cadetblue3' => [122,197,205],
'x11:royalblue1' => [72,118,255],'x11:deepskyblue1' => [0,191,255],
'x11:deepskyblue' => [0,191,255],'x11:firebrick1' => [255,48,48],
'x11:grey80' => [204,204,204],'x11:grey28' => [71,71,71],
'x11:palegreen2' => [144,238,144],'x11:lightgreen' => [144,238,144],
'x11:blueviolet' => [138,43,226],'x11:deeppink1' => [255,20,147],
'x11:deeppink' => [255,20,147],'x11:deeppink2' => [238,18,137],
'x11:lightskyblue2' => [164,211,238],'x11:grey77' => [196,196,196],
'x11:grey72' => [184,184,184],'x11:tomato2' => [238,92,66],
'x11:steelblue2' => [92,172,238],'x11:hotpink' => [255,105,180],
'x11:slateblue4' => [71,60,139],'x11:pink' => [255,192,203],
'x11:darkturquoise' => [0,206,209],'x11:antiquewhite3' => [205,192,176],
'x11:grey32' => [82,82,82],'x11:lightyellow2' => [238,238,209],
'x11:olivedrab4' => [105,139,34],'x11:lightblue4' => [104,131,139],
'x11:royalblue2' => [67,110,238],'x11:navajowhite1' => [255,222,173],
'x11:navajowhite' => [255,222,173],'x11:lightgoldenrod' => [238,221,130],
'x11:grey85' => [217,217,217],'x11:maroon4' => [139,28,98],
'x11:grey90' => [229,229,229],'x11:grey17' => [43,43,43],
'x11:seashell4' => [139,134,130],'x11:greenyellow' => [173,255,47],
'x11:dodgerblue1' => [30,144,255],'x11:dodgerblue' => [30,144,255],
'x11:grey89' => [227,227,227],'x11:brown2' => [238,59,59],
'x11:paleturquoise2' => [174,238,238],'x11:lightskyblue' => [135,206,250],
'x11:salmon4' => [139,76,57],'x11:chocolate3' => [205,102,29],
'x11:grey70' => [179,179,179],'x11:grey25' => [64,64,64],
'x11:darkolivegreen4' => [110,139,61],'x11:mediumorchid2' => [209,95,238],
'x11:brown' => [165,42,42],'x11:grey67' => [171,171,171],
'x11:grey41' => [105,105,105],'x11:dimgrey' => [105,105,105],
'x11:grey60' => [153,153,153],'x11:indianred3' => [205,85,85],
'x11:chocolate' => [210,105,30],'x11:darkslategrey1' => [151,255,255],
'x11:grey2' => [5,5,5],'x11:firebrick3' => [205,38,38],
'x11:snow4' => [139,137,137],'x11:mediumseagreen' => [60,179,113],
'x11:darkorchid1' => [191,62,255],'x11:pink3' => [205,145,158],
'x11:violetred1' => [255,62,150],'x11:grey83' => [212,212,212],
'x11:olivedrab1' => [192,255,62],'x11:darkkhaki' => [189,183,107],
'x11:deepskyblue4' => [0,104,139],'x11:darkorchid2' => [178,58,238],
'x11:skyblue' => [135,206,235],'x11:mediumorchid3' => [180,82,205],
'x11:rosybrown4' => [139,105,105],'x11:grey16' => [41,41,41],
'x11:yellow4' => [139,139,0],'x11:maroon' => [176,48,96],
'x11:turquoise2' => [0,229,238],'x11:mistyrose2' => [238,213,210],
'x11:blue3' => [0,0,205],'x11:mediumblue' => [0,0,205],
'x11:grey4' => [10,10,10],'x11:pink2' => [238,169,184],
'x11:chocolate2' => [238,118,33],'x11:lightyellow4' => [139,139,122],
'x11:grey99' => [252,252,252],'x11:red2' => [238,0,0],
'x11:tan4' => [139,90,43],'x11:yellow2' => [238,238,0],
'x11:grey12' => [31,31,31],'x11:deeppink4' => [139,10,80],
'x11:lightsalmon4' => [139,87,66],'x11:lightcyan4' => [122,139,139],
'x11:snow1' => [255,250,250],'x11:snow' => [255,250,250],
'x11:brown4' => [139,35,35],'x11:darkseagreen2' => [180,238,180],
'x11:lightsteelblue2' => [188,210,238],'x11:rosybrown' => [188,143,143],
'x11:maroon1' => [255,52,179],'x11:slategrey' => [112,128,144],
'x11:orange' => [255,165,0],'x11:orange1' => [255,165,0],
'x11:orangered3' => [205,55,0],'x11:plum3' => [205,150,205],
'x11:turquoise3' => [0,197,205],'x11:pink4' => [139,99,108],
'x11:violet' => [238,130,238],'x11:grey96' => [245,245,245],
'x11:whitesmoke' => [245,245,245],'x11:lightgoldenrod1' => [255,236,139],
'x11:darkorange1' => [255,127,0],'x11:seashell2' => [238,229,222],
'x11:midnightblue' => [25,25,112],'x11:grey27' => [69,69,69],
'x11:mediumpurple2' => [159,121,238],'x11:bisque4' => [139,125,107],
'x11:black' => [0,0,0],'x11:grey0' => [0,0,0],
'x11:lavenderblush4' => [139,131,134],'x11:bisque1' => [255,228,196],
'x11:bisque' => [255,228,196],'x11:mediumaquamarine' => [102,205,170],
'x11:aquamarine3' => [102,205,170],'x11:goldenrod1' => [255,193,37],
'x11:green4' => [0,139,0],'x11:bisque3' => [205,183,158],
'x11:salmon' => [250,128,114],'x11:grey1' => [3,3,3],
'x11:purple3' => [125,38,205],'x11:khaki4' => [139,134,78],
'x11:grey' => [190,190,190],'x11:cadetblue4' => [83,134,139],
'x11:cadetblue1' => [152,245,255],'x11:hotpink3' => [205,96,144],
'x11:antiquewhite2' => [238,223,204],'x11:darkorange4' => [139,69,0],
'x11:cornsilk2' => [238,232,205],'x11:grey93' => [237,237,237],
'x11:thistle3' => [205,181,205],'x11:plum2' => [238,174,238],
'x11:burlywood2' => [238,197,145],'x11:skyblue4' => [74,112,139],
'x11:peachpuff2' => [238,203,173],'x11:grey62' => [158,158,158],
'x11:paleturquoise3' => [150,205,205],'x11:lightblue1' => [191,239,255],
'x11:mediumpurple' => [147,112,219],'x11:peachpuff3' => [205,175,149],
'x11:grey49' => [125,125,125],'x11:grey3' => [8,8,8],
'x11:steelblue1' => [99,184,255],'x11:grey73' => [186,186,186],
'x11:grey44' => [112,112,112],'x11:palevioletred4' => [139,71,93],
'x11:khaki2' => [238,230,133],'x11:gold3' => [205,173,0],
'x11:grey47' => [120,120,120],'x11:aliceblue' => [240,248,255],
'x11:grey58' => [148,148,148],'x11:darkslategrey4' => [82,139,139],
'x11:mediumorchid4' => [122,55,139],'x11:thistle1' => [255,225,255],
'x11:mistyrose4' => [139,125,123],'x11:orchid1' => [255,131,250],
'x11:hotpink2' => [238,106,167],'x11:azure' => [240,255,255],
'x11:azure1' => [240,255,255],'x11:darkred' => [139,0,0],
'x11:red4' => [139,0,0],'x11:chartreuse2' => [118,238,0],
'x11:slateblue1' => [131,111,255],'x11:grey15' => [38,38,38],
'x11:grey71' => [181,181,181],'x11:darkslategrey2' => [141,238,238],
'x11:snow3' => [205,201,201],'x11:bisque2' => [238,213,183],
'x11:darkslateblue' => [72,61,139],'x11:coral4' => [139,62,47],
'x11:grey69' => [176,176,176],'x11:burlywood4' => [139,115,85],
'x11:coral3' => [205,91,69],'x11:purple' => [160,32,240],
'x11:grey36' => [92,92,92],'x11:grey94' => [240,240,240],
'x11:palevioletred2' => [238,121,159],'x11:grey46' => [117,117,117],
'x11:palevioletred' => [219,112,147],'x11:mediumslateblue' => [123,104,238],
'x11:seagreen1' => [84,255,159],'x11:gainsboro' => [220,220,220],
'x11:yellow1' => [255,255,0],'x11:yellow' => [255,255,0],
'x11:limegreen' => [50,205,50],'x11:darkgrey' => [169,169,169],
'x11:darkmagenta' => [139,0,139],'x11:magenta4' => [139,0,139],
'x11:grey59' => [150,150,150],'x11:firebrick2' => [238,44,44],
'x11:coral' => [255,127,80],'x11:honeydew' => [240,255,240],
'x11:honeydew1' => [240,255,240],'x11:grey86' => [219,219,219],
'x11:grey13' => [33,33,33],'x11:purple1' => [155,48,255],
'x11:grey82' => [209,209,209],'x11:grey65' => [166,166,166],
'x11:grey97' => [247,247,247],'x11:azure4' => [131,139,139],
'x11:darkslategrey3' => [121,205,205],'x11:lightcyan3' => [180,205,205],
'x11:aquamarine2' => [118,238,198],'x11:grey92' => [235,235,235],
'x11:slateblue' => [106,90,205],'x11:darkcyan' => [0,139,139],
'x11:cyan4' => [0,139,139],'x11:chartreuse3' => [102,205,0],
'x11:moccasin' => [255,228,181],'x11:mediumvioletred' => [199,21,133],
'x11:tomato3' => [205,79,57],'x11:grey31' => [79,79,79],
'x11:sienna2' => [238,121,66],'x11:grey98' => [250,250,250],
'x11:gold4' => [139,117,0],'x11:slateblue3' => [105,89,205],
'x11:grey14' => [36,36,36],'x11:honeydew4' => [131,139,131],
'x11:grey61' => [156,156,156],'x11:violetred3' => [205,50,120],
'x11:grey39' => [99,99,99],'x11:aquamarine4' => [69,139,116],
'x11:darkgoldenrod4' => [139,101,8],'x11:mediumpurple1' => [171,130,255],
'x11:lightsalmon1' => [255,160,122],'x11:lightsalmon' => [255,160,122],
'x11:darkolivegreen3' => [162,205,90],'x11:grey10' => [26,26,26],
'x11:khaki3' => [205,198,115],'x11:navajowhite3' => [205,179,139],
'x11:lightpink1' => [255,174,185],'x11:grey81' => [207,207,207],
'x11:grey45' => [115,115,115],'x11:wheat3' => [205,186,150],
'x11:steelblue4' => [54,100,139],'x11:grey48' => [122,122,122],
'x11:olivedrab3' => [154,205,50],'x11:yellowgreen' => [154,205,50],
'x11:mediumturquoise' => [72,209,204],'x11:palegoldenrod' => [238,232,170],
'x11:ivory2' => [238,238,224],'x11:darkolivegreen1' => [202,255,112],
'x11:red1' => [255,0,0],'x11:red' => [255,0,0],
'x11:lemonchiffon4' => [139,137,112],'x11:lightseagreen' => [32,178,170],
'x11:seagreen4' => [46,139,87],'x11:seagreen' => [46,139,87],
'x11:ivory' => [255,255,240],'x11:ivory1' => [255,255,240],
'x11:linen' => [250,240,230],'x11:grey34' => [87,87,87],
'x11:thistle2' => [238,210,238],'x11:tomato' => [255,99,71],
'x11:tomato1' => [255,99,71],'x11:slategrey1' => [198,226,255],
'x11:orchid3' => [205,105,201],'x11:lightcyan2' => [209,238,238],
'x11:grey54' => [138,138,138],'x11:darkolivegreen' => [85,107,47],
'x11:lightsteelblue' => [176,196,222],'x11:grey33' => [84,84,84],
'x11:chocolate4' => [139,69,19],'x11:saddlebrown' => [139,69,19],
'x11:orange3' => [205,133,0],'x11:lightyellow' => [255,255,224],
'x11:lightyellow1' => [255,255,224],'x11:grey75' => [191,191,191],
'x11:khaki1' => [255,246,143],'x11:thistle' => [216,191,216],
'x11:grey79' => [201,201,201],'x11:plum1' => [255,187,255],
'x11:paleturquoise4' => [102,139,139],'x11:cyan1' => [0,255,255],
'x11:cyan' => [0,255,255],'x11:maroon3' => [205,41,144],
'x11:papayawhip' => [255,239,213],'x11:seagreen3' => [67,205,128],
'x11:lightgoldenrod4' => [139,129,76],'x11:lightskyblue1' => [176,226,255],
'x11:firebrick' => [178,34,34],'x11:grey30' => [77,77,77],
'x11:grey26' => [66,66,66],'x11:antiquewhite4' => [139,131,120],
'x11:navyblue' => [0,0,128],'x11:navy' => [0,0,128],
'x11:grey7' => [18,18,18],'x11:grey5' => [13,13,13],
'x11:grey29' => [74,74,74],'x11:turquoise1' => [0,245,255],
'x11:darkgoldenrod3' => [205,149,12],'x11:goldenrod4' => [139,105,20],
'x11:palevioletred3' => [205,104,137],'x11:lightslategrey' => [119,136,153],
'x11:snow2' => [238,233,233],'x11:grey24' => [61,61,61],
'x11:slategrey4' => [108,123,139],'x11:grey55' => [140,140,140],
'x11:seashell3' => [205,197,191],'x11:deeppink3' => [205,16,118],
'x11:lawngreen' => [124,252,0],'x11:darkorchid3' => [154,50,205],
'x11:lightcoral' => [240,128,128],'x11:palegreen' => [152,251,152],
'x11:grey56' => [143,143,143],'x11:grey23' => [59,59,59],
'x11:grey74' => [189,189,189],'x11:azure2' => [224,238,238],
'x11:darkseagreen3' => [155,205,155],'x11:grey20' => [51,51,51],
'x11:cadetblue2' => [142,229,238],'x11:grey84' => [214,214,214],
'x11:cornsilk4' => [139,136,120],'x11:grey38' => [97,97,97],
'x11:magenta1' => [255,0,255],'x11:magenta' => [255,0,255],
'x11:darkseagreen' => [143,188,143],'x11:aquamarine1' => [127,255,212],
'x11:aquamarine' => [127,255,212],'x11:lightblue3' => [154,192,205],
'x11:olivedrab2' => [179,238,58],'x11:grey40' => [102,102,102],
'x11:peachpuff4' => [139,119,101],'x11:paleturquoise1' => [187,255,255],
'x11:darkseagreen1' => [193,255,193],'x11:darkorange3' => [205,102,0],
'x11:brown3' => [205,51,51],'x11:grey51' => [130,130,130],
'x11:mediumpurple4' => [93,71,139],'x11:lightpink2' => [238,162,173],
'x11:cadetblue' => [95,158,160],'x11:lemonchiffon2' => [238,233,191],
'x11:green2' => [0,238,0],'x11:azure3' => [193,205,205],
'x11:turquoise' => [64,224,208],'x11:brown1' => [255,64,64],
'x11:lightsteelblue4' => [110,123,139],'x11:orange2' => [238,154,0],
'x11:antiquewhite' => [250,235,215],'x11:wheat2' => [238,216,174],
'x11:rosybrown2' => [238,180,180],'x11:lightsteelblue3' => [162,181,205],
'x11:grey78' => [199,199,199],'x11:grey21' => [54,54,54],
'x11:sandybrown' => [244,164,96],'x11:lavenderblush2' => [238,224,229],
'x11:steelblue' => [70,130,180],'x11:grey95' => [242,242,242],
'x11:indianred' => [205,92,92],'x11:skyblue1' => [135,206,255],
'x11:khaki' => [240,230,140],'x11:orchid4' => [139,71,137],
'x11:chocolate1' => [255,127,36],'x11:goldenrod3' => [205,155,29],
'x11:sienna4' => [139,71,38],'x11:lightblue' => [173,216,230],
'x11:grey88' => [224,224,224],'x11:palegreen4' => [84,139,84],
'x11:mediumorchid' => [186,85,211],'x11:blue' => [0,0,255],
'x11:blue1' => [0,0,255],'x11:dodgerblue3' => [24,116,205],
'x11:indianred1' => [255,106,106],'x11:cyan3' => [0,205,205],
'x11:tan1' => [255,165,79],'x11:darkorange' => [255,140,0],
'x11:skyblue2' => [126,192,238],'x11:coral1' => [255,114,86],
'x11:darkslategrey' => [47,79,79],'x11:burlywood' => [222,184,135],
'x11:sienna3' => [205,104,57],'x11:darkgreen' => [0,100,0],
'x11:mistyrose3' => [205,183,181],'x11:grey68' => [173,173,173],
'x11:grey53' => [135,135,135],'x11:lemonchiffon' => [255,250,205],
'x11:lemonchiffon1' => [255,250,205],'x11:palegreen1' => [154,255,154],
'x11:grey76' => [194,194,194],'x11:steelblue3' => [79,148,205],
'x11:grey11' => [28,28,28],'x11:oldlace' => [253,245,230],
'x11:mintcream' => [245,255,250],'x11:firebrick4' => [139,26,26],
'x11:lemonchiffon3' => [205,201,165],'x11:olivedrab' => [107,142,35],
'x11:honeydew2' => [224,238,224],'x11:deepskyblue2' => [0,178,238],
'x11:slateblue2' => [122,103,238],'x11:slategrey2' => [185,211,238],
'x11:seagreen2' => [78,238,148],'x11:salmon2' => [238,130,98],
'x11:ivory3' => [205,205,193],'x11:mediumorchid1' => [224,102,255],
'x11:tan2' => [238,154,73],'x11:springgreen' => [0,255,127],
'x11:springgreen1' => [0,255,127],'x11:seashell1' => [255,245,238],
'x11:seashell' => [255,245,238],'x11:skyblue3' => [108,166,205],
'x11:chartreuse4' => [69,139,0],'x11:burlywood3' => [205,170,125],
'x11:plum' => [221,160,221],'x11:ivory4' => [139,139,131],
'x11:darkviolet' => [148,0,211],'x11:lightblue2' => [178,223,238],
'x11:wheat' => [245,222,179],'x11:darkgoldenrod1' => [255,185,15],
'x11:cornflowerblue' => [100,149,237],'x11:purple2' => [145,44,238],
'x11:grey6' => [15,15,15],'x11:magenta2' => [238,0,238],
'x11:sienna1' => [255,130,71],'x11:darkgoldenrod' => [184,134,11],
'x11:forestgreen' => [34,139,34],'x11:navajowhite4' => [139,121,94],
'x11:royalblue4' => [39,64,139],'x11:wheat1' => [255,231,186],
'x11:lightskyblue4' => [96,123,139],'x11:grey18' => [46,46,46],
'x11:orangered4' => [139,37,0],'x11:salmon3' => [205,112,84],
'x11:white' => [255,255,255],'x11:grey100' => [255,255,255],
'x11:orange4' => [139,90,0],'x11:wheat4' => [139,126,102],
'x11:rosybrown1' => [255,193,193],'x11:grey8' => [20,20,20],
'x11:lightgoldenrod2' => [238,220,130],'x11:lightskyblue3' => [141,182,205],
'x11:violetred' => [208,32,144],'x11:blue4' => [0,0,139],
'x11:darkblue' => [0,0,139],'x11:lavenderblush3' => [205,193,197],
'x11:thistle4' => [139,123,139],'x11:hotpink1' => [255,110,180],
'x11:darkorange2' => [238,118,0],'x11:lightsalmon2' => [238,149,114],
'x11:orangered1' => [255,69,0],'x11:orangered' => [255,69,0],
'x11:burlywood1' => [255,211,155],'x11:lightgrey' => [211,211,211],
'x11:grey22' => [56,56,56],'x11:tomato4' => [139,54,38],
'x11:rosybrown3' => [205,155,155],'x11:gold2' => [238,201,0],
'x11:beige' => [245,245,220]
	      );

# Returns a hash-ref: color-name => RGB triple
sub available_names {
  return \%_colors;
}

# Returns the RGB triple for a name, undef if name not found
# The name is normalized before lookup is attempted. Normalization consists
# of: lowercasing and elimination of whitespace. Also, "gray" is replaced 
# with "grey".
# If the name is prefixed with a namespace (separated by colon ':'),
# only this namespace is searched. If no namespace is specified, then
# the lookup occurs first in the global namespace, then in the default
# namespace.

sub name2rgb {
  my ( $name ) = @_;

  my ( $ns, $core, $norm ) = _normalize_name( $name );

  # If explicit namespace:
  if( $ns ne '' ) {
    if( exists $_colors{ $norm } ) {
      return _fmt( @{ $_colors{ $norm } } );
    } else {
      return; # Do not search further if explicit namespace is given
    }
  }

  # No explicit namespace
  if( exists $_colors{ $core } ) {
    return _fmt( @{ $_colors{ $core } } ); # global namespace
  }

  # No namespace, but ':' prefix: search global ONLY, but not default ns
  if( $core ne $norm ) { return; }

  $norm = get_default_namespace() . ':' . $core;
  if( exists $_colors{ $norm } ) {
    return _fmt( @{ $_colors{ $norm } } ); # default namespace
  }

  # Not found
  return;
}

# Takes a name and an RGB triple. Registers the triple for the given name.
# The name will be normalized (lowercased, whitespace eliminated, 'gray' 
# replaced by 'grey') before assignment is made.
# If the name is not prefixed by a namespace, the color will be entered 
# into the global namespace. 
# Returns the old value for the name, if the name already exists.

sub register_name {
  my ( $name, $r, $g, $b ) = @_;

  my ( $ns, $core, $norm ) = _normalize_name( $name );

  # If no explicit ns is given, lookup and replace for $core, which is
  # guaranteed not preceeded by ':'. Otherwise, use fully qualified name.
  my $crr = ( $ns eq '' ) ? $core : $norm;

  if( exists $_colors{ $crr } ) {
    my $old = $_colors{ $crr };
    $_colors{ $crr } = [ $r, $g, $b ];

    return _fmt( @$old );
  }

  $_colors{ $crr } = [ $r, $g, $b ];

  return;
}

sub _normalize_name {
  my ( $name ) = @_;

  $name = lc( $name );   # Lowercase
  $name =~ s/\s//g;      # Eliminate whitespace
  $name =~ s/gray/grey/; # gray -> grey

  my ( $ns, $core ) = ( '', $name );
  if( $name =~ /:/ ) {
    ( $ns, $core ) = split ':', $name;
  }

  return ( $ns, $core, $name );
}

# Sets the default namespace. Returns the previous value.
# Giving an empty string as argument makes the global namespace the default.
# Note that the global namespace is initially EMPTY!

sub set_default_namespace {
  my $old = $_default_namespace;
  $_default_namespace = $_[0];
  return $old;
}

sub get_default_namespace {
  return $_default_namespace;
}

} # end BEGIN (Names)



1;
__END__


# ==================================================
# ++++++++++++++++++++++++++++++++++++++++++++++++++
# ==================================================

=head1 NAME

Graphics::ColorUtils - Easy-to-use color space conversions and more.

=head1 SYNOPSIS

  use Graphics::ColorUtils;

  ( $y, $i, $q ) = rgb2yiq( $r, $g, $b );
  ( $r, $g, $b ) = yiq2rgb( $y, $i, $q );
  $hex_string    = yiq2rgb( $y, $i, $q );

  ( $c, $m, $y ) = rgb2cmy( $r, $g, $b );
  ( $r, $g, $b ) = cmy2rgb( $c, $m, $y );
  $hex_string    = cmy2rgb( $c, $m, $y );

  ( $h, $l, $s ) = rgb2hls( $r, $g, $b );
  ( $r, $g, $b ) = hls2rgb( $h, $l, $s );
  $hex_string    = hls2rgb( $h, $l, $s );

  ( $h, $s, $v ) = rgb2hsv( $r, $g, $b );
  ( $r, $g, $b ) = hsv2rgb( $h, $s, $v );
  $hex_string    = hsv2rgb( $h, $s, $v );

  # -----

  use Graphics::ColorUtils qw( :gradients );

  ( $r, $g, $b ) = grad2rgb( $name, $f );  # where 0.0 <= $f < 1.0
  $hex_string    = grad2rgb( $name, $f );

  %color_count_for_gradient_name = available_gradients();
  $array_ref_of_rgb_triples      = gradient( $name );
  $array_ref_old_grad            = register_gradient( $name, $array_ref_of_rgb_triples ); 

  # -----

  use Graphics::ColorUtils qw( :names );

  ( $r, $g, $b ) = name2rgb( $name );
  $hex_string    = name2rgb( $name );

  $hash_ref_rgb_triples_for_name = available_names();
  ( $old_r, $old_g, $old_b )     = register_name( $name, $r, $g, $b );
  $old_hex_string                = register_name( $name, $r, $g, $b );
  $default_ns                    = get_default_namespace();
  $old_ns                        = set_default_namespace( $new_ns );


=head1 DESCRIPTION

This modules provides some utility functions to handle colors and
color space conversions.

The interface has been kept simple, so that most functions can be called
"inline" when making calls to graphics libraries such as GD, Tk, or
when generating HTML/CSS. (E.g. for GD:
C<$c = $img-E<gt>colorAllocate( hsv2rgb( 270, 0.5, 0.3 ) );>.)

Features:

=over 4

=item Color Space Conversions

Color space conversions, in particular between the "intuitive" color
spaces HSV (Hue/Saturation/Value) and HLS (Hue/Lightness/Saturation)
to and from RGB (Red/Green/Blue).

=item Color Lookup

Color lookup by name for three standard sets of colors: WWW/CSS, SVG, and X11.

=item Color Gradients

Management of color gradients, which can be indexed by a floating point
number in the range 0..1. (Mostly intended for false-color data visualization.)

=back


=head1 CONVENTIONS

Legal values:

  Y, I, Q: 0..1
  C, M, Y: 0..1

  R, G, B: 0..255 (may be float on input, guaranteed int on output)

  H:       0..360 (red=0->yellow->green=120->cyan->blue=240->magenta steps of 60)
  S, V:    0..1
  L, S:    0..1

All C<...2rgb> functions return a three-element array in list context, 
and a string formatted according to C<"#%02x%02x%02x"> (e.g. C<'#ff3a18'>)
in scalar context.


=head1 METHODS

=head2 Color Space Conversions

=over 4

=item YIQ

C<rgb2yiq( $r, $g, $b )> and C<yiq2rgb( $y, $i, $q)>

=item CMY

C<rgb2cmy( $r, $g, $b )> and C<cmy2rgb( $c, $m, $y)>

=item HSV

C<rgb2hsv( $r, $g, $b )> and C<hsv2rgb( $h, $s, $v)>

=item HLS

C<rgb2hls( $r, $g, $b )> and C<hls2rgb( $h, $l, $s)>

=back

All these methods take a triple of values and return a triple of
converted values. However, B<in scalar context> the C<...2rgb>
methods return a string formatted according to C<"#%02x%02x%02x"> 
(e.g. C<'#ff3a18'>). This format is appropriate e.g. for calls to
Tk routines: C<$mw-E<gt>widget( -color => hls2rgb( 180, 0.2, 0.1 ) );>, etc.


=head2 Color Names

Names can be arbitrary strings. If names contain a colon (C<':'>),
the part of the name before the colon is considered a "namespace"
specification. Namespaces allow to have multiple color values
corresponding to the same name and to control the priority in 
which those values will be retrieved.

=over 4

=item C<name2rgb( $name )>

Returns a triple C<( $r, $g, $b )> in list context or a a hex-string
in scalar context if the name has been found, C<undef> otherwise.

The name is normalized before lookup is attempted. Normalization consists
of: lowercasing and elimination of whitespace. Also, "gray" is replaced 
with "grey".

If the name is prefixed with a namespace (separated by colon a C<':'>),
only this namespace is searched. If no namespace is specified, then
the lookup occurs first in the global namespace, then in the default
namespace.

=item C<available_names()>

Returns a reference to a hash, the keys of which are the color names,
and the values are references to three-element arrays of RGB values.

=item C<register_name( $name, $r, $g, $b )>

Takes a name and an RGB triple. Stores the triple for the given name.
The name will be normalized (lowercased, whitespace eliminated, 'gray' 
replaced by 'grey') before assignment is made.

If the name is not prefixed by a namespace, the color will be entered 
into the global namespace. 

Returns the old value for the name, if the name already exists, 
C<undef> otherwise.

=item C<get_default_namespace()>

Returns the current value of the default namespace. Note that the 
empty string C<''> corresponds to the I<global> namespace.

=item C<set_default_namespace()>

Sets the default namespace. Returns the previous value.

Giving an empty string as argument makes the global namespace the default.
Note that the global namespace is initially I<empty>.

(On startup, the default namespace is C<'x11'>.)

=back


=head2 Color Gradients

=over 4

=item C<grad2rgb( $name, $f )>

Given the name of a gradient and a floating point number between 0 and 1,
returns the color (as RGB triple or formatted hex-string) corresponding 
to the position in the gradient given by C<$f>.
Returns C<undef> when gradient not found or C<$f> outside valid range.

=item C<available_gradients()>

Returns a hash, the keys of which are the names of the known gradients
and the values being the number of colors in the corresponding gradient.

=item C<gradient( $name )>

Given the name of a gradient, returns a reference to an array of RGB 
triples or C<undef> if the gradient is not found.

=item C<register_gradient( $name, $array_ref )>

Takes the name of a (possibly new) gradient and a reference to an 
array of RGB triples. Stores the array as gradient for that name.
If the gradient name already existed, returns a reference to the
old array, C<undef> otherwise.

=back

An introduction, together with a large number of sample gradients
can be found at Paul Bourke's webpage:
http://local.wasp.uwa.edu.au/~pbourke/texture_colour/colourramp/


=head1 EXPORT

Exports by default:

  rgb2yiq(), yiq2rgb()
  rgb2cmy(), cmy2rgb()
  rgb2hls(), hls2rgb()
  rgb2hsv(), hsv2rgb()

Using the export tag C<:names>, exports the following additional methods:

  name2rgb()
  available_names()
  register_name()
  set_default_namespace()
  get_default_namespace()

Using the export tag C<:gradients>, exports the following additional methods:

  gradient()
  grad2rgb()
  available_gradients()
  register_gradient()


=head1 BUGS

=over 4

=item Input parameter validation

Most methods do I<not> explicitly validate that their arguments lie
in the valid range. 

=item Multiple namespaces

Names containing multiple colons may not be handled correctly.

=item Hue wrap-around

While hue should be restricted to 0..360, both C<hsv2rgb()> and
C<hls2rgb()> tolerate "moderate" violation of this constraint (up
to +/- 359). 

=back


=head1 TODO

=over 4

=item Perl Versions

This module has only been explicitly tested with Perl 5.8,
but nothing (should) prevent it from running fine with other
versions of Perl.

=item Additional color space conversions

For instance to and from XYZ, CIE, Luv; I<if desired!>.

=item Additional pre-defined gradients

Suggestions welcome!

=back

=head1 SEE ALSO

=head2 Related Modules

=over 4

=item Color::Rgb

Lookup of color values for names. Similar to the "names" methods
in this module. Requires F<X11/rgb.txt>.

=item Graphics::ColorNames

Lookup of color values for names. Similar to the "names" methods
in this module. Does I<not> require F<X11/rgb.txt>. Comes with
several sets of predefined color names (similar to this module).

=item Graphics::ColorObject

Color space conversions, including conversions to and from XYZ
and Luv. Object-oriented interface requires instantiation of a 
"color-object" for each color, which can then provide a 
representation of itself in all color spaces.

=item Color::Scheme

Generates pleasant color schemes (sets of colors).

=back


=head2 Standard Color Sets

=over 4

=item WWW/CSS

The 16 (or 17, including "orange") colors defined by the W3:
http://www.w3.org/TR/css3-color

=item SVG

The 138 unique named colors (140 normalized unique names) defined for 
SVG by the W3: http://www.w3.org/TR/SVG/types.html#ColorKeywords

=item X11

The 502 unique named colors (549 normalized unique names) defined by
the X11 libraries in /usr/lib/X11/rgb.txt on an X11 system

=back


=head2 Websites

=over 4

=item *

Poynton's Color FAQ: http://www.poynton.com/ColorFAQ.html

=item *

Paper on Color Conversion Algorithms: http://www.poynton.com/PDFs/coloureq.pdf

=item *

Paul Bourke's Webpage with many relevant details: http://local.wasp.uwa.edu.au/~pbourke/texture_colour/

=back


=head2 Books

=over 4

=item *

B<Computer Graphics - Principles and Practice>
by James D. Foley, Andries van Dam, Steven K. Feiner, John F. Hughes
(Second Edition in C, 1990, mult. print runs)

I<A comprehensive reference. B<Beware of typos in the algorithms!>>

=item *

B<Introduction to Computer Graphics>
by James D. Foley, Andries van Dam, Steven K. Feiner, John F. Hughes, 
Richard L. Phillips (1990, mult. print runs)

I<A textbook based on the previous title. Possibly more accessible and
available.>

=item *

B<Computer Graphics - C Version>
by Donald Hearn and M. Pauline Baker (2nd ed, 1997)

I<Another textbook.>

=back


=begin IMPLEMENTATION_NOTE

There were two intents that drove part of the design:
- I wanted to avoid dependency on other modules as much as possible.
  (This is a small module, it should not have all kinds of requirements
  on its installation environment.)
- Including the VALUES for the color names and gradients in the source
  file itself is certainly a somewhat contentious decision. Here is the
  rationale: By embedding the data directly, we avoid the issue of files
  missing at run-time and the required error detection and recovery code.
  The impact on loading the module (as compared to requiring the data
  files) should be minimal - the same amount of data gets read one way
  or the other. 
- And obviously I did not want to rely on the file rgb.txt to be there.
  That's fine for Unix, not ok elsewhere.

=end IMPLEMENTATION_NOTE


=head1 AUTHOR

Philipp K. Janert, E<lt>janert at ieee dot org E<gt>, http://www.beyondcode.org

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2006 by Philipp K. Janert

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.3 or,
at your option, any later version of Perl 5 you may have available.

=cut