/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef ECXX_CTL_GFX_COLOR_TYPES_H
#define ECXX_CTL_GFX_COLOR_TYPES_H


#include "ColorInterpolator.h"
#include "ColorSpace.h"
#include "ColorXPolicy.h"


namespace eCxx {


//
// Basic color structures
//
// # Multibyte integers (16 bits or more) for color data that are stored in program memory must
//   be stored in little endian mode.
// # Color data in program memory must be stored in their standard/natural order, such as: HSV,
//   HSL, RGB, RGB+W, and X+RGB (not in BGR, W+RGB, etc.).
//
template <typename Value0T_>
struct Color1 {
    static constexpr uint8_t ChannelCount = 1;
    static constexpr uint8_t RawByteSize  = sizeof(Value0T_);

    static constexpr uint8_t A = CCI::_A; // Denotes all channel when used in lerpColor<ChannelIndexes, ColorT>()

    using Value0T = Value0T_;

    Value0T c0;

    constexpr Color1() : c0(0) {}
    constexpr Color1(Value0T c0_) : c0(c0_) {}

    inline bool operator==(const Color1& cmp) const
    { return c0 == cmp.c0; }

    template <typename PGMPointerT>
    inline void pgmGet(PGMPointerT ptr)
    { c0 = PGMReadValue<Value0T>()(ptr.p); }

    template <typename PGMPointerT>
    inline void pgmGetInc(PGMPointerT& ptr)
    { c0 = PGMReadValue<Value0T>()(ptr.p); ptr.p += sizeof(Value0T); }
};


template <typename Value0T_, typename Value1T_, typename Value2T_>
struct Color3 {
    static constexpr uint8_t ChannelCount = 3;
    static constexpr uint8_t RawByteSize  = sizeof(Value0T_) + sizeof(Value1T_) + sizeof(Value2T_);

    static constexpr uint8_t A = CCI::_A; // Denotes all channel when used in lerpColor<ChannelIndexes, ColorT>()

    using Value0T = Value0T_;
    using Value1T = Value1T_;
    using Value2T = Value2T_;

    Value0T c0;
    Value1T c1;
    Value2T c2;

    constexpr Color3() : c0(0), c1(0), c2(0) {}
    constexpr Color3(Value0T c0_, Value1T c1_, Value2T c2_) : c0(c0_), c1(c1_), c2(c2_) {}

    inline bool operator==(const Color3& cmp) const
    { return c0 == cmp.c0 && c1 == cmp.c1 && c2 == cmp.c2; }

    template <typename PGMPointerT>
    inline void pgmGet(PGMPointerT ptr)
    {
        c0 = PGMReadValue<Value0T>()(ptr.p); ptr.p += sizeof(Value0T);
        c1 = PGMReadValue<Value1T>()(ptr.p); ptr.p += sizeof(Value1T);
        c2 = PGMReadValue<Value2T>()(ptr.p);
    }

    template <typename PGMPointerT>
    inline void pgmGetInc(PGMPointerT& ptr)
    {
        c0 = PGMReadValue<Value0T>()(ptr.p); ptr.p += sizeof(Value0T);
        c1 = PGMReadValue<Value1T>()(ptr.p); ptr.p += sizeof(Value1T);
        c2 = PGMReadValue<Value2T>()(ptr.p); ptr.p += sizeof(Value2T);
    }
};


template <typename Value0T_, typename Value1T_, typename Value2T_, typename Value3T_>
struct Color4 {
    static constexpr uint8_t ChannelCount = 4;
    static constexpr uint8_t RawByteSize  = sizeof(Value0T_) + sizeof(Value1T_) + sizeof(Value2T_) + sizeof(Value3T_);

    static constexpr uint8_t A = CCI::_A; // Denotes all channel when used in lerpColor<ChannelIndexes, ColorT>()

    using Value0T = Value0T_;
    using Value1T = Value1T_;
    using Value2T = Value2T_;
    using Value3T = Value3T_;

    Value0T c0;
    Value1T c1;
    Value2T c2;
    Value3T c3;

    constexpr Color4() : c0(0), c1(0), c2(0), c3(0) {}
    constexpr Color4(Value0T c0_, Value1T c1_, Value2T c2_, Value3T c3_) : c0(c0_), c1(c1_), c2(c2_), c3(c3_) {}

    inline bool operator==(const Color4& cmp) const
    { return c0 == cmp.c0 && c1 == cmp.c1 && c2 == cmp.c2 && c3 == cmp.c3; }

    template <typename PGMPointerT>
    inline void pgmGet(PGMPointerT ptr)
    {
        c0 = PGMReadValue<Value0T>()(ptr.p); ptr.p += sizeof(Value0T);
        c1 = PGMReadValue<Value1T>()(ptr.p); ptr.p += sizeof(Value1T);
        c2 = PGMReadValue<Value2T>()(ptr.p); ptr.p += sizeof(Value2T);
        c3 = PGMReadValue<Value3T>()(ptr.p);
    }

    template <typename PGMPointerT>
    inline void pgmGetInc(PGMPointerT& ptr)
    {
        c0 = PGMReadValue<Value0T>()(ptr.p); ptr.p += sizeof(Value0T);
        c1 = PGMReadValue<Value1T>()(ptr.p); ptr.p += sizeof(Value1T);
        c2 = PGMReadValue<Value2T>()(ptr.p); ptr.p += sizeof(Value2T);
        c3 = PGMReadValue<Value3T>()(ptr.p); ptr.p += sizeof(Value3T);
    }
};


template <typename ImplementationT>
struct BasicRGBColor {
    static constexpr uint8_t MaxRValue  = 255;
    static constexpr uint8_t MaxGValue  = 255;
    static constexpr uint8_t MaxBValue  = 255;

    static constexpr uint8_t MaxC0Value = 255;
    static constexpr uint8_t MaxC1Value = 255;
    static constexpr uint8_t MaxC2Value = 255;
    static constexpr uint8_t MaxC3Value =   0;

    constexpr BasicRGBColor() {}

    template <typename SrcColorT>
    inline const ImplementationT& assignFrom(const SrcColorT& src)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        src.toRGB( impl.r(), impl.g(), impl.b() );
        return impl;
    }

    inline void toW(uint8_t& w) const
    {
        const ImplementationT& impl = *static_cast<const ImplementationT*>(this);

        rgb2w( w, impl.r(), impl.g(), impl.b() );
    }

    inline void toRGB(uint8_t& r, uint8_t& g, uint8_t& b) const
    {
        const ImplementationT& impl = *static_cast<const ImplementationT*>(this);

        r = impl.r();
        g = impl.g();
        b = impl.b();
    }

    inline void toRGBW(uint8_t& r, uint8_t& g, uint8_t& b, uint8_t& w) const
    {
        const ImplementationT& impl = *static_cast<const ImplementationT*>(this);

        rgb2rgbw( r, g, b, w, impl.r(), impl.g(), impl.b() );
    }

    inline void fromHTMLHex(uint32_t htmlHex6)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        html2rgb( impl.r(), impl.g(), impl.b(), htmlHex6 );
    }

    inline void fromHTMLHexString(const char* htmlHex6)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        html2rgb( impl.r(), impl.g(), impl.b(), htmlHex6 );
    }

    template <uint8_t ChannelIndexes>
    inline void adjust(int8_t adjustmentFactor)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        if(ChannelIndexes & ImplementationT::R) impl.r() = eCxx::adjustColor( impl.r(), adjustmentFactor );
        if(ChannelIndexes & ImplementationT::G) impl.g() = eCxx::adjustColor( impl.g(), adjustmentFactor );
        if(ChannelIndexes & ImplementationT::B) impl.b() = eCxx::adjustColor( impl.b(), adjustmentFactor );
    }

    inline void adjust(int8_t adjustmentFactor)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        impl.r() = eCxx::adjustColor( impl.r(), adjustmentFactor );
        impl.g() = eCxx::adjustColor( impl.g(), adjustmentFactor );
        impl.b() = eCxx::adjustColor( impl.b(), adjustmentFactor );
    }

    template <uint8_t ChannelIndexes>
    inline void scale(int8_t scaleFactor)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        if(ChannelIndexes & ImplementationT::R) impl.r() = eCxx::scaleColor( impl.r(), scaleFactor );
        if(ChannelIndexes & ImplementationT::G) impl.g() = eCxx::scaleColor( impl.g(), scaleFactor );
        if(ChannelIndexes & ImplementationT::B) impl.b() = eCxx::scaleColor( impl.b(), scaleFactor );
    }

    inline void scale(uint8_t scaleFactor)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        impl.r() = eCxx::scaleColor( impl.r(), scaleFactor );
        impl.g() = eCxx::scaleColor( impl.g(), scaleFactor );
        impl.b() = eCxx::scaleColor( impl.b(), scaleFactor );
    }

    template <uint8_t N>
    inline void cie1931lcc(CIE1931LVal (&lut)[N])
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        impl.r() = eCxx::cie1931lcc( lut, impl.r() );
        impl.g() = eCxx::cie1931lcc( lut, impl.g() );
        impl.b() = eCxx::cie1931lcc( lut, impl.b() );
    }

    template <uint8_t N>
    inline void cie1931lut(CIE1931LVal (&lut)[N])
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        impl.r() = eCxx::cie1931lut( lut, impl.r() );
        impl.g() = eCxx::cie1931lut( lut, impl.g() );
        impl.b() = eCxx::cie1931lut( lut, impl.b() );
    }

    inline void preXPolicy()
    {}

    inline void postXPolicy()
    {}

    static inline void preXPolicy(uint8_t*)
    {}

    static inline void postXPolicy(uint8_t*)
    {}

    static inline const ImplementationT fromPGMCharPointerRaw(_PGMCharPointer ptr3)
    {
        ImplementationT impl;

        impl.r() = PGMReadValue<uint8_t>()(ptr3 + 0);
        impl.g() = PGMReadValue<uint8_t>()(ptr3 + 1);
        impl.b() = PGMReadValue<uint8_t>()(ptr3 + 2);

        return impl;
    }

    static inline const ImplementationT fromPGMCharPointer(const uint8_t (&ptr)[3])
    { return fromPGMCharPointerRaw( reinterpret_cast<_PGMCharPointer>(ptr) ); }

#ifdef __AVR_HAVE_ELPM__
    static inline const ImplementationT fromPGMCharPointerRaw(_PGMCharPointerFar ptr3)
    {
        ImplementationT impl;

        impl.r() = PGMReadValue<uint8_t>()(ptr3 + 0);
        impl.g() = PGMReadValue<uint8_t>()(ptr3 + 1);
        impl.b() = PGMReadValue<uint8_t>()(ptr3 + 2);

        return impl;
    }
#endif
};


template <typename ImplementationT>
struct BasicRGBWColor {
    static constexpr uint8_t MaxRValue  = 255;
    static constexpr uint8_t MaxGValue  = 255;
    static constexpr uint8_t MaxBValue  = 255;
    static constexpr uint8_t MaxWValue  = 255;

    static constexpr uint8_t MaxC0Value = 255;
    static constexpr uint8_t MaxC1Value = 255;
    static constexpr uint8_t MaxC2Value = 255;
    static constexpr uint8_t MaxC3Value = 255;

    constexpr BasicRGBWColor() {}

    template <typename SrcColorT>
    inline const ImplementationT& assignFrom(const SrcColorT& src)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        src.toRGBW( impl.r(), impl.g(), impl.b(), impl.w() );
        return impl;
    }

    inline void toW(uint8_t& w) const
    {
        const ImplementationT& impl = *static_cast<const ImplementationT*>(this);

        rgb2w( w, impl.r() + impl.w(), impl.g() + impl.w(), impl.b() + impl.w() );
    }

    inline void toRGB(uint8_t& r, uint8_t& g, uint8_t& b) const
    {
        const ImplementationT& impl = *static_cast<const ImplementationT*>(this);

        r = impl.r() + impl.w();
        g = impl.g() + impl.w();
        b = impl.b() + impl.w();
    }

    inline void toRGBW(uint8_t& r, uint8_t& g, uint8_t& b, uint8_t& w) const
    {
        const ImplementationT& impl = *static_cast<const ImplementationT*>(this);

        r = impl.r();
        g = impl.g();
        b = impl.b();
        w = impl.w();
    }

    inline void fromHTMLHex(uint32_t htmlHex6)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        html2rgb( impl.r(), impl.g(), impl.b(), htmlHex6 );
        rgb2rgbw( impl.r(), impl.g(), impl.b(), impl.w(), impl.r(), impl.g(), impl.b() );
    }

    inline void fromHTMLHexString(const char* htmlHex6)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        html2rgb( impl.r(), impl.g(), impl.b(), htmlHex6 );
        rgb2rgbw( impl.r(), impl.g(), impl.b(), impl.w(), impl.r(), impl.g(), impl.b() );
    }

    template <uint8_t ChannelIndexes>
    inline void adjust(int8_t adjustmentFactor)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        if(ChannelIndexes & ImplementationT::R) impl.r() = eCxx::adjustColor( impl.r(), adjustmentFactor );
        if(ChannelIndexes & ImplementationT::G) impl.g() = eCxx::adjustColor( impl.g(), adjustmentFactor );
        if(ChannelIndexes & ImplementationT::B) impl.b() = eCxx::adjustColor( impl.b(), adjustmentFactor );
        if(ChannelIndexes & ImplementationT::W) impl.w() = eCxx::adjustColor( impl.w(), adjustmentFactor );
    }

    inline void adjust(int8_t adjustmentFactor)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        impl.r() = eCxx::adjustColor( impl.r(), adjustmentFactor );
        impl.g() = eCxx::adjustColor( impl.g(), adjustmentFactor );
        impl.b() = eCxx::adjustColor( impl.b(), adjustmentFactor );
        impl.w() = eCxx::adjustColor( impl.w(), adjustmentFactor );
    }

    template <uint8_t ChannelIndexes>
    inline void scale(int8_t scaleFactor)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        if(ChannelIndexes & ImplementationT::R) impl.r() = eCxx::scaleColor( impl.r(), scaleFactor);
        if(ChannelIndexes & ImplementationT::G) impl.g() = eCxx::scaleColor( impl.g(), scaleFactor);
        if(ChannelIndexes & ImplementationT::B) impl.b() = eCxx::scaleColor( impl.b(), scaleFactor);
        if(ChannelIndexes & ImplementationT::W) impl.w() = eCxx::scaleColor( impl.w(), scaleFactor);
    }

    inline void scale(uint8_t scaleFactor)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        impl.r() = eCxx::scaleColor( impl.r(), scaleFactor );
        impl.g() = eCxx::scaleColor( impl.g(), scaleFactor );
        impl.b() = eCxx::scaleColor( impl.b(), scaleFactor );
        impl.w() = eCxx::scaleColor( impl.w(), scaleFactor );
    }

    template <uint8_t N>
    inline void cie1931lcc(CIE1931LVal (&lut)[N])
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        impl.r() = eCxx::cie1931lcc( lut, impl.r() );
        impl.g() = eCxx::cie1931lcc( lut, impl.g() );
        impl.b() = eCxx::cie1931lcc( lut, impl.b() );
        impl.w() = eCxx::cie1931lcc( lut, impl.w() );
    }

    template <uint8_t N>
    inline void cie1931lut(CIE1931LVal (&lut)[N])
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        impl.r() = eCxx::cie1931lut( lut, impl.r() );
        impl.g() = eCxx::cie1931lut( lut, impl.g() );
        impl.b() = eCxx::cie1931lut( lut, impl.b() );
        impl.w() = eCxx::cie1931lut( lut, impl.w() );
    }

    inline void preXPolicy()
    {}

    inline void postXPolicy()
    {}

    static inline void preXPolicy(uint8_t*)
    {}

    static inline void postXPolicy(uint8_t*)
    {}

    static inline const ImplementationT fromPGMCharPointerRaw(_PGMCharPointer ptr4)
    {
        ImplementationT impl;

        impl.r() = PGMReadValue<uint8_t>()(ptr4 + 0);
        impl.g() = PGMReadValue<uint8_t>()(ptr4 + 1);
        impl.b() = PGMReadValue<uint8_t>()(ptr4 + 2);
        impl.w() = PGMReadValue<uint8_t>()(ptr4 + 3);

        return impl;
    }

    static inline const ImplementationT fromPGMCharPointer(const uint8_t (&ptr)[4])
    { return fromPGMCharPointerRaw( reinterpret_cast<_PGMCharPointer>(ptr) ); }

#ifdef __AVR_HAVE_ELPM__
    static inline const ImplementationT fromPGMCharPointerRaw(_PGMCharPointerFar ptr4)
    {
        ImplementationT impl;

        impl.r() = PGMReadValue<uint8_t>()(ptr4 + 0);
        impl.g() = PGMReadValue<uint8_t>()(ptr4 + 1);
        impl.b() = PGMReadValue<uint8_t>()(ptr4 + 2);
        impl.w() = PGMReadValue<uint8_t>()(ptr4 + 3);

        return impl;
    }
#endif
};


template <typename ImplementationT>
struct BasicXRGBColor {
    static constexpr uint8_t MaxRValue  = 255;
    static constexpr uint8_t MaxGValue  = 255;
    static constexpr uint8_t MaxBValue  = 255;

    static constexpr uint8_t MaxC0Value = 255;
    static constexpr uint8_t MaxC1Value = 255;
    static constexpr uint8_t MaxC2Value = 255;
    static constexpr uint8_t MaxC3Value = 255;

    constexpr BasicXRGBColor() {}

    inline void sx()
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        impl.x() = ImplementationT::XPolicy::_( impl.r(), impl.g(), impl.b() );
    }

    inline void sx(uint8_t x_)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        impl.x() = ImplementationT::XPolicy::_( x_, impl.r(), impl.g(), impl.b() );
    }

    inline const uint8_t& gx() const
    { return static_cast<const ImplementationT*>(this)->x(); }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    template <typename SrcColorT>
    inline const ImplementationT& assignFrom(const SrcColorT& src)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        src.toRGB( impl.r(), impl.g(), impl.b() );
        impl.sx();
        return impl;
    }

    inline void toW(uint8_t& w) const
    {
        const ImplementationT& impl = *static_cast<const ImplementationT*>(this);

        rgb2w( w, impl.r(), impl.g(), impl.b() );
    }

    inline void toRGB(uint8_t& r, uint8_t& g, uint8_t& b) const
    {
        const ImplementationT& impl = *static_cast<const ImplementationT*>(this);

        r = impl.r();
        g = impl.g();
        b = impl.b();
    }

    inline void toRGBW(uint8_t& r, uint8_t& g, uint8_t& b, uint8_t& w) const
    {
        const ImplementationT& impl = *static_cast<const ImplementationT*>(this);

        rgb2rgbw( r, g, b, w, impl.r(), impl.g(), impl.b() );
    }

    inline void fromHTMLHex(uint32_t htmlHex6)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        html2rgb( impl.r(), impl.g(), impl.b(), htmlHex6 );
        impl.sx( impl.gx() );
    }

    inline void fromHTMLHexString(const char* htmlHex6)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        html2rgb( impl.r(), impl.g(), impl.b(), htmlHex6 );
        impl.sx( impl.gx() );
    }

    template <uint8_t ChannelIndexes>
    inline void adjust(int8_t adjustmentFactor)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        preXPolicy();

        if(ChannelIndexes & ImplementationT::X) impl.x() = eCxx::adjustColor( impl.x(), adjustmentFactor );
        if(ChannelIndexes & ImplementationT::R) impl.r() = eCxx::adjustColor( impl.r(), adjustmentFactor );
        if(ChannelIndexes & ImplementationT::G) impl.g() = eCxx::adjustColor( impl.g(), adjustmentFactor );
        if(ChannelIndexes & ImplementationT::B) impl.b() = eCxx::adjustColor( impl.b(), adjustmentFactor );

        postXPolicy();
    }

    inline void adjust(int8_t adjustmentFactor)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        preXPolicy();

        impl.x() = eCxx::adjustColor( impl.x(), adjustmentFactor );
        impl.r() = eCxx::adjustColor( impl.r(), adjustmentFactor );
        impl.g() = eCxx::adjustColor( impl.g(), adjustmentFactor );
        impl.b() = eCxx::adjustColor( impl.b(), adjustmentFactor );

        postXPolicy();
    }

    template <uint8_t ChannelIndexes>
    inline void scale(int8_t scaleFactor)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        preXPolicy();

        if(ChannelIndexes & ImplementationT::X) impl.x() = eCxx::scaleColor( impl.x(), scaleFactor );
        if(ChannelIndexes & ImplementationT::R) impl.r() = eCxx::scaleColor( impl.r(), scaleFactor );
        if(ChannelIndexes & ImplementationT::G) impl.g() = eCxx::scaleColor( impl.g(), scaleFactor );
        if(ChannelIndexes & ImplementationT::B) impl.b() = eCxx::scaleColor( impl.b(), scaleFactor );

        postXPolicy();
    }

    inline void scale(uint8_t scaleFactor)
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        preXPolicy();

        impl.x() = eCxx::scaleColor( impl.x(), scaleFactor );
        impl.r() = eCxx::scaleColor( impl.r(), scaleFactor );
        impl.g() = eCxx::scaleColor( impl.g(), scaleFactor );
        impl.b() = eCxx::scaleColor( impl.b(), scaleFactor );

        postXPolicy();
    }

    template <uint8_t N>
    inline void cie1931lcc(CIE1931LVal (&lut)[N])
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        impl.r() = eCxx::cie1931lcc( lut, impl.r() );
        impl.g() = eCxx::cie1931lcc( lut, impl.g() );
        impl.b() = eCxx::cie1931lcc( lut, impl.b() );

        impl.sx( impl.gx() );
    }

    template <uint8_t N>
    inline void cie1931lut(CIE1931LVal (&lut)[N])
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        impl.r() = eCxx::cie1931lut( lut, impl.r() );
        impl.g() = eCxx::cie1931lut( lut, impl.g() );
        impl.b() = eCxx::cie1931lut( lut, impl.b() );

        impl.sx( impl.gx() );
    }

    inline void preXPolicy()
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        ImplementationT::XPolicy::preXPolicy( impl.x(), impl.r(), impl.g(), impl.b() );
    }

    inline void postXPolicy()
    {
        ImplementationT& impl = *static_cast<ImplementationT*>(this);

        ImplementationT::XPolicy::postXPolicy( impl.x(), impl.r(), impl.g(), impl.b() );
    }

    static inline void preXPolicy(uint8_t* channels)
    {
        ImplementationT::XPolicy::preXPolicy(
            channels[ CCI::toPlainIndex<ImplementationT::X>::Value ],
            channels[ CCI::toPlainIndex<ImplementationT::R>::Value ],
            channels[ CCI::toPlainIndex<ImplementationT::G>::Value ],
            channels[ CCI::toPlainIndex<ImplementationT::B>::Value ]
        );
    }

    static inline void postXPolicy(uint8_t* channels)
    {
        ImplementationT::XPolicy::postXPolicy(
            channels[ CCI::toPlainIndex<ImplementationT::X>::Value ],
            channels[ CCI::toPlainIndex<ImplementationT::R>::Value ],
            channels[ CCI::toPlainIndex<ImplementationT::G>::Value ],
            channels[ CCI::toPlainIndex<ImplementationT::B>::Value ]
        );
    }

    static inline const ImplementationT fromPGMCharPointer(const uint8_t (&ptr)[3])
    {
        ImplementationT impl;

        impl.r() = PGMReadValue<uint8_t>()(ptr + 0);
        impl.g() = PGMReadValue<uint8_t>()(ptr + 1);
        impl.b() = PGMReadValue<uint8_t>()(ptr + 2);

        impl.sx();

        return impl;
    }

    static inline const ImplementationT fromPGMCharPointer(const uint8_t (&ptr)[4])
    {
        ImplementationT impl;

        impl.r() = PGMReadValue<uint8_t>()(ptr + 1);
        impl.g() = PGMReadValue<uint8_t>()(ptr + 2);
        impl.b() = PGMReadValue<uint8_t>()(ptr + 3);

        impl.sx( PGMReadValue<uint8_t>()(ptr + 0) );

        return impl;
    }
};


//
// 1-element Kelvin color structure
//
struct KColor : public Color1<uint16_t> {
    using XPolicy = bool;

    static constexpr uint8_t K                = CCI::_1;
    static constexpr uint8_t ChannelAvailable = K;

    static constexpr uint16_t MaxKValue  = CBP::MaxKValue;

    static constexpr uint16_t MaxC0Value = CBP::MaxKValue;
    static constexpr uint8_t  MaxC1Value = 0;
    static constexpr uint8_t  MaxC2Value = 0;
    static constexpr uint8_t  MaxC3Value = 0;

    constexpr KColor() {}
    constexpr KColor(uint16_t k) : Color1(k) {}

    inline       uint16_t& k()       { return c0; }
    inline const uint16_t& k() const { return c0; }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    inline void toW(uint8_t& w) const
    {
        uint8_t r, g, b;
        k2rgb( r, g, b, k() );
        rgb2w( w, r, g, b   );
    }

    inline void toRGB(uint8_t& r, uint8_t& g, uint8_t& b) const
    { k2rgb( r, g, b, k() ); }

    inline void toRGBW(uint8_t& r, uint8_t& g, uint8_t& b, uint8_t& w) const
    {
        k2rgb   ( r, g, b, k()        );
        rgb2rgbw( r, g, b, w, r, g, b );
    }

    template <uint8_t ChannelIndexes>
    inline void adjust(int8_t adjustmentFactor)
    { if(ChannelIndexes & K) k() = eCxx::adjustColor<CBP::MaxKValue>( k(), adjustmentFactor ); }

    inline void adjust(int8_t adjustmentFactor)
    { k() = eCxx::adjustColor<CBP::MaxKValue>( k(), adjustmentFactor ); }

    template <uint8_t ChannelIndexes>
    inline void adjust16(int16_t adjustmentFactor)
    { if(ChannelIndexes & K) k() = eCxx::adjustColor<CBP::MaxKValue>( k(), adjustmentFactor ); }

    inline void adjust16(int16_t adjustmentFactor)
    { k() = eCxx::adjustColor<CBP::MaxKValue>( k(), adjustmentFactor ); }

    template <uint8_t ChannelIndexes>
    inline void scale(uint8_t scaleFactor)
    { if(ChannelIndexes & K) k() = eCxx::scaleColor<CBP::MaxKValue>( k(), scaleFactor ); }

    inline void scale(uint8_t scaleFactor)
    { k() = eCxx::scaleColor<CBP::MaxKValue>( k(), scaleFactor ); }

    inline void preXPolicy()
    {}

    inline void postXPolicy()
    {}

    static inline void preXPolicy(uint8_t*)
    {}

    static inline void postXPolicy(uint8_t*)
    {}

    static inline const KColor fromPGMCharPointerRaw(_PGMCharPointer ptr2)
    {
        KColor k;

        k.k() = PGMReadValue<uint16_t>()(ptr2);

        return k;
    }

    static inline const KColor fromPGMCharPointer(const uint8_t (&ptr)[2])
    { return fromPGMCharPointerRaw( reinterpret_cast<_PGMCharPointer>(ptr) ); }

#ifdef __AVR_HAVE_ELPM__
    static inline const KColor fromPGMCharPointerRaw(_PGMCharPointerFar ptr2)
    {
        KColor k;

        k.k() = PGMReadValue<uint16_t>()(ptr2);

        return k;
    }
#endif
};


//
// 3-element HSV color structure
//
struct HSVColor : public Color3<uint16_t, uint8_t, uint8_t> {
    using XPolicy = bool;

    static constexpr uint8_t H                = CCI::_1;
    static constexpr uint8_t S                = CCI::_2;
    static constexpr uint8_t V                = CCI::_3;
    static constexpr uint8_t ChannelAvailable = H | S | V;

    static constexpr uint16_t MaxHValue  = CBP::MaxHValue;
    static constexpr uint8_t  MaxSValue  = CBP::MaxSValue;
    static constexpr uint8_t  MaxVValue  = CBP::MaxVValue;

    static constexpr uint16_t MaxC0Value = CBP::MaxHValue;
    static constexpr uint8_t  MaxC1Value = CBP::MaxSValue;
    static constexpr uint8_t  MaxC2Value = CBP::MaxVValue;
    static constexpr uint8_t  MaxC3Value = 0;

    constexpr HSVColor() {}
    constexpr HSVColor(uint16_t h, uint8_t s, uint8_t v) : Color3(h, s, v) {}

    inline       uint16_t& h()       { return c0; }
    inline const uint16_t& h() const { return c0; }
    inline       uint8_t&  s()       { return c1; }
    inline const uint8_t&  s() const { return c1; }
    inline       uint8_t&  v()       { return c2; }
    inline const uint8_t&  v() const { return c2; }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    inline void toW(uint8_t& w) const
    { w = v(); }

    inline void toRGB(uint8_t& r, uint8_t& g, uint8_t& b) const
    { hsv2rgb( r, g, b, h(), s(), v() ); }

    inline void toRGBW(uint8_t& r, uint8_t& g, uint8_t& b, uint8_t& w) const
    {
        hsv2rgb ( r, g, b, h(), s(), v() );
        rgb2rgbw( r, g, b, w, r, g, b    );
    }

    template <uint8_t ChannelIndexes>
    inline void adjust(int8_t adjustmentFactor)
    {
        if(ChannelIndexes & H) h() = eCxx::adjustColor<CBP::MaxHValue>( h(), adjustmentFactor );
        if(ChannelIndexes & S) s() = eCxx::adjustColor<CBP::MaxSValue>( s(), adjustmentFactor );
        if(ChannelIndexes & V) v() = eCxx::adjustColor<CBP::MaxVValue>( v(), adjustmentFactor );
    }

    inline void adjust(int8_t adjustmentFactor)
    {
        h() = eCxx::adjustColor<CBP::MaxHValue>( h(), adjustmentFactor );
        s() = eCxx::adjustColor<CBP::MaxSValue>( s(), adjustmentFactor );
        v() = eCxx::adjustColor<CBP::MaxVValue>( v(), adjustmentFactor );
    }

    template <uint8_t ChannelIndexes>
    inline void scale(int8_t scaleFactor)
    {
        if(ChannelIndexes & H) h() = eCxx::scaleColor<CBP::MaxHValue>( h(), scaleFactor );
        if(ChannelIndexes & S) s() = eCxx::scaleColor<CBP::MaxSValue>( s(), scaleFactor );
        if(ChannelIndexes & V) v() = eCxx::scaleColor<CBP::MaxVValue>( v(), scaleFactor );
    }

    inline void scale(uint8_t scaleFactor)
    {
        h() = eCxx::scaleColor<CBP::MaxHValue>( h(), scaleFactor );
        s() = eCxx::scaleColor<CBP::MaxSValue>( s(), scaleFactor );
        v() = eCxx::scaleColor<CBP::MaxVValue>( v(), scaleFactor );
    }

    inline void preXPolicy()
    {}

    inline void postXPolicy()
    {}

    static inline void preXPolicy(uint8_t*)
    {}

    static inline void postXPolicy(uint8_t*)
    {}

    static inline const HSVColor fromPGMCharPointerRaw(_PGMCharPointer ptr4)
    {
        HSVColor hsv;

        hsv.h() = PGMReadValue<uint16_t>()(ptr4 + 0);
        hsv.s() = PGMReadValue< uint8_t>()(ptr4 + 2);
        hsv.v() = PGMReadValue< uint8_t>()(ptr4 + 3);

        return hsv;
    }

    static inline const HSVColor fromPGMCharPointer(const uint8_t (&ptr)[4])
    { return fromPGMCharPointerRaw( reinterpret_cast<_PGMCharPointer>(ptr) ); }

#ifdef __AVR_HAVE_ELPM__
    static inline const HSVColor fromPGMCharPointerRaw(_PGMCharPointerFar ptr4)
    {
        HSVColor hsv;

        hsv.h() = PGMReadValue<uint16_t>()(ptr4 + 0);
        hsv.s() = PGMReadValue< uint8_t>()(ptr4 + 2);
        hsv.v() = PGMReadValue< uint8_t>()(ptr4 + 3);

        return hsv;
    }
#endif
};


//
// 3-element HSL color structure
//
struct HSLColor : public Color3<uint16_t, uint8_t, uint8_t> {
    using XPolicy = bool;

    static constexpr uint8_t H                = CCI::_1;
    static constexpr uint8_t S                = CCI::_2;
    static constexpr uint8_t L                = CCI::_3;
    static constexpr uint8_t ChannelAvailable = H | S | L;

    static constexpr uint16_t MaxHValue  = CBP::MaxHValue;
    static constexpr uint8_t  MaxSValue  = CBP::MaxSValue;
    static constexpr uint8_t  MaxLValue  = CBP::MaxLValue;

    static constexpr uint16_t MaxC0Value = CBP::MaxHValue;
    static constexpr uint8_t  MaxC1Value = CBP::MaxSValue;
    static constexpr uint8_t  MaxC2Value = CBP::MaxLValue;
    static constexpr uint8_t  MaxC3Value = 0;

    constexpr HSLColor() {}
    constexpr HSLColor(uint16_t h, uint8_t s, uint8_t l) : Color3(h, s, l) {}

    inline       uint16_t& h()       { return c0; }
    inline const uint16_t& h() const { return c0; }
    inline       uint8_t&  s()       { return c1; }
    inline const uint8_t&  s() const { return c1; }
    inline       uint8_t&  l()       { return c2; }
    inline const uint8_t&  l() const { return c2; }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    inline void toW(uint8_t& w) const
    {
        uint16_t vh;
        uint8_t  vs;
        hsl2hsv( vh, vs, w, h(), s(), l() );
    }

    inline void toRGB(uint8_t& r, uint8_t& g, uint8_t& b) const
    { hsl2rgb( r, g, b, h(), s(), l() ); }

    inline void toRGBW(uint8_t& r, uint8_t& g, uint8_t& b, uint8_t& w) const
    {
        hsl2rgb ( r, g, b, h(), s(), l() );
        rgb2rgbw( r, g, b, w, r, g, b    );
    }

    template <uint8_t ChannelIndexes>
    inline void adjust(int8_t adjustmentFactor)
    {
        if(ChannelIndexes & H) h() = eCxx::adjustColor<CBP::MaxHValue>( h(), adjustmentFactor );
        if(ChannelIndexes & S) s() = eCxx::adjustColor<CBP::MaxSValue>( s(), adjustmentFactor );
        if(ChannelIndexes & L) l() = eCxx::adjustColor<CBP::MaxLValue>( l(), adjustmentFactor );
    }

    inline void adjust(int8_t adjustmentFactor)
    {
        h() = eCxx::adjustColor<CBP::MaxHValue>( h(), adjustmentFactor );
        s() = eCxx::adjustColor<CBP::MaxSValue>( s(), adjustmentFactor );
        l() = eCxx::adjustColor<CBP::MaxLValue>( l(), adjustmentFactor );
    }

    template <uint8_t ChannelIndexes>
    inline void scale(int8_t scaleFactor)
    {
        if(ChannelIndexes & H) h() = eCxx::scaleColor<CBP::MaxHValue>( h(), scaleFactor );
        if(ChannelIndexes & S) s() = eCxx::scaleColor<CBP::MaxSValue>( s(), scaleFactor );
        if(ChannelIndexes & L) l() = eCxx::scaleColor<CBP::MaxLValue>( l(), scaleFactor );
    }

    inline void scale(uint8_t scaleFactor)
    {
        h() = eCxx::scaleColor<CBP::MaxHValue>( h(), scaleFactor );
        s() = eCxx::scaleColor<CBP::MaxSValue>( s(), scaleFactor );
        l() = eCxx::scaleColor<CBP::MaxLValue>( l(), scaleFactor );
    }

    inline void preXPolicy()
    {}

    inline void postXPolicy()
    {}

    static inline void preXPolicy(uint8_t*)
    {}

    static inline void postXPolicy(uint8_t*)
    {}

    static inline const HSLColor fromPGMCharPointerRaw(_PGMCharPointer ptr4)
    {
        HSLColor hsl;

        hsl.h() = PGMReadValue<uint16_t>()(ptr4 + 0);
        hsl.s() = PGMReadValue< uint8_t>()(ptr4 + 2);
        hsl.l() = PGMReadValue< uint8_t>()(ptr4 + 3);

        return hsl;
    }

    static inline const HSLColor fromPGMCharPointer(const uint8_t (&ptr)[4])
    { return fromPGMCharPointerRaw( reinterpret_cast<_PGMCharPointer>(ptr) ); }

#ifdef __AVR_HAVE_ELPM__
    static inline const HSLColor fromPGMCharPointerRaw(_PGMCharPointerFar ptr4)
    {
        HSLColor hsl;

        hsl.h() = PGMReadValue<uint16_t>()(ptr4 + 0);
        hsl.s() = PGMReadValue< uint8_t>()(ptr4 + 2);
        hsl.l() = PGMReadValue< uint8_t>()(ptr4 + 3);

        return hsl;
    }
#endif
};


//
// 1-element color structure (white/grayscale)
//
struct WColor : public Color1<uint8_t> {
    using XPolicy = bool;

    static constexpr uint8_t W                = CCI::_1;
    static constexpr uint8_t ChannelAvailable = W;

    static constexpr uint8_t MaxWValue  = 255;

    static constexpr uint8_t MaxC0Value = 255;
    static constexpr uint8_t MaxC1Value =   0;
    static constexpr uint8_t MaxC2Value =   0;
    static constexpr uint8_t MaxC3Value =   0;

    constexpr WColor() {}
    constexpr WColor( int8_t  w) : Color1(w) {}
    constexpr WColor(uint8_t  w) : Color1(w) {}
    constexpr WColor( int16_t w) : Color1(w) {}
    constexpr WColor(uint16_t w) : Color1(w) {}
    constexpr WColor( int32_t w) : Color1(w) {}
    constexpr WColor(uint32_t w) : Color1(w) {}
    constexpr WColor( int64_t w) : Color1(w) {}
    constexpr WColor(uint64_t w) : Color1(w) {}

    inline WColor(const WColor& src) = default;

    template <typename SrcColorT>
    inline WColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const WColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& w()       { return c0; }
    inline const uint8_t& w() const { return c0; }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    template <typename SrcColorT>
    inline const WColor& assignFrom(const SrcColorT& src)
    {
        src.toW( w() );
        return *this;
    }

    inline void toW(uint8_t& w_) const
    { w_ = w(); }

    inline void toRGB(uint8_t& r_, uint8_t& g_, uint8_t& b_) const
    {  r_ = g_ = b_ = w(); }

    inline void toRGBW(uint8_t& r_, uint8_t& g_, uint8_t& b_, uint8_t& w_) const
    {
        r_ = g_ = b_ = 0;
        w_ = w();
    }

    inline void fromHTMLHex(uint32_t htmlHex6)
    {
        uint8_t r, g, b;
        html2rgb( r, g, b, htmlHex6 );
        rgb2w( w(), r, g, b );
    }

    inline void fromHTMLHexString(const char* htmlHex6)
    {
        uint8_t r, g, b;
        html2rgb( r, g, b, htmlHex6 );
        rgb2w( w(), r, g, b );
    }

    template <uint8_t ChannelIndexes>
    inline void adjust(int8_t adjustmentFactor)
    { if(ChannelIndexes & W) c0 = eCxx::adjustColor(c0, adjustmentFactor); }

    inline void adjust(int8_t adjustmentFactor)
    { w() = eCxx::adjustColor( w(), adjustmentFactor ); }

    template <uint8_t ChannelIndexes>
    inline void scale(uint8_t scaleFactor)
    { if(ChannelIndexes & W) c0 = eCxx::scaleColor(c0, scaleFactor); }

    inline void scale(uint8_t scaleFactor)
    { w() = eCxx::scaleColor( w(), scaleFactor ); }

    template <uint8_t N>
    inline void cie1931lcc(CIE1931LVal (&lut)[N])
    { c0 = eCxx::cie1931lcc(lut, c0); }

    template <uint8_t N>
    inline void cie1931lut(CIE1931LVal (&lut)[N])
    { c0 = eCxx::cie1931lut(lut, c0); }

    inline void preXPolicy()
    {}

    inline void postXPolicy()
    {}

    static inline void preXPolicy(uint8_t*)
    {}

    static inline void postXPolicy(uint8_t*)
    {}

    static inline const WColor fromPGMCharPointerRaw(_PGMCharPointer ptr1)
    {
        WColor w;

        w.w() = PGMReadValue<uint8_t>()(ptr1);

        return w;
    }

    static inline const WColor fromPGMCharPointer(const uint8_t (&ptr)[1])
    { return fromPGMCharPointerRaw( reinterpret_cast<_PGMCharPointer>(ptr) ); }

#ifdef __AVR_HAVE_ELPM__
    static inline const WColor fromPGMCharPointerRaw(_PGMCharPointerFar ptr1)
    {
        WColor w;

        w.w() = PGMReadValue<uint8_t>()(ptr1);

        return w;
    }
#endif
};


//
// 3-element color structures (variants of RGB)
//
struct RGBColor : public BasicRGBColor<RGBColor>, public Color3<uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t R                = CCI::_1;
    static constexpr uint8_t G                = CCI::_2;
    static constexpr uint8_t B                = CCI::_3;
    static constexpr uint8_t ChannelAvailable = R | G | B;

    constexpr RGBColor() {}
    constexpr RGBColor(uint8_t r, uint8_t g, uint8_t b) : Color3(r, g, b) {}

    inline RGBColor(const RGBColor& src) = default;

    template <typename SrcColorT>
    inline RGBColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const RGBColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& r()       { return c0; }
    inline const uint8_t& r() const { return c0; }
    inline       uint8_t& g()       { return c1; }
    inline const uint8_t& g() const { return c1; }
    inline       uint8_t& b()       { return c2; }
    inline const uint8_t& b() const { return c2; }
};


struct RBGColor : public BasicRGBColor<RBGColor>, public Color3<uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t R                = CCI::_1;
    static constexpr uint8_t G                = CCI::_3;
    static constexpr uint8_t B                = CCI::_2;
    static constexpr uint8_t ChannelAvailable = R | G | B;

    constexpr RBGColor() {}
    constexpr RBGColor(uint8_t r, uint8_t b, uint8_t g) : Color3(r, b, g) {}

    inline RBGColor(const RBGColor& src) = default;

    template <typename SrcColorT>
    inline RBGColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const RBGColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& r()       { return c0; }
    inline const uint8_t& r() const { return c0; }
    inline       uint8_t& b()       { return c1; }
    inline const uint8_t& b() const { return c1; }
    inline       uint8_t& g()       { return c2; }
    inline const uint8_t& g() const { return c2; }
};


struct GRBColor : public BasicRGBColor<GRBColor>, public Color3<uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t R                = CCI::_2;
    static constexpr uint8_t G                = CCI::_1;
    static constexpr uint8_t B                = CCI::_3;
    static constexpr uint8_t ChannelAvailable = R | G | B;

    constexpr GRBColor() {}
    constexpr GRBColor(uint8_t g, uint8_t r, uint8_t b) : Color3(g, r, b) {}

    inline GRBColor(const GRBColor& src) = default;

    template <typename SrcColorT>
    inline GRBColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const GRBColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& g()       { return c0; }
    inline const uint8_t& g() const { return c0; }
    inline       uint8_t& r()       { return c1; }
    inline const uint8_t& r() const { return c1; }
    inline       uint8_t& b()       { return c2; }
    inline const uint8_t& b() const { return c2; }
};


struct GBRColor : public BasicRGBColor<GBRColor>, public Color3<uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t R                = CCI::_3;
    static constexpr uint8_t G                = CCI::_1;
    static constexpr uint8_t B                = CCI::_2;
    static constexpr uint8_t ChannelAvailable = R | G | B;

    constexpr GBRColor() {}
    constexpr GBRColor(uint8_t g, uint8_t b, uint8_t r) : Color3(g, b, r) {}

    inline GBRColor(const GBRColor& src) = default;

    template <typename SrcColorT>
    inline GBRColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const GBRColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& g()       { return c0; }
    inline const uint8_t& g() const { return c0; }
    inline       uint8_t& b()       { return c1; }
    inline const uint8_t& b() const { return c1; }
    inline       uint8_t& r()       { return c2; }
    inline const uint8_t& r() const { return c2; }
};


struct BRGColor : public BasicRGBColor<BRGColor>, public Color3<uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t R                = CCI::_2;
    static constexpr uint8_t G                = CCI::_3;
    static constexpr uint8_t B                = CCI::_1;
    static constexpr uint8_t ChannelAvailable = R | G | B;

    constexpr BRGColor() {}
    constexpr BRGColor(uint8_t b, uint8_t r, uint8_t g) : Color3(b, r, g) {}

    inline BRGColor(const BRGColor& src) = default;

    template <typename SrcColorT>
    inline BRGColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const BRGColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& b()       { return c0; }
    inline const uint8_t& b() const { return c0; }
    inline       uint8_t& r()       { return c1; }
    inline const uint8_t& r() const { return c1; }
    inline       uint8_t& g()       { return c2; }
    inline const uint8_t& g() const { return c2; }
};


struct BGRColor : public BasicRGBColor<BGRColor>, public Color3<uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t R                = CCI::_3;
    static constexpr uint8_t G                = CCI::_2;
    static constexpr uint8_t B                = CCI::_1;
    static constexpr uint8_t ChannelAvailable = R | G | B;

    constexpr BGRColor() {}
    constexpr BGRColor(uint8_t b, uint8_t g, uint8_t r) : Color3(b, g, r) {}

    inline BGRColor(const BGRColor& src) = default;

    template <typename SrcColorT>
    inline BGRColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const BGRColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& b()       { return c0; }
    inline const uint8_t& b() const { return c0; }
    inline       uint8_t& g()       { return c1; }
    inline const uint8_t& g() const { return c1; }
    inline       uint8_t& r()       { return c2; }
    inline const uint8_t& r() const { return c2; }
};


//
// 4-element color structures (variants of W+RGB)
//
struct WRGBColor : public BasicRGBWColor<WRGBColor>, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t W                = CCI::_1;
    static constexpr uint8_t R                = CCI::_2;
    static constexpr uint8_t G                = CCI::_3;
    static constexpr uint8_t B                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = W | R | G | B;

    constexpr WRGBColor() {}
    constexpr WRGBColor(uint8_t w, uint8_t r, uint8_t g, uint8_t b) : Color4(w, r, g, b) {}

    inline WRGBColor(const WRGBColor& src) = default;

    template <typename SrcColorT>
    inline WRGBColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const WRGBColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& w()       { return c0; }
    inline const uint8_t& w() const { return c0; }
    inline       uint8_t& r()       { return c1; }
    inline const uint8_t& r() const { return c1; }
    inline       uint8_t& g()       { return c2; }
    inline const uint8_t& g() const { return c2; }
    inline       uint8_t& b()       { return c3; }
    inline const uint8_t& b() const { return c3; }
};


struct WRBGColor : public BasicRGBWColor<WRBGColor>, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t W                = CCI::_1;
    static constexpr uint8_t R                = CCI::_2;
    static constexpr uint8_t G                = CCI::_4;
    static constexpr uint8_t B                = CCI::_3;
    static constexpr uint8_t ChannelAvailable = W | R | G | B;

    constexpr WRBGColor() {}
    constexpr WRBGColor(uint8_t w, uint8_t r, uint8_t b, uint8_t g) : Color4(w, r, b, g) {}

    inline WRBGColor(const WRBGColor& src) = default;

    template <typename SrcColorT>
    inline WRBGColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const WRBGColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& w()       { return c0; }
    inline const uint8_t& w() const { return c0; }
    inline       uint8_t& r()       { return c1; }
    inline const uint8_t& r() const { return c1; }
    inline       uint8_t& b()       { return c2; }
    inline const uint8_t& b() const { return c2; }
    inline       uint8_t& g()       { return c3; }
    inline const uint8_t& g() const { return c3; }
};


struct WGRBColor : public BasicRGBWColor<WGRBColor>, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t W                = CCI::_1;
    static constexpr uint8_t R                = CCI::_3;
    static constexpr uint8_t G                = CCI::_2;
    static constexpr uint8_t B                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = W | R | G | B;

    constexpr WGRBColor() {}
    constexpr WGRBColor(uint8_t w, uint8_t g, uint8_t r, uint8_t b) : Color4(w, g, r, b) {}

    inline WGRBColor(const WGRBColor& src) = default;

    template <typename SrcColorT>
    inline WGRBColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const WGRBColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& w()       { return c0; }
    inline const uint8_t& w() const { return c0; }
    inline       uint8_t& g()       { return c1; }
    inline const uint8_t& g() const { return c1; }
    inline       uint8_t& r()       { return c2; }
    inline const uint8_t& r() const { return c2; }
    inline       uint8_t& b()       { return c3; }
    inline const uint8_t& b() const { return c3; }
};


struct WGBRColor : public BasicRGBWColor<WGBRColor>, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t W                = CCI::_1;
    static constexpr uint8_t R                = CCI::_4;
    static constexpr uint8_t G                = CCI::_2;
    static constexpr uint8_t B                = CCI::_3;
    static constexpr uint8_t ChannelAvailable = W | R | G | B;

    constexpr WGBRColor() {}
    constexpr WGBRColor(uint8_t w, uint8_t g, uint8_t b, uint8_t r) : Color4(w, g, b, r) {}

    inline WGBRColor(const WGBRColor& src) = default;

    template <typename SrcColorT>
    inline WGBRColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const WGBRColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& w()       { return c0; }
    inline const uint8_t& w() const { return c0; }
    inline       uint8_t& g()       { return c1; }
    inline const uint8_t& g() const { return c1; }
    inline       uint8_t& b()       { return c2; }
    inline const uint8_t& b() const { return c2; }
    inline       uint8_t& r()       { return c3; }
    inline const uint8_t& r() const { return c3; }
};


struct WBRGColor : public BasicRGBWColor<WBRGColor>, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t W                = CCI::_1;
    static constexpr uint8_t R                = CCI::_3;
    static constexpr uint8_t G                = CCI::_4;
    static constexpr uint8_t B                = CCI::_2;
    static constexpr uint8_t ChannelAvailable = W | R | G | B;

    constexpr WBRGColor() {}
    constexpr WBRGColor(uint8_t w, uint8_t b, uint8_t r, uint8_t g) : Color4(w, b, r, g) {}

    inline WBRGColor(const WBRGColor& src) = default;

    template <typename SrcColorT>
    inline WBRGColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const WBRGColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& w()       { return c0; }
    inline const uint8_t& w() const { return c0; }
    inline       uint8_t& b()       { return c1; }
    inline const uint8_t& b() const { return c1; }
    inline       uint8_t& r()       { return c2; }
    inline const uint8_t& r() const { return c2; }
    inline       uint8_t& g()       { return c3; }
    inline const uint8_t& g() const { return c3; }
};


struct WBGRColor : public BasicRGBWColor<WBGRColor>, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t W                = CCI::_1;
    static constexpr uint8_t R                = CCI::_4;
    static constexpr uint8_t G                = CCI::_3;
    static constexpr uint8_t B                = CCI::_2;
    static constexpr uint8_t ChannelAvailable = W | R | G | B;

    constexpr WBGRColor() {}
    constexpr WBGRColor(uint8_t w, uint8_t b, uint8_t g, uint8_t r) : Color4(w, b, g, r) {}

    inline WBGRColor(const WBGRColor& src) = default;

    template <typename SrcColorT>
    inline WBGRColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const WBGRColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& w()       { return c0; }
    inline const uint8_t& w() const { return c0; }
    inline       uint8_t& b()       { return c1; }
    inline const uint8_t& b() const { return c1; }
    inline       uint8_t& g()       { return c2; }
    inline const uint8_t& g() const { return c2; }
    inline       uint8_t& r()       { return c3; }
    inline const uint8_t& r() const { return c3; }
};


//
// 4-element color structures (variants of RGB+W)
//
struct RGBWColor : public BasicRGBWColor<RGBWColor>, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t R                = CCI::_1;
    static constexpr uint8_t G                = CCI::_2;
    static constexpr uint8_t B                = CCI::_3;
    static constexpr uint8_t W                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = R | G | B | W;

    constexpr RGBWColor() {}
    constexpr RGBWColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w) : Color4(r, g, b, w) {}

    inline RGBWColor(const RGBWColor& src) = default;

    template <typename SrcColorT>
    inline RGBWColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const RGBWColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& r()       { return c0; }
    inline const uint8_t& r() const { return c0; }
    inline       uint8_t& g()       { return c1; }
    inline const uint8_t& g() const { return c1; }
    inline       uint8_t& b()       { return c2; }
    inline const uint8_t& b() const { return c2; }
    inline       uint8_t& w()       { return c3; }
    inline const uint8_t& w() const { return c3; }
};


struct RBGWColor : public BasicRGBWColor<RBGWColor>, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t R                = CCI::_1;
    static constexpr uint8_t G                = CCI::_3;
    static constexpr uint8_t B                = CCI::_2;
    static constexpr uint8_t W                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = R | G | B | W;

    constexpr RBGWColor() {}
    constexpr RBGWColor(uint8_t r, uint8_t b, uint8_t g, uint8_t w) : Color4(r, b, g, w) {}

    inline RBGWColor(const RBGWColor& src) = default;

    template <typename SrcColorT>
    inline RBGWColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const RBGWColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& r()       { return c0; }
    inline const uint8_t& r() const { return c0; }
    inline       uint8_t& b()       { return c1; }
    inline const uint8_t& b() const { return c1; }
    inline       uint8_t& g()       { return c2; }
    inline const uint8_t& g() const { return c2; }
    inline       uint8_t& w()       { return c3; }
    inline const uint8_t& w() const { return c3; }
};


struct GRBWColor : public BasicRGBWColor<GRBWColor>, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t R                = CCI::_2;
    static constexpr uint8_t G                = CCI::_1;
    static constexpr uint8_t B                = CCI::_3;
    static constexpr uint8_t W                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = R | G | B | W;

    constexpr GRBWColor() {}
    constexpr GRBWColor(uint8_t g, uint8_t r, uint8_t b, uint8_t w) : Color4(g, r, b, w) {}

    inline GRBWColor(const GRBWColor& src) = default;

    template <typename SrcColorT>
    inline GRBWColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const GRBWColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& g()       { return c0; }
    inline const uint8_t& g() const { return c0; }
    inline       uint8_t& r()       { return c1; }
    inline const uint8_t& r() const { return c1; }
    inline       uint8_t& b()       { return c2; }
    inline const uint8_t& b() const { return c2; }
    inline       uint8_t& w()       { return c3; }
    inline const uint8_t& w() const { return c3; }
};


struct GBRWColor : public BasicRGBWColor<GBRWColor>, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t R                = CCI::_3;
    static constexpr uint8_t G                = CCI::_1;
    static constexpr uint8_t B                = CCI::_2;
    static constexpr uint8_t W                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = R | G | B | W;

    constexpr GBRWColor() {}
    constexpr GBRWColor(uint8_t g, uint8_t b, uint8_t r, uint8_t w) : Color4(g, b, r, w) {}

    inline GBRWColor(const GBRWColor& src) = default;

    template <typename SrcColorT>
    inline GBRWColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const GBRWColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& g()       { return c0; }
    inline const uint8_t& g() const { return c0; }
    inline       uint8_t& b()       { return c1; }
    inline const uint8_t& b() const { return c1; }
    inline       uint8_t& r()       { return c2; }
    inline const uint8_t& r() const { return c2; }
    inline       uint8_t& w()       { return c3; }
    inline const uint8_t& w() const { return c3; }
};


struct BRGWColor : public BasicRGBWColor<BRGWColor>, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t R                = CCI::_2;
    static constexpr uint8_t G                = CCI::_3;
    static constexpr uint8_t B                = CCI::_1;
    static constexpr uint8_t W                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = R | G | B | W;

    constexpr BRGWColor() {}
    constexpr BRGWColor(uint8_t b, uint8_t r, uint8_t g, uint8_t w) : Color4(b, r, g, w) {}

    inline BRGWColor(const BRGWColor& src) = default;

    template <typename SrcColorT>
    inline BRGWColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const BRGWColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& b()       { return c0; }
    inline const uint8_t& b() const { return c0; }
    inline       uint8_t& r()       { return c1; }
    inline const uint8_t& r() const { return c1; }
    inline       uint8_t& g()       { return c2; }
    inline const uint8_t& g() const { return c2; }
    inline       uint8_t& w()       { return c3; }
    inline const uint8_t& w() const { return c3; }
};


struct BGRWColor : public BasicRGBWColor<BGRWColor>, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = void;

    static constexpr uint8_t R                = CCI::_3;
    static constexpr uint8_t G                = CCI::_2;
    static constexpr uint8_t B                = CCI::_1;
    static constexpr uint8_t W                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = R | G | B | W;

    constexpr BGRWColor() {}
    constexpr BGRWColor(uint8_t b, uint8_t g, uint8_t r, uint8_t w) : Color4(b, g, r, w) {}

    inline BGRWColor(const BGRWColor& src) = default;

    template <typename SrcColorT>
    inline BGRWColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const BGRWColor& operator=(const SrcColorT& src) { return assignFrom(src); }

    inline       uint8_t& b()       { return c0; }
    inline const uint8_t& b() const { return c0; }
    inline       uint8_t& g()       { return c1; }
    inline const uint8_t& g() const { return c1; }
    inline       uint8_t& r()       { return c2; }
    inline const uint8_t& r() const { return c2; }
    inline       uint8_t& w()       { return c3; }
    inline const uint8_t& w() const { return c3; }
};


//
// 4-element color structures (variants of X+RGB)
//
template <typename XPolicyT>
struct XRGBColor : public BasicXRGBColor< XRGBColor<XPolicyT> >, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = XPolicyT;

    static constexpr uint8_t X                = CCI::_1;
    static constexpr uint8_t R                = CCI::_2;
    static constexpr uint8_t G                = CCI::_3;
    static constexpr uint8_t B                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = X | R | G | B;

    constexpr XRGBColor(                                          ) : Color4( XPolicy::_(          ), 0, 0, 0 ) {}
    constexpr XRGBColor(           uint8_t r, uint8_t g, uint8_t b) : Color4( XPolicy::_(   r, g, b), r, g, b ) {}
    constexpr XRGBColor(uint8_t x, uint8_t r, uint8_t g, uint8_t b) : Color4( XPolicy::_(x, r, g, b), r, g, b ) {}

    inline XRGBColor(const XRGBColor& src) = default;

    template <typename SrcColorT>
    inline XRGBColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const XRGBColor<XPolicyT>& operator=(const SrcColorT& src) { return BasicXRGBColor< XRGBColor<XPolicyT> >::assignFrom(src); }

    inline       uint8_t& x()       { return c0; }
    inline const uint8_t& x() const { return c0; }
    inline       uint8_t& r()       { return c1; }
    inline const uint8_t& r() const { return c1; }
    inline       uint8_t& g()       { return c2; }
    inline const uint8_t& g() const { return c2; }
    inline       uint8_t& b()       { return c3; }
    inline const uint8_t& b() const { return c3; }
};


template <typename XPolicyT>
struct XRBGColor : public BasicXRGBColor< XRBGColor<XPolicyT> >, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = XPolicyT;

    static constexpr uint8_t X                = CCI::_1;
    static constexpr uint8_t R                = CCI::_2;
    static constexpr uint8_t G                = CCI::_4;
    static constexpr uint8_t B                = CCI::_3;
    static constexpr uint8_t ChannelAvailable = X | R | G | B;

    constexpr XRBGColor(                                          ) : Color4( XPolicy::_(          ), 0, 0, 0 ) {}
    constexpr XRBGColor(           uint8_t r, uint8_t b, uint8_t g) : Color4( XPolicy::_(   r, g, b), r, b, g ) {}
    constexpr XRBGColor(uint8_t x, uint8_t r, uint8_t b, uint8_t g) : Color4( XPolicy::_(x, r, b, b), r, b, g ) {}

    inline XRBGColor(const XRBGColor& src) = default;

    template <typename SrcColorT>
    inline XRBGColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const XRBGColor<XPolicyT>& operator=(const SrcColorT& src) { return BasicXRGBColor< XRBGColor<XPolicyT> >::assignFrom(src); }

    inline       uint8_t& x()       { return c0; }
    inline const uint8_t& x() const { return c0; }
    inline       uint8_t& r()       { return c1; }
    inline const uint8_t& r() const { return c1; }
    inline       uint8_t& b()       { return c2; }
    inline const uint8_t& b() const { return c2; }
    inline       uint8_t& g()       { return c3; }
    inline const uint8_t& g() const { return c3; }
};


template <typename XPolicyT>
struct XGRBColor : public BasicXRGBColor< XGRBColor<XPolicyT> >, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = XPolicyT;

    static constexpr uint8_t X                = CCI::_1;
    static constexpr uint8_t R                = CCI::_3;
    static constexpr uint8_t G                = CCI::_2;
    static constexpr uint8_t B                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = X | R | G | B;

    constexpr XGRBColor(                                          ) : Color4( XPolicy::_(          ), 0, 0, 0 ) {}
    constexpr XGRBColor(           uint8_t g, uint8_t r, uint8_t b) : Color4( XPolicy::_(   r, g, b), g, r, b ) {}
    constexpr XGRBColor(uint8_t x, uint8_t g, uint8_t r, uint8_t b) : Color4( XPolicy::_(x, r, g, b), g, r, b ) {}

    inline XGRBColor(const XGRBColor& src) = default;

    template <typename SrcColorT>
    inline XGRBColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const XGRBColor<XPolicyT>& operator=(const SrcColorT& src) { return BasicXRGBColor< XGRBColor<XPolicyT> >::assignFrom(src); }

    inline       uint8_t& x()       { return c0; }
    inline const uint8_t& x() const { return c0; }
    inline       uint8_t& g()       { return c1; }
    inline const uint8_t& g() const { return c1; }
    inline       uint8_t& r()       { return c2; }
    inline const uint8_t& r() const { return c2; }
    inline       uint8_t& b()       { return c3; }
    inline const uint8_t& b() const { return c3; }
};


template <typename XPolicyT>
struct XGBRColor : public BasicXRGBColor< XGBRColor<XPolicyT> >, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = XPolicyT;

    static constexpr uint8_t X                = CCI::_1;
    static constexpr uint8_t R                = CCI::_4;
    static constexpr uint8_t G                = CCI::_2;
    static constexpr uint8_t B                = CCI::_3;
    static constexpr uint8_t ChannelAvailable = X | R | G | B;

    constexpr XGBRColor(                                          ) : Color4( XPolicy::_(          ), 0, 0, 0 ) {}
    constexpr XGBRColor(           uint8_t g, uint8_t b, uint8_t r) : Color4( XPolicy::_(   r, g, b), g, b, r ) {}
    constexpr XGBRColor(uint8_t x, uint8_t g, uint8_t b, uint8_t r) : Color4( XPolicy::_(x, r, g, b), g, b, r ) {}

    inline XGBRColor(const XGBRColor& src) = default;

    template <typename SrcColorT>
    inline XGBRColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const XGBRColor<XPolicyT>& operator=(const SrcColorT& src) { return BasicXRGBColor< XGBRColor<XPolicyT> >::assignFrom(src); }

    inline       uint8_t& x()       { return c0; }
    inline const uint8_t& x() const { return c0; }
    inline       uint8_t& g()       { return c1; }
    inline const uint8_t& g() const { return c1; }
    inline       uint8_t& b()       { return c2; }
    inline const uint8_t& b() const { return c2; }
    inline       uint8_t& r()       { return c3; }
    inline const uint8_t& r() const { return c3; }
};


template <typename XPolicyT>
struct XBRGColor : public BasicXRGBColor< XBRGColor<XPolicyT> >, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = XPolicyT;

    static constexpr uint8_t X                = CCI::_1;
    static constexpr uint8_t R                = CCI::_3;
    static constexpr uint8_t G                = CCI::_4;
    static constexpr uint8_t B                = CCI::_2;
    static constexpr uint8_t ChannelAvailable = X | R | G | B;

    constexpr XBRGColor(                                          ) : Color4( XPolicy::_(          ), 0, 0, 0 ) {}
    constexpr XBRGColor(           uint8_t b, uint8_t r, uint8_t g) : Color4( XPolicy::_(   r, g, b), b, r, g ) {}
    constexpr XBRGColor(uint8_t x, uint8_t b, uint8_t r, uint8_t g) : Color4( XPolicy::_(x, r, g, b), b, r, g ) {}

    inline XBRGColor(const XBRGColor& src) = default;

    template <typename SrcColorT>
    inline XBRGColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const XBRGColor<XPolicyT>& operator=(const SrcColorT& src) { return BasicXRGBColor< XBRGColor<XPolicyT> >::assignFrom(src); }

    inline       uint8_t& x()       { return c0; }
    inline const uint8_t& x() const { return c0; }
    inline       uint8_t& b()       { return c1; }
    inline const uint8_t& b() const { return c1; }
    inline       uint8_t& r()       { return c2; }
    inline const uint8_t& r() const { return c2; }
    inline       uint8_t& g()       { return c3; }
    inline const uint8_t& g() const { return c3; }
};


template <typename XPolicyT>
struct XBGRColor : public BasicXRGBColor< XBGRColor<XPolicyT> >, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = XPolicyT;

    static constexpr uint8_t X                = CCI::_1;
    static constexpr uint8_t R                = CCI::_4;
    static constexpr uint8_t G                = CCI::_3;
    static constexpr uint8_t B                = CCI::_2;
    static constexpr uint8_t ChannelAvailable = X | R | G | B;

    constexpr XBGRColor(                                          ) : Color4( XPolicy::_(          ), 0, 0, 0 ) {}
    constexpr XBGRColor(           uint8_t b, uint8_t g, uint8_t r) : Color4( XPolicy::_(   r, g, b), b, g, r ) {}
    constexpr XBGRColor(uint8_t x, uint8_t b, uint8_t g, uint8_t r) : Color4( XPolicy::_(x, r, g, b), b, g, r ) {}

    inline XBGRColor(const XBGRColor& src) = default;

    template <typename SrcColorT>
    inline XBGRColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const XBGRColor<XPolicyT>& operator=(const SrcColorT& src) { return BasicXRGBColor< XBGRColor<XPolicyT> >::assignFrom(src); }

    inline       uint8_t& x()       { return c0; }
    inline const uint8_t& x() const { return c0; }
    inline       uint8_t& b()       { return c1; }
    inline const uint8_t& b() const { return c1; }
    inline       uint8_t& g()       { return c2; }
    inline const uint8_t& g() const { return c2; }
    inline       uint8_t& r()       { return c3; }
    inline const uint8_t& r() const { return c3; }
};


//
// 4-element color structures (variants of RGB+X)
//
template <typename XPolicyT>
struct RGBXColor : public BasicXRGBColor< RGBXColor<XPolicyT> >, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = XPolicyT;

    static constexpr uint8_t R                = CCI::_1;
    static constexpr uint8_t G                = CCI::_2;
    static constexpr uint8_t B                = CCI::_3;
    static constexpr uint8_t X                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = R | G | B | X;

    constexpr RGBXColor(                                          ) : Color4( 0, 0, 0, XPolicy::_(          ) ) {}
    constexpr RGBXColor(uint8_t r, uint8_t g, uint8_t b           ) : Color4( r, g, b, XPolicy::_(   r, g, b) ) {}
    constexpr RGBXColor(uint8_t r, uint8_t g, uint8_t b, uint8_t x) : Color4( r, g, b, XPolicy::_(x, r, g, b) ) {}

    inline RGBXColor(const RGBXColor& src) = default;

    template <typename SrcColorT>
    inline RGBXColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const RGBXColor<XPolicyT>& operator=(const SrcColorT& src) { return BasicXRGBColor< RGBXColor<XPolicyT> >::assignFrom(src); }

    inline       uint8_t& r()       { return c0; }
    inline const uint8_t& r() const { return c0; }
    inline       uint8_t& g()       { return c1; }
    inline const uint8_t& g() const { return c1; }
    inline       uint8_t& b()       { return c2; }
    inline const uint8_t& b() const { return c2; }
    inline       uint8_t& x()       { return c3; }
    inline const uint8_t& x() const { return c3; }
};


template <typename XPolicyT>
struct RBGXColor : public BasicXRGBColor< RBGXColor<XPolicyT> >, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = XPolicyT;

    static constexpr uint8_t R                = CCI::_1;
    static constexpr uint8_t G                = CCI::_3;
    static constexpr uint8_t B                = CCI::_2;
    static constexpr uint8_t X                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = R | G | B | X;

    constexpr RBGXColor(                                          ) : Color4( 0, 0, 0, XPolicy::_(          ) ) {}
    constexpr RBGXColor(uint8_t r, uint8_t b, uint8_t g           ) : Color4( r, b, g, XPolicy::_(   r, g, b) ) {}
    constexpr RBGXColor(uint8_t r, uint8_t b, uint8_t g, uint8_t x) : Color4( r, b, g, XPolicy::_(x, r, b, b) ) {}

    inline RBGXColor(const RBGXColor& src) = default;

    template <typename SrcColorT>
    inline RBGXColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const RBGXColor<XPolicyT>& operator=(const SrcColorT& src) { return BasicXRGBColor< RBGXColor<XPolicyT> >::assignFrom(src); }

    inline       uint8_t& r()       { return c0; }
    inline const uint8_t& r() const { return c0; }
    inline       uint8_t& b()       { return c1; }
    inline const uint8_t& b() const { return c1; }
    inline       uint8_t& g()       { return c2; }
    inline const uint8_t& g() const { return c2; }
    inline       uint8_t& x()       { return c3; }
    inline const uint8_t& x() const { return c3; }
};


template <typename XPolicyT>
struct GRBXColor : public BasicXRGBColor< GRBXColor<XPolicyT> >, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = XPolicyT;

    static constexpr uint8_t R                = CCI::_2;
    static constexpr uint8_t G                = CCI::_1;
    static constexpr uint8_t B                = CCI::_3;
    static constexpr uint8_t X                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = R | G | B | X;

    constexpr GRBXColor(                                          ) : Color4( 0, 0, 0, XPolicy::_(          ) ) {}
    constexpr GRBXColor(uint8_t g, uint8_t r, uint8_t b           ) : Color4( g, r, b, XPolicy::_(   r, g, b) ) {}
    constexpr GRBXColor(uint8_t g, uint8_t r, uint8_t b, uint8_t x) : Color4( g, r, b, XPolicy::_(x, r, g, b) ) {}

    inline GRBXColor(const GRBXColor& src) = default;

    template <typename SrcColorT>
    inline GRBXColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const GRBXColor<XPolicyT>& operator=(const SrcColorT& src) { return BasicXRGBColor< GRBXColor<XPolicyT> >::assignFrom(src); }

    inline       uint8_t& g()       { return c0; }
    inline const uint8_t& g() const { return c0; }
    inline       uint8_t& r()       { return c1; }
    inline const uint8_t& r() const { return c1; }
    inline       uint8_t& b()       { return c2; }
    inline const uint8_t& b() const { return c2; }
    inline       uint8_t& x()       { return c3; }
    inline const uint8_t& x() const { return c3; }
};


template <typename XPolicyT>
struct GBRXColor : public BasicXRGBColor< GBRXColor<XPolicyT> >, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = XPolicyT;

    static constexpr uint8_t R                = CCI::_3;
    static constexpr uint8_t G                = CCI::_1;
    static constexpr uint8_t B                = CCI::_2;
    static constexpr uint8_t X                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = R | G | B | X;

    constexpr GBRXColor(                                          ) : Color4( 0, 0, 0, XPolicy::_(          ) ) {}
    constexpr GBRXColor(uint8_t g, uint8_t b, uint8_t r           ) : Color4( g, b, r, XPolicy::_(   r, g, b) ) {}
    constexpr GBRXColor(uint8_t g, uint8_t b, uint8_t r, uint8_t x) : Color4( g, b, r, XPolicy::_(x, r, g, b) ) {}

    inline GBRXColor(const GBRXColor& src) = default;

    template <typename SrcColorT>
    inline GBRXColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const GBRXColor<XPolicyT>& operator=(const SrcColorT& src) { return BasicXRGBColor< GBRXColor<XPolicyT> >::assignFrom(src); }

    inline       uint8_t& g()       { return c0; }
    inline const uint8_t& g() const { return c0; }
    inline       uint8_t& b()       { return c1; }
    inline const uint8_t& b() const { return c1; }
    inline       uint8_t& r()       { return c2; }
    inline const uint8_t& r() const { return c2; }
    inline       uint8_t& x()       { return c3; }
    inline const uint8_t& x() const { return c3; }
};


template <typename XPolicyT>
struct BRGXColor : public BasicXRGBColor< BRGXColor<XPolicyT> >, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = XPolicyT;

    static constexpr uint8_t R                = CCI::_2;
    static constexpr uint8_t G                = CCI::_3;
    static constexpr uint8_t B                = CCI::_1;
    static constexpr uint8_t X                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = R | G | B | X;

    constexpr BRGXColor(                                          ) : Color4( 0, 0, 0, XPolicy::_(          ) ) {}
    constexpr BRGXColor(uint8_t b, uint8_t r, uint8_t g           ) : Color4( b, r, g, XPolicy::_(   r, g, b) ) {}
    constexpr BRGXColor(uint8_t b, uint8_t r, uint8_t g, uint8_t x) : Color4( b, r, g, XPolicy::_(x, r, g, b) ) {}

    inline BRGXColor(const BRGXColor& src) = default;

    template <typename SrcColorT>
    inline BRGXColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const BRGXColor<XPolicyT>& operator=(const SrcColorT& src) { return BasicXRGBColor< BRGXColor<XPolicyT> >::assignFrom(src); }

    inline       uint8_t& b()       { return c0; }
    inline const uint8_t& b() const { return c0; }
    inline       uint8_t& r()       { return c1; }
    inline const uint8_t& r() const { return c1; }
    inline       uint8_t& g()       { return c2; }
    inline const uint8_t& g() const { return c2; }
    inline       uint8_t& x()       { return c3; }
    inline const uint8_t& x() const { return c3; }
};


template <typename XPolicyT>
struct BGRXColor : public BasicXRGBColor< BGRXColor<XPolicyT> >, public Color4<uint8_t, uint8_t, uint8_t, uint8_t> {
    using XPolicy = XPolicyT;

    static constexpr uint8_t R                = CCI::_3;
    static constexpr uint8_t G                = CCI::_2;
    static constexpr uint8_t B                = CCI::_1;
    static constexpr uint8_t X                = CCI::_4;
    static constexpr uint8_t ChannelAvailable = R | G | B | X;

    constexpr BGRXColor(                                          ) : Color4( 0, 0, 0, XPolicy::_(          ) ) {}
    constexpr BGRXColor(uint8_t b, uint8_t g, uint8_t r           ) : Color4( b, g, r, XPolicy::_(   r, g, b) ) {}
    constexpr BGRXColor(uint8_t b, uint8_t g, uint8_t r, uint8_t x) : Color4( b, g, r, XPolicy::_(x, r, g, b) ) {}

    inline BGRXColor(const BGRXColor& src) = default;

    template <typename SrcColorT>
    inline BGRXColor(const SrcColorT& src) { *this = src; }

    template <typename SrcColorT>
    inline const BGRXColor<XPolicyT>& operator=(const SrcColorT& src) { return BasicXRGBColor< BGRXColor<XPolicyT> >::assignFrom(src); }

    inline       uint8_t& b()       { return c0; }
    inline const uint8_t& b() const { return c0; }
    inline       uint8_t& g()       { return c1; }
    inline const uint8_t& g() const { return c1; }
    inline       uint8_t& r()       { return c2; }
    inline const uint8_t& r() const { return c2; }
    inline       uint8_t& x()       { return c3; }
    inline const uint8_t& x() const { return c3; }
};


} // namespace eCxx


#endif
