Listing 1: Part of the clrspace class hierarchy

// clrspace.h

#if !defined(CLRSPACE_H)
#define CLRSPACE_H

#include <math.h> // for sqrt(), atan2(), and M_PI

// Some color spaces in current use including transformations
// between them. Type T is assumed to be a floating point type
// (float, double, or long double). This library can be extended
// to integer types by writing class specializations.

namespace clrspace {

template <class T>
class Color1 { // base class for 1-D color spaces
protected:
    T
    _zeta1; // coordinate value

public:
    Color1() {}
    Color1 (T izeta1) : _zeta1(izeta1) {}

    T // generic coordinate name (mostly for internal use)
    zeta1() const {return _zeta1;}
};

template <class T>
class Color2 : public Color1<T> { // base class for
protected:                        // 2-D color spaces
    T
    _zeta2; // second coordinate

public:
    Color2() {}
    Color2 (T izeta1, T izeta2) :
        Color1<T>(izeta1), _zeta2(izeta2) {}

    T // generic coordinate name (mostly for internal use)
    zeta2() const {return _zeta2;}
};

template <class T>
class Color3 : public Color2<T> { // base class for
protected:                        // 3-D color spaces
    T
    _zeta3; // third coordinate

public:
    Color3() {}
    Color3 (T izeta1, T izeta2, T izeta3) :
       Color2<T>(izeta1,izeta2), _zeta3(izeta3) {}

    T // generic coordinate name (mostly for internal use)
    zeta3() const {return _zeta3;}
};

// by definition, all "colorimetric" color spaces can be
// transformed to XYZ
template <class T>
class XYZ : public Color3<T> { // CIE (X,Y,Z) space
public:
    XYZ() {}
    XYZ (T ix, T iy, T iz) : Color3<T>(ix,iy,iz) {}

    T X() const {return _zeta1;}
    T Y() const {return _zeta2;}
    T Z() const {return _zeta3;}
};

// ~~~~~Brightness and Lightness Spaces~~~~~

template <class T>
class CIE_L; // forward reference

// two colors with equal Y values appear equally bright
template <class T>
class CIE_Y : public Color1<T> { // same as Y() from XYZ
public:
    CIE_Y() {}
    CIE_Y (T iY) : Color1<T>(iY) {}
    CIE_Y (const CIE_L<T> &L, const CIE_Y<T> &Yn); // Yn == 
                                                   // white ref.
    T  // read access to CIE Y coordinate
    Y() const {return _zeta1;}
};

// lightness is brightness judged in comparison to a
// white reference
template <class T>
class CIE_L : public Color1<T> { // CIE L* lightness metric
public:
    CIE_L() {}
    CIE_L (T iL) : Color1<T>(iL) {} 
    CIE_L (const CIE_Y<T> &Y, const CIE_Y<T> &Yn); // Yn ==
                                                   // white ref.
    T // read access to CIE L* coordinate
    L() const {return _zeta1;}
};

//~~~~~chromaticity coordinates~~~~~

// a chromaticity space is normalized so that the three
// coordinates add to 1
template <class T>
class ChromaticitySpace : public Color2<T> { // only two
                                             // coordinates are
                                             // stored
protected:
    T // compute the redundant third coordinate
    zeta3() const;

public:
    ChromaticitySpace() {}
    ChromaticitySpace (T izeta1, T izeta2) :
        Color2<T>(izeta1,izeta2) {}
};

// normalized XYZ space
template <class T>
class xy : public ChromaticitySpace<T> { // CIE xyz color space
public:
    xy() {}
    xy (T ix, T iy) : ChromaticitySpace<T>(ix,iy) {}
    xy (const uvPrime<T> &); // convert from CIE u'v'w'
    xy (const XYZ<T> &xyz); // convert from CIE XYZ

    XYZ<T> // convert to CIE XYZ
    toXYZ (const CIE_Y<T> &Y) const; // argument means no
                                     // operator XYZ<T>
    T x() const {return _zeta1;} // read access to x
    T y() const {return _zeta2;} // read access to y
    T z() const {return zeta3();} // compute 1 - x - y
};

// more perceptually flat than xy<T>
template <class T>
class uvPrime : public ChromaticitySpace<T> { // CIE u'v'w'
                                              // color space
public:
    uvPrime() {}
    uvPrime (T iuPrime, T ivPrime) : 
        ChromaticitySpace<T>(iuPrime,ivPrime) {}
    uvPrime (const xy<T> &); // convert from CIE xyz
    uvPrime (const XYZ<T> &xyz); // convert from CIE XYZ

    XYZ<T> toXYZ (const CIE_Y<T> &Y) const // convert to CIE XYZ
        {return xy<T>(*this).toXYZ(Y);}

    T uPrime() const {return _zeta1;} // read access to uPrime
    T vPrime() const {return _zeta2;} // read access to vPrime
    T wPrime() const {return zeta3();} // compute 1-uPrime-vPrime
};

// ~~~~~RGBBase family of color spaces~~~~~

// RGBBase means red, green, blue
template <class T>
class RGBBase : public Color3<T> { // uncalibrated (R,G,B) space
public:
    RGBBase() {}
    RGBBase (T ir, T ig, T ib) : Color3<T>(ir,ig,ib) {}

    T R() const {return _zeta1;}
    T G() const {return _zeta2;}
    T B() const {return _zeta3;}
};

// An RGBConv<T,CONV> is an RGBBase<T> having a static CONV
template <class T, class CONV> // CONV derived from ConvertRGB<T>
class RGBConv : public RGBBase<T> { // base class for RGBLinear
                                    // and RGBGamma
protected:
    static const CONV // XYZ and Gamma conversions
    _conv;

public:
    RGBConv() {}
    RGBConv (T ir, T ig, T ib) : RGBBase<T>(ir,ig,ib) {}
    RGBConv (const RGBBase<T> &rgb) : RGBBase<T>(rgb) {}
};

template <class T, class CONV>
class RGBGamma; // forward reference

// RGBLinear<T,CONV> is a base class for a linear-light
// RGBBase space
template <class T, class CONV>
class RGBLinear : public RGBConv<T,CONV> {
protected:
    RGBLinear (const RGBBase<T> &rgb) : // for internal use only
       RGBConv<T,CONV>(rgb.R(),rgb.G(),rgb.B()) {}

public:
    RGBLinear() {}
    RGBLinear (T ir, T ig, T ib) : RGBConv<T,CONV>(ir,ig,ib) {}
    RGBLinear (const RGBGamma<T,CONV> &rgbgam) :
        RGBConv<T,CONV>(_conv.fromGamma(rgbgam)) {}
    RGBLinear (const XYZ<T> &xyz) :
        RGBConv<T,CONV>(_conv.fromXYZ(xyz)) {}

    XYZ<T> // explicit conversion to XYZ
    toXYZ() const {return _conv.toXYZ(*this);}

    operator // implicit converstion to XYZ
    XYZ<T> () const {return toXYZ();}
};

// RGBLinear<T,CONV> is a base class for a gamma-corrected
// RGBBase space
template <class T, class CONV>
class RGBGamma : public RGBConv<T,CONV> { // gamma corrected
                                          // (R,G,B) space
protected:
    RGBGamma (const RGBBase<T> &rgb) :
       RGBConv<T,CONV>(rgb.R(),rgb.G(),rgb.B()) {}

public:
    RGBGamma() {}
    RGBGamma (T ir, T ig, T ib) : RGBConv<T,CONV>(ir,ig,ib) {}
    RGBGamma (const RGBLinear<T,CONV> &rgblin) :
        RGBConv<T,CONV>(_conv.toGamma(rgblin)) {}

    RGBGamma (const XYZ<T> &xyz) :
        RGBConv<T,CONV>(_conv.toGamma(_conv.fromXYZ(xyz))) {}

    XYZ<T> toXYZ() const // explicit conversion to XYZ
        {return _conv.toXYZ(_conv.fromGamma(*this));}

    operator XYZ<T> () const // implicit conversion to XYZ
        {return toXYZ();} 
};

//~~~~~RGB709<T>: an example use of RGBLinear<T> and ~~~~~
//     RGBGamma<T>

// step 1: derive a conversion class with a default constructor
template <class T> // needs to be changed if T is an integer type
class Convert_RGB709 : public ConvertRGB<T> {
public:
    // RGB709 is a proposed HDTV standard (ITU.BT-709)
    Convert_RGB709() : ConvertRGB<T>
    (
        xy<T>(0.64,0.33), // red chromaticity
        xy<T>(0.30,0.60), // green chromaticity
        xy<T>(0.15,0.06), // blue chromaticity
        D65<T>(),         // white point chromaticity
        1.0/0.45,         // gamma
        0.018             // epsilon
    ) {}
};

template <class T>
class RGB709; // forward reference

// step 2: derive a new linear-light RGBBase space using the
// converter from step 1
template <class T>
class RGB709Linear : public RGBLinear<T,Convert_RGB709<T> > {
public:
    RGB709Linear() {}

    RGB709Linear (T ir, T ig, T ib) :
        RGBLinear<T,Convert_RGB709<T> >(ir,ig,ib) {}

    RGB709Linear (const RGB709<T> &gamma) :
        RGBLinear<T,Convert_RGB709<T> >(gamma) {}

    RGB709Linear (const XYZ<T> &xyz) :
        RGBLinear<T,Convert_RGB709<T> >(xyz) {}
};

// step 3: derive an associated gamma-corrected RGBBase space
template <class T>
class RGB709 : public RGBGamma<T,Convert_RGB709<T> > {
public:
    RGB709() {}

    RGB709 (T ir, T ig, T ib) :
        RGBGamma<T,Convert_RGB709<T> >(ir,ig,ib) {}

    RGB709 (const RGB709Linear<T> &linear) :
        RGBGamma<T,Convert_RGB709<T> >(linear) {}

    RGB709 (const XYZ<T> &xyz) :
        RGBGamma<T,Convert_RGB709<T> >(xyz) {}
};

#endif
/* End of File */