/*
 * 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_2D_PAINTER_TPP
#error "this file cannot be included directly"
#endif
#undef INCLUDE_ECXX_CTL_GFX_COLOR_FX_2D_PAINTER_TPP


#ifndef ECXX_CTL_GFX_COLOR_FX_2D_PAINTER_TPP
#define ECXX_CTL_GFX_COLOR_FX_2D_PAINTER_TPP


namespace eCxx {


//
// Helper structure for ColorFX2DPainter<T> implementation
//
template <typename ColorFX2DPainter_T, typename BufferT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN>
struct ColorFX2DPainter_CMHelper {
    // Put pixel with coordinate checking
    template <typename ColorT>
    static inline void putPixelWCC(
        ColorFX2DPainter_T* cfxp, BufferT    /* buffer */,
        CBP::DSInt          x,    CBP::DSInt y,
        const ColorT&       color
    )
    {
        if(x < 0 || x >= cfxp->XElementCount || y < 0 || y >= cfxp->YElementCount) return;

        cfxp->setXY(x, y, color);
    }

    // Put character XY
    template <typename ColorT, typename FontPGMPointerT>
    static inline void putCharXY(
        ColorFX2DPainter_T*                cfxp,
        BufferT                            /* buffer */,
        CBP::DSInt                         x,
        CBP::DSInt                         y,
        const ColorT&                      color,
        uint8_t                            ch,
        const BitmapFont<FontPGMPointerT>& font
    )
    {
        // Check if the character is within range
        if(ch < font.startChar || ch > font.endChar) return;

        // Get the start address of the character data
        typename FontPGMPointerT::_PGMPointerType pgmPtr =
            font.pgmCharData.p
            +
            static_cast<uint16_t>(ch - font.startChar) * font.bytesPerCol * font.sizeX;

        // Loop the destination X elements
        for(CBP::DSInt dx = x; dx < x + font.sizeX; ++dx) {
            // If the destination X index is less than 0, skip a column
            if(dx < 0) {
                pgmPtr += font.bytesPerCol;
                continue;
            }
            // If the destination X index is larger than the maximum X, break
            if(dx >= cfxp->XElementCount) break;
            // Character data iterator
            uint8_t colData = 0;
            uint8_t rowCnt  = 0;
            // Loop the destination Y elements
            for(CBP::DSInt dy = y; dy < y + font.sizeY; ++dy) {
                // Read the column data as needed
                if( !(rowCnt % 8) ) {
                    colData = PGMReadValue<uint8_t>()(pgmPtr);
                    ++pgmPtr;
                }
                ++rowCnt;
                // If the destination Y index is in range
                if(dy >= 0 && dy < cfxp->YElementCount) {
                    // Put the color if the bit is set in the column data
                    if(colData & 0x01) cfxp->setXY(dx, dy, color);
                }
                // Shift the column data
                colData >>= 1;
            }
        }
    }

    // Draw horizontal line
    template <typename ColorT>
    static inline void drawHLine(
        ColorFX2DPainter_T* cfxp, BufferT       /* buffer */,
        CBP::DSInt          x,    CBP::DSInt    y,
        CBP::DSInt          size, const ColorT& color
    )
    {
        // Check the Y coordinate
        if(y < 0 || y >= cfxp->YElementCount) return;

        // Clip the X coordinate
        if(x < 0) x = 0;

        // Loop the destination X coordinates
        for(CBP::DSInt dx = x; dx < x + size; ++dx) {
            // Done if the destination X index is larger than the maximum X
            if(dx >= cfxp->XElementCount) break;
            // Put the color
            cfxp->setXY(dx, y, color);
        }
    }

    // Draw vertical line
    template <typename ColorT>
    static inline void drawVLine(
        ColorFX2DPainter_T* cfxp, BufferT       /* buffer */,
        CBP::DSInt          x,    CBP::DSInt    y,
        CBP::DSInt          size, const ColorT& color
    )
    {
        // Check the X coordinate
        if(x < 0 || x >= cfxp->XElementCount) return;

        // Clip the Y coordinate
        if(y < 0) y = 0;

        // Loop the destination Y coordinates
        for(CBP::DSInt dy = y; dy < y + size; ++dy) {
            // Done if the destination Y index is larger than the maximum Y
            if(dy >= cfxp->YElementCount) break;
            // Put the color
            cfxp->setXY(x, dy, color);
        }
    }

    // Draw generic line
    template <typename ColorT>
    static inline void drawGLine(
        ColorFX2DPainter_T* cfxp, BufferT    buffer,
        CBP::DSInt          x1,   CBP::DSInt y1,
        CBP::DSInt          x2,   CBP::DSInt y2,
        const ColorT&       color
    )
    {
        // https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm

        // Preparation
        const CBP::DSInt dx  = x2 - x1;
        const CBP::DSInt dy  = y2 - y1;
        const CBP::DSInt dxa = eCxx::abs(dx);
        const CBP::DSInt dya = eCxx::abs(dy);
              CBP::DSInt px  = 2 * dya - dxa;
              CBP::DSInt py  = 2 * dxa - dya;

        // Delta Y is less than or equal to delta X
        if(dya <= dxa) {
            // Iteration variables
            CBP::DSInt x, y, xe;
            // Draw the first point
            if(dx >= 0) {
                x  = x1;
                y  = y1;
                xe = x2;
            }
            else {
                x  = x2;
                y  = y2;
                xe = x1;
            }
            putPixelWCC(cfxp, buffer, x, y, color);
            // Draw the remaining point(s)
            for(++x; x <= xe; ++x) {
                // Update the Y coordinate
                if(px < 0) {
                    px = px + 2 * dya;
                }
                else {
                    if( (dx < 0 && dy < 0) || (dx > 0 && dy > 0) ) ++y;
                    else                                           --y;
                    px = px + 2 * (dya - dxa);
                }
                // Check the coordinates
                if(x < 0 || y < 0) continue;
                if(x >= cfxp->XElementCount || y >= cfxp->YElementCount) return;
                // Put the color
                cfxp->setXY(x, y, color);
            }
        }
        // Delta Y greater than delta X
        else {
            // Iteration variables
            CBP::DSInt x, y, ye;
            // Draw the first point
            if(dy >= 0) {
                x  = x1;
                y  = y1;
                ye = y2;
            }
            else {
                x  = x2;
                y  = y2;
                ye = y1;
            }
            putPixelWCC(cfxp, buffer, x, y, color);
            // Draw the remaining point(s)
            for(++y; y < ye; ++y) {
                // Update the X coordinate
                if(py <= 0) {
                    py = py + 2 * dxa;
                }
                else {
                    if( (dx < 0 && dy < 0) || (dx > 0 && dy > 0) ) ++x;
                    else                                           --x;
                    py = py + 2 * (dxa - dya);
                }
                // Check the coordinates
                if(x < 0 || y < 0) continue;
                if(x >= cfxp->XElementCount || y >= cfxp->YElementCount) return;
                // Put the color
                cfxp->setXY(x, y, color);
            }
        }
    }

    // Draw rectangle
    template <typename ColorT>
    static inline void drawRectangle(
        ColorFX2DPainter_T* cfxp, BufferT    buffer,
        CBP::DSInt          x1,   CBP::DSInt y1,
        CBP::DSInt          x2,   CBP::DSInt y2,
        const ColorT&       color
    )
    {
        drawHLine(cfxp, buffer, x1, y1,     x2 - x1 + 1, color);
        drawHLine(cfxp, buffer, x1, y2,     x2 - x1 + 1, color);

        drawVLine(cfxp, buffer, x1, y1 + 1, y2 - y1 - 1, color);
        drawVLine(cfxp, buffer, x2, y1 + 1, y2 - y1 - 1, color);
    }

    // Draw filled rectangle
    template <typename ColorT>
    static inline void fillRectangle(
        ColorFX2DPainter_T* cfxp, BufferT    buffer,
        CBP::DSInt          x1,   CBP::DSInt y1,
        CBP::DSInt          x2,   CBP::DSInt y2,
        const ColorT&       color
    )
    {
        for(CBP::DSInt y = y1; y <= y2; ++y) {
            drawHLine(cfxp, buffer, x1, y, x2 - x1 + 1, color);
        }
    }

    // Draw circle points
    template <typename ColorT>
    static inline void drawCirclePoints(
        ColorFX2DPainter_T* cfxp, BufferT    buffer,
        CBP::DSInt          xc,   CBP::DSInt yc,
        CBP::DSInt          x,    CBP::DSInt y,
        const ColorT&       color
    )
    {
        putPixelWCC(cfxp, buffer, xc + x, yc + y, color);
        putPixelWCC(cfxp, buffer, xc + x, yc - y, color);
        putPixelWCC(cfxp, buffer, xc - x, yc + y, color);
        putPixelWCC(cfxp, buffer, xc - x, yc - y, color);

        putPixelWCC(cfxp, buffer, xc + y, yc + x, color);
        putPixelWCC(cfxp, buffer, xc + y, yc - x, color);
        putPixelWCC(cfxp, buffer, xc - y, yc + x, color);
        putPixelWCC(cfxp, buffer, xc - y, yc - x, color);
    }

    // Fill circle points
    template <typename ColorT>
    static inline void fillCirclePoints(
        ColorFX2DPainter_T* cfxp, BufferT    buffer,
        CBP::DSInt          xc,   CBP::DSInt yc,
        CBP::DSInt          x,    CBP::DSInt y,
        const ColorT&       color
    )
    {
        drawHLine(cfxp, buffer, xc - x, yc + y, 2 * x + 1, color);
        drawHLine(cfxp, buffer, xc - x, yc - y, 2 * x + 1, color);

        drawHLine(cfxp, buffer, xc - y, yc + x, 2 * y + 1, color);
        drawHLine(cfxp, buffer, xc - y, yc - x, 2 * y + 1, color);
    }

    // Draw ellipse points
    template <typename ColorT>
    static inline void drawEllipsePoints(
        ColorFX2DPainter_T* cfxp, BufferT    buffer,
        CBP::DSInt          xc,   CBP::DSInt yc,
        CBP::DSInt          x,    CBP::DSInt y,
        const ColorT&       color
    )
    {
        putPixelWCC(cfxp, buffer, xc + x, yc + y, color);
        putPixelWCC(cfxp, buffer, xc + x, yc - y, color);
        putPixelWCC(cfxp, buffer, xc - x, yc + y, color);
        putPixelWCC(cfxp, buffer, xc - x, yc - y, color);
    }

    // Fill ellipse points
    template <typename ColorT>
    static inline void fillEllipsePoints(
        ColorFX2DPainter_T* cfxp, BufferT    buffer,
        CBP::DSInt          xc,   CBP::DSInt yc,
        CBP::DSInt          x,    CBP::DSInt y,
        const ColorT&       color
    )
    {
        drawHLine(cfxp, buffer, xc - x, yc + y, 2 * x + 1, color);
        drawHLine(cfxp, buffer, xc - x, yc - y, 2 * x + 1, color);
    }
};


//
// A simple ColorFX2D<T> based painter
//

// Put one character
template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT, typename FontPGMPointerT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::putCharXY(CBP::DSInt x, CBP::DSInt y, const ColorT& color, char ch, const BitmapFont<FontPGMPointerT>& font)
{ _CMHelper::putCharXY(this, this->begin(), x, y, color, static_cast<uint8_t>(ch), font); }


// Put text - flow horizontally
template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <bool RevFlowDir, typename ColorT, typename FontPGMPointerT, typename TextPointerT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::_putGenHTextXY(CBP::DSInt x, CBP::DSInt y, const ColorT& color, TextPointerT text, const BitmapFont<FontPGMPointerT>& font, CBP::SSInt nextCharOffset)
{
    const BufferColorType cnv = color;
          CBP::DSInt      xi  = RevFlowDir ? (x - font.sizeX + 1) : x;

    for(;;) {
        const uint8_t c = PGMReadCharPtrInc<uint8_t, TextPointerT>::_(text);
        if(!c) break;

        _CMHelper::putCharXY(this, this->begin(), xi, y, cnv, c, font);

        if(RevFlowDir) xi -= (font.sizeX + nextCharOffset);
        else           xi += (font.sizeX + nextCharOffset);
    }
}


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT, typename FontPGMPointerT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::putHTextXY(CBP::DSInt x, CBP::DSInt y, const ColorT& color, const char* text, const BitmapFont<FontPGMPointerT>& font, CBP::SSInt nextCharOffset)
{ _putGenHTextXY<false>(x, y, color, text, font, nextCharOffset); }


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT, typename TextPGMPointerT, typename FontPGMPointerT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::putHTextXY(CBP::DSInt x, CBP::DSInt y, const ColorT& color, TextPGMPointerT text, const BitmapFont<FontPGMPointerT>& font, CBP::SSInt nextCharOffset)
{ _putGenHTextXY<false>(x, y, color, text, font, nextCharOffset); }


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT, typename FontPGMPointerT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::putRevHTextXY(CBP::DSInt x, CBP::DSInt y, const ColorT& color, const char* text, const BitmapFont<FontPGMPointerT>& font, CBP::SSInt nextCharOffset)
{ _putGenHTextXY<true>(x, y, color, text, font, nextCharOffset); }


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT, typename TextPGMPointerT, typename FontPGMPointerT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::putRevHTextXY(CBP::DSInt x, CBP::DSInt y, const ColorT& color, TextPGMPointerT text, const BitmapFont<FontPGMPointerT>& font, CBP::SSInt nextCharOffset)
{ _putGenHTextXY<true>(x, y, color, text, font, nextCharOffset); }


// Put text - flow vertically
template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <bool RevFlowDir, typename ColorT, typename FontPGMPointerT, typename TextPointerT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::_putGenVTextXY(CBP::DSInt x, CBP::DSInt y, const ColorT& color, TextPointerT text, const BitmapFont<FontPGMPointerT>& font, CBP::SSInt nextCharOffset)
{
    const BufferColorType cnv = color;
          CBP::DSInt      yi  = RevFlowDir ? (y - font.sizeY + 1) : y;

    for(;;) {
        const uint8_t c = PGMReadCharPtrInc<uint8_t, TextPointerT>::_(text);
        if(!c) break;

        _CMHelper::putCharXY(this, this->begin(), x, yi, cnv, c, font);

        if(RevFlowDir) yi -= (font.sizeY + nextCharOffset);
        else           yi += (font.sizeY + nextCharOffset);
    }
}


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT, typename FontPGMPointerT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::putVTextXY(CBP::DSInt x, CBP::DSInt y, const ColorT& color, const char* text, const BitmapFont<FontPGMPointerT>& font, CBP::SSInt nextCharOffset)
{ _putGenVTextXY<false>(x, y, color, text, font, nextCharOffset); }


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT, typename TextPGMPointerT, typename FontPGMPointerT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::putVTextXY(CBP::DSInt x, CBP::DSInt y, const ColorT& color, TextPGMPointerT text, const BitmapFont<FontPGMPointerT>& font, CBP::SSInt nextCharOffset)
{ _putGenVTextXY<false>(x, y, color, text, font, nextCharOffset); }


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT, typename FontPGMPointerT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::putRevVTextXY(CBP::DSInt x, CBP::DSInt y, const ColorT& color, const char* text, const BitmapFont<FontPGMPointerT>& font, CBP::SSInt nextCharOffset)
{ _putGenVTextXY<true>(x, y, color, text, font, nextCharOffset); }


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT, typename TextPGMPointerT, typename FontPGMPointerT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::putRevVTextXY(CBP::DSInt x, CBP::DSInt y, const ColorT& color, TextPGMPointerT text, const BitmapFont<FontPGMPointerT>& font, CBP::SSInt nextCharOffset)
{ _putGenVTextXY<true>(x, y, color, text, font, nextCharOffset); }


// Draw line
template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::drawHLine(CBP::DSInt x, CBP::DSInt y, CBP::DUInt size, const ColorT& color)
{
    // Convert the color
    const BufferColorType cnv = color;

    // Draw line
    _CMHelper::drawHLine(this, this->begin(), x, y, size, cnv);
}


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::drawVLine(CBP::DSInt x, CBP::DSInt y, CBP::DUInt size, const ColorT& color)
{
    // Convert the color
    const BufferColorType cnv = color;

    // Draw line
    _CMHelper::drawVLine(this, this->begin(), x, y, size, cnv);
}


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::drawGLine(CBP::DSInt x1, CBP::DSInt y1, CBP::DSInt x2, CBP::DSInt y2, const ColorT& color)
{
    // Convert the color
    const BufferColorType cnv = color;

    // Draw line
    _CMHelper::drawGLine(this, this->begin(), x1, y1, x2, y2, cnv);
}


// Draw rectangle
template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::drawRectangle(CBP::DSInt x1, CBP::DSInt y1, CBP::DSInt x2, CBP::DSInt y2, const ColorT& color)
{
    // Convert the color
    const BufferColorType cnv = color;

    // Draw rectangle
    _CMHelper::drawRectangle(this, this->begin(), x1, y1, x2, y2, cnv);
}


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::fillRectangle(CBP::DSInt x1, CBP::DSInt y1, CBP::DSInt x2, CBP::DSInt y2, const ColorT& color)
{
    // Convert the color
    const BufferColorType cnv = color;

    // Clip the coordinates
         if(x1 <  0            ) x1 = 0;
    else if(x1 >= XElementCount) x1 = XElementCount - 1;

         if(x2 <  0            ) x2 = 0;
    else if(x2 >= XElementCount) x2 = XElementCount - 1;

         if(y1 <  0            ) y1 = 0;
    else if(y1 >= YElementCount) y1 = YElementCount - 1;

         if(y2 <  0            ) y2 = 0;
    else if(y2 >= YElementCount) y2 = YElementCount - 1;

    // Swap the coordinates
    if(x1 > x2) eCxx::swap(x1, x2);
    if(y1 > y2) eCxx::swap(y1, y2);

    // Draw filled rectangle
    _CMHelper::fillRectangle(this, this->begin(), x1, y1, x2, y2, cnv);
}


// Draw circle
template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT, bool Fill>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::_dfCircle(CBP::DSInt xc, CBP::DSInt yc, CBP::DSInt r, const ColorT& color)
{
    // Based on https://www.geeksforgeeks.org/bresenhams-circle-drawing-algorithm

    // Convert the color
    const BufferColorType cnv = color;

    // Handle special case
    if(!r) {
        _CMHelper::putPixelWCC(this, this->begin(), xc, yc, cnv);
        return;
    }

    // Draw circle
    CBP::DSInt x = 0;
    CBP::DSInt y = r;
    CBP::DSInt d = 3 - 2 * r;

    if(Fill) _CMHelper::fillCirclePoints(this, this->begin(), xc, yc, x, y, cnv);
    else     _CMHelper::drawCirclePoints(this, this->begin(), xc, yc, x, y, cnv);

    while(y >= x) {
        ++x;

        if(d > 0) {
            --y;
            d = d + 4 * (x - y) + 10;
        }
        else {
            d = d + 4 * x + 6;
        }

        if(Fill) _CMHelper::fillCirclePoints(this, this->begin(), xc, yc, x, y, cnv);
        else     _CMHelper::drawCirclePoints(this, this->begin(), xc, yc, x, y, cnv);
    }
}


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::drawCircle(CBP::DSInt xc, CBP::DSInt yc, CBP::DSInt r, const ColorT& color)
{ _dfCircle<ColorT, false>(xc, yc, r, color); }


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::fillCircle(CBP::DSInt xc, CBP::DSInt yc, CBP::DSInt r, const ColorT& color)
{ _dfCircle<ColorT, true>(xc, yc, r, color); }


// Draw ellipse
template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT, bool Fill>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::_dfEllipse(CBP::DSInt xc, CBP::DSInt yc, CBP::DSInt rx, CBP::DSInt ry, const ColorT& color)
{
    // Convert the color
    const BufferColorType cnv = color;

    // Handle special cases
    if(!rx && !ry) {
        _CMHelper::putPixelWCC(this, this->begin(), xc, yc, cnv);
        return;
    }
    else if(!rx) {
        _CMHelper::drawVLine(this, this->begin(), xc, yc - ry, ry * 2 + 1, cnv);
        return;
    }
    else if(!ry) {
        _CMHelper::drawHLine(this, this->begin(), xc - rx, yc, rx * 2 + 1, cnv);
        return;
    }

    //
    // Draw ellipse
    //

#if 1

    // Based on http://www.dapsen.com/SWAG/GRAPHICS/0276.PAS.html

    // Draw ellipse - the horizontal part
    if(Fill) {
        _CMHelper::drawHLine(this, this->begin(), xc - rx, yc, rx * 2 + 1, cnv);
    }
    else {
        _CMHelper::putPixelWCC(this, this->begin(), xc - rx, yc, cnv);
        _CMHelper::putPixelWCC(this, this->begin(), xc + rx, yc, cnv);
    }

    // Draw ellipse - the remaining part
    const CBP::DSInt dx = 2 * rx * rx;
    const CBP::DSInt dy = 2 * ry * ry;

    CBP::DSInt mx1 = xc - rx;
    CBP::DSInt my1 = yc;

    CBP::DSInt mx2 = xc + rx;
    CBP::DSInt my2 = yc;

    CBP::DSInt r   = rx * ry * ry;
    CBP::DSInt rx2 = r * 2;
    CBP::DSInt ry2 = 0;
    CBP::DSInt x   = rx;

    while(x > 0) {
        // Update coordinates
        if(r > 0) {
            ++my1;
            --my2;
            ry2 -= dx;
            r   += ry2;
        }
        if(r <= 0) {
            --x;
            ++mx1;
            --mx2;
            rx2 -= dy;
            r   += rx2;
        }
        // Draw points
        if(Fill) {
            _CMHelper::drawHLine(this, this->begin(), mx1, my1, mx2 - mx1 + 1, cnv);
            _CMHelper::drawHLine(this, this->begin(), mx1, my2, mx2 - mx1 + 1, cnv);
        }
        else {
            _CMHelper::putPixelWCC(this, this->begin(), mx1, my1, cnv);
            _CMHelper::putPixelWCC(this, this->begin(), mx1, my2, cnv);
            _CMHelper::putPixelWCC(this, this->begin(), mx2, my1, cnv);
            _CMHelper::putPixelWCC(this, this->begin(), mx2, my2, cnv);
        }
    }

#else

    // Based on https://dai.fmph.uniba.sk/upload/0/01/Ellipse.pdf

    // Draw ellipse - preparation
    const CBP::DSInt a = 2 * rx * rx;
    const CBP::DSInt b = 2 * ry * ry;

    // Draw ellipse - 1st set of points (left - right)
    CBP::DSInt err  = 0;
    CBP::DSInt xChg = ry * ry * (1 - 2 * rx);
    CBP::DSInt yChg = rx * rx;
    CBP::DSInt xStp = rx * b;
    CBP::DSInt yStp = 0;
    CBP::DSInt x    = rx;
    CBP::DSInt y    = 0;

    while(xStp >= yStp) {
        if(Fill) _CMHelper::fillEllipsePoints(this, this->begin(), xc, yc, x, y, cnv);
        else     _CMHelper::drawEllipsePoints(this, this->begin(), xc, yc, x, y, cnv);

        ++y;
        yStp += a;
        err  += yChg;
        yChg += a;

        if(2 * err + xChg > 0) {
            --x;
            xStp -= b;
            err  += xChg;
            xChg += b;
        }
    }

    // Draw ellipse - 2nd set of points (top - bottom)
    err  = 0;
    xChg = ry * ry;
    yChg = rx * rx * (1 - 2 * ry);
    xStp = 0;
    yStp = ry * a;
    x    = 0;
    y    = ry;

    while(xStp <= yStp) {
        if(Fill) _CMHelper::fillEllipsePoints(this, this->begin(), xc, yc, x, y, cnv);
        else     _CMHelper::drawEllipsePoints(this, this->begin(), xc, yc, x, y, cnv);

        ++x;
        xStp += b;
        err  += xChg;
        xChg += b;

        if(2 * err + yChg > 0) {
            --y;
            yStp -= a;
            err  += yChg;
            yChg += a;
        }
    }

#endif
}


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::drawEllipse(CBP::DSInt xc, CBP::DSInt yc, CBP::DSInt rx, CBP::DSInt ry, const ColorT& color)
{ _dfEllipse<ColorT, false>(xc, yc, rx, ry, color); }


template <typename BufferColorT, CBP::SUInt XSizeN, CBP::SUInt YSizeN, typename LayoutPolicyT, typename BufferPolicyT, CBP::SUInt BlockXSizeN, CBP::SUInt BlockYSizeN, CBP::SUInt InterYSizeN, bool UseDoubleBufferring, typename ColorPGMPtrT>
template <typename ColorT>
inline void ColorFX2DPainter<BufferColorT, XSizeN, YSizeN, LayoutPolicyT, BufferPolicyT, BlockXSizeN, BlockYSizeN, InterYSizeN, UseDoubleBufferring, ColorPGMPtrT>
::fillEllipse(CBP::DSInt xc, CBP::DSInt yc, CBP::DSInt rx, CBP::DSInt ry, const ColorT& color)
{ _dfEllipse<ColorT, true>(xc, yc, rx, ry, color); }


} // namespace eCxx


#endif
