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


#ifndef INCLUDE_ECXX_CTL_GFX_COLOR_FX_1D_TPP
#error "this file cannot be included directly"
#endif
#undef INCLUDE_ECXX_CTL_GFX_COLOR_FX_1D_TPP


#ifndef ECXX_CTL_GFX_COLOR_FX_1D_TPP
#define ECXX_CTL_GFX_COLOR_FX_1D_TPP


namespace eCxx {


//
// Helper structures for ColorFX1D<T> implementation
//
template <typename ColorFX1D_T, typename BufferT, CBP::DUInt BufferElemCountN, typename BufferElemCountT>
struct ColorFX1D_SRHelper<ColorFX1D_T, BufferT, BufferElemCountN, CBP::EncapsulatedBuffer, BufferElemCountT> {
    // Shift right
    template <typename ColorT>
    static inline void shiftRight(ColorFX1D_T*, BufferT buff, const ColorT& fillColor)
    {
        for(BufferElemCountT i = BufferElemCountN - 1; i > 0; --i) {
            buff[i] = buff[i - 1];
        }

        buff[0] = fillColor;
    }

    // Shift left
    template <typename ColorT>
    static inline void shiftLeft(ColorFX1D_T*, BufferT buff, const ColorT& fillColor)
    {
        for(BufferElemCountT i = 0; i < BufferElemCountN - 1; ++i) {
            buff[i] = buff[i + 1];
        }

        buff[BufferElemCountN - 1] = fillColor;
    }

    // Rotate right
    static inline void rotateRight(ColorFX1D_T*, BufferT buff)
    {
        const auto saveCol = buff[BufferElemCountN - 1];

        for(BufferElemCountT i = BufferElemCountN - 1; i > 0; --i) {
            buff[i] = buff[i - 1];
        }

        buff[0] = saveCol;
    }

    // Rotate left
    static inline void rotateLeft(ColorFX1D_T*, BufferT buff)
    {
        const auto saveCol = buff[0];

        for(BufferElemCountT i = 0; i < BufferElemCountN - 1; ++i) {
            buff[i] = buff[i + 1];
        }

        buff[BufferElemCountN - 1] = saveCol;
    }
};


template <typename ColorFX1D_T, typename BufferT, CBP::DUInt BufferElemCountN, typename BufferElemCountT>
struct ColorFX1D_SRHelper<ColorFX1D_T, BufferT, BufferElemCountN, CBP::RawBuffer, BufferElemCountT> {
    // Shift right
    template <typename ColorT>
    static inline void shiftRight(ColorFX1D_T* cfx, BufferT, const ColorT& fillColor)
    {
        for(BufferElemCountT i = BufferElemCountN - 1; i > 0; --i) {
            cfx->set( i, cfx->get(i - 1) );
        }

        cfx->set(0, fillColor);
    }

    // Shift left
    template <typename ColorT>
    static inline void shiftLeft(ColorFX1D_T* cfx, BufferT, const ColorT& fillColor)
    {
        for(BufferElemCountT i = 0; i < BufferElemCountN - 1; ++i) {
            cfx->set( i, cfx->get(i + 1) );
        }

        cfx->set(BufferElemCountN - 1, fillColor);
    }

    // Rotate right
    static inline void rotateRight(ColorFX1D_T* cfx, BufferT)
    {
        const auto& saveCol = cfx->get(BufferElemCountN - 1);

        for(BufferElemCountT i = BufferElemCountN - 1; i > 0; --i) {
            cfx->set( i, cfx->get(i - 1) );
        }

        cfx->set(0, saveCol);
    }

    // Rotate left
    static inline void rotateLeft(ColorFX1D_T* cfx, BufferT)
    {
        const auto& saveCol = cfx->get(0);

        for(BufferElemCountT i = 0; i < BufferElemCountN - 1; ++i) {
            cfx->set( i, cfx->get(i + 1) );
        }

        cfx->set(BufferElemCountN - 1, saveCol);
    }
};


template <typename ColorFX1D_T, typename BufferT, CBP::DUInt BufferElemCountN, typename BufferPolicyT, typename BufferElemCountT>
struct ColorFX1D_CMHelper {
    // Put
    template <typename BufferColorT_, CBP::DUInt BufferSizeN_, typename BufferPolicyT_, typename BufferSizeT_>
    static inline void put(ColorFX1D_T* cfx, BufferT, CBP::DSInt index, const ColorFX1D<BufferColorT_, BufferSizeN_, BufferPolicyT_, BufferSizeT_>& strip)
    {
        // Source index
        CBP::SUInt si = 0;

        // Loop the destination elements
        for(CBP::DSInt di = index; di < index + strip.BufferElementCount; ++di) {
            // Check the destination X index
            if( di <  0                                         ) continue;
            if( di >= static_cast<CBP::DSInt>(BufferElemCountN) ) break;
            // Copy source color to destination color
            cfx->set( di, strip.get(si) );
            // Increment the source index
            ++si;
        }
    }
};


//
// A simple 1-dimensional color array and effect class
//

// Flip the buffer
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::flipBuffer()
{ _buff.flipBuffer(); }


// Return the raw data (front buffer)
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::ConstIterator ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::colorFrontBufferData() const
{ return _CBSP::itCBegin( _buff.frontBuffer() ); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::ConstReverseIterator ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::rcolorFrontBufferData() const
{ return eCxx::makeReverseIterator( _CBSP::itCEnd( _buff.frontBuffer() ) ); }


// Return the raw data (back buffer)
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::ConstIterator ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::colorBufferData() const
{ return _CBSP::itCBegin(*_buff); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::ConstReverseIterator ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::rcolorBufferData() const
{ return eCxx::makeReverseIterator( _CBSP::itCEnd(*_buff) ); }


// Iterators
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::Iterator ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::begin()
{ return _CBSP::itBegin(*_buff); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::Iterator ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::end()
{ return _CBSP::itEnd(*_buff); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::ReverseIterator ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::rbegin()
{ return eCxx::makeReverseIterator( _CBSP::itEnd(*_buff) ); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::ReverseIterator ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::rend()
{ return eCxx::makeReverseIterator( _CBSP::itBegin(*_buff) ); }


// Constant iterators
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::ConstIterator ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::cbegin() const
{ return _CBSP::itCBegin(*_buff); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::ConstIterator ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::cend() const
{ return _CBSP::itCEnd(*_buff); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::ConstReverseIterator ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::crbegin() const
{ return eCxx::makeReverseIterator( _CBSP::itCEnd(*_buff) ); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::ConstReverseIterator ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::crend() const
{ return eCxx::makeReverseIterator( _CBSP::itCBegin(*_buff) ); }


// Clear all with zeroes
#if __GNUC__ >= 8
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::clear()
{ memset( *_buff, 0, sizeof(*_buff) ); }
#if __GNUC__ >= 8
    #pragma GCC diagnostic pop
#endif


// Get one color
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline const typename ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>::BufferColorType ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::get(BufferSizeType index) const
{ return _CBSP::get(*_buff, index); }


// Set all colors
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::set(const ColorT& color)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    const BufferColorType cnv = color;
    while(it != end) _CBSP::itAssignInc(it, cnv);
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::set(const ColorT* colors)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    while(it != end) _CBSP::itAssignInc(it, *colors++);
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::set(ColorPGMPointerType colors)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    ColorT f;

    while(it != end) {
        f.pgmGetInc(colors);
        _CBSP::itAssignInc(it, f);
    }
}


// Set one color
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::set(BufferSizeType index, const ColorT& color)
{
    Iterator it = _CBSP::itBegin(*_buff);

    _CBSP::itInc   (it, index);
    _CBSP::itAssign(it, color);
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::set(BufferSizeType index, ColorPGMPointerType color)
{
    Iterator it = _CBSP::itBegin(*_buff);

    ColorT f;
    f.pgmGet(color);

    _CBSP::itInc   (it, index);
    _CBSP::itAssign(it, f    );
}


// Put a strip of colors
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename BufferColorT_, CBP::DUInt BufferSizeN_, typename BufferPolicyT_, typename BufferSizeT_>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::put(CBP::DSInt index, const ColorFX1D<BufferColorT_, BufferSizeN_, BufferPolicyT_, BufferSizeT_>& strip)
{ _CMHelper::put(this, *_buff, index, strip); }


// Linear interpolation between two colors
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes, typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(const ColorT& from, const ColorT& to, uint8_t progressPercent)
{
    ColorT dst;
    lerpColor<ChannelIndexes>(dst, from, to, progressPercent);

    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    const BufferColorType cnv = dst;
    while(it != end) _CBSP::itAssignInc(it, cnv);
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(const ColorT& from, const ColorT& to, uint8_t progressPercent)
{ lerp<ColorT::A>(from, to, progressPercent); }


// Linear interpolation between one source color to destination colors
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes, typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(const ColorT& from, const ColorT* to, uint8_t progressPercent)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    ColorT mix;

    while(it != end) {
        lerpColor<ChannelIndexes>(mix, from, *to++, progressPercent);
        _CBSP::itAssignInc(it, mix);
    }
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(const ColorT& from, const ColorT* to, uint8_t progressPercent)
{ lerp<ColorT::A>(from, to, progressPercent); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes, typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(const ColorT& from, ColorPGMPointerType to, uint8_t progressPercent)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    ColorT t, mix;

    while(it != end) {
        t.pgmGetInc(to);
        lerpColor<ChannelIndexes>(mix, from, t, progressPercent);
        _CBSP::itAssignInc(it, mix);
    }
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(const ColorT& from, ColorPGMPointerType to, uint8_t progressPercent)
{ lerp<ColorT::A>(from, to, progressPercent); }


// Linear interpolation between source colors to one destination color
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes, typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(const ColorT* from, const ColorT& to, uint8_t progressPercent)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    ColorT mix;

    while(it != end) {
        lerpColor<ChannelIndexes>(mix, *from++, to, progressPercent);
        _CBSP::itAssignInc(it, mix);
    }
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(const ColorT* from, const ColorT& to, uint8_t progressPercent)
{ lerp<ColorT::A>(from, to, progressPercent); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes, typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(ColorPGMPointerType from, const ColorT& to, uint8_t progressPercent)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    ColorT f, mix;

    while(it != end) {
        f.pgmGetInc(from);
        lerpColor<ChannelIndexes>(mix, f, to, progressPercent);
        _CBSP::itAssignInc(it, mix);
    }
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(ColorPGMPointerType from, const ColorT& to, uint8_t progressPercent)
{ lerp<ColorT::A>(from, to, progressPercent); }


// Linear interpolation between source colors to destination colors
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes, typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(const ColorT* from, const ColorT* to, uint8_t progressPercent)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    ColorT mix;

    while(it != end) {
        lerpColor<ChannelIndexes>(mix, *from++, *to++, progressPercent);
        _CBSP::itAssignInc(it, mix);
    }
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(const ColorT* from, const ColorT* to, uint8_t progressPercent)
{ lerp<ColorT::A>(from, to, progressPercent); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes, typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(ColorPGMPointerType from, const ColorT* to, uint8_t progressPercent)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    ColorT f, mix;

    while(it != end) {
        f.pgmGetInc(from);
        lerpColor<ChannelIndexes>(mix, f, *to++, progressPercent);
        _CBSP::itAssignInc(it, mix);
    }

}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(ColorPGMPointerType from, const ColorT* to, uint8_t progressPercent)
{ lerp<ColorT::A>(from, to, progressPercent); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes, typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(const ColorT* from, ColorPGMPointerType to, uint8_t progressPercent)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    ColorT t, mix;

    while(it != end) {
        t.pgmGetInc(to);
        lerpColor<ChannelIndexes>(mix, *from++, t, progressPercent);
        _CBSP::itAssignInc(it, mix);
    }
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(const ColorT* from, ColorPGMPointerType to, uint8_t progressPercent)
{ lerp<ColorT::A>(from, to, progressPercent); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes, typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(ColorPGMPointerType from, ColorPGMPointerType to, uint8_t progressPercent)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    ColorT f, t, mix;

    while(it != end) {
        f.pgmGetInc(from);
        t.pgmGetInc(to  );
        lerpColor<ChannelIndexes>(mix, f, t, progressPercent);
        _CBSP::itAssignInc(it, mix);
    }
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerp(ColorPGMPointerType from, ColorPGMPointerType to, uint8_t progressPercent)
{ lerp<ColorT::A>(from, to, progressPercent); }


// Generate gradient by using linear interpolation between two colors
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes, typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerpG(const ColorT& from, const ColorT& to, BufferSizeType e1, BufferSizeType e2)
{
    // Get the iterator
    Iterator it = _CBSP::itBegin(*_buff);
    if(e1) _CBSP::itInc(it, e1);

    // Loop the destination elements
    for(BufferSizeType d = 0; d <= (e2 - e1); ++d) {
        // Perform linear interpolation
        ColorT mix;
        lerpColor<ChannelIndexes>(mix, from, to, d, e2 - e1);
        // Set the interpolated color as the destination color
        _CBSP::itAssignInc(it, mix);
    }
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::lerpG(const ColorT& from, const ColorT& to, BufferSizeType e1, BufferSizeType e2)
{ lerpG<ColorT::A>(from, to, e1, e2); }


// Shift and rotate colors
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::shiftRight(const ColorT& fillColor)
{ _SRHelper::shiftRight(this, *_buff, fillColor); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::shiftLeft(const ColorT& fillColor)
{ _SRHelper::shiftLeft(this, *_buff, fillColor); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::rotateRight()
{ _SRHelper::rotateRight(this, *_buff); }


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::rotateLeft()
{ _SRHelper::rotateLeft(this, *_buff); }


// Adjust colors
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::adjust(int8_t adjustmentFactor)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    while(it != end) _CBSP::template itAdjustInc<ChannelIndexes>(it, adjustmentFactor);
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::adjust(int8_t adjustmentFactor)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    while(it != end) _CBSP::itAdjustInc(it, adjustmentFactor);
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::adjust16(int16_t adjustmentFactor)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    while(it != end) _CBSP::template itAdjustInc<ChannelIndexes>(it, adjustmentFactor);
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::adjust16(int16_t adjustmentFactor)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    while(it != end) _CBSP::itAdjustInc(it, adjustmentFactor);
}


// Scale colors
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t ChannelIndexes>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::scale(uint8_t scaleFactor)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    while(it != end) _CBSP::template itScaleInc<ChannelIndexes>(it, scaleFactor);
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::scale(uint8_t scaleFactor)
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    while(it != end) _CBSP::itScaleInc(it, scaleFactor);
}


// Perform color correction using CIE-1931 lightness to approximate the response of the human eye
template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t N>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::cie1931lcc(CIE1931LVal (&lut)[N])
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    while(it != end) _CBSP::itCIE1931LCCInc(it, lut);
}


template <typename BufferColorT, CBP::DUInt BufferSizeN, typename BufferPolicyT, typename BufferSizeT, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <uint8_t N>
inline void ColorFX1D<BufferColorT, BufferSizeN, BufferPolicyT, BufferSizeT, UseDoubleBufferring, ColorPGMPtrT>
::cie1931lut(CIE1931LVal (&lut)[N])
{
    Iterator it  = _CBSP::itBegin(*_buff);
    Iterator end = _CBSP::itEnd  (*_buff);

    while(it != end) _CBSP::itCIE1931LUTInc(it, lut);
}


} // namespace eCxx


#endif
