/**
 * @file lv_draw_sw_blend_al88.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_draw_sw_blend_to_al88.h"
#if LV_USE_DRAW_SW

#if LV_DRAW_SW_SUPPORT_AL88

#include "lv_draw_sw_blend_private.h"
#include "../../../misc/lv_math.h"
#include "../../../display/lv_display.h"
#include "../../../core/lv_refr.h"
#include "../../../misc/lv_color.h"
#include "../../../stdlib/lv_string.h"

#if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_NEON
    #include "neon/lv_blend_neon.h"
#elif LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM
    #include "helium/lv_blend_helium.h"
#elif LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_CUSTOM
    #include LV_DRAW_SW_ASM_CUSTOM_INCLUDE
#endif

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

typedef struct {
    lv_color16a_t fg_saved;
    lv_color16a_t bg_saved;
    lv_color16a_t res_saved;
    lv_opa_t res_alpha_saved;
    lv_opa_t ratio_saved;
} lv_color_mix_alpha_cache_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/

#if LV_DRAW_SW_SUPPORT_L8
    static void /* LV_ATTRIBUTE_FAST_MEM */ l8_image_blend(lv_draw_sw_blend_image_dsc_t * dsc);
#endif

#if LV_DRAW_SW_SUPPORT_I1
    static void /* LV_ATTRIBUTE_FAST_MEM */ i1_image_blend(lv_draw_sw_blend_image_dsc_t * dsc);

    static inline uint8_t /* LV_ATTRIBUTE_FAST_MEM */ get_bit(const uint8_t * buf, int32_t bit_idx);
#endif

static void /* LV_ATTRIBUTE_FAST_MEM */ al88_image_blend(lv_draw_sw_blend_image_dsc_t * dsc);

#if LV_DRAW_SW_SUPPORT_RGB565
    static void /* LV_ATTRIBUTE_FAST_MEM */ rgb565_image_blend(lv_draw_sw_blend_image_dsc_t * dsc);
#endif

#if LV_DRAW_SW_SUPPORT_RGB888 || LV_DRAW_SW_SUPPORT_XRGB8888
static void /* LV_ATTRIBUTE_FAST_MEM */ rgb888_image_blend(lv_draw_sw_blend_image_dsc_t * dsc,
                                                           const uint8_t src_px_size);
#endif

#if LV_DRAW_SW_SUPPORT_ARGB8888
    static void /* LV_ATTRIBUTE_FAST_MEM */ argb8888_image_blend(lv_draw_sw_blend_image_dsc_t * dsc);
#endif

static void lv_color_mix_with_alpha_cache_init(lv_color_mix_alpha_cache_t * cache);

static inline void /* LV_ATTRIBUTE_FAST_MEM */ blend_non_normal_pixel(lv_color16a_t * dest, lv_color16a_t src,
                                                                      lv_blend_mode_t mode, lv_color_mix_alpha_cache_t * cache);

static inline void * /* LV_ATTRIBUTE_FAST_MEM */ drawbuf_next_row(const void * buf, uint32_t stride);

static inline bool lv_color16a_eq(lv_color16a_t c1, lv_color16a_t c2);

static inline lv_color16a_t /* LV_ATTRIBUTE_FAST_MEM */ lv_color_mix16a(lv_color16a_t fg, lv_color16a_t bg);

static inline void /* LV_ATTRIBUTE_FAST_MEM */ lv_color_16a_16a_mix(lv_color16a_t src, lv_color16a_t * dest,
                                                                    lv_color_mix_alpha_cache_t * cache);

/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 *      MACROS
 **********************/

#ifndef LV_DRAW_SW_COLOR_BLEND_TO_AL88
    #define LV_DRAW_SW_COLOR_BLEND_TO_AL88(...)                         LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_COLOR_BLEND_TO_AL88_WITH_OPA
    #define LV_DRAW_SW_COLOR_BLEND_TO_AL88_WITH_OPA(...)                LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_COLOR_BLEND_TO_AL88_WITH_MASK
    #define LV_DRAW_SW_COLOR_BLEND_TO_AL88_WITH_MASK(...)               LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_COLOR_BLEND_TO_AL88_MIX_MASK_OPA
    #define LV_DRAW_SW_COLOR_BLEND_TO_AL88_MIX_MASK_OPA(...)            LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_AL88
    #define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_AL88(...)                 LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_AL88_WITH_OPA
    #define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_AL88_WITH_OPA(...)        LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_AL88_WITH_MASK
    #define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_AL88_WITH_MASK(...)       LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA
    #define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA(...)    LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_AL88
    #define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_AL88(...)                 LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_AL88_WITH_OPA
    #define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_AL88_WITH_OPA(...)        LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_AL88_WITH_MASK
    #define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_AL88_WITH_MASK(...)       LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA
    #define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA(...)    LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_AL88
    #define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_AL88(...)                 LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_AL88_WITH_OPA
    #define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_AL88_WITH_OPA(...)        LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_AL88_WITH_MASK
    #define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_AL88_WITH_MASK(...)       LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA
    #define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA(...)    LV_RESULT_INVALID
#endif


#ifndef LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88
    #define LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88(...)               LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88_WITH_OPA
    #define LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88_WITH_OPA(...)      LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88_WITH_MASK
    #define LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88_WITH_MASK(...)     LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA
    #define LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA(...)  LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_I1_BLEND_NORMAL_TO_AL88
    #define LV_DRAW_SW_I1_BLEND_NORMAL_TO_AL88(...)  LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_I1_BLEND_NORMAL_TO_AL88_WITH_OPA
    #define LV_DRAW_SW_I1_BLEND_NORMAL_TO_AL88_WITH_OPA(...)  LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_I1_BLEND_NORMAL_TO_AL88_WITH_MASK
    #define LV_DRAW_SW_I1_BLEND_NORMAL_TO_AL88_WITH_MASK(...)  LV_RESULT_INVALID
#endif

#ifndef LV_DRAW_SW_I1_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA
    #define LV_DRAW_SW_I1_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA(...)  LV_RESULT_INVALID
#endif

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void LV_ATTRIBUTE_FAST_MEM lv_draw_sw_blend_color_to_al88(lv_draw_sw_blend_fill_dsc_t * dsc)
{
    int32_t w = dsc->dest_w;
    int32_t h = dsc->dest_h;
    lv_opa_t opa = dsc->opa;
    const lv_opa_t * mask = dsc->mask_buf;
    int32_t mask_stride = dsc->mask_stride;
    int32_t dest_stride = dsc->dest_stride;

    lv_color_mix_alpha_cache_t cache;
    lv_color_mix_with_alpha_cache_init(&cache);

    int32_t x;
    int32_t y;

    LV_UNUSED(w);
    LV_UNUSED(h);
    LV_UNUSED(x);
    LV_UNUSED(y);
    LV_UNUSED(opa);
    LV_UNUSED(mask);
    LV_UNUSED(mask_stride);
    LV_UNUSED(dest_stride);

    /*Simple fill*/
    if(mask == NULL && opa >= LV_OPA_MAX) {
        if(LV_RESULT_INVALID == LV_DRAW_SW_COLOR_BLEND_TO_AL88(dsc)) {
            lv_color16a_t color16a;
            color16a.lumi = lv_color_luminance(dsc->color);
            color16a.alpha = 255;
            lv_color16a_t * dest_buf = dsc->dest_buf;
            for(y = 0; y < h; y++) {
                for(x = 0; x < w - 16; x += 16) {
                    dest_buf[x + 0] = color16a;
                    dest_buf[x + 1] = color16a;
                    dest_buf[x + 2] = color16a;
                    dest_buf[x + 3] = color16a;

                    dest_buf[x + 4] = color16a;
                    dest_buf[x + 5] = color16a;
                    dest_buf[x + 6] = color16a;
                    dest_buf[x + 7] = color16a;

                    dest_buf[x + 8] = color16a;
                    dest_buf[x + 9] = color16a;
                    dest_buf[x + 10] = color16a;
                    dest_buf[x + 11] = color16a;

                    dest_buf[x + 12] = color16a;
                    dest_buf[x + 13] = color16a;
                    dest_buf[x + 14] = color16a;
                    dest_buf[x + 15] = color16a;
                }
                for(; x < w; x ++) {
                    dest_buf[x] = color16a;
                }

                dest_buf = drawbuf_next_row(dest_buf, dest_stride);
            }
        }
    }
    /*Opacity only*/
    else if(mask == NULL && opa < LV_OPA_MAX) {
        if(LV_RESULT_INVALID == LV_DRAW_SW_COLOR_BLEND_TO_AL88_WITH_OPA(dsc)) {
            lv_color16a_t color16a;
            color16a.lumi = lv_color_luminance(dsc->color);
            color16a.alpha = opa;
            lv_color16a_t * dest_buf = dsc->dest_buf;
            for(y = 0; y < h; y++) {
                for(x = 0; x < w; x++) {
                    lv_color_16a_16a_mix(color16a, &dest_buf[x], &cache);
                }
                dest_buf = drawbuf_next_row(dest_buf, dest_stride);
            }
        }

    }
    /*Masked with full opacity*/
    else if(mask && opa >= LV_OPA_MAX) {
        if(LV_RESULT_INVALID == LV_DRAW_SW_COLOR_BLEND_TO_AL88_WITH_MASK(dsc)) {
            lv_color16a_t color16a;
            color16a.lumi = lv_color_luminance(dsc->color);
            lv_color16a_t * dest_buf = (lv_color16a_t *)dsc->dest_buf;
            for(y = 0; y < h; y++) {
                for(x = 0; x < w; x++) {
                    color16a.alpha = mask[x];
                    lv_color_16a_16a_mix(color16a, &dest_buf[x], &cache);
                }
                dest_buf = drawbuf_next_row(dest_buf, dest_stride);
                mask += mask_stride;
            }
        }

    }
    /*Masked with opacity*/
    else {
        if(LV_RESULT_INVALID == LV_DRAW_SW_COLOR_BLEND_TO_AL88_MIX_MASK_OPA(dsc)) {
            lv_color16a_t color16a;
            color16a.lumi = lv_color_luminance(dsc->color);
            lv_color16a_t * dest_buf = (lv_color16a_t *)dsc->dest_buf;
            for(y = 0; y < h; y++) {
                for(x = 0; x < w; x++) {
                    color16a.alpha = LV_OPA_MIX2(mask[x], opa);
                    lv_color_16a_16a_mix(color16a, &dest_buf[x], &cache);
                }
                dest_buf = drawbuf_next_row(dest_buf, dest_stride);
                mask += mask_stride;
            }
        }
    }
}

void LV_ATTRIBUTE_FAST_MEM lv_draw_sw_blend_image_to_al88(lv_draw_sw_blend_image_dsc_t * dsc)
{
    switch(dsc->src_color_format) {
#if LV_DRAW_SW_SUPPORT_RGB565
        case LV_COLOR_FORMAT_RGB565:
            rgb565_image_blend(dsc);
            break;
#endif
#if LV_DRAW_SW_SUPPORT_RGB888
        case LV_COLOR_FORMAT_RGB888:
            rgb888_image_blend(dsc, 3);
            break;
#endif
#if LV_DRAW_SW_SUPPORT_XRGB8888
        case LV_COLOR_FORMAT_XRGB8888:
            rgb888_image_blend(dsc, 4);
            break;
#endif
#if LV_DRAW_SW_SUPPORT_ARGB8888
        case LV_COLOR_FORMAT_ARGB8888:
            argb8888_image_blend(dsc);
            break;
#endif
#if LV_DRAW_SW_SUPPORT_L8
        case LV_COLOR_FORMAT_L8:
            l8_image_blend(dsc);
            break;
#endif
        case LV_COLOR_FORMAT_AL88:
            al88_image_blend(dsc);
            break;
#if LV_DRAW_SW_SUPPORT_I1
        case LV_COLOR_FORMAT_I1:
            i1_image_blend(dsc);
            break;
#endif
        default:
            LV_LOG_WARN("Not supported source color format");
            break;
    }
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
#if LV_DRAW_SW_SUPPORT_I1
static void LV_ATTRIBUTE_FAST_MEM i1_image_blend(lv_draw_sw_blend_image_dsc_t * dsc)
{
    int32_t w = dsc->dest_w;
    int32_t h = dsc->dest_h;
    lv_opa_t opa = dsc->opa;
    lv_color16a_t * dest_buf_al88 = dsc->dest_buf;
    int32_t dest_stride = dsc->dest_stride;
    const uint8_t * src_buf_i1 = dsc->src_buf;
    int32_t src_stride = dsc->src_stride;
    const lv_opa_t * mask_buf = dsc->mask_buf;
    int32_t mask_stride = dsc->mask_stride;

    lv_color_mix_alpha_cache_t cache;
    lv_color_mix_with_alpha_cache_init(&cache);

    int32_t x, y;

    if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
        if(mask_buf == NULL && opa >= LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_I1_BLEND_NORMAL_TO_AL88(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        dest_buf_al88[x].lumi = get_bit(src_buf_i1, x) * 255;
                        dest_buf_al88[x].alpha = 255;
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_i1 = drawbuf_next_row(src_buf_i1, src_stride);
                }
            }
        }
        else if(mask_buf == NULL && opa < LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_I1_BLEND_NORMAL_TO_AL88_WITH_OPA(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = get_bit(src_buf_i1, x) * 255;
                        src_color.alpha = opa;
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_i1 = drawbuf_next_row(src_buf_i1, src_stride);
                }
            }
        }
        else if(mask_buf && opa >= LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_I1_BLEND_NORMAL_TO_AL88_WITH_MASK(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = get_bit(src_buf_i1, x) * 255;
                        src_color.alpha = mask_buf[x];
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_i1 = drawbuf_next_row(src_buf_i1, src_stride);
                    mask_buf += mask_stride;
                }
            }
        }
        else if(mask_buf && opa < LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_I1_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = get_bit(src_buf_i1, x) * 255;
                        src_color.alpha = LV_OPA_MIX2(mask_buf[x], opa);
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_i1 = drawbuf_next_row(src_buf_i1, src_stride);
                    mask_buf += mask_stride;
                }
            }
        }
        else {
            for(y = 0; y < h; y++) {
                for(x = 0; x < w; x++) {
                    lv_color16a_t src_color;
                    src_color.lumi = get_bit(src_buf_i1, x) * 255;
                    if(mask_buf == NULL) src_color.alpha = opa;
                    else src_color.alpha = LV_OPA_MIX2(mask_buf[x], opa);
                    blend_non_normal_pixel(&dest_buf_al88[x], src_color, dsc->blend_mode, &cache);
                }
                if(mask_buf) mask_buf += mask_stride;
                dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                src_buf_i1 = drawbuf_next_row(src_buf_i1, src_stride);
            }
        }
    }
}
#endif

#if LV_DRAW_SW_SUPPORT_L8
static void LV_ATTRIBUTE_FAST_MEM l8_image_blend(lv_draw_sw_blend_image_dsc_t * dsc)
{
    int32_t w = dsc->dest_w;
    int32_t h = dsc->dest_h;
    lv_opa_t opa = dsc->opa;
    lv_color16a_t * dest_buf_al88 = dsc->dest_buf;
    int32_t dest_stride = dsc->dest_stride;
    const uint8_t * src_buf_l8 = dsc->src_buf;
    int32_t src_stride = dsc->src_stride;
    const lv_opa_t * mask_buf = dsc->mask_buf;
    int32_t mask_stride = dsc->mask_stride;

    lv_color_mix_alpha_cache_t cache;
    lv_color_mix_with_alpha_cache_init(&cache);

    int32_t x, y;

    if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
        if(mask_buf == NULL && opa >= LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        dest_buf_al88[x].lumi = src_buf_l8[x];
                        dest_buf_al88[x].alpha = 255;
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_l8 = drawbuf_next_row(src_buf_l8, src_stride);
                }
            }
        }
        else if(mask_buf == NULL && opa < LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88_WITH_OPA(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = src_buf_l8[x];
                        src_color.alpha = opa;
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_l8 = drawbuf_next_row(src_buf_l8, src_stride);
                }
            }
        }
        else if(mask_buf && opa >= LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88_WITH_MASK(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = src_buf_l8[x];
                        src_color.alpha = mask_buf[x];
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_l8 = drawbuf_next_row(src_buf_l8, src_stride);
                    mask_buf += mask_stride;
                }
            }
        }
        else if(mask_buf && opa < LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = src_buf_l8[x];
                        src_color.alpha = LV_OPA_MIX2(mask_buf[x], opa);
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_l8 = drawbuf_next_row(src_buf_l8, src_stride);
                    mask_buf += mask_stride;
                }
            }
        }
    }
    else {
        for(y = 0; y < h; y++) {
            for(x = 0; x < w; x++) {
                lv_color16a_t src_color;
                src_color.lumi = src_buf_l8[x];
                if(mask_buf == NULL) src_color.alpha = opa;
                else src_color.alpha = LV_OPA_MIX2(mask_buf[x], opa);
                blend_non_normal_pixel(&dest_buf_al88[x], src_color, dsc->blend_mode, &cache);
            }
            if(mask_buf) mask_buf += mask_stride;
            dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
            src_buf_l8 = drawbuf_next_row(src_buf_l8, src_stride);
        }
    }
}

#endif

static void LV_ATTRIBUTE_FAST_MEM al88_image_blend(lv_draw_sw_blend_image_dsc_t * dsc)
{
    int32_t w = dsc->dest_w;
    int32_t h = dsc->dest_h;
    lv_opa_t opa = dsc->opa;
    lv_color16a_t * dest_buf_al88 = dsc->dest_buf;
    int32_t dest_stride = dsc->dest_stride;
    const lv_color16a_t * src_buf_al88 = dsc->src_buf;
    int32_t src_stride = dsc->src_stride;
    const lv_opa_t * mask_buf = dsc->mask_buf;
    int32_t mask_stride = dsc->mask_stride;

    lv_color_mix_alpha_cache_t cache;
    lv_color_mix_with_alpha_cache_init(&cache);

    int32_t x, y;

    if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
        if(mask_buf == NULL && opa >= LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color_16a_16a_mix(src_buf_al88[x], &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
                }
            }
        }
        else if(mask_buf == NULL && opa < LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88_WITH_OPA(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color = src_buf_al88[x];
                        src_color.alpha = LV_OPA_MIX2(src_color.alpha, opa);
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
                }
            }
        }
        else if(mask_buf && opa >= LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88_WITH_MASK(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color = src_buf_al88[x];
                        src_color.alpha = LV_OPA_MIX2(src_color.alpha, mask_buf[x]);
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
                    mask_buf += mask_stride;
                }
            }
        }
        else if(mask_buf && opa < LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color = src_buf_al88[x];
                        src_color.alpha = LV_OPA_MIX3(src_color.alpha, mask_buf[x], opa);
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
                    mask_buf += mask_stride;
                }
            }
        }
    }
    else {
        for(y = 0; y < h; y++) {
            for(x = 0; x < w; x++) {
                lv_color16a_t src_color = src_buf_al88[x];
                if(mask_buf == NULL) src_color.alpha = LV_OPA_MIX2(src_color.alpha, opa);
                else src_color.alpha = LV_OPA_MIX3(src_color.alpha, mask_buf[x], opa);
                blend_non_normal_pixel(&dest_buf_al88[x], src_color, dsc->blend_mode, &cache);
            }
            if(mask_buf) mask_buf += mask_stride;
            dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
            src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
        }
    }
}

#if LV_DRAW_SW_SUPPORT_RGB565

static void LV_ATTRIBUTE_FAST_MEM rgb565_image_blend(lv_draw_sw_blend_image_dsc_t * dsc)
{
    int32_t w = dsc->dest_w;
    int32_t h = dsc->dest_h;
    lv_opa_t opa = dsc->opa;
    lv_color16a_t * dest_buf_al88 = dsc->dest_buf;
    int32_t dest_stride = dsc->dest_stride;
    const lv_color16_t * src_buf_c16 = (const lv_color16_t *)dsc->src_buf;
    int32_t src_stride = dsc->src_stride;
    const lv_opa_t * mask_buf = dsc->mask_buf;
    int32_t mask_stride = dsc->mask_stride;

    lv_color_mix_alpha_cache_t cache;
    lv_color_mix_with_alpha_cache_init(&cache);

    int32_t x, y;

    if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
        if(mask_buf == NULL && opa >= LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_AL88(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        dest_buf_al88[x].lumi = lv_color16_luminance(src_buf_c16[x]);
                        dest_buf_al88[x].alpha = 255;
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_c16 = drawbuf_next_row(src_buf_c16, src_stride);
                }
            }
        }
        else if(mask_buf == NULL && opa < LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_AL88_WITH_OPA(dsc, dest_px_size)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = lv_color16_luminance(src_buf_c16[x]);
                        src_color.alpha = opa;
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_c16 = drawbuf_next_row(src_buf_c16, src_stride);
                }
            }
        }
        else if(mask_buf && opa >= LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_AL88_WITH_MASK(dsc, dest_px_size)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = lv_color16_luminance(src_buf_c16[x]);
                        src_color.alpha = mask_buf[x];
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_c16 = drawbuf_next_row(src_buf_c16, src_stride);
                    mask_buf += mask_stride;
                }
            }
        }
        else {
            if(LV_RESULT_INVALID == LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA(dsc, dest_px_size)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = lv_color16_luminance(src_buf_c16[x]);
                        src_color.alpha = LV_OPA_MIX2(mask_buf[x], opa);
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_c16 = drawbuf_next_row(src_buf_c16, src_stride);
                    mask_buf += mask_stride;
                }
            }
        }
    }
    else {
        for(y = 0; y < h; y++) {
            for(x = 0; x < w; x++) {
                lv_color16a_t src_color;
                src_color.lumi = lv_color16_luminance(src_buf_c16[x]);
                if(mask_buf == NULL) src_color.alpha = opa;
                else src_color.alpha = LV_OPA_MIX2(mask_buf[x], opa);
                blend_non_normal_pixel(&dest_buf_al88[x], src_color, dsc->blend_mode, &cache);
            }
            if(mask_buf) mask_buf += mask_stride;
            dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
            src_buf_c16 = drawbuf_next_row(src_buf_c16, src_stride);
        }
    }
}

#endif

#if LV_DRAW_SW_SUPPORT_RGB888 || LV_DRAW_SW_SUPPORT_XRGB8888

static void LV_ATTRIBUTE_FAST_MEM rgb888_image_blend(lv_draw_sw_blend_image_dsc_t * dsc,
                                                     const uint8_t src_px_size)
{
    int32_t w = dsc->dest_w;
    int32_t h = dsc->dest_h;
    lv_opa_t opa = dsc->opa;
    lv_color16a_t * dest_buf_al88 = dsc->dest_buf;
    int32_t dest_stride = dsc->dest_stride;
    const uint8_t * src_buf_u8 = dsc->src_buf;
    int32_t src_stride = dsc->src_stride;
    const lv_opa_t * mask_buf = dsc->mask_buf;
    int32_t mask_stride = dsc->mask_stride;

    lv_color_mix_alpha_cache_t cache;
    lv_color_mix_with_alpha_cache_init(&cache);

    int32_t dest_x;
    int32_t src_x;
    int32_t y;

    if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
        /*Special case*/
        if(mask_buf == NULL && opa >= LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_AL88(dsc, src_px_size)) {
                for(y = 0; y < h; y++) {
                    for(dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += src_px_size) {
                        dest_buf_al88[dest_x].lumi = lv_color24_luminance(&src_buf_u8[src_x]);
                        dest_buf_al88[dest_x].alpha = 255;
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_u8 += src_stride;
                }
            }
        }
        if(mask_buf == NULL && opa < LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_AL88_WITH_OPA(dsc, dest_px_size, src_px_size)) {
                for(y = 0; y < h; y++) {
                    for(dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += src_px_size) {
                        lv_color16a_t src_color;
                        src_color.lumi = lv_color24_luminance(&src_buf_u8[src_x]);
                        src_color.alpha = opa;
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[dest_x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_u8 += src_stride;
                }
            }
        }
        if(mask_buf && opa >= LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_AL88_WITH_MASK(dsc, dest_px_size, src_px_size)) {
                uint32_t mask_x;
                for(y = 0; y < h; y++) {
                    for(mask_x = 0, dest_x = 0, src_x = 0; dest_x < w; mask_x++, dest_x++, src_x += src_px_size) {
                        lv_color16a_t src_color;
                        src_color.lumi = lv_color24_luminance(&src_buf_u8[src_x]);
                        src_color.alpha = mask_buf[mask_x];
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[dest_x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_u8 += src_stride;
                    mask_buf += mask_stride;
                }
            }
        }
        if(mask_buf && opa < LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA(dsc, dest_px_size, src_px_size)) {
                uint32_t mask_x;
                for(y = 0; y < h; y++) {
                    for(mask_x = 0, dest_x = 0, src_x = 0; dest_x < w; mask_x++, dest_x++, src_x += src_px_size) {
                        lv_color16a_t src_color;
                        src_color.lumi = lv_color24_luminance(&src_buf_u8[src_x]);
                        src_color.alpha = LV_OPA_MIX2(mask_buf[mask_x], opa);
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[dest_x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_u8 += src_stride;
                    mask_buf += mask_stride;
                }
            }
        }
    }
    else {
        for(y = 0; y < h; y++) {
            for(dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += src_px_size) {
                lv_color16a_t src_color;
                src_color.lumi = lv_color24_luminance(&src_buf_u8[src_x]);
                if(mask_buf == NULL) src_color.alpha = opa;
                else src_color.alpha = LV_OPA_MIX2(mask_buf[dest_x], opa);

                blend_non_normal_pixel(&dest_buf_al88[dest_x], src_color, dsc->blend_mode, &cache);
            }
            if(mask_buf) mask_buf += mask_stride;
            dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
            src_buf_u8 += src_stride;
        }
    }
}

#endif

#if LV_DRAW_SW_SUPPORT_ARGB8888

static void LV_ATTRIBUTE_FAST_MEM argb8888_image_blend(lv_draw_sw_blend_image_dsc_t * dsc)
{
    int32_t w = dsc->dest_w;
    int32_t h = dsc->dest_h;
    lv_opa_t opa = dsc->opa;
    lv_color16a_t * dest_buf_al88 = dsc->dest_buf;
    int32_t dest_stride = dsc->dest_stride;
    const lv_color32_t * src_buf_c32 = dsc->src_buf;
    int32_t src_stride = dsc->src_stride;
    const lv_opa_t * mask_buf = dsc->mask_buf;
    int32_t mask_stride = dsc->mask_stride;

    lv_color_mix_alpha_cache_t cache;
    lv_color_mix_with_alpha_cache_init(&cache);

    int32_t x;
    int32_t y;

    if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
        if(mask_buf == NULL && opa >= LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_AL88(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = lv_color32_luminance(src_buf_c32[x]);
                        src_color.alpha = src_buf_c32[x].alpha;
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_c32 = drawbuf_next_row(src_buf_c32, src_stride);
                }
            }
        }
        else if(mask_buf == NULL && opa < LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_AL88_WITH_OPA(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = lv_color32_luminance(src_buf_c32[x]);
                        src_color.alpha = LV_OPA_MIX2(src_buf_c32[x].alpha, opa);
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_c32 = drawbuf_next_row(src_buf_c32, src_stride);
                }
            }
        }
        else if(mask_buf && opa >= LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_AL88_WITH_MASK(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = lv_color32_luminance(src_buf_c32[x]);
                        src_color.alpha = LV_OPA_MIX2(src_buf_c32[x].alpha, mask_buf[x]);
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_c32 = drawbuf_next_row(src_buf_c32, src_stride);
                    mask_buf += mask_stride;
                }
            }
        }
        else if(mask_buf && opa < LV_OPA_MAX) {
            if(LV_RESULT_INVALID == LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_AL88_MIX_MASK_OPA(dsc)) {
                for(y = 0; y < h; y++) {
                    for(x = 0; x < w; x++) {
                        lv_color16a_t src_color;
                        src_color.lumi = lv_color32_luminance(src_buf_c32[x]);
                        src_color.alpha = LV_OPA_MIX3(src_buf_c32[x].alpha, mask_buf[x], opa);
                        lv_color_16a_16a_mix(src_color, &dest_buf_al88[x], &cache);
                    }
                    dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
                    src_buf_c32 = drawbuf_next_row(src_buf_c32, src_stride);
                    mask_buf += mask_stride;
                }
            }
        }
    }
    else {
        for(y = 0; y < h; y++) {
            for(x = 0; x < w; x++) {
                lv_color16a_t src_color;
                src_color.lumi = lv_color32_luminance(src_buf_c32[x]);
                src_color.alpha = src_buf_c32[x].alpha;
                if(mask_buf == NULL) src_color.alpha = LV_OPA_MIX2(src_color.alpha, opa);
                else src_color.alpha = LV_OPA_MIX3(src_color.alpha, mask_buf[x], opa);
                blend_non_normal_pixel(&dest_buf_al88[x], src_color, dsc->blend_mode, &cache);
            }
            if(mask_buf) mask_buf += mask_stride;
            dest_buf_al88 = drawbuf_next_row(dest_buf_al88, dest_stride);
            src_buf_c32 = drawbuf_next_row(src_buf_c32, src_stride);
        }
    }
}

#endif

/**
 * Check if two AL88 colors are equal
 * @param c1    the first color
 * @param c2    the second color
 * @return      true: equal
 */
static inline bool lv_color16a_eq(lv_color16a_t c1, lv_color16a_t c2)
{
    return *((uint16_t *)&c1) == *((uint16_t *)&c2);
}

static inline lv_color16a_t LV_ATTRIBUTE_FAST_MEM lv_color_mix16a(lv_color16a_t fg, lv_color16a_t bg)
{
#if 0
    if(fg.alpha >= LV_OPA_MAX) {
        fg.alpha = bg.alpha;
        return fg;
    }
    if(fg.alpha <= LV_OPA_MIN) {
        return bg;
    }
#endif
    bg.lumi = (uint32_t)((uint32_t)fg.lumi * fg.alpha + (uint32_t)bg.lumi * (255 - fg.alpha)) >> 8;
    return bg;
}

static inline void LV_ATTRIBUTE_FAST_MEM lv_color_16a_16a_mix(lv_color16a_t fg, lv_color16a_t * bg,
                                                              lv_color_mix_alpha_cache_t * cache)
{
    /*Pick the foreground if it's fully opaque or the Background is fully transparent*/
    if(fg.alpha >= LV_OPA_MAX || bg->alpha <= LV_OPA_MIN) {
        *bg = fg;
    }
    /*Transparent foreground: use the Background*/
    else if(fg.alpha <= LV_OPA_MIN) {
        /* no need to copy */
    }
    /*Opaque background: use simple mix*/
    else if(bg->alpha == 255) {
        *bg = lv_color_mix16a(fg, *bg);
    }
    /*Both colors have alpha. Expensive calculation needs to be applied*/
    else {
        /*Save the parameters and the result. If they will be asked again don't compute again*/

        /*Update the ratio and the result alpha value if the input alpha values change*/
        if(bg->alpha != cache->bg_saved.alpha || fg.alpha != cache->fg_saved.alpha) {
            /*Info:
             * https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator*/
            cache->res_alpha_saved = 255 - LV_OPA_MIX2(255 - fg.alpha, 255 - bg->alpha);
            LV_ASSERT(cache->res_alpha_saved != 0);
            cache->ratio_saved = (uint32_t)((uint32_t)fg.alpha * 255) / cache->res_alpha_saved;
        }

        if(!lv_color16a_eq(*bg, cache->bg_saved) || !lv_color16a_eq(fg, cache->fg_saved)) {
            cache->fg_saved = fg;
            cache->bg_saved = *bg;
            fg.alpha = cache->ratio_saved;
            cache->res_saved = lv_color_mix16a(fg, *bg);
            cache->res_saved.alpha = cache->res_alpha_saved;
        }

        *bg = cache->res_saved;
    }
}

void lv_color_mix_with_alpha_cache_init(lv_color_mix_alpha_cache_t * cache)
{
    lv_memzero(&cache->fg_saved, sizeof(lv_color16a_t));
    lv_memzero(&cache->bg_saved, sizeof(lv_color16a_t));
    lv_memzero(&cache->res_saved, sizeof(lv_color16a_t));
    cache->res_alpha_saved = 255;
    cache->ratio_saved = 255;
}

#if LV_DRAW_SW_SUPPORT_I1

static inline uint8_t LV_ATTRIBUTE_FAST_MEM get_bit(const uint8_t * buf, int32_t bit_idx)
{
    return (buf[bit_idx / 8] >> (7 - (bit_idx % 8))) & 1;
}

#endif

static inline void LV_ATTRIBUTE_FAST_MEM blend_non_normal_pixel(lv_color16a_t * dest, lv_color16a_t src,
                                                                lv_blend_mode_t mode, lv_color_mix_alpha_cache_t * cache)
{
    lv_color16a_t res;
    switch(mode) {
        case LV_BLEND_MODE_ADDITIVE:
            res.lumi = LV_MIN(dest->lumi + src.lumi, 255);
            break;
        case LV_BLEND_MODE_SUBTRACTIVE:
            res.lumi = LV_MAX(dest->lumi - src.lumi, 0);
            break;
        case LV_BLEND_MODE_MULTIPLY:
            res.lumi = (dest->lumi * src.lumi) >> 8;
            break;
        default:
            LV_LOG_WARN("Not supported blend mode: %d", mode);
            return;
    }
    res.alpha = src.alpha;
    lv_color_16a_16a_mix(res, dest, cache);
}

static inline void * LV_ATTRIBUTE_FAST_MEM drawbuf_next_row(const void * buf, uint32_t stride)
{
    return (void *)((uint8_t *)buf + stride);
}

#endif

#endif
