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


#include <eCxx/eCxx_ColorSpace.h>

#include "TestUtility.h"


//
// Helper functions
//
template <typename ColorFX1D_T>
static void dumpBufferRaw3StripSum(const char* cs, const ColorFX1D_T& colorFX1D)
{
    typename ColorFX1D_T::ConstIterator it = colorFX1D.colorBufferData();

    printf("%s\n| ", cs);
    for(typename ColorFX1D_T::BufferSizeType i = 0; i < ColorFX1D_T::BufferElementCount; ++i) {
        printf( "%d ", (int) *(it + 0) + *(it + 1) + *(it + 2) );
        it += ColorFX1D_T::ColorByteSize;
    }
    printf("| \n");
}


template <typename ColorFX2D_T>
static void dumpBufferRaw3(const char* cs, const ColorFX2D_T& colorFX2D)
{
    typename ColorFX2D_T::ConstIterator it = colorFX2D.colorBufferData();

    printf("%s\n", cs);
    for(typename ColorFX2D_T::BufferSizeType y = 0; y < ColorFX2D_T::YElementCount; ++y) {
        printf("| ");
        for(typename ColorFX2D_T::BufferSizeType x = 0; x < ColorFX2D_T::XElementCount; ++x) {
            printf( "(%03d,%03d,%03d) ", (int) *(it + 0), (int) *(it + 1), (int) *(it + 2) );
            it += ColorFX2D_T::ColorByteSize;
        }
        printf("|\n");
    }
}


template <typename ColorFX2D_T>
static void dumpBufferRaw3Sum(const char* cs, const ColorFX2D_T& colorFX2D)
{
    typename ColorFX2D_T::ConstIterator it = colorFX2D.colorBufferData();

    printf("%s\n", cs);
    for(typename ColorFX2D_T::BufferSizeType y = 0; y < ColorFX2D_T::YElementCount; ++y) {
        printf("| ");
        for(typename ColorFX2D_T::BufferSizeType x = 0; x < ColorFX2D_T::XElementCount; ++x) {
            printf( "%d ", (int) *(it + 0) + *(it + 1) + *(it + 2) );
            it += ColorFX2D_T::ColorByteSize;
        }
        printf("|\n");
    }
}


template <typename ColorFX2D_T>
static void dumpBufferRaw3XY(const char* cs, const ColorFX2D_T& colorFX2D, bool xydd)
{
    typename ColorFX2D_T::ConstIterator it1 = colorFX2D.colorBufferData();
    typename ColorFX2D_T::ConstIterator it2 = colorFX2D.colorBufferData();

    const char* xyFmtStr = xydd ? "(%2d,%2d) " : "(%d,%d) ";

    printf("%s\n", cs);
    printf("| ");
    if(xydd) {
        for(eCxx::uint8_t x = 0; x < ColorFX2D_T::XElementCount; ++x) printf("( X, Y) ");
    }
    else {
        for(eCxx::uint8_t x = 0; x < ColorFX2D_T::XElementCount; ++x) printf("(X,Y) ");
    }
    printf("|   | Mem.Address ");
    if(ColorFX2D_T::XElementCount > 4) {
        for(eCxx::uint8_t x = 0; x < ColorFX2D_T::XElementCount * 3 - 12; ++x) printf(" ");
    }
    printf("|\n");
    for(typename ColorFX2D_T::BufferSizeType y = 0; y < ColorFX2D_T::YElementCount; ++y) {
        printf("| ");
        for(typename ColorFX2D_T::BufferSizeType x = 0; x < ColorFX2D_T::XElementCount; ++x) {
            printf( xyFmtStr, (int) *(it1 + 0), (int) *(it1 + 1) );
            it1 += ColorFX2D_T::ColorByteSize;
        }
        printf("|   ");
        printf("| ");
        for(typename ColorFX2D_T::BufferSizeType x = 0; x < ColorFX2D_T::XElementCount; ++x) {
            printf( "%2d ", ( (int) *(it2 + 1) * ColorFX2D_T::XElementCount + *(it2 + 0) ) );
            it2 += ColorFX2D_T::ColorByteSize;
        }
        printf("|\n");
    }
}



//
// Test application
//
int main()
{
    printf("\n");
    const eCxx::RGBColor rgbBlack(  0,   0,   0);
    const eCxx::RGBColor rgbRed  (255,   0,   0);
    const eCxx::RGBColor rgbGreen(  0, 255,   0);
    const eCxx::RGBColor rgbBlue (  0,   0, 255);
    const eCxx::RGBColor rgbWhite(255, 255, 255);



    eCxx::ColorFX2D<eCxx::RGBColor, 4, 4, eCxx::CBP::DefaultLayout, eCxx::CBP::RawBuffer> colorFXRRGBawDefault;
    colorFXRRGBawDefault.set(rgbBlack);
    dumpBufferRaw3("RGB CBP::DefaultLayout", colorFXRRGBawDefault);
    printf("\n");

    colorFXRRGBawDefault.setXY(0, 2, rgbRed  );
    colorFXRRGBawDefault.setXY(1, 0, rgbGreen);
    colorFXRRGBawDefault.setXY(2, 2, rgbBlue );
    colorFXRRGBawDefault.setXY(3, 1, rgbWhite);
    dumpBufferRaw3("RGB CBP::DefaultLayout", colorFXRRGBawDefault);
    printf("\n");
    _DIVIDERLINE_DASHSTR_NL2();



    #define SET_DUMP_LAYOUT_MEMORY_XY(LAYOUT) do {                                             \
        eCxx::ColorFX2D<eCxx::RGBColor, 4, 4, eCxx::CBP::LAYOUT, eCxx::CBP::RawBuffer> LAYOUT; \
        LAYOUT.set  (rgbBlack);                                                                \
        LAYOUT.setXY( 0, 0, eCxx::RGBColor(0, 0, 0) );                                         \
        LAYOUT.setXY( 1, 0, eCxx::RGBColor(1, 0, 0) );                                         \
        LAYOUT.setXY( 2, 0, eCxx::RGBColor(2, 0, 0) );                                         \
        LAYOUT.setXY( 3, 0, eCxx::RGBColor(3, 0, 0) );                                         \
        LAYOUT.setXY( 0, 1, eCxx::RGBColor(0, 1, 0) );                                         \
        LAYOUT.setXY( 1, 1, eCxx::RGBColor(1, 1, 0) );                                         \
        LAYOUT.setXY( 2, 1, eCxx::RGBColor(2, 1, 0) );                                         \
        LAYOUT.setXY( 3, 1, eCxx::RGBColor(3, 1, 0) );                                         \
        LAYOUT.setXY( 0, 2, eCxx::RGBColor(0, 2, 0) );                                         \
        LAYOUT.setXY( 1, 2, eCxx::RGBColor(1, 2, 0) );                                         \
        LAYOUT.setXY( 2, 2, eCxx::RGBColor(2, 2, 0) );                                         \
        LAYOUT.setXY( 3, 2, eCxx::RGBColor(3, 2, 0) );                                         \
        LAYOUT.setXY( 0, 3, eCxx::RGBColor(0, 3, 0) );                                         \
        LAYOUT.setXY( 1, 3, eCxx::RGBColor(1, 3, 0) );                                         \
        LAYOUT.setXY( 2, 3, eCxx::RGBColor(2, 3, 0) );                                         \
        LAYOUT.setXY( 3, 3, eCxx::RGBColor(3, 3, 0) );                                         \
        dumpBufferRaw3XY( "CBP::" STRINGIFY(LAYOUT), LAYOUT, false );                          \
        printf("\n");                                                                          \
    } while(0)
    SET_DUMP_LAYOUT_MEMORY_XY(RowMajor   );
    SET_DUMP_LAYOUT_MEMORY_XY(RowMajorRX );
    SET_DUMP_LAYOUT_MEMORY_XY(RowMajorRY );
    SET_DUMP_LAYOUT_MEMORY_XY(RowMajorRXY);
    SET_DUMP_LAYOUT_MEMORY_XY(ColMajor   );
    SET_DUMP_LAYOUT_MEMORY_XY(ColMajorRX );
    SET_DUMP_LAYOUT_MEMORY_XY(ColMajorRY );
    SET_DUMP_LAYOUT_MEMORY_XY(ColMajorRXY);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_XY(AltRowMajor   );
    SET_DUMP_LAYOUT_MEMORY_XY(AltRowMajorRX );
    SET_DUMP_LAYOUT_MEMORY_XY(AltRowMajorRY );
    SET_DUMP_LAYOUT_MEMORY_XY(AltRowMajorRXY);
    SET_DUMP_LAYOUT_MEMORY_XY(AltColMajor   );
    SET_DUMP_LAYOUT_MEMORY_XY(AltColMajorRX );
    SET_DUMP_LAYOUT_MEMORY_XY(AltColMajorRY );
    SET_DUMP_LAYOUT_MEMORY_XY(AltColMajorRXY);

    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_XY(StandardLayout);
    SET_DUMP_LAYOUT_MEMORY_XY(HorizontalFlip);
    SET_DUMP_LAYOUT_MEMORY_XY(VerticalFlip  );
    SET_DUMP_LAYOUT_MEMORY_XY(BothFlip      );
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_XY(StandardLayout);
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate90CW );
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate180CW);
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate270CW);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate90CW              );
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate90CWHorizontalFlip);
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate90CWVerticalFlip  );
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate90CWBothFlip      );
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate180CW              );
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate180CWHorizontalFlip);
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate180CWVerticalFlip  );
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate180CWBothFlip      );
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate270CW              );
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate270CWHorizontalFlip);
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate270CWVerticalFlip  );
    SET_DUMP_LAYOUT_MEMORY_XY(Rotate270CWBothFlip      );
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL2();



    #define SET_DUMP_LAYOUT_COLOR(LAYOUT) do {                                                 \
        eCxx::ColorFX2D<eCxx::RGBColor, 4, 3, eCxx::CBP::LAYOUT, eCxx::CBP::RawBuffer> LAYOUT; \
        LAYOUT.set(rgbBlack);                                                                  \
        LAYOUT.setXY(0, 0, rgbRed  );                                                          \
        LAYOUT.setXY(1, 0, rgbGreen);                                                          \
        LAYOUT.setXY(2, 0, rgbBlue );                                                          \
        LAYOUT.setXY(3, 2, rgbWhite);                                                          \
        dumpBufferRaw3( "CBP::" STRINGIFY(LAYOUT), LAYOUT );                                   \
        printf("\n");                                                                          \
    } while(0)
    SET_DUMP_LAYOUT_COLOR(StandardLayout);
    SET_DUMP_LAYOUT_COLOR(HorizontalFlip);
    SET_DUMP_LAYOUT_COLOR(VerticalFlip  );
    SET_DUMP_LAYOUT_COLOR(BothFlip      );
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_COLOR(StandardLayout);
    SET_DUMP_LAYOUT_COLOR(Rotate90CW );
    SET_DUMP_LAYOUT_COLOR(Rotate180CW);
    SET_DUMP_LAYOUT_COLOR(Rotate270CW);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_COLOR(Rotate90CW              );
    SET_DUMP_LAYOUT_COLOR(Rotate90CWHorizontalFlip);
    SET_DUMP_LAYOUT_COLOR(Rotate90CWVerticalFlip  );
    SET_DUMP_LAYOUT_COLOR(Rotate90CWBothFlip      );
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_COLOR(Rotate180CW              );
    SET_DUMP_LAYOUT_COLOR(Rotate180CWHorizontalFlip);
    SET_DUMP_LAYOUT_COLOR(Rotate180CWVerticalFlip  );
    SET_DUMP_LAYOUT_COLOR(Rotate180CWBothFlip      );
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_COLOR(Rotate270CW              );
    SET_DUMP_LAYOUT_COLOR(Rotate270CWHorizontalFlip);
    SET_DUMP_LAYOUT_COLOR(Rotate270CWVerticalFlip  );
    SET_DUMP_LAYOUT_COLOR(Rotate270CWBothFlip      );
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL2();



    eCxx::ColorFX2D<eCxx::RGBColor, 4, 4, eCxx::CBP::RowMajor, eCxx::CBP::RawBuffer> rgbImageSrc;
    eCxx::ColorFX2D<eCxx::RGBColor, 4, 4, eCxx::CBP::RowMajor, eCxx::CBP::RawBuffer> rgbImageDst;
    rgbImageSrc.set  (      rgbBlack);
    rgbImageSrc.setXY(0, 0, rgbRed  );
    rgbImageSrc.setXY(1, 0, rgbGreen);
    rgbImageSrc.setXY(2, 0, rgbBlue );
    rgbImageSrc.setXY(3, 3, rgbWhite);
    rgbImageDst = rgbImageSrc;                               dumpBufferRaw3("CBP::RowMajor - Original",        rgbImageDst); printf("\n");
    rgbImageDst = rgbImageSrc; rgbImageDst.flipHorizontal(); dumpBufferRaw3("CBP::RowMajor - Flip Horizontal", rgbImageDst); printf("\n");
    rgbImageDst = rgbImageSrc; rgbImageDst.flipVertical  (); dumpBufferRaw3("CBP::RowMajor - Flip Vertical",   rgbImageDst); printf("\n");
    rgbImageDst = rgbImageSrc; rgbImageDst.rotate90CW    (); dumpBufferRaw3("CBP::RowMajor - Rotate  90°  CW", rgbImageDst); printf("\n");
    rgbImageDst = rgbImageSrc; rgbImageDst.rotate90CCW   (); dumpBufferRaw3("CBP::RowMajor - Rotate  90° CCW", rgbImageDst); printf("\n");
    rgbImageDst = rgbImageSrc; rgbImageDst.rotate180     (); dumpBufferRaw3("CBP::RowMajor - Rotate 180°",     rgbImageDst); printf("\n");
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL2();



    #define SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(XSIZE, YSIZE, LAYOUT, BX, BY, IY, XYDD) do {                                           \
        eCxx::ColorFX2D<eCxx::RGBColor, XSIZE, YSIZE, eCxx::CBP::LAYOUT, eCxx::CBP::RawBuffer, BX, BY, IY> LAYOUT;                 \
        LAYOUT.set(rgbBlack);                                                                                                      \
        for(eCxx::uint8_t y = 0; y < YSIZE; ++y) {                                                                                 \
            for(eCxx::uint8_t x = 0; x < XSIZE; ++x) {                                                                             \
                LAYOUT.setXY( x, y, eCxx::RGBColor(x, y, 0) );                                                                     \
            }                                                                                                                      \
        }                                                                                                                          \
        dumpBufferRaw3XY( "CBP::" STRINGIFY(LAYOUT) " [" STRINGIFY(BX) ", " STRINGIFY(BY) ", " STRINGIFY(IY) "]" , LAYOUT, XYDD ); \
        printf("\n");                                                                                                              \
    } while(0)
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockRowMajorPixelRowMajor,    4, 2, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockRowMajorPixelAltRowMajor, 4, 2, 0, false);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockRowMajorPixelRowMajor,    2, 4, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockRowMajorPixelAltRowMajor, 2, 4, 0, false);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(9, 6, BlockRowMajorPixelRowMajor,    3, 3, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(9, 6, BlockRowMajorPixelAltRowMajor, 3, 3, 0, false);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 8, BlockRowMajorPixelRowMajor,    4, 4, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 8, BlockRowMajorPixelAltRowMajor, 4, 4, 0, false);
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockRowMajorPixelRowMajor,       4, 2, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockRowMajorPixelAltRowMajor,    4, 2, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockAltRowMajorPixelRowMajor,    4, 2, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockAltRowMajorPixelAltRowMajor, 4, 2, 0, false);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockAltRowMajorPixelRowMajor,          4, 2, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockAltRowMajorAltRXYPixelRowMajor,    4, 2, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockAltRowMajorAltRXYPixelAltRowMajor, 4, 2, 0, false);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockRowMajorPixelColMajor,       4, 2, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockRowMajorPixelAltColMajor,    4, 2, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockAltRowMajorPixelColMajor,    4, 2, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockAltRowMajorPixelAltColMajor, 4, 2, 0, false);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockAltRowMajorPixelColMajor,          4, 2, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockAltRowMajorAltRXYPixelColMajor,    4, 2, 0, false);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 4, BlockAltRowMajorAltRXYPixelAltColMajor, 4, 2, 0, false);
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, InterRowMajor,        0, 0, 4, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, InterRowMajorRX,      0, 0, 4, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, InterRowMajorRY,      0, 0, 4, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, InterRowMajorRXY,     0, 0, 4, true);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, RInterRowMajor,       0, 0, 4, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, RInterRowMajorRX,     0, 0, 4, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, RInterRowMajorRY,     0, 0, 4, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, RInterRowMajorRXY,    0, 0, 4, true);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, InterAltRowMajor,     0, 0, 4, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, InterAltRowMajorRX,   0, 0, 4, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, InterAltRowMajorRY,   0, 0, 4, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, InterAltRowMajorRXY,  0, 0, 4, true);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, RInterAltRowMajor,    0, 0, 4, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, RInterAltRowMajorRX,  0, 0, 4, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, RInterAltRowMajorRY,  0, 0, 4, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 12, RInterAltRowMajorRXY, 0, 0, 4, true);
    _DIVIDERLINE_DASHSTR_NL2();
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 8, BlockRowMajorPixelInterRowMajor,    4, 4, 2, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 8, BlockRowMajorPixelInterAltRowMajor, 4, 4, 2, true);
    _DIVIDERLINE_DASHSTR_NL2();
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 8, BlockRowMajorPixelInterRowMajorRXY,    4, 4, 2, true);
    SET_DUMP_LAYOUT_MEMORY_BLOCK_XY(8, 8, BlockRowMajorPixelInterAltRowMajorRXY, 4, 4, 2, true);
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL2();



    const eCxx::HSVColor hsvFrom_[7] {
      //    H    S    V
        {   0,   0, 200 }, // Red
        { 120,   0, 200 }, // Green
        { 240,   0, 200 }, // Blue
        {   0,   0, 200 }, // White
        { 240,   0, 200 }, // Blue
        { 120,   0, 200 }, // Green
        {   0,   0, 200 }  // Red
    };
    const eCxx::HSVColor hsvTo_[7] {
      //    H    S    V
        {   0, 200, 200 }, // Red
        { 120, 200, 200 }, // Green
        { 240, 200, 200 }, // Blue
        {   0,   0, 200 }, // White
        { 240, 200, 200 }, // Blue
        { 120, 200, 200 }, // Green
        {   0, 200, 200 }  // Red
    };
    eCxx::ColorFX1D<eCxx::HSVColor, 7> hsvFrom; hsvFrom.set(hsvFrom_);
    eCxx::ColorFX1D<eCxx::HSVColor, 7> hsvTo;   hsvTo  .set(hsvTo_  );

    rgbImageDst.set(eCxx::Ext::RGB::black);
    rgbImageDst.lerpGV<eCxx::HSVColor::S>(0, hsvFrom, rgbImageDst.XElementCount - 1, hsvTo);
    dumpBufferRaw3("RGB", rgbImageDst);
    printf("\n");

    rgbImageDst.set(eCxx::Ext::RGB::black);
    rgbImageDst.lerpGV<eCxx::HSVColor::S>(0, hsvFrom, 3, rgbImageDst.XElementCount - 1, hsvTo, 3);
    dumpBufferRaw3("RGB", rgbImageDst);
    printf("\n");

    rgbImageDst.set(eCxx::Ext::RGB::black);
    rgbImageDst.lerpGH<eCxx::HSVColor::S>(0, hsvFrom, rgbImageDst.YElementCount - 1, hsvTo);
    dumpBufferRaw3("RGB", rgbImageDst);
    printf("\n");

    rgbImageDst.set(eCxx::Ext::RGB::black);
    rgbImageDst.lerpGH<eCxx::HSVColor::S>(0, hsvFrom, 3, rgbImageDst.YElementCount - 1, hsvTo, 3);
    dumpBufferRaw3("RGB", rgbImageDst);
    printf("\n");
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL2();



    eCxx::ColorFX2D<eCxx::RGBColor,  3,  4, eCxx::CBP::RowMajor, eCxx::CBP::RawBuffer> rgbImageSprite;
    rgbImageSprite.set( eCxx::RGBColor(1, 2, 4) );

    eCxx::ColorFX2D<eCxx::RGBColor, 32, 32, eCxx::CBP::RowMajor, eCxx::CBP::RawBuffer> rgbImageLarge;
    rgbImageLarge.set(eCxx::Ext::RGB::black);

    rgbImageLarge.putXY(-2, -2, rgbImageSprite); // Top
    rgbImageLarge.putXY(10, -2, rgbImageSprite);
    rgbImageLarge.putXY(30, -2, rgbImageSprite);

    rgbImageLarge.putXY(-1, 12, rgbImageSprite); // Middle
    rgbImageLarge.putXY(17, 12, rgbImageSprite);
    rgbImageLarge.putXY(31, 12, rgbImageSprite);

    rgbImageLarge.putXY(-2, 30, rgbImageSprite); // Bottom
    rgbImageLarge.putXY(20, 30, rgbImageSprite);
    rgbImageLarge.putXY(30, 30, rgbImageSprite);

    hsvFrom.set( eCxx::HSVColor(0, 1, 1) );
    rgbImageLarge.putVStrip( 3, -2, hsvFrom); rgbImageLarge.putVStrip(13, 23, hsvFrom);
    rgbImageLarge.putVStrip( 5,  0, hsvFrom); rgbImageLarge.putVStrip(15, 25, hsvFrom);
    rgbImageLarge.putVStrip( 7,  2, hsvFrom); rgbImageLarge.putVStrip(17, 27, hsvFrom);
    rgbImageLarge.putHStrip(-2, 20, hsvFrom); rgbImageLarge.putHStrip(23,  5, hsvFrom);
    rgbImageLarge.putHStrip( 0, 22, hsvFrom); rgbImageLarge.putHStrip(25,  7, hsvFrom);
    rgbImageLarge.putHStrip( 2, 24, hsvFrom); rgbImageLarge.putHStrip(27,  9, hsvFrom);

    dumpBufferRaw3Sum("RGB", rgbImageLarge);
    printf("\n");
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL2();



    eCxx::ColorFX1D<eCxx::RGBColor, 32,                         eCxx::CBP::RawBuffer> rgbGetStrip;
    eCxx::ColorFX2D<eCxx::RGBColor,  8, 8, eCxx::CBP::RowMajor, eCxx::CBP::RawBuffer> rgbGetBlock;

    rgbImageLarge.getVStrip(0, 0, rgbGetStrip);
    dumpBufferRaw3StripSum("RGB", rgbGetStrip);
    printf("\n");

    rgbImageLarge.getHStrip(0, 0, rgbGetStrip);
    dumpBufferRaw3StripSum("RGB", rgbGetStrip);
    printf("\n");

    rgbImageLarge.getXY(5, 1, rgbGetBlock);
    dumpBufferRaw3Sum("RGB", rgbGetBlock);
    printf("\n");
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL2();



    static const eCxx::uint8_t PROGMEM pgmColors[32 * 32] = {
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    };

    rgbImageLarge.set(eCxx::Ext::RGB::black);
    rgbImageLarge.setXY<eCxx::WColor>( eCxx::stdPGMCharPointer(pgmColors) );
    dumpBufferRaw3Sum("RGB", rgbImageLarge);
    printf("\n");

    static const eCxx::uint8_t PROGMEM_FAR pgmColorsFar[32 * 32] = {
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    };

    rgbImageLarge.set(eCxx::Ext::RGB::black);
    rgbImageLarge.setXY<eCxx::WColor>( eCxx::stdPGMCharPointerFar(pgmColorsFar) );
    dumpBufferRaw3Sum("RGB", rgbImageLarge);
    printf("\n");



    return 0;
}
