/**
 * @file lv_draw_sw_blend.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "../../../misc/lv_area_private.h"
#include "lv_draw_sw_blend_private.h"
#include "../../lv_draw_private.h"
#include "../lv_draw_sw.h"
#if LV_DRAW_SW_SUPPORT_L8
    #include "lv_draw_sw_blend_to_l8.h"
#endif
#if LV_DRAW_SW_SUPPORT_AL88
    #include "lv_draw_sw_blend_to_al88.h"
#endif
#if LV_DRAW_SW_SUPPORT_RGB565
    #include "lv_draw_sw_blend_to_rgb565.h"
#endif
#if LV_DRAW_SW_SUPPORT_ARGB8888
    #include "lv_draw_sw_blend_to_argb8888.h"
#endif
#if LV_DRAW_SW_SUPPORT_RGB888 || LV_DRAW_SW_SUPPORT_XRGB8888
    #include "lv_draw_sw_blend_to_rgb888.h"
#endif
#if LV_DRAW_SW_SUPPORT_I1
    #include "lv_draw_sw_blend_to_i1.h"
#endif
#if LV_USE_DRAW_SW

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

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

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

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

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

void lv_draw_sw_blend(lv_draw_unit_t * draw_unit, const lv_draw_sw_blend_dsc_t * blend_dsc)
{
    /*Do not draw transparent things*/
    if(blend_dsc->opa <= LV_OPA_MIN) return;
    if(blend_dsc->mask_buf && blend_dsc->mask_res == LV_DRAW_SW_MASK_RES_TRANSP) return;

    lv_area_t blend_area;
    if(!lv_area_intersect(&blend_area, blend_dsc->blend_area, draw_unit->clip_area)) return;

    LV_PROFILER_DRAW_BEGIN;
    lv_layer_t * layer = draw_unit->target_layer;
    uint32_t layer_stride_byte = layer->draw_buf->header.stride;

    if(blend_dsc->src_buf == NULL) {
        lv_draw_sw_blend_fill_dsc_t fill_dsc;
        fill_dsc.dest_w = lv_area_get_width(&blend_area);
        fill_dsc.dest_h = lv_area_get_height(&blend_area);
        fill_dsc.dest_stride = layer_stride_byte;
        fill_dsc.opa = blend_dsc->opa;
        fill_dsc.color = blend_dsc->color;
        fill_dsc.mask_stride = 0;

        if(blend_dsc->mask_buf == NULL) fill_dsc.mask_buf = NULL;
        else if(blend_dsc->mask_res == LV_DRAW_SW_MASK_RES_FULL_COVER) fill_dsc.mask_buf = NULL;
        else fill_dsc.mask_buf = blend_dsc->mask_buf;


        fill_dsc.relative_area  = blend_area;
        lv_area_move(&fill_dsc.relative_area, -layer->buf_area.x1, -layer->buf_area.y1);
        fill_dsc.dest_buf = lv_draw_layer_go_to_xy(layer, blend_area.x1 - layer->buf_area.x1,
                                                   blend_area.y1 - layer->buf_area.y1);
        if(fill_dsc.mask_buf) {
            fill_dsc.mask_stride = blend_dsc->mask_stride == 0  ? lv_area_get_width(blend_dsc->mask_area) : blend_dsc->mask_stride;
            fill_dsc.mask_buf += fill_dsc.mask_stride * (blend_area.y1 - blend_dsc->mask_area->y1) +
                                 (blend_area.x1 - blend_dsc->mask_area->x1);
        }

        switch(layer->color_format) {
#if LV_DRAW_SW_SUPPORT_RGB565
            case LV_COLOR_FORMAT_RGB565:
                lv_draw_sw_blend_color_to_rgb565(&fill_dsc);
                break;
#endif
#if LV_DRAW_SW_SUPPORT_ARGB8888
            case LV_COLOR_FORMAT_ARGB8888:
                lv_draw_sw_blend_color_to_argb8888(&fill_dsc);
                break;
#endif
#if LV_DRAW_SW_SUPPORT_RGB888
            case LV_COLOR_FORMAT_RGB888:
                lv_draw_sw_blend_color_to_rgb888(&fill_dsc, 3);
                break;
#endif
#if LV_DRAW_SW_SUPPORT_XRGB8888
            case LV_COLOR_FORMAT_XRGB8888:
                lv_draw_sw_blend_color_to_rgb888(&fill_dsc, 4);
                break;
#endif
#if LV_DRAW_SW_SUPPORT_L8
            case LV_COLOR_FORMAT_L8:
                lv_draw_sw_blend_color_to_l8(&fill_dsc);
                break;
#endif
#if LV_DRAW_SW_SUPPORT_AL88
            case LV_COLOR_FORMAT_AL88:
                lv_draw_sw_blend_color_to_al88(&fill_dsc);
                break;
#endif
#if LV_DRAW_SW_SUPPORT_I1
            case LV_COLOR_FORMAT_I1:
                lv_draw_sw_blend_color_to_i1(&fill_dsc);
                break;
#endif
            default:
                break;
        }
    }
    else {
        if(!lv_area_intersect(&blend_area, &blend_area, blend_dsc->src_area)) {
            LV_PROFILER_DRAW_END;
            return;
        }

        if(blend_dsc->mask_area && !lv_area_intersect(&blend_area, &blend_area, blend_dsc->mask_area)) {
            LV_PROFILER_DRAW_END;
            return;
        }

        lv_draw_sw_blend_image_dsc_t image_dsc;
        image_dsc.dest_w = lv_area_get_width(&blend_area);
        image_dsc.dest_h = lv_area_get_height(&blend_area);
        image_dsc.dest_stride = layer_stride_byte;

        image_dsc.opa = blend_dsc->opa;
        image_dsc.blend_mode = blend_dsc->blend_mode;
        image_dsc.src_stride = blend_dsc->src_stride;
        image_dsc.src_color_format = blend_dsc->src_color_format;

        const uint8_t * src_buf = blend_dsc->src_buf;
        uint32_t src_px_size = lv_color_format_get_bpp(blend_dsc->src_color_format);
        src_buf += image_dsc.src_stride * (blend_area.y1 - blend_dsc->src_area->y1);
        src_buf += ((blend_area.x1 - blend_dsc->src_area->x1) * src_px_size) >> 3;
        image_dsc.src_buf = src_buf;
        image_dsc.mask_stride = 0;

        if(blend_dsc->mask_buf == NULL) image_dsc.mask_buf = NULL;
        else if(blend_dsc->mask_res == LV_DRAW_SW_MASK_RES_FULL_COVER) image_dsc.mask_buf = NULL;
        else image_dsc.mask_buf = blend_dsc->mask_buf;

        if(image_dsc.mask_buf) {
            LV_ASSERT_NULL(blend_dsc->mask_area);
            image_dsc.mask_buf = blend_dsc->mask_buf;
            image_dsc.mask_stride = blend_dsc->mask_stride ? blend_dsc->mask_stride : lv_area_get_width(blend_dsc->mask_area);
            image_dsc.mask_buf += image_dsc.mask_stride * (blend_area.y1 - blend_dsc->mask_area->y1) +
                                  (blend_area.x1 - blend_dsc->mask_area->x1);
        }

        image_dsc.relative_area  = blend_area;
        lv_area_move(&image_dsc.relative_area, -layer->buf_area.x1, -layer->buf_area.y1);

        image_dsc.src_area  = *blend_dsc->src_area;
        lv_area_move(&image_dsc.src_area, -layer->buf_area.x1, -layer->buf_area.y1);

        image_dsc.dest_buf = lv_draw_layer_go_to_xy(layer, blend_area.x1 - layer->buf_area.x1,
                                                    blend_area.y1 - layer->buf_area.y1);

        switch(layer->color_format) {
#if LV_DRAW_SW_SUPPORT_RGB565
            case LV_COLOR_FORMAT_RGB565:
            case LV_COLOR_FORMAT_RGB565A8:
                lv_draw_sw_blend_image_to_rgb565(&image_dsc);
                break;
#endif
#if LV_DRAW_SW_SUPPORT_ARGB8888
            case LV_COLOR_FORMAT_ARGB8888:
                lv_draw_sw_blend_image_to_argb8888(&image_dsc);
                break;
#endif
#if LV_DRAW_SW_SUPPORT_RGB888
            case LV_COLOR_FORMAT_RGB888:
                lv_draw_sw_blend_image_to_rgb888(&image_dsc, 3);
                break;
#endif
#if LV_DRAW_SW_SUPPORT_XRGB8888
            case LV_COLOR_FORMAT_XRGB8888:
                lv_draw_sw_blend_image_to_rgb888(&image_dsc, 4);
                break;
#endif
#if LV_DRAW_SW_SUPPORT_L8
            case LV_COLOR_FORMAT_L8:
                lv_draw_sw_blend_image_to_l8(&image_dsc);
                break;
#endif
#if LV_DRAW_SW_SUPPORT_AL88
            case LV_COLOR_FORMAT_AL88:
                lv_draw_sw_blend_image_to_al88(&image_dsc);
                break;
#endif
#if LV_DRAW_SW_SUPPORT_I1
            case LV_COLOR_FORMAT_I1:
                lv_draw_sw_blend_image_to_i1(&image_dsc);
                break;
#endif
            default:
                break;
        }
    }
    LV_PROFILER_DRAW_END;
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

#endif
