/***************************************************************************
 *   crengine-ng                                                           *
 *   Copyright (C) 2007-2014 Vadim Lopatin <coolreader.org@gmail.com>      *
 *   Copyright (C) 2011 Konstantin Potapov <pkbo@users.sourceforge.net>    *
 *   Copyright (C) 2015,2016 Yifei(Frank) ZHU <fredyifei@gmail.com>        *
 *   Copyright (C) 2018-2020 poire-z <poire-z@users.noreply.github.com>    *
 *   Copyright (C) 2019-2021 NiLuJe <ninuje@gmail.com>                     *
 *   Copyright (C) 2021 Aleksey Chernov <valexlin@gmail.com>               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU General Public License           *
 *   as published by the Free Software Foundation; either version 2        *
 *   of the License, or (at your option) any later version.                *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,            *
 *   MA 02110-1301, USA.                                                   *
 ***************************************************************************/

#include <lvcolordrawbuf.h>

#include "lvimagescaleddrawcallback.h"
#include "lvdrawbuf_utils.h"

// Blend mono 1-bit bitmap (8 pixels per byte)
static inline void blendBitmap_monoTo16bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch, lUInt16 color) {
    lUInt16* dst;
    lUInt16* dstline;
    const lUInt8* src;
    unsigned char mask;
    for (; height; height--) {
        src = bitmap;
        dstline = ((lUInt16*)d->GetScanLine(y++)) + x;
        dst = dstline;
        if (!dst) { // Should not happen, but avoid clang-tidy warning below
            break;
        }
        mask = 0x80;
        for (int xx = width; xx > 0; --xx) {
            if (*src & mask)
                *dst = color;
            mask >>= 1;
            if (0 == mask) {
                mask = 0x80;
                src++;
            }
            /* next pixel */
            dst++;
        }
        /* new dest line */
        bitmap += bmp_pitch;
    }
}

// Blend gray 8-bit bitmap (1 byte per pixel)
static inline void blendBitmap_grayTo16bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch, lUInt16 color) {
    lUInt16 tr = (color >> 11);
    lUInt16 tg = ((color >> 5) & 0x003F);
    lUInt16 tb = (color & 0x001F);
    lUInt16* dst;
    lUInt16* dstline;
    const lUInt8* src;
    lUInt16 alpha;
    lUInt16 alpha_r, alpha_g, alpha_b;
    lUInt16 r, g, b;
    for (; height; height--) {
        src = bitmap;
        dstline = ((lUInt16*)d->GetScanLine(y++)) + x;
        dst = dstline;
        if (!dst) { // Should not happen, but avoid clang-tidy warning below
            break;
        }
        for (int xx = width; xx > 0; --xx) {
            alpha = *(src++);
            alpha_r = alpha >> 3;
            alpha_g = alpha >> 2;
            alpha_b = alpha >> 3;
            // blending function (OVER operator)
            // See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_render_glyph
            r = (alpha_r * tr + (31 - alpha_r) * ((*dst) >> 11) + 15) / 31;
            g = (alpha_g * tg + (63 - alpha_g) * (((*dst) >> 5) & 0x003F) + 31) / 63;
            b = (alpha_b * tb + (31 - alpha_b) * ((*dst) & 0x00001F) + 15) / 31;
            *dst = (r << 11) | (g << 5) | b;
            /* next pixel */
            dst++;
        }
        /* new dest line */
        bitmap += bmp_pitch;
    }
}

// Blend color 24-bit bitmap (3 bytes per pixel)
static inline void blendBitmap_rgbTo16bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch, lUInt16 color) {
    lUInt16 tr = (color >> 11);
    lUInt16 tg = ((color >> 5) & 0x003F);
    lUInt16 tb = (color & 0x001F);
    lUInt16* dst;
    lUInt16* dstline;
    const lUInt8* src;
    lUInt16 alpha_r, alpha_g, alpha_b;
    lUInt16 r, g, b;
    for (; height; height--) {
        src = bitmap;
        dstline = ((lUInt16*)d->GetScanLine(y++)) + x;
        dst = dstline;
        if (!dst) { // Should not happen, but avoid clang-tidy warning below
            break;
        }
        for (int xx = width; xx > 0; --xx) {
            alpha_r = *(src++) >> 3;
            alpha_g = *(src++) >> 2;
            alpha_b = *(src++) >> 3;
            // blending function (OVER operator)
            // See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_render_glyph
            r = (alpha_r * tr + (31 - alpha_r) * ((*dst) >> 11) + 15) / 31;
            g = (alpha_g * tg + (63 - alpha_g) * (((*dst) >> 5) & 0x003F) + 31) / 63;
            b = (alpha_b * tb + (31 - alpha_b) * ((*dst) & 0x001F) + 15) / 31;
            *dst = (r << 11) | (g << 5) | b;
            /* next pixel */
            dst++;
        }
        /* new dest line */
        bitmap += bmp_pitch;
    }
}

// Blend color 24-bit bitmap (3 bytes per pixel)
static inline void blendBitmap_bgrTo16bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch, lUInt16 color) {
    lUInt16 tr = (color >> 11);
    lUInt16 tg = ((color >> 5) & 0x003F);
    lUInt16 tb = (color & 0x001F);
    lUInt16* dst;
    lUInt16* dstline;
    const lUInt8* src;
    lUInt16 alpha_r, alpha_g, alpha_b;
    lUInt16 r, g, b;
    for (; height; height--) {
        src = bitmap;
        dstline = ((lUInt16*)d->GetScanLine(y++)) + x;
        dst = dstline;
        if (!dst) { // Should not happen, but avoid clang-tidy warning below
            break;
        }
        for (int xx = width; xx > 0; --xx) {
            alpha_b = *(src++) >> 3;
            alpha_g = *(src++) >> 2;
            alpha_r = *(src++) >> 3;
            // blending function (OVER operator)
            // See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_render_glyph
            r = (alpha_r * tr + (31 - alpha_r) * ((*dst) >> 11) + 15) / 31;
            g = (alpha_g * tg + (63 - alpha_g) * (((*dst) >> 5) & 0x003F) + 31) / 63;
            b = (alpha_b * tb + (31 - alpha_b) * ((*dst) & 0x001F) + 15) / 31;
            *dst = (r << 11) | (g << 5) | b;
            /* next pixel */
            dst++;
        }
        /* new dest line */
        bitmap += bmp_pitch;
    }
}

// Blend color 24-bit bitmap (3 bytes per pixel, vertical)
static inline void blendBitmap_rgbvTo16bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch, lUInt16 color) {
    lUInt16 tr = (color >> 11);
    lUInt16 tg = ((color >> 5) & 0x003F);
    lUInt16 tb = (color & 0x001F);
    lUInt16* dst;
    lUInt16* dstline;
    const lUInt8* src;
    lUInt16 alpha_r, alpha_g, alpha_b;
    lUInt16 r, g, b;
    for (int xx = 0; xx < width; xx++) {
        src = bitmap;
        for (int yy = 0; yy < height; yy++) {
            dstline = ((lUInt16*)d->GetScanLine(y + yy)) + x + xx;
            dst = dstline;
            if (!dst) { // Should not happen, but avoid clang-tidy warning below
                break;
            }
            alpha_r = src[xx] >> 3;
            alpha_g = src[xx + bmp_pitch] >> 2;
            alpha_b = src[xx + 2 * bmp_pitch] >> 3;
            // blending function (OVER operator)
            // See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_render_glyph
            r = (alpha_r * tr + (31 - alpha_r) * ((*dst) >> 11) + 15) / 31;
            g = (alpha_g * tg + (63 - alpha_g) * (((*dst) >> 5) & 0x003F) + 31) / 63;
            b = (alpha_b * tb + (31 - alpha_b) * ((*dst) & 0x001F) + 15) / 31;
            *dst = (r << 11) | (g << 5) | b;
            /* next pixel */
            //dst++;
            /* new dest line */
            src += 3 * bmp_pitch;
        }
    }
}

// Blend color 24-bit bitmap (3 bytes per pixel, vertical)
static inline void blendBitmap_bgrvTo16bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch, lUInt16 color) {
    lUInt16 tr = (color >> 11);
    lUInt16 tg = ((color >> 5) & 0x003F);
    lUInt16 tb = (color & 0x001F);
    lUInt16* dst;
    lUInt16* dstline;
    const lUInt8* src;
    lUInt16 alpha_r, alpha_g, alpha_b;
    lUInt16 r, g, b;
    for (int xx = 0; xx < width; xx++) {
        src = bitmap;
        for (int yy = 0; yy < height; yy++) {
            dstline = ((lUInt16*)d->GetScanLine(y + yy)) + x + xx;
            dst = dstline;
            if (!dst) { // Should not happen, but avoid clang-tidy warning below
                break;
            }
            alpha_b = src[xx] >> 3;
            alpha_g = src[xx + bmp_pitch] >> 2;
            alpha_r = src[xx + 2 * bmp_pitch] >> 3;
            // blending function (OVER operator)
            // See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_render_glyph
            r = (alpha_r * tr + (31 - alpha_r) * ((*dst) >> 11) + 15) / 31;
            g = (alpha_g * tg + (63 - alpha_g) * (((*dst) >> 5) & 0x003F) + 31) / 63;
            b = (alpha_b * tb + (31 - alpha_b) * ((*dst) & 0x001F) + 15) / 31;
            *dst = (r << 11) | (g << 5) | b;
            /* next pixel */
            //dst++;
            /* new dest line */
            src += 3 * bmp_pitch;
        }
    }
}

// Blend mono 1-bit bitmap (8 pixels per byte)
static inline void blendBitmap_monoTo32bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch, lUInt32 color) {
    lUInt32* dst;
    lUInt32* dstline;
    const lUInt8* src;
    for (; height; height--) {
        src = bitmap;
        dstline = ((lUInt32*)d->GetScanLine(y++)) + x;
        dst = dstline;
        if (!dst) { // Should not happen, but avoid clang-tidy warning below
            break;
        }
        unsigned char mask = 0x80;
        for (int xx = width; xx > 0; --xx) {
            if (*src & mask)
                *dst = color;
            mask >>= 1;
            if (0 == mask) {
                mask = 0x80;
                src++;
            }
            /* next pixel */
            dst++;
        }
        /* new dest line */
        bitmap += bmp_pitch;
    }
}

// Blend gray 8-bit bitmap (1 byte per pixel)
static inline void blendBitmap_grayTo32bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch, lUInt32 color) {
    lUInt32 tr = (color >> 16);
    lUInt32 tg = ((color >> 8) & 0x00FF);
    lUInt32 tb = (color & 0x00FF);
    lUInt32* dst;
    lUInt32* dstline;
    const lUInt8* src;
    lUInt32 alpha;
    lUInt32 r, g, b;
    for (; height; height--) {
        src = bitmap;
        dstline = ((lUInt32*)d->GetScanLine(y++)) + x;
        dst = dstline;
        if (!dst) { // Should not happen, but avoid clang-tidy warning below
            break;
        }
        for (int xx = width; xx > 0; --xx) {
            alpha = *(src++);
            // blending function (OVER operator)
            // See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_render_glyph
            r = (alpha * tr + (255 - alpha) * ((*dst) >> 16) + 127) / 255;
            g = (alpha * tg + (255 - alpha) * (((*dst) >> 8) & 0x00FF) + 127) / 255;
            b = (alpha * tb + (255 - alpha) * ((*dst) & 0x00FF) + 127) / 255;
            *dst = (r << 16) | (g << 8) | b;
            /* next pixel */
            dst++;
        }
        /* new dest line */
        bitmap += bmp_pitch;
    }
}

// Blend color 24-bit bitmap (3 bytes per pixel)
static inline void blendBitmap_rgbTo32bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch, lUInt32 color) {
    lUInt32 tr = (color >> 16);
    lUInt32 tg = ((color >> 8) & 0x00FF);
    lUInt32 tb = (color & 0x00FF);
    lUInt32* dst;
    lUInt32* dstline;
    const lUInt8* src;
    lUInt32 alpha_r, alpha_g, alpha_b;
    lUInt32 r, g, b;
    for (; height; height--) {
        src = bitmap;
        dstline = ((lUInt32*)d->GetScanLine(y++)) + x;
        dst = dstline;
        if (!dst) { // Should not happen, but avoid clang-tidy warning below
            break;
        }
        for (int xx = width; xx > 0; --xx) {
            alpha_r = *(src++);
            alpha_g = *(src++);
            alpha_b = *(src++);
            // blending function (OVER operator)
            // See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_render_glyph
            r = (alpha_r * tr + (255 - alpha_r) * ((*dst) >> 16) + 127) / 255;
            g = (alpha_g * tg + (255 - alpha_g) * (((*dst) >> 8) & 0x00FF) + 127) / 255;
            b = (alpha_b * tb + (255 - alpha_b) * ((*dst) & 0x00FF) + 127) / 255;
            *dst = (r << 16) | (g << 8) | b;
            /* next pixel */
            dst++;
        }
        /* new dest line */
        bitmap += bmp_pitch;
    }
}

// Blend color 24-bit bitmap (3 bytes per pixel)
static inline void blendBitmap_bgrTo32bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch, lUInt32 color) {
    lUInt32 tr = (color >> 16);
    lUInt32 tg = ((color >> 8) & 0x00FF);
    lUInt32 tb = (color & 0x00FF);
    lUInt32* dst;
    lUInt32* dstline;
    const lUInt8* src;
    lUInt32 alpha_r, alpha_g, alpha_b;
    lUInt32 r, g, b;
    for (; height; height--) {
        src = bitmap;
        dstline = ((lUInt32*)d->GetScanLine(y++)) + x;
        dst = dstline;
        if (!dst) { // Should not happen, but avoid clang-tidy warning below
            break;
        }
        for (int xx = width; xx > 0; --xx) {
            alpha_b = *(src++);
            alpha_g = *(src++);
            alpha_r = *(src++);
            // blending function (OVER operator)
            // See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_render_glyph
            r = (alpha_r * tr + (255 - alpha_r) * ((*dst) >> 16) + 127) / 255;
            g = (alpha_g * tg + (255 - alpha_g) * (((*dst) >> 8) & 0x00FF) + 127) / 255;
            b = (alpha_b * tb + (255 - alpha_b) * ((*dst) & 0x00FF) + 127) / 255;
            *dst = (r << 16) | (g << 8) | b;
            /* next pixel */
            dst++;
        }
        /* new dest line */
        bitmap += bmp_pitch;
    }
}

// Blend color 24-bit bitmap (3 bytes per pixel, vertical)
static inline void blendBitmap_rgbvTo32bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch, lUInt32 color) {
    lUInt32 tr = (color >> 16);
    lUInt32 tg = ((color >> 8) & 0x00FF);
    lUInt32 tb = (color & 0x00FF);
    lUInt32* dst;
    lUInt32* dstline;
    const lUInt8* src;
    lUInt32 alpha_r, alpha_g, alpha_b;
    lUInt32 r, g, b;
    for (int xx = 0; xx < width; xx++) {
        src = bitmap;
        for (int yy = 0; yy < height; yy++) {
            dstline = ((lUInt32*)d->GetScanLine(y + yy)) + x + xx;
            dst = dstline;
            if (!dst) { // Should not happen, but avoid clang-tidy warning below
                break;
            }
            alpha_r = src[xx];
            alpha_g = src[xx + bmp_pitch];
            alpha_b = src[xx + 2 * bmp_pitch];
            // blending function (OVER operator)
            // See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_render_glyph
            r = (alpha_r * tr + (255 - alpha_r) * ((*dst) >> 16) + 127) / 255;
            g = (alpha_g * tg + (255 - alpha_g) * (((*dst) >> 8) & 0x00FF) + 127) / 255;
            b = (alpha_b * tb + (255 - alpha_b) * ((*dst) & 0x00FF) + 127) / 255;
            *dst = (r << 16) | (g << 8) | b;
            /* next pixel */
            //dst++;
            /* new dest line */
            src += 3 * bmp_pitch;
        }
    }
}

// Blend color 24-bit bitmap (3 bytes per pixel, vertical)
static inline void blendBitmap_bgrvTo32bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch, lUInt32 color) {
    lUInt32 tr = (color >> 16);
    lUInt32 tg = ((color >> 8) & 0x00FF);
    lUInt32 tb = (color & 0x00FF);
    lUInt32* dst;
    lUInt32* dstline;
    const lUInt8* src;
    lUInt32 alpha_r, alpha_g, alpha_b;
    lUInt32 r, g, b;
    for (int xx = 0; xx < width; xx++) {
        src = bitmap;
        for (int yy = 0; yy < height; yy++) {
            dstline = ((lUInt32*)d->GetScanLine(y + yy)) + x + xx;
            dst = dstline;
            if (!dst) { // Should not happen, but avoid clang-tidy warning below
                break;
            }
            alpha_b = src[xx];
            alpha_g = src[xx + bmp_pitch];
            alpha_r = src[xx + 2 * bmp_pitch];
            // blending function (OVER operator)
            // See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_render_glyph
            r = (alpha_r * tr + (255 - alpha_r) * ((*dst) >> 16) + 127) / 255;
            g = (alpha_g * tg + (255 - alpha_g) * (((*dst) >> 8) & 0x00FF) + 127) / 255;
            b = (alpha_b * tb + (255 - alpha_b) * ((*dst) & 0x00FF) + 127) / 255;
            *dst = (r << 16) | (g << 8) | b;
            /* next pixel */
            //dst++;
            /* new dest line */
            src += 3 * bmp_pitch;
        }
    }
}

// Blend color 32-bit bitmap (4 bytes per pixel, BGRA)
static inline void blendBitmap_bgraTo32bpp(LVDrawBuf* d, int x, int y, const lUInt8* bitmap, int width, int height, int bmp_pitch) {
    lUInt32* dst;
    lUInt32* dstline;
    const lUInt8* src;
    lUInt32 alpha;
    lUInt32 r, g, b;
    for (; height; height--) {
        src = bitmap;
        dstline = ((lUInt32*)d->GetScanLine(y++)) + x;
        dst = dstline;
        if (!dst) { // Should not happen, but avoid clang-tidy warning below
            break;
        }
        for (int xx = width; xx > 0; --xx) {
            b = *(src++);
            g = *(src++);
            r = *(src++);
            alpha = *(src++);
            // BGRA bitmap from FreeType are premultipled
            // Unpremultiply:
            if (alpha > 0 && alpha < 255) {
                r = 255 * r / alpha;
                g = 255 * g / alpha;
                b = 255 * b / alpha;
            }
            // blending function (OVER operator)
            // See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_render_glyph
            r = (alpha * r + (255 - alpha) * ((*dst) >> 16) + 127) / 255;
            g = (alpha * g + (255 - alpha) * (((*dst) >> 8) & 0x00FF) + 127) / 255;
            b = (alpha * b + (255 - alpha) * ((*dst) & 0x00FF) + 127) / 255;
            *dst = (r << 16) | (g << 8) | b;
            /* next pixel */
            dst++;
        }
        /* new dest line */
        bitmap += bmp_pitch;
    }
}

/// rotates buffer contents by specified angle
void LVColorDrawBuf::Rotate(cr_rotate_angle_t angle) {
    if (angle == CR_ROTATE_ANGLE_0)
        return;
    if (_bpp == 16) {
        int sz = (_dx * _dy);
        if (angle == CR_ROTATE_ANGLE_180) {
            lUInt16* buf = (lUInt16*)_data;
            for (int i = sz / 2 - 1; i >= 0; i--) {
                lUInt16 tmp = buf[i];
                buf[i] = buf[sz - i - 1];
                buf[sz - i - 1] = tmp;
            }
            return;
        }
        int newrowsize = _dy * 2;
        sz = (_dx * newrowsize);
        lUInt16* dst = (lUInt16*)malloc(sz);
#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
        bool cw = angle != CR_ROTATE_ANGLE_90;
#else
        bool cw = angle == CR_ROTATE_ANGLE_90;
#endif
        for (int y = 0; y < _dy; y++) {
            lUInt16* src = (lUInt16*)_data + _dx * y;
            int nx, ny;
            if (cw) {
                nx = _dy - 1 - y;
            } else {
                nx = y;
            }
            for (int x = 0; x < _dx; x++) {
                if (cw) {
                    ny = x;
                } else {
                    ny = _dx - 1 - x;
                }
                dst[_dy * ny + nx] = src[x];
            }
        }
#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
        memcpy(_data, dst, sz);
        free(dst);
#else
        free(_data);
        _data = (lUInt8*)dst;
#endif
        int tmp = _dx;
        _dx = _dy;
        _dy = tmp;
        _rowsize = newrowsize;
    } else {
        int sz = (_dx * _dy);
        if (angle == CR_ROTATE_ANGLE_180) {
            lUInt32* buf = (lUInt32*)_data;
            for (int i = sz / 2 - 1; i >= 0; i--) {
                lUInt32 tmp = buf[i];
                buf[i] = buf[sz - i - 1];
                buf[sz - i - 1] = tmp;
            }
            return;
        }
        int newrowsize = _dy * 4;
        sz = (_dx * newrowsize);
        lUInt32* dst = (lUInt32*)malloc(sz);
#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
        bool cw = angle != CR_ROTATE_ANGLE_90;
#else
        bool cw = angle == CR_ROTATE_ANGLE_90;
#endif
        for (int y = 0; y < _dy; y++) {
            lUInt32* src = (lUInt32*)_data + _dx * y;
            int nx, ny;
            if (cw) {
                nx = _dy - 1 - y;
            } else {
                nx = y;
            }
            for (int x = 0; x < _dx; x++) {
                if (cw) {
                    ny = x;
                } else {
                    ny = _dx - 1 - x;
                }
                dst[_dy * ny + nx] = src[x];
            }
        }
#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
        memcpy(_data, dst, sz);
        free(dst);
#else
        free(_data);
        _data = (lUInt8*)dst;
#endif
        int tmp = _dx;
        _dx = _dy;
        _dy = tmp;
        _rowsize = newrowsize;
    }
}

/// invert image
void LVColorDrawBuf::Invert() {
}

/// get buffer bits per pixel
int LVColorDrawBuf::GetBitsPerPixel() const {
    return _bpp;
}

void LVColorDrawBuf::Draw(LVImageSourceRef img, int x, int y, int width, int height, bool dither) {
    //fprintf( stderr, "LVColorDrawBuf::Draw( img(%d, %d), %d, %d, %d, %d\n", img->GetWidth(), img->GetHeight(), x, y, width, height );
    LVImageScaledDrawCallback drawcb(this, img, x, y, width, height, dither, _invertImages, _smoothImages);
    img->Decode(&drawcb);
    _drawnImagesCount++;
    _drawnImagesSurface += width * height;
}

/// fills buffer with specified color
void LVColorDrawBuf::Clear(lUInt32 color) {
    if (_bpp == 16) {
        lUInt16 cl16 = rgb888to565(color);
        for (int y = 0; y < _dy; y++) {
            lUInt16* dst = (lUInt16*)GetScanLine(y);
            size_t px_count = _dx;
            while (px_count--) {
                *dst++ = cl16;
            }
        }
    } else {
        lUInt32 cl32 = color;
        for (int y = 0; y < _dy; y++) {
            lUInt32* dst = (lUInt32*)GetScanLine(y);
            size_t px_count = _dx;
            while (px_count--) {
                *dst++ = cl32;
            }
        }
    }
}

/// get pixel value
lUInt32 LVColorDrawBuf::GetPixel(int x, int y) const {
    if (!_data || y < 0 || x < 0 || y >= _dy || x >= _dx)
        return 0;
    if (_bpp == 16)
        return rgb565to888(((lUInt16*)GetScanLine(y))[x]);
    return ((lUInt32*)GetScanLine(y))[x];
}

/// fills rectangle with specified color
void LVColorDrawBuf::FillRect(int x0, int y0, int x1, int y1, lUInt32 color) {
    if (x0 < _clip.left)
        x0 = _clip.left;
    if (y0 < _clip.top)
        y0 = _clip.top;
    if (x1 > _clip.right)
        x1 = _clip.right;
    if (y1 > _clip.bottom)
        y1 = _clip.bottom;
    if (x0 >= x1 || y0 >= y1)
        return;
    int alpha = (color >> 24) & 0xFF;
    if (_bpp == 16) {
        lUInt16 cl16 = rgb888to565(color);
        for (int y = y0; y < y1; y++) {
            lUInt16* line = (lUInt16*)GetScanLine(y);
            for (int x = x0; x < x1; x++) {
                if (alpha)
                    ApplyAlphaRGB565(line[x], cl16, alpha);
                else
                    line[x] = cl16;
            }
        }
    } else {
        for (int y = y0; y < y1; y++) {
            lUInt32* line = (lUInt32*)GetScanLine(y);
            for (int x = x0; x < x1; x++) {
                if (alpha)
                    ApplyAlphaRGB(line[x], color, alpha);
                else
                    line[x] = color;
            }
        }
    }
}

void LVColorDrawBuf::DrawLine(int x0, int y0, int x1, int y1, lUInt32 color0, int length1, int length2, int direction) {
    if (x0 < _clip.left)
        x0 = _clip.left;
    if (y0 < _clip.top)
        y0 = _clip.top;
    if (x1 > _clip.right)
        x1 = _clip.right;
    if (y1 > _clip.bottom)
        y1 = _clip.bottom;
    if (x0 >= x1 || y0 >= y1)
        return;
    if (_bpp == 16) {
        lUInt16 cl16 = rgb888to565(color0);
        for (int y = y0; y < y1; y++) {
            lUInt16* line = (lUInt16*)GetScanLine(y);
            for (int x = x0; x < x1; x++) {
                if (direction == 0 && x % (length1 + length2) < length1)
                    line[x] = cl16;
                if (direction == 1 && y % (length1 + length2) < length1)
                    line[x] = cl16;
            }
        }
    } else {
        for (int y = y0; y < y1; y++) {
            lUInt32* line = (lUInt32*)GetScanLine(y);
            for (int x = x0; x < x1; x++) {
                if (direction == 0 && x % (length1 + length2) < length1)
                    line[x] = color0;
                if (direction == 1 && y % (length1 + length2) < length1)
                    line[x] = color0;
            }
        }
    }
}

/// fills rectangle with specified color
void LVColorDrawBuf::FillRectPattern(int x0, int y0, int x1, int y1, lUInt32 color0, lUInt32 color1, const lUInt8* pattern) {
    if (x0 < _clip.left)
        x0 = _clip.left;
    if (y0 < _clip.top)
        y0 = _clip.top;
    if (x1 > _clip.right)
        x1 = _clip.right;
    if (y1 > _clip.bottom)
        y1 = _clip.bottom;
    if (x0 >= x1 || y0 >= y1)
        return;
    if (_bpp == 16) {
        lUInt16 cl16_0 = rgb888to565(color0);
        lUInt16 cl16_1 = rgb888to565(color1);
        for (int y = y0; y < y1; y++) {
            lUInt8 patternMask = pattern[y & 3];
            lUInt16* line = (lUInt16*)GetScanLine(y);
            for (int x = x0; x < x1; x++) {
                lUInt8 patternBit = (patternMask << (x & 7)) & 0x80;
                line[x] = patternBit ? cl16_1 : cl16_0;
            }
        }
    } else {
        for (int y = y0; y < y1; y++) {
            lUInt8 patternMask = pattern[y & 3];
            lUInt32* line = (lUInt32*)GetScanLine(y);
            for (int x = x0; x < x1; x++) {
                lUInt8 patternBit = (patternMask << (x & 7)) & 0x80;
                line[x] = patternBit ? color1 : color0;
            }
        }
    }
}

/// sets new size
void LVColorDrawBuf::Resize(int dx, int dy) {
    if (dx == _dx && dy == _dy) {
        //CRLog::trace("LVColorDrawBuf::Resize : no resize, not changed");
        return;
    }
    if (!_ownData) {
        //CRLog::trace("LVColorDrawBuf::Resize : no resize, own data");
        return;
    }
    //CRLog::trace("LVColorDrawBuf::Resize : resizing %d x %d to %d x %d", _dx, _dy, dx, dy);
    // delete old bitmap
    if (_dx > 0 && _dy > 0 && _data) {
#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
        if (_drawbmp)
            DeleteObject(_drawbmp);
        if (_drawdc)
            DeleteObject(_drawdc);
        _drawbmp = NULL;
        _drawdc = NULL;
#else
        free(_data);
#endif
        _data = NULL;
        _rowsize = 0;
        _dx = 0;
        _dy = 0;
    }

    if (dx > 0 && dy > 0) {
        // create new bitmap
        _dx = dx;
        _dy = dy;
        _rowsize = dx * (_bpp >> 3);
#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
        BITMAPINFO bmi = { 0 };
        bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
        bmi.bmiHeader.biWidth = _dx;
        bmi.bmiHeader.biHeight = _dy;
        bmi.bmiHeader.biPlanes = 1;
        bmi.bmiHeader.biBitCount = 32;
        bmi.bmiHeader.biCompression = BI_RGB;
        bmi.bmiHeader.biSizeImage = 0;
        bmi.bmiHeader.biXPelsPerMeter = 1024;
        bmi.bmiHeader.biYPelsPerMeter = 1024;
        bmi.bmiHeader.biClrUsed = 0;
        bmi.bmiHeader.biClrImportant = 0;

        _drawbmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)(&_data), NULL, 0);
        _drawdc = CreateCompatibleDC(NULL);
        SelectObject(_drawdc, _drawbmp);
        memset(_data, 0, _rowsize * _dy);
#else
        _data = (lUInt8*)calloc((_bpp >> 3) * _dx * _dy, sizeof(*_data));
#endif
    }
    SetClipRect(NULL);
}

void LVColorDrawBuf::InvertRect(int x0, int y0, int x1, int y1) {
    if (x0 < _clip.left)
        x0 = _clip.left;
    if (y0 < _clip.top)
        y0 = _clip.top;
    if (x1 > _clip.right)
        x1 = _clip.right;
    if (y1 > _clip.bottom)
        y1 = _clip.bottom;
    if (x0 >= x1 || y0 >= y1)
        return;

    if (_bpp == 16) {
        for (int y = y0; y < y1; y++) {
            lUInt16* line = (lUInt16*)GetScanLine(y);
            for (int x = x0; x < x1; x++) {
                line[x] ^= 0xFFFF;
            }
        }
    } else {
        for (int y = y0; y < y1; y++) {
            lUInt32* line = (lUInt32*)GetScanLine(y);
            for (int x = x0; x < x1; x++) {
                line[x] ^= 0x00FFFFFF;
            }
        }
    }
}

/// blend font bitmap using specified palette
void LVColorDrawBuf::BlendBitmap(int x, int y, const lUInt8* bitmap, FontBmpPixelFormat bitmap_fmt, int width, int height, int bmp_pitch, const lUInt32* palette) {
    if (!_data)
        return;
    int initial_height = height;
    int bx = 0;
    int by = 0;
    lUInt32 bmpcl = palette ? palette[0] : GetTextColor();

    if (x < _clip.left) {
        width += x - _clip.left;
        bx -= x - _clip.left;
        x = _clip.left;
        if (width <= 0)
            return;
    }
    if (y < _clip.top) {
        height += y - _clip.top;
        by -= y - _clip.top;
        y = _clip.top;
        if (_hidePartialGlyphs && height <= initial_height / 2) // HIDE PARTIAL VISIBLE GLYPHS
            return;
        if (height <= 0)
            return;
    }
    if (x + width > _clip.right) {
        width = _clip.right - x;
    }
    if (width <= 0 || !bitmap)
        return;
    if (y + height > _clip.bottom) {
        if (_hidePartialGlyphs && height <= initial_height / 2) // HIDE PARTIAL VISIBLE GLYPHS
            return;
        int clip_bottom = _clip.bottom;
        if (_hidePartialGlyphs)
            clip_bottom = this->_dy;
        if (y + height > clip_bottom)
            height = clip_bottom - y;
    }
    if (height <= 0)
        return;

    bitmap += bx + by * bmp_pitch;

    if (_bpp == 16) {
        switch (bitmap_fmt) {
            case BMP_PIXEL_FORMAT_MONO:
                blendBitmap_monoTo16bpp(this, x, y, bitmap, width, height, bmp_pitch, rgb888to565(bmpcl));
                break;
            case BMP_PIXEL_FORMAT_GRAY:
                blendBitmap_grayTo16bpp(this, x, y, bitmap, width, height, bmp_pitch, rgb888to565(bmpcl));
                break;
            case BMP_PIXEL_FORMAT_GRAY2:
                // TODO: implement this
                break;
            case BMP_PIXEL_FORMAT_GRAY4:
                // TODO: implement this
                break;
            case BMP_PIXEL_FORMAT_RGB:
                blendBitmap_rgbTo16bpp(this, x, y, bitmap, width, height, bmp_pitch, rgb888to565(bmpcl));
                break;
            case BMP_PIXEL_FORMAT_BGR:
                blendBitmap_bgrTo16bpp(this, x, y, bitmap, width, height, bmp_pitch, rgb888to565(bmpcl));
                break;
            case BMP_PIXEL_FORMAT_RGB_V:
                blendBitmap_rgbvTo16bpp(this, x, y, bitmap, width, height, bmp_pitch, rgb888to565(bmpcl));
                break;
            case BMP_PIXEL_FORMAT_BGR_V:
                blendBitmap_bgrvTo16bpp(this, x, y, bitmap, width, height, bmp_pitch, rgb888to565(bmpcl));
                break;
            case BMP_PIXEL_FORMAT_BGRA:
                // TODO: implement this
                break;
        }
    } else {
        switch (bitmap_fmt) {
            case BMP_PIXEL_FORMAT_MONO:
                blendBitmap_monoTo32bpp(this, x, y, bitmap, width, height, bmp_pitch, bmpcl);
                break;
            case BMP_PIXEL_FORMAT_GRAY:
                blendBitmap_grayTo32bpp(this, x, y, bitmap, width, height, bmp_pitch, bmpcl);
                break;
            case BMP_PIXEL_FORMAT_GRAY2:
                // TODO: implement this
                break;
            case BMP_PIXEL_FORMAT_GRAY4:
                // TODO: implement this
                break;
            case BMP_PIXEL_FORMAT_RGB:
                blendBitmap_rgbTo32bpp(this, x, y, bitmap, width, height, bmp_pitch, bmpcl);
                break;
            case BMP_PIXEL_FORMAT_BGR:
                blendBitmap_bgrTo32bpp(this, x, y, bitmap, width, height, bmp_pitch, bmpcl);
                break;
            case BMP_PIXEL_FORMAT_RGB_V:
                blendBitmap_rgbvTo32bpp(this, x, y, bitmap, width, height, bmp_pitch, bmpcl);
                break;
            case BMP_PIXEL_FORMAT_BGR_V:
                blendBitmap_bgrvTo32bpp(this, x, y, bitmap, width, height, bmp_pitch, bmpcl);
                break;
            case BMP_PIXEL_FORMAT_BGRA:
                blendBitmap_bgraTo32bpp(this, x, y, bitmap, width, height, bmp_pitch);
                break;
        }
    }
}

#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
/// draws buffer content to DC doing color conversion if necessary
void LVColorDrawBuf::DrawTo(HDC dc, int x, int y, int options, const lUInt32* palette) {
    if (dc != NULL && _drawdc != NULL)
        BitBlt(dc, x, y, _dx, _dy, _drawdc, 0, 0, SRCCOPY);
}
#endif

/// draws buffer content to another buffer doing color conversion if necessary
void LVColorDrawBuf::DrawTo(LVDrawBuf* buf, int x, int y, int options, const lUInt32* palette) {
    CR_UNUSED(options);
    CR_UNUSED(palette);
    //
    if (!_data)
        return;
    lvRect clip;
    buf->GetClipRect(&clip);
    int bpp = buf->GetBitsPerPixel();
    for (int yy = 0; yy < _dy; yy++) {
        if (y + yy >= clip.top && y + yy < clip.bottom) {
            if (_bpp == 16) {
                lUInt16* src = (lUInt16*)GetScanLine(yy);
                if (bpp == 1) {
                    int shift = x & 7;
                    lUInt8* dst = buf->GetScanLine(y + yy) + (x >> 3);
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            //lUInt8 mask = ~((lUInt8)0xC0>>shift);
#if (GRAY_INVERSE == 1)
                            lUInt8 cl = (((lUInt8)(*src) & 0x8000) ^ 0x8000) >> (shift + 8);
#else
                            lUInt8 cl = (((lUInt8)(*src) & 0x8000)) >> (shift + 8);
#endif
                            *dst |= cl;
                        }
                        if (!((shift = (shift + 1) & 7)))
                            dst++;
                        src++;
                    }
                } else if (bpp == 2) {
                    int shift = x & 3;
                    lUInt8* dst = buf->GetScanLine(y + yy) + (x >> 2);
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            //lUInt8 mask = ~((lUInt8)0xC0>>shift);
#if (GRAY_INVERSE == 1)
                            lUInt8 cl = (((lUInt8)(*src) & 0xC000) ^ 0xC000) >> ((shift << 1) + 8);
#else
                            lUInt8 cl = (((lUInt8)(*src) & 0xC000)) >> ((shift << 1) + 8);
#endif
                            *dst |= cl;
                        }
                        if (!((shift = ((shift + 1) & 3))))
                            dst++;
                        src++;
                    }
                } else if (bpp <= 8) {
                    lUInt8* dst = buf->GetScanLine(y + yy) + x;
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            //lUInt8 mask = ~((lUInt8)0xC0>>shift);
                            *dst = (lUInt8)(*src >> 8);
                        }
                        dst++;
                        src++;
                    }
                } else if (bpp == 16) {
                    lUInt16* dst = ((lUInt16*)buf->GetScanLine(y + yy)) + x;
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            *dst = *src;
                        }
                        dst++;
                        src++;
                    }
                } else if (bpp == 32) {
                    lUInt32* dst = ((lUInt32*)buf->GetScanLine(y + yy)) + x;
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            *dst = rgb565to888(*src);
                        }
                        dst++;
                        src++;
                    }
                }
            } else {
                lUInt32* src = (lUInt32*)GetScanLine(yy);
                if (bpp == 1) {
                    int shift = x & 7;
                    lUInt8* dst = buf->GetScanLine(y + yy) + (x >> 3);
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            //lUInt8 mask = ~((lUInt8)0xC0>>shift);
#if (GRAY_INVERSE == 1)
                            lUInt8 cl = (((lUInt8)(*src) & 0x80) ^ 0x80) >> (shift);
#else
                            lUInt8 cl = (((lUInt8)(*src) & 0x80)) >> (shift);
#endif
                            *dst |= cl;
                        }
                        if (!((shift = (shift + 1) & 7)))
                            dst++;
                        src++;
                    }
                } else if (bpp == 2) {
                    int shift = x & 3;
                    lUInt8* dst = buf->GetScanLine(y + yy) + (x >> 2);
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            //lUInt8 mask = ~((lUInt8)0xC0>>shift);
#if (GRAY_INVERSE == 1)
                            lUInt8 cl = (((lUInt8)(*src) & 0xC0) ^ 0xC0) >> (shift << 1);
#else
                            lUInt8 cl = (((lUInt8)(*src) & 0xC0)) >> (shift << 1);
#endif
                            *dst |= cl;
                        }
                        if (!((shift = (shift + 1) & 3)))
                            dst++;
                        src++;
                    }
                } else if (bpp <= 8) {
                    lUInt8* dst = buf->GetScanLine(y + yy) + x;
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            //lUInt8 mask = ~((lUInt8)0xC0>>shift);
                            *dst = (lUInt8)*src;
                        }
                        dst++;
                        src++;
                    }
                } else if (bpp == 32) {
                    lUInt32* dst = ((lUInt32*)buf->GetScanLine(y + yy)) + x;
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            *dst = *src;
                        }
                        dst++;
                        src++;
                    }
                }
            }
        }
    }
}
/// draws buffer content on top of another buffer doing color conversion if necessary
void LVColorDrawBuf::DrawOnTop(LVDrawBuf* buf, int x, int y) {
    //
    if (!_data)
        return;
    lvRect clip;
    buf->GetClipRect(&clip);
    int bpp = buf->GetBitsPerPixel();
    for (int yy = 0; yy < _dy; yy++) {
        if (y + yy >= clip.top && y + yy < clip.bottom) {
            if (_bpp == 16) {
                lUInt16* src = (lUInt16*)GetScanLine(yy);
                if (bpp == 1) {
                    int shift = x & 7;
                    lUInt8* dst = buf->GetScanLine(y + yy) + (x >> 3);
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            //lUInt8 mask = ~((lUInt8)0xC0>>shift);
#if (GRAY_INVERSE == 1)
                            lUInt8 cl = (((lUInt8)(*src) & 0x8000) ^ 0x8000) >> (shift + 8);
#else
                            lUInt8 cl = (((lUInt8)(*src) & 0x8000)) >> (shift + 8);
#endif
                            if (cl != 0)
                                *dst |= cl;
                        }
                        if (!((shift = (shift + 1) & 7)))
                            dst++;
                        src++;
                    }
                } else if (bpp == 2) {
                    int shift = x & 3;
                    lUInt8* dst = buf->GetScanLine(y + yy) + (x >> 2);
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            //lUInt8 mask = ~((lUInt8)0xC0>>shift);
#if (GRAY_INVERSE == 1)
                            lUInt8 cl = (((lUInt8)(*src) & 0xC000) ^ 0xC000) >> ((shift << 1) + 8);
#else
                            lUInt8 cl = (((lUInt8)(*src) & 0xC000)) >> ((shift << 1) + 8);
#endif
                            if (cl != 0)
                                *dst |= cl;
                        }
                        if (!((shift = ((shift + 1) & 3))))
                            dst++;
                        src++;
                    }
                } else if (bpp <= 8) {
                    lUInt8* dst = buf->GetScanLine(y + yy) + x;
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            //lUInt8 mask = ~((lUInt8)0xC0>>shift);
                            if (src != 0)
                                *dst = (lUInt8)(*src >> 8);
                        }
                        dst++;
                        src++;
                    }
                } else if (bpp == 16) {
                    lUInt16* dst = ((lUInt16*)buf->GetScanLine(y + yy)) + x;
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            if (src != 0)
                                *dst = *src;
                        }
                        dst++;
                        src++;
                    }
                } else if (bpp == 32) {
                    lUInt32* dst = ((lUInt32*)buf->GetScanLine(y + yy)) + x;
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            if (src != 0)
                                *dst = rgb565to888(*src);
                        }
                        dst++;
                        src++;
                    }
                }
            } else {
                lUInt32* src = (lUInt32*)GetScanLine(yy);
                if (bpp == 1) {
                    int shift = x & 7;
                    lUInt8* dst = buf->GetScanLine(y + yy) + (x >> 3);
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            //lUInt8 mask = ~((lUInt8)0xC0>>shift);
#if (GRAY_INVERSE == 1)
                            lUInt8 cl = (((lUInt8)(*src) & 0x80) ^ 0x80) >> (shift);
#else
                            lUInt8 cl = (((lUInt8)(*src) & 0x80)) >> (shift);
#endif
                            if (*src != 0)
                                *dst |= cl;
                        }
                        if (!((shift = (shift + 1) & 7)))
                            dst++;
                        src++;
                    }
                } else if (bpp == 2) {
                    int shift = x & 3;
                    lUInt8* dst = buf->GetScanLine(y + yy) + (x >> 2);
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            //lUInt8 mask = ~((lUInt8)0xC0>>shift);
#if (GRAY_INVERSE == 1)
                            lUInt8 cl = (((lUInt8)(*src) & 0xC0) ^ 0xC0) >> (shift << 1);
#else
                            lUInt8 cl = (((lUInt8)(*src) & 0xC0)) >> (shift << 1);
#endif
                            if (*src != 0)
                                *dst |= cl;
                        }
                        if (!((shift = (shift + 1) & 3)))
                            dst++;
                        src++;
                    }
                } else if (bpp <= 8) {
                    lUInt8* dst = buf->GetScanLine(y + yy) + x;
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            //lUInt8 mask = ~((lUInt8)0xC0>>shift);
                            if (*src != 0)
                                *dst = (lUInt8)*src;
                        }
                        dst++;
                        src++;
                    }
                } else if (bpp == 16) {
                    lUInt16* dst = ((lUInt16*)buf->GetScanLine(y + yy)) + x;
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            if (src != 0)
                                *dst = rgb888to565(*src);
                        }
                        dst++;
                        src++;
                    }
                } else if (bpp == 32) {
                    lUInt32* dst = ((lUInt32*)buf->GetScanLine(y + yy)) + x;
                    for (int xx = 0; xx < _dx; xx++) {
                        if (x + xx >= clip.left && x + xx < clip.right) {
                            if (*src != 0)
                                *dst = *src;
                        }
                        dst++;
                        src++;
                    }
                }
            }
        }
    }
}

/// draws rescaled buffer content to another buffer doing color conversion if necessary
void LVColorDrawBuf::DrawRescaled(LVDrawBuf* src, int x, int y, int dx, int dy, int options) {
    CR_UNUSED(options);
    if (dx < 1 || dy < 1)
        return;
    lvRect clip;
    GetClipRect(&clip);
    int srcdx = src->GetWidth();
    int srcdy = src->GetHeight();
    bool linearInterpolation = (srcdx <= dx || srcdy <= dy);
    for (int yy = 0; yy < dy; yy++) {
        if (y + yy >= clip.top && y + yy < clip.bottom) {
            if (linearInterpolation) {
                // linear interpolation
                int srcy16 = srcdy * yy * 16 / dy;
                for (int xx = 0; xx < dx; xx++) {
                    if (x + xx >= clip.left && x + xx < clip.right) {
                        int srcx16 = srcdx * xx * 16 / dx;
                        lUInt32 cl = src->GetInterpolatedColor(srcx16, srcy16);
                        if (_bpp == 16) {
                            lUInt16* dst = (lUInt16*)GetScanLine(y + yy);
                            dst[x + xx] = rgb888to565(cl);
                        } else {
                            lUInt32* dst = (lUInt32*)GetScanLine(y + yy);
                            dst[x + xx] = cl;
                        }
                    }
                }
            } else {
                // area average
                lvRect srcRect;
                srcRect.top = srcdy * yy * 16 / dy;
                srcRect.bottom = srcdy * (yy + 1) * 16 / dy;
                for (int xx = 0; xx < dx; xx++) {
                    if (x + xx >= clip.left && x + xx < clip.right) {
                        srcRect.left = srcdx * xx * 16 / dx;
                        srcRect.right = srcdx * (xx + 1) * 16 / dx;
                        lUInt32 cl = src->GetAvgColor(srcRect);
                        if (_bpp == 16) {
                            lUInt16* dst = (lUInt16*)GetScanLine(y + yy);
                            dst[x + xx] = rgb888to565(cl);
                        } else {
                            lUInt32* dst = (lUInt32*)GetScanLine(y + yy);
                            dst[x + xx] = cl;
                        }
                    }
                }
            }
        }
    }
}

/// returns scanline pointer
lUInt8* LVColorDrawBuf::GetScanLine(int y) const {
    if (!_data || y < 0 || y >= _dy)
        return NULL;
#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
    return _data + _rowsize * (_dy - 1 - y);
#else
    return _data + _rowsize * y;
#endif
}

/// returns white pixel value
lUInt32 LVColorDrawBuf::GetWhiteColor() const {
    return 0xFFFFFF;
}
/// returns black pixel value
lUInt32 LVColorDrawBuf::GetBlackColor() const {
    return 0x000000;
}

/// constructor
LVColorDrawBuf::LVColorDrawBuf(int dx, int dy, int bpp)
        : LVBaseDrawBuf()
#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
        , _drawdc(NULL)
        , _drawbmp(NULL)
#endif
        , _bpp(bpp)
        , _ownData(true) {
    _rowsize = dx * (_bpp >> 3);
    Resize(dx, dy); // NOLINT: Call to virtual function during construction
}

/// creates wrapper around external RGBA buffer
LVColorDrawBuf::LVColorDrawBuf(int dx, int dy, lUInt8* externalBuffer, int bpp)
        : LVBaseDrawBuf()
#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
        , _drawdc(NULL)
        , _drawbmp(NULL)
#endif
        , _bpp(bpp)
        , _ownData(false) {
    _dx = dx;
    _dy = dy;
    _rowsize = dx * (_bpp >> 3);
    _data = externalBuffer;
    SetClipRect(NULL);
}

/// destructor
LVColorDrawBuf::~LVColorDrawBuf() {
    if (!_ownData)
        return;
#if !defined(__SYMBIAN32__) && defined(_WIN32) && !defined(QT_GL)
    if (_drawdc)
        DeleteDC(_drawdc);
    if (_drawbmp)
        DeleteObject(_drawbmp);
#else
    if (_data)
        free(_data);
#endif
}

/// convert to 1-bit bitmap
void LVColorDrawBuf::ConvertToBitmap(bool flgDither) {
    // not implemented
    CR_UNUSED(flgDither);
}
