Colour Colour System Library: John Walker
Colour Colour System Library: John Walker
1.   Introduction.
COLOUR
Colour  System  Library
John Walker
This program is in the public domain.
This   program  provides   tools   for   representing  colours,   interconverting  colour   spaces,   and  transforming
physical denitions  of colour (for example,  spectra or black  body  emission) into  perceptual  colour  metrics
such as the CIE tristimulus values, and thence to computer approximations such as RGB values for various
phosphors.
This  program  is  not  presently  used  by  the  analysis  suite;  it  is  only  exercised  by  its  own
built-in  test  program.   It  is  provided  as  part  of   the  eventual   goal   of   supporting  internal
plotting  without   the   need  to  invoke   GNUPLOT,   and  also  facilitate   visual   presentation  of
data  in  the  form  of  colour  (for  example,   one  might  wish  to  express  a  z  score  as  a  colour
representing  how  hot  it  was,  expressing  z  as  a  black  body  temperature).
References
Hunt, R.W.G. Measuring Colour.   West Sussex England:   Ellis Horwood Ltd., 1987.   (Distributed in the U.S.
by John Wiley Sons).   ISBN 0-470-20986-0.
Adobe  Systems,   Inc.   PostScript   Language  Reference  Manual,   3rd  ed.   Reading  Massachusetts:   Addison-
Wesley, 1999.   ISBN 0-201-37922-8.
Rossotti, Hazel.   Colour:   Why the World Isnt Grey.   Princeton:   Princeton University Press, 1983.   ISBN 0-
691-02386-7.
Judd,  Deane B. and G unter Wyszecki.   Color  in  Business,  Science,  and  Industry.   New York:   John Wiley
Sons, 1975.
Arvo, James ed.   Graphics Gems II. San Diego:   Academic Press, Inc., 1991, section III.6, Television Color
Encoding.   ISBN 0-12-064480-0.
Foley, J.D., A. van Dam, S.K. Feiner, and J.F. Hughes.   Computer  Graphics:   Principles  and  Practice,  2nd
ed. in C. Reading Massachusetts:   Addison-Wesley, 1996.   ISBN 0-201-84840-6.
Pantone, Inc.   PANTONE Process Color Imaging Guide.   Moonachie NJ: Pantone, Inc., 1990.
 colour_test.c   1 ) 
#dene  REVDATE   "1stFebruary2002"
See  also  section  40.
2   PROGRAM  GLOBAL  CONTEXT   COLOUR   2
2.   Program  global  context.
#include  "config.h"   /  System-dependent conguration /
 Preprocessor denitions )
 Application include les   4 )
 Colour system data tables   37 )
 Class implementations   5 )
3.   We export the class denitions for this package in the external le  colour.h that programs which use
this library may include.
 colour.h   3 ) 
#ifndef   COLOUR_HEADER_DEFINES
#dene  COLOUR_HEADER_DEFINES
#include  <math.h>   /  Make sure  math.h  is available /
#include  <iostream>
#include  <exception>
#include  <stdexcept>
#include  <string>
#include  <vector>
#include  <algorithm>
using  namespace  std;
#include  "bitmap.h"
#include  <stdio.h>   /  Needed to dene  FILE  for  gifout.h  /
#include  "gifout.h"
#include  "graphics.h"
#include  "pstampr.h"
#include  "psrtext.h"
 Class denitions   6 )
#endif
4.   The following include les provide access to external components of the program not dened herein.
 Application include les   4 ) 
#include  "colour.h"   /  Class denitions for this package /
This  code  is  used  in  section  2.
5.   The following classes are dened and their implementations provided.
 Class implementations   5 ) 
 Colour systems   17 )
This  code  is  used  in  section  2.
6   COLOUR   COLOUR  SYSTEM  PARENT  CLASS:  CSCOLOUR   3
6.   Colour  system  parent  class:   csColour.
This is the parent class of all colour classes.
 Class denitions   6 ) 
class  csColour 
public:
virtual  string  colourSystemName(void) = 0;   /  Return colour system name /
virtual  void  writeParameters (ostream  &of )
   /  Write description of colour system to output stream /
of   "Coloursystem:"  colourSystemName( )  "\n";
;
See  also  sections  7,  8,  9,  10,  11,  12,  and  33.
This  code  is  used  in  section  3.
4   SPECTRAL  COLOUR  SYSTEMS  PARENT  CLASS:  CSSPECTRALCOLOUR   COLOUR   7
7.   Spectral  colour  systems  parent  class:   csSpectralColour.
The most general of all our notions of colour is that of an function that maps wavelengths (given in metres)
onto intensities.   This form allows specication of any radiation in the electromagnetic spectrum.   Intensities
are  normalised  to  the  range  zero  to  one  by  dividing  the  intensity  for  a  given  frequency  by  the  integrated
intensity over the entire electromagnetic spectrum.
 Class denitions   6 ) +
class  csSpectralColour  :   public  csColour 
public:   /  Return normalised intensity for a given wavelength in metres /
virtual  double  getIntensity(double  waveLength) = 0;
/  Return true  if this is a pure (monochromatic) radiator, false   /   /  otherwise.   /
virtual  bool  isMonochromatic(void) = 0;
;
8   COLOUR   MONOCHROMATIC  SPECTRAL  COLOUR  SYSTEMS:  CSMONOCHROMATICCOLOUR   5
8.   Monochromatic  spectral  colour  systems:   csMonochromaticColour.
A  monochromatic   colour   is   a  abstract   notion  of   a  source   which  radiates   all   its   intensity  at   a  single
wavelength.   These  dont  really  exist  in  the  real   world,   since  fundamental   quantum  processes  will   always
spread the spectrum of any real radiator.
 Class denitions   6 ) +
class  csMonochromaticColour  :   public  csSpectralColour 
private:
double  wavelength;
public:
virtual  string  colourSystemName(void)
return "monochromaticspectral";
bool isMonochromatic(void)
return  true;
   /  Constructors and destructors /
csMonochromaticColour(void)
wavelength = 0.0;
csMonochromaticColour(double waveLength)
wavelength  = waveLength;
   /  Class-specic methods /
void  setWavelength(double  waveLength)
wavelength = waveLength;
double getWavelength(void)
return wavelength;
;
6   BLACK:  CSSPECTRALBLACK   COLOUR   9
9.   Black:   csSpectralBlack.   Black is the absence of colour.   We dene it as a spectral colour with zero
intensity at any wavelength.
 Class denitions   6 ) +
class  csSpectralBlack  :   public  csSpectralColour 
public:
virtual  string  colourSystemName(void)
return "blackspectral";
return 0.0;
bool isMonochromatic(void)
return false;
;
10.   White:   csSpectralWhite.   A theoretical source of white noise has equal power at all wavelengths.
Such a source is impossible in reality since the energy ux would be innite.
 Class denitions   6 ) +
class  csSpectralWhite  :   public  csSpectralColour 
public:
virtual  string  colourSystemName(void)
return "whitespectral";
return 1.0;
bool isMonochromatic(void)
return false;
;
11   COLOUR   PLANCKIAN  BLACK  BODY:  CSBLACKBODY   7
11.   Planckian black body:   csBlackBody.   Dene the colour of a Planckian black body radiator with
a given colour temperature.
 Class denitions   6 ) +
class  csBlackBody  :   public  csSpectralColour 
private:   /  Change temperature to class with system conversions /
double  temperature;   /  Temperature of radiator (
K) /
public:
double  getIntensity(double  waveLength)
return 0.0;
bool isMonochromatic(void)
return  false;
   /  Constructors and destructors /
csBlackBody(void)
temperature = 0.0;
csBlackBody(double temp)
temperature  = temp;
   /  Class-specic methods /
void  setTemperature(double  temp)
temperature = temp;
double getTemperature(void)
return temperature;
5
(e
C
2
/(T)
1)
1
8   PLANCKIAN  BLACK  BODY:  CSBLACKBODY   COLOUR   11
where:
C
1
 = 3.74183e 16Wm
2
C
2
 = 1.4388e 2m
K
/
return  3.74183  10
16
/((wl  wl  wl  wl  wl )  (exp(1.4388  10
2
/(wl  temperature)) 1));
;
12   COLOUR   DEVICE  COLOUR  SYSTEMS  PARENT  CLASS:  CSDEVICECOLOUR   9
12.   Device  colour  systems  parent  class:   csDeviceColour.   This class is the parent of all classes
which specify colour in a device-specic way, assuming a particular set of illuminants which are summed to
form the colour.
 Class denitions   6 ) +
class  csDeviceColour  :   public  csColour 
public:
 Device colour fundamental methods   13 )
 Device colour derived methods   14 )
 Device colour system conversion utilities   15 )
;
13.   The  following  three  must-implement  methods  allow  retrieving  the  colour  in  any  of   the  three  fun-
damental   device  colour  spaces:   RGB  (additive),   CMYK  (subtractive),   or  Greyscale.   Typically,   a  specic
device colour class will store the colour in one of these forms and implement the other retrieval methods by
converting the colour representation to that form.
 Device colour fundamental methods   13 ) 
virtual  void  asRGB(double  &r, double  &g, double  &b) = 0;
virtual  void  asCMYK(double  &c, double  &m, double  &y, double  &k) = 0;
virtual  void  asGreyScale(double  &g) = 0;
This  code  is  used  in  section  12.
14.   The   following  methods   allow  retrieval   of   a  device   colour   in  other   colour   mapping  spaces.   The
csDeviceColour   class   implements   methods   for   these  functions   which  provide  default   denitions   which
use  the  subclass  asRGB  method  to  obtain  RGB  components  which  are  the  converted  into  the  requested
colour space.
 Device colour derived methods   14 ) 
virtual  void  asHSV (double  &h, double  &s, double  &v);
virtual  void  asHLS (double  &h, double  &l, double  &s);
virtual  void  asYIQ(double  &y, double  &i, double  &q);
virtual  void  asYUV (double  &y, double  &u, double  &v);
virtual  void  asSMPTE(double  &y, double  &Pb, double  &Pr );
virtual  void  asCMY (double  &c, double  &m, double  &y);
This  code  is  used  in  section  12.
10   DEVICE  COLOUR  SYSTEMS  PARENT  CLASS:  CSDEVICECOLOUR   COLOUR   15
15.   The  following  static  methods  of   the  csDeviceColour  class  provide  interconversions  of   common
colour systems using the convention that colour components  c are double  values 0  c  1.
 Device colour system conversion utilities   15 ) 
protected:
static  double  hlsval (double  n1 , double  n2 , double  hue);
public:
static  void  hsv rgb(double  h, double  s, double  v, double r, double g, double b);
/  HSV  RGB /
static  void  rgb hsv (double  r, double  g, double  b, double h, double s, double v);
/  RGB  HSV /
static  void  rgb hls (double  r, double  g, double  b, double h, double l, double s);
/  RGB  HLS /
static  void  hls rgb(double  h, double  l, double  s, double r, double g, double b);
/  HLS  RGB /
static  void  rgb yiq (double  r, double  g, double  b, double y, double i, double q);
/  RGB  YIQ /
static  void  yiq rgb(double  y, double  i, double  q, double r, double g, double b);
/  YIQ  RGB /
static  void  rgb yuv (double  r, double  g, double  b, double y, double u, double v);
/  RGB  YUV /
static  void  yuv rgb(double  y, double  u, double  v, double r, double g, double b);
/  YUV  RGB /
static  void  rgb smpte 204M (double  r, double  g, double  b, double y, double Pb, double Pr );
/  RGB  SMPTE 204M /
static  void  smpte 204M rgb(double  y, double  Pb, double  Pr , double r, double g, double b);
/  SMPTE 204M  RGB /
static  void  rgb cmy(double  r, double  g, double  b, double c, double m, double y);
/  RGB  CMY /
static  void  cmy rgb(double  c, double  m, double  y, double r, double g, double b);
/  CMY  RGB /
static  void  cmy cmyk (double  c, double  m, double  y, double oc, double om, double oy, double
ok );   /  CMY  CMYK /
static  void  cmyk cmy(double  c, double  m, double  y, double  k, double oc, double om, double
oy);   /  CMYK  CMY /
This  code  is  used  in  section  12.
16.   Colour system conversion utilities.   The following static methods of the csDeviceColour class
implement  interconversions  of  common  colour  systems  using  the  convention  that  colour  components  c  are
double  values 0  c  1.
17   COLOUR   COLOUR  SYSTEM  CONVERSION  UTILITIES   11
17.   HSV_RGB:   Convert  HSV  colour  specication  to  RGB  intensities.   Hue  (h)  is  specied  as  a  double
value from 0 to 360
, Saturation (s) and Intensity (v) as doubles from 0 to 1.   The (r, g, b) components are
returned as doubles from 0 to 1.
 Colour systems   17 ) 
void  csDeviceColour :: hsv rgb(double  h, double  s, double  v, double r, double g, double b)
int  i;
double  f,   p,   q,   t;
if   (s  0) 
r = g = b = v;
else  
if   (h  360.0)   h = 0;
h /= 60.0;
i = (int)  h;
f  = h i;
p = v  (1.0 s);
q = v  (1.0 (s  f));
t = v  (1.0 (s  (1.0 f)));
assert (i  0  i  5);
switch  (i) 
case  0: r = v;
g = t;
b = p;
break;
case  1: r = q;
g = v;
b = p;
break;
case  2: r = p;
g = v;
b = t;
break;
case  3: r = p;
g = q;
b = v;
break;
case  4: r = t;
g = p;
b = v;
break;
case  5: r = v;
g = p;
b = q;
break;
See  also  sections  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,  34,  35,  and  36.
This  code  is  used  in  section  5.
12   COLOUR  SYSTEM  CONVERSION  UTILITIES   COLOUR   18
18.   RGB_HSV:   Map  r, g, b intensities in the range from 0 to 1 into Hue (h), Saturation (s), and Value (v):
Hue from 0 to 360
double  imax  = max (r, max (g, b)),   imin  = min(r, min(g, b)),   rc,   gc,   bc;
v = imax ;
if   (imax ,= 0) s = (imax imin)/imax ;
else  s = 0;
if   (s  0) 
h = 1;
else  
rc  = (imax r)/(imax imin);
gc  = (imax g)/(imax imin);
bc  = (imax b)/(imax imin);
if   (r  imax ) h = bc gc;
else  if   (g  imax ) h = 2.0 + rc bc;
else  h = 4.0 + gc rc;
h = 60.0;
if   (h < 0.0) h += 360.0;
double  imax  = max (r, max (g, b)),   imin  = min(r, min(g, b)),   rc,   gc,   bc;
l = (imax  + imin)/2;
if   (imax  imin) 
s = 0;
h = 1;
else  
if   (l  0.5) s = (imax imin)/(imax  + imin);
else  s = (imax imin)/(2.0 imax imin);
rc  = (imax r)/(imax imin);
gc  = (imax g)/(imax imin);
bc  = (imax b)/(imax imin);
if   (r  imax ) h = bc gc;
else  if   (g  imax ) h = 2.0 + rc bc;
else  h = 4.0 + gc rc;
h = 60.0;
if   (h < 0) h += 360.0;
; Lightness (l) and Saturation (s) as doubles from 0 to 1.   The RGB components are
returned as doubles from 0 to 1.
 Colour systems   17 ) +
double  csDeviceColour :: hlsval (double  n1 , double  n2 , double  hue)
else  
return  n1 ;
double  m1 ,   m2 ;
if   (l  0.5)   m2  = l  (1.0 +s);
else   m2  = l +s (l  s);
m1  = 2  l m2 ;
if   (s  0) 
r = g = b = l;
else  
r = hlsval (m1 , m2 , h + 120.0);
g = hlsval (m1 , m2 , h);
b = hlsval (m1 , m2 , h 120.0);
Y
I
Q
R
G
B
 Colour systems   17 ) +
void  csDeviceColour :: rgb yiq (double  r, double  g, double  b, double y, double i, double q)
i = ai ;
q = aq ;
22.   YIQ_RGB:   Convert YIQ colour specication,   Y, I, Q given as  doubles:   0   Y   1, 0.6   I  0.6,
0.52   Q  0.52,  to  R, G, B  intensities in the range from 0 to 1.   The matrix below is the inverse of the
RGB_YIQ  matrix above.   YIQ is the encoding used in NTSC television.
R
G
B
Y
I
Q
 Colour systems   17 ) +
void  csDeviceColour :: yiq rgb(double  y, double  i, double  q, double r, double g, double b)
Y
U
V
R
G
B
 Colour systems   17 ) +
void  csDeviceColour :: rgb yuv (double  r, double  g, double  b, double y, double u, double v)
u = au;
v = av ;
24.   YUV_RGB:   Convert YUV colour specication,   Y, U, V   given as doubles, to  R, G, B  intensities in the
range from 0 to 1.   The matrix below is the inverse of the rgb yuv  matrix above.   YUV is the encoding used
by PAL television.
R
G
B
Y
U
V
 Colour systems   17 ) +
void  csDeviceColour :: yuv rgb(double  y, double  u, double  v, double r, double g, double b)
Y
P
b
P
r
R
G
B
 Colour systems   17 ) +
void  csDeviceColour :: rgb smpte 204M (double  r, double  g, double  b, double y, double
Pb, double Pr )
Pb  = aPb;
Pr  = aPr ;
26.   SMPTE_204M_RGB:   Convert a colour specied using the SMPTE reference phosphors as given in the
SMPTE  204M  (1988)  specication  for  HDTV  to  R, G, B  intensities  in  the  range  from  0  to  1.   The  matrix
below is the inverse of the  RGB_SMPTE_204M  matrix above.
R
G
B
Y
P
b
P
r
 Colour systems   17 ) +
void  csDeviceColour :: smpte 204M rgb(double  y, double  Pb, double  Pr , double r, double
g, double b)
C
M
Y
1
1
1
R
G
B
 Colour systems   17 ) +
void  csDeviceColour :: rgb cmy(double  r, double  g, double  b, double c, double m, double y)
c = 1.0 r;
m = 1.0 g;
y = 1.0 b;
28.   CMY_RGB:   Convert CMY colour specication,  C, M, Y  ranging from 0 to 1, to  R, G, B  colour speci-
cation, also ranging from 0 to 1.
R
G
B
1
1
1
C
M
Y
 Colour systems   17 ) +
void  csDeviceColour :: cmy rgb(double  c, double  m, double  y, double r, double g, double b)
r = 1.0 c;
g = 1.0 m;
b = 1.0 y;
29.   cmy cmyk :   Convert  CMY  colour  specication,   C, M, Y   ranging  from  0  to  1,   to  C, M, Y, K  colour
specication, also ranging from 0 to 1.   K  is the black ink component in four colour printing processes.   We
convert  C, M, Y   by  computing  K  =  min(C, M, Y ),   then  subtracting  that  value  from  each  of  the  C, M, Y
components.
 Colour systems   17 ) +
void  csDeviceColour :: cmy cmyk (double  c, double  m, double  y, double oc, double om, double
oy, double ok )
oc  = c +k;
om  = m+k;
oy  = y +k;
double  r,   g,   b;
asRGB(r, g, b);
rgb hsv (r, g, b, &h, &s, &v);
void csDeviceColour :: asHLS (double &h, double &l, double &s) / To HLS /
double  r,   g,   b;
asRGB(r, g, b);
rgb hls (r, g, b, &h, &l, &s);
double  r,   g,   b;
asRGB(r, g, b);
rgb yiq (r, g, b, &y, &i, &q);
void csDeviceColour :: asYUV (double &y, double &u, double &v) / To YUV /
double  r,   g,   b;
asRGB(r, g, b);
rgb yuv (r, g, b, &y, &u, &v);
double  r,   g,   b;
asRGB(r, g, b);
rgb smpte 204M (r, g, b, &y, &Pb, &Pr );
void csDeviceColour :: asCMY (double &c, double &m, double &y) / To CMY /
double  r,   g,   b;
asRGB(r, g, b);
rgb cmy(r, g, b, &c, &m, &y);
name  = c name;
xRed  = c xRed ;
yRed  = x yRed ;
xGreen  = c xGreen;
yGreen  = c yGreen;
xBlue  = c xBlue;
yBlue  = c yBlue;
xWhite  = c xWhite;
yWhite  = c yWhite;
void  xyz to rgb(double  xc, double  yc, double  zc, double r, double g, double b);
static  bool  inside gamut (double  r, double  g, double  b);
bool   constrain rgb(double x, double y, double z, double r, double g, double b, bool
interpwp  = false);
protected:
static  double  clamp(double  v, double  l, double  h)
;
extern  CIEColourSystem  NTSCsystem,   EBUsystem,   SMPTEsystem,   HDTVsystem,   CIEsystem;
/  Predened standard colour systems /
34   COLOUR   CIE  COLOUR  SYSTEMS   23
34.   xyz to rgb:   Given  an  additive  tricolour   system  dened  by  the  CIE  x  and  y  chromaticities   of   its
three primaries  (z  is derived trivially as 1  (x + y)),  and a desired chromaticity (x
c
, y
c
, z
c
) in CIE space,
determine the contribution of each primary in a linear combination which sums to the desired chromaticity.
If the requested chromaticity falls outside the Maxwell triangle (colour gamut) formed by the three primaries,
one of the  r,  g, or  b weights will be negative.   Use inside gamut   to test for a valid colour and constrain rgb
to desaturate an outside-gamut colour to the closest representation within the available gamut.
 Colour systems   17 ) +
void CIEColourSystem:: xyz to rgb(double xc, double yc, double zc, double r, double g, double
b)
35.   inside gamut :   Test whether a requested colour is within the gamut achievable with the primaries of
the current colour system.   This amounts simply to testing whether all the primary weights are non-negative.
 Colour systems   17 ) +
bool  CIEColourSystem:: inside gamut (double  r, double  g, double  b)
return (r 0) (g 0) (b 0);
else  
xw  = (xRed + xGreen + xBlue)/3;
yw  = (yRed + yGreen + yBlue)/3;
   /  Yes.   Find the RGB mixing weights of the white point (we assume the white point is in the
gamut!).   /
xyz to rgb(xw, yw, 1 (xw  + yw), &wr , &wg , &wb);
/  Find the primary with negative weight and calculate the parameter of the point on the vector
from the white point to the original requested colour in RGB space.   /
if   (r < g  r < b) 
par  = wr /(wr r);
else  
par  = wb/(wb b);
   /  Since XYZ space is a linear transformation of RGB space, we can nd the XYZ space
coordinates of the point where the edge of the gamut intersects the vector from the white
point to the original colour by multiplying the parameter in RGB space by the dierence
vector in XYZ space.   /
x = clamp(xw  + par  (x xw), 0, 1);
y = clamp(yw  + par  (y yw), 0, 1);
z = clamp(1 (x +y), 0, 1);   /  Now nally calculate the gamut-constrained RGB weights.   /
r = clamp(wr  + par  (r wr ), 0, 1);
g = clamp(wg + par  (g wg ), 0, 1);
b = clamp(wb + par  (b wb), 0, 1);
return  true;   /  Colour modied to t RGB gamut /
41.   We   use   getopt   to  process   command  line   options.   This   permits   aggregation  of   options   without
arguments and both  darg and  d  arg syntax.
 Process command-line options   41 ) 
while  ((opt  = getopt (argc, argv , "nu:")) ,= 1) 
switch  (opt ) 
case  u:   /   u  Print how-to-call information /
case  ?:   usage( );
return  0;
case  :   /     Extended options /
switch  (optarg [0]) 
case  c:   /   copyright  /
cout  "Thisprogramisinthepublicdomain.\n";
return  0;
case  h:   /   help  /
usage( );
return  0;
case  v:   /   version  /
cout  PRODUCT  ""  VERSION  "\n";
cout  "Lastrevised:"  REVDATE  "\n";
cout  "Thelatestversionisalwaysavailable\n";
cout  "athttp://www.fourmilab.ch/eggtools/eggshell\n";
return  0;