This repository has been archived on 2023-07-17. You can view files and clone it, but cannot push or open issues or pull requests.
bl_mcu_sdk/components/lvgl/lv_draw/lv_draw_blend.c

1234 lines
46 KiB
C
Raw Normal View History

/**
* @file lv_draw_blend.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_blend.h"
#include "lv_img_decoder.h"
#include "../lv_misc/lv_math.h"
#include "../lv_hal/lv_hal_disp.h"
#include "../lv_core/lv_refr.h"
#if LV_USE_GPU_NXP_PXP
2021-06-20 12:25:46 +08:00
#include "../lv_gpu/lv_gpu_nxp_pxp.h"
#elif LV_USE_GPU_NXP_VG_LITE
2021-06-20 12:25:46 +08:00
#include "../lv_gpu/lv_gpu_nxp_vglite.h"
#elif LV_USE_GPU_STM32_DMA2D
2021-06-20 12:25:46 +08:00
#include "../lv_gpu/lv_gpu_stm32_dma2d.h"
#endif
/*********************
* DEFINES
*********************/
2021-06-20 12:25:46 +08:00
#define GPU_SIZE_LIMIT 240
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
2021-06-20 12:25:46 +08:00
static void fill_set_px(const lv_area_t *disp_area, lv_color_t *disp_buf, const lv_area_t *draw_area,
lv_color_t color, lv_opa_t opa,
2021-06-20 12:25:46 +08:00
const lv_opa_t *mask, lv_draw_mask_res_t mask_res);
2021-06-20 12:25:46 +08:00
LV_ATTRIBUTE_FAST_MEM static void fill_normal(const lv_area_t *disp_area, lv_color_t *disp_buf,
const lv_area_t *draw_area,
lv_color_t color, lv_opa_t opa,
2021-06-20 12:25:46 +08:00
const lv_opa_t *mask, lv_draw_mask_res_t mask_res);
#if LV_USE_BLEND_MODES
2021-06-20 12:25:46 +08:00
static void fill_blended(const lv_area_t *disp_area, lv_color_t *disp_buf, const lv_area_t *draw_area,
lv_color_t color, lv_opa_t opa,
2021-06-20 12:25:46 +08:00
const lv_opa_t *mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode);
#endif
2021-06-20 12:25:46 +08:00
static void map_set_px(const lv_area_t *disp_area, lv_color_t *disp_buf, const lv_area_t *draw_area,
const lv_area_t *map_area, const lv_color_t *map_buf, lv_opa_t opa,
const lv_opa_t *mask, lv_draw_mask_res_t mask_res);
2021-06-20 12:25:46 +08:00
LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t *disp_area, lv_color_t *disp_buf,
const lv_area_t *draw_area,
const lv_area_t *map_area, const lv_color_t *map_buf, lv_opa_t opa,
const lv_opa_t *mask, lv_draw_mask_res_t mask_res);
#if LV_USE_BLEND_MODES
2021-06-20 12:25:46 +08:00
static void map_blended(const lv_area_t *disp_area, lv_color_t *disp_buf, const lv_area_t *draw_area,
const lv_area_t *map_area, const lv_color_t *map_buf, lv_opa_t opa,
const lv_opa_t *mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode);
static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa);
static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa);
#endif
/**********************
* STATIC VARIABLES
**********************/
#if (LV_USE_GPU || LV_USE_GPU_STM32_DMA2D) && (LV_USE_GPU_NXP_PXP == 0) && (LV_USE_GPU_NXP_VG_LITE == 0)
2021-06-20 12:25:46 +08:00
LV_ATTRIBUTE_DMA static lv_color_t blend_buf[LV_HOR_RES_MAX];
#endif
/**********************
* MACROS
**********************/
2021-06-20 12:25:46 +08:00
#define FILL_NORMAL_MASK_PX(out_x, color) \
if (*mask_tmp_x) { \
if (*mask_tmp_x == LV_OPA_COVER) \
disp_buf_first[out_x] = color; \
else \
disp_buf_first[out_x] = lv_color_mix(color, disp_buf_first[out_x], *mask_tmp_x); \
} \
mask_tmp_x++;
2021-06-20 12:25:46 +08:00
#define FILL_NORMAL_MASK_PX_SCR_TRANSP(out_x, color) \
if (*mask_tmp_x) { \
if (*mask_tmp_x == LV_OPA_COVER) \
disp_buf_first[out_x] = color; \
else if (disp->driver.screen_transp) \
lv_color_mix_with_alpha(disp_buf_first[out_x], disp_buf_first[out_x].ch.alpha, \
color, *mask_tmp_x, &disp_buf_first[out_x], &disp_buf_first[out_x].ch.alpha); \
else \
disp_buf_first[out_x] = lv_color_mix(color, disp_buf_first[out_x], *mask_tmp_x); \
} \
mask_tmp_x++;
2021-06-20 12:25:46 +08:00
#define MAP_NORMAL_MASK_PX(x) \
if (*mask_tmp_x) { \
if (*mask_tmp_x == LV_OPA_COVER) \
disp_buf_first[x] = map_buf_first[x]; \
else \
disp_buf_first[x] = lv_color_mix(map_buf_first[x], disp_buf_first[x], *mask_tmp_x); \
} \
mask_tmp_x++;
2021-06-20 12:25:46 +08:00
#define MAP_NORMAL_MASK_PX_SCR_TRANSP(x) \
if (*mask_tmp_x) { \
if (*mask_tmp_x == LV_OPA_COVER) \
disp_buf_first[x] = map_buf_first[x]; \
else if (disp->driver.screen_transp) \
lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, \
map_buf_first[x], *mask_tmp_x, &disp_buf_first[x], &disp_buf_first[x].ch.alpha); \
else \
disp_buf_first[x] = lv_color_mix(map_buf_first[x], disp_buf_first[x], *mask_tmp_x); \
} \
mask_tmp_x++;
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Fill and area in the display buffer.
* @param clip_area clip the fill to this area (absolute coordinates)
* @param fill_area fill this area (absolute coordinates) (should be clipped)
* @param color fill color
* @param mask a mask to apply on the fill (uint8_t array with 0x00..0xff values).
* Relative to fill area but its width is truncated to clip area.
* @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask),
* LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent),
* LV_MASK_RES_CHANGED: the mask has mixed values
* @param opa overall opacity in 0x00..0xff range
* @param mode blend mode from `lv_blend_mode_t`
*/
2021-06-20 12:25:46 +08:00
LV_ATTRIBUTE_FAST_MEM void _lv_blend_fill(const lv_area_t *clip_area, const lv_area_t *fill_area,
lv_color_t color, lv_opa_t *mask, lv_draw_mask_res_t mask_res, lv_opa_t opa,
lv_blend_mode_t mode)
{
/*Do not draw transparent things*/
2021-06-20 12:25:46 +08:00
if (opa < LV_OPA_MIN) {
return;
}
2021-06-20 12:25:46 +08:00
if (mask_res == LV_DRAW_MASK_RES_TRANSP) {
return;
}
2021-06-20 12:25:46 +08:00
lv_disp_t *disp = _lv_refr_get_disp_refreshing();
lv_disp_buf_t *vdb = lv_disp_get_buf(disp);
const lv_area_t *disp_area = &vdb->area;
lv_color_t *disp_buf = vdb->buf_act;
if (disp->driver.gpu_wait_cb) {
disp->driver.gpu_wait_cb(&disp->driver);
}
/* Get clipped fill area which is the real draw area.
* It is always the same or inside `fill_area` */
lv_area_t draw_area;
bool is_common;
is_common = _lv_area_intersect(&draw_area, clip_area, fill_area);
2021-06-20 12:25:46 +08:00
if (!is_common) {
return;
}
/* Now `draw_area` has absolute coordinates.
* Make it relative to `disp_area` to simplify draw to `disp_buf`*/
draw_area.x1 -= disp_area->x1;
draw_area.y1 -= disp_area->y1;
draw_area.x2 -= disp_area->x1;
draw_area.y2 -= disp_area->y1;
/*Round the values in the mask if anti-aliasing is disabled*/
#if LV_ANTIALIAS
2021-06-20 12:25:46 +08:00
if (mask && disp->driver.antialiasing == 0)
#else
2021-06-20 12:25:46 +08:00
if (mask)
#endif
{
int32_t mask_w = lv_area_get_width(&draw_area);
int32_t i;
2021-06-20 12:25:46 +08:00
for (i = 0; i < mask_w; i++) {
mask[i] = mask[i] > 128 ? LV_OPA_COVER : LV_OPA_TRANSP;
}
}
2021-06-20 12:25:46 +08:00
if (disp->driver.set_px_cb) {
fill_set_px(disp_area, disp_buf, &draw_area, color, opa, mask, mask_res);
2021-06-20 12:25:46 +08:00
} else if (mode == LV_BLEND_MODE_NORMAL) {
fill_normal(disp_area, disp_buf, &draw_area, color, opa, mask, mask_res);
}
#if LV_USE_BLEND_MODES
else {
fill_blended(disp_area, disp_buf, &draw_area, color, opa, mask, mask_res, mode);
}
2021-06-20 12:25:46 +08:00
#endif
}
/**
* Copy a map (image) to a display buffer.
* @param clip_area clip the map to this area (absolute coordinates)
* @param map_area area of the image (absolute coordinates)
* @param map_buf a pixels of the map (image)
* @param mask a mask to apply on every pixel (uint8_t array with 0x00..0xff values).
* Relative to map area but its width is truncated to clip area.
* @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask),
* LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent),
* LV_MASK_RES_CHANGED: the mask has mixed values
* @param opa overall opacity in 0x00..0xff range
* @param mode blend mode from `lv_blend_mode_t`
*/
2021-06-20 12:25:46 +08:00
LV_ATTRIBUTE_FAST_MEM void _lv_blend_map(const lv_area_t *clip_area, const lv_area_t *map_area,
const lv_color_t *map_buf,
lv_opa_t *mask, lv_draw_mask_res_t mask_res,
lv_opa_t opa, lv_blend_mode_t mode)
{
/*Do not draw transparent things*/
2021-06-20 12:25:46 +08:00
if (opa < LV_OPA_MIN) {
return;
}
if (mask_res == LV_DRAW_MASK_RES_TRANSP) {
return;
}
/* Get clipped fill area which is the real draw area.
* It is always the same or inside `fill_area` */
lv_area_t draw_area;
bool is_common;
is_common = _lv_area_intersect(&draw_area, clip_area, map_area);
2021-06-20 12:25:46 +08:00
if (!is_common) {
return;
}
lv_disp_t *disp = _lv_refr_get_disp_refreshing();
lv_disp_buf_t *vdb = lv_disp_get_buf(disp);
const lv_area_t *disp_area = &vdb->area;
lv_color_t *disp_buf = vdb->buf_act;
2021-06-20 12:25:46 +08:00
if (disp->driver.gpu_wait_cb) {
disp->driver.gpu_wait_cb(&disp->driver);
}
/* Now `draw_area` has absolute coordinates.
* Make it relative to `disp_area` to simplify draw to `disp_buf`*/
draw_area.x1 -= disp_area->x1;
draw_area.y1 -= disp_area->y1;
draw_area.x2 -= disp_area->x1;
draw_area.y2 -= disp_area->y1;
/*Round the values in the mask if anti-aliasing is disabled*/
#if LV_ANTIALIAS
2021-06-20 12:25:46 +08:00
if (mask && disp->driver.antialiasing == 0)
#else
2021-06-20 12:25:46 +08:00
if (mask)
#endif
{
int32_t mask_w = lv_area_get_width(&draw_area);
int32_t i;
2021-06-20 12:25:46 +08:00
for (i = 0; i < mask_w; i++) {
mask[i] = mask[i] > 128 ? LV_OPA_COVER : LV_OPA_TRANSP;
}
}
2021-06-20 12:25:46 +08:00
if (disp->driver.set_px_cb) {
map_set_px(disp_area, disp_buf, &draw_area, map_area, map_buf, opa, mask, mask_res);
2021-06-20 12:25:46 +08:00
} else if (mode == LV_BLEND_MODE_NORMAL) {
map_normal(disp_area, disp_buf, &draw_area, map_area, map_buf, opa, mask, mask_res);
}
#if LV_USE_BLEND_MODES
else {
map_blended(disp_area, disp_buf, &draw_area, map_area, map_buf, opa, mask, mask_res, mode);
}
2021-06-20 12:25:46 +08:00
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
2021-06-20 12:25:46 +08:00
static void fill_set_px(const lv_area_t *disp_area, lv_color_t *disp_buf, const lv_area_t *draw_area,
lv_color_t color, lv_opa_t opa,
2021-06-20 12:25:46 +08:00
const lv_opa_t *mask, lv_draw_mask_res_t mask_res)
{
2021-06-20 12:25:46 +08:00
lv_disp_t *disp = _lv_refr_get_disp_refreshing();
/*Get the width of the `disp_area` it will be used to go to the next line*/
int32_t disp_w = lv_area_get_width(disp_area);
int32_t x;
int32_t y;
2021-06-20 12:25:46 +08:00
if (mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
for (y = draw_area->y1; y <= draw_area->y2; y++) {
for (x = draw_area->x1; x <= draw_area->x2; x++) {
disp->driver.set_px_cb(&disp->driver, (void *)disp_buf, disp_w, x, y, color, opa);
}
}
2021-06-20 12:25:46 +08:00
} else {
/* The mask is relative to the clipped area.
* In the cycles below mask will be indexed from `draw_area.x1`
* but it corresponds to zero index. So prepare `mask_tmp` accordingly. */
2021-06-20 12:25:46 +08:00
const lv_opa_t *mask_tmp = mask - draw_area->x1;
/*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
int32_t draw_area_w = lv_area_get_width(draw_area);
2021-06-20 12:25:46 +08:00
for (y = draw_area->y1; y <= draw_area->y2; y++) {
for (x = draw_area->x1; x <= draw_area->x2; x++) {
if (mask_tmp[x]) {
disp->driver.set_px_cb(&disp->driver, (void *)disp_buf, disp_w, x, y, color,
(uint32_t)((uint32_t)opa * mask_tmp[x]) >> 8);
}
}
2021-06-20 12:25:46 +08:00
mask_tmp += draw_area_w;
}
}
}
/**
* Fill an area with a color
* @param disp_area the current display area (destination area)
* @param disp_buf destination buffer
* @param draw_area fill this area (relative to `disp_area`)
* @param color fill color
* @param opa overall opacity in 0x00..0xff range
* @param mask a mask to apply on every pixel (uint8_t array with 0x00..0xff values).
* It fits into draw_area.
* @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask),
* LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent),
* LV_MASK_RES_CHANGED: the mask has mixed values
*/
2021-06-20 12:25:46 +08:00
LV_ATTRIBUTE_FAST_MEM static void fill_normal(const lv_area_t *disp_area, lv_color_t *disp_buf,
const lv_area_t *draw_area,
lv_color_t color, lv_opa_t opa,
2021-06-20 12:25:46 +08:00
const lv_opa_t *mask, lv_draw_mask_res_t mask_res)
{
#if LV_USE_GPU || LV_COLOR_SCREEN_TRANSP
2021-06-20 12:25:46 +08:00
lv_disp_t *disp = _lv_refr_get_disp_refreshing();
#endif
/*Get the width of the `disp_area` it will be used to go to the next line*/
int32_t disp_w = lv_area_get_width(disp_area);
int32_t draw_area_w = lv_area_get_width(draw_area);
int32_t draw_area_h = lv_area_get_height(draw_area);
/*Create a temp. disp_buf which always point to the first pixel of the destination area*/
2021-06-20 12:25:46 +08:00
lv_color_t *disp_buf_first = disp_buf + disp_w * draw_area->y1 + draw_area->x1;
int32_t x;
int32_t y;
/*Simple fill (maybe with opacity), no masking*/
2021-06-20 12:25:46 +08:00
if (mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
if (opa > LV_OPA_MAX) {
#if LV_USE_GPU_NXP_PXP
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) >= LV_GPU_NXP_PXP_FILL_SIZE_LIMIT) {
lv_gpu_nxp_pxp_fill(disp_buf, disp_w, draw_area, color, opa);
return;
}
2021-06-20 12:25:46 +08:00
#elif LV_USE_GPU_NXP_VG_LITE
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) >= LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT) {
if (lv_gpu_nxp_vglite_fill(disp_buf, disp_w, lv_area_get_height(disp_area), draw_area, color, opa) == LV_RES_OK) {
return;
}
2021-06-20 12:25:46 +08:00
/* Fall down to SW render in case of error */
}
2021-06-20 12:25:46 +08:00
#elif LV_USE_GPU_STM32_DMA2D
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) >= 240) {
lv_gpu_stm32_dma2d_fill(disp_buf_first, disp_w, color, draw_area_w, draw_area_h);
return;
}
2021-06-20 12:25:46 +08:00
#elif LV_USE_GPU
2021-06-20 12:25:46 +08:00
if (disp->driver.gpu_fill_cb && lv_area_get_size(draw_area) > GPU_SIZE_LIMIT) {
disp->driver.gpu_fill_cb(&disp->driver, disp_buf, disp_w, draw_area, color);
return;
}
2021-06-20 12:25:46 +08:00
#endif
2021-06-20 12:25:46 +08:00
/*Software rendering*/
2021-06-20 12:25:46 +08:00
for (y = 0; y < draw_area_h; y++) {
lv_color_fill(disp_buf_first, color, draw_area_w);
disp_buf_first += disp_w;
}
}
/*No mask with opacity*/
else {
#if LV_USE_GPU_NXP_PXP
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) >= LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT) {
lv_gpu_nxp_pxp_fill(disp_buf, disp_w, draw_area, color, opa);
return;
}
2021-06-20 12:25:46 +08:00
#elif LV_USE_GPU_NXP_VG_LITE
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) >= LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT) {
if (lv_gpu_nxp_vglite_fill(disp_buf, disp_w, lv_area_get_height(disp_area), draw_area, color, opa) == LV_RES_OK) {
return;
}
2021-06-20 12:25:46 +08:00
/* Fall down to SW render in case of error */
}
2021-06-20 12:25:46 +08:00
#elif LV_USE_GPU
2021-06-20 12:25:46 +08:00
if (disp->driver.gpu_blend_cb && lv_area_get_size(draw_area) > GPU_SIZE_LIMIT) {
for (x = 0; x < draw_area_w; x++) {
blend_buf[x].full = color.full;
}
for (y = draw_area->y1; y <= draw_area->y2; y++) {
disp->driver.gpu_blend_cb(&disp->driver, disp_buf_first, blend_buf, draw_area_w, opa);
disp_buf_first += disp_w;
}
2021-06-20 12:25:46 +08:00
return;
}
2021-06-20 12:25:46 +08:00
#endif
#if LV_USE_GPU_STM32_DMA2D
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) >= 240) {
if (blend_buf[0].full != color.full) {
lv_color_fill(blend_buf, color, LV_HOR_RES_MAX);
}
lv_coord_t line_h = LV_HOR_RES_MAX / draw_area_w;
2021-06-20 12:25:46 +08:00
for (y = 0; y <= draw_area_h - line_h; y += line_h) {
lv_gpu_stm32_dma2d_blend(disp_buf_first, disp_w, blend_buf, opa, draw_area_w, draw_area_w, line_h);
lv_gpu_stm32_dma2d_wait_cb(NULL);
disp_buf_first += disp_w * line_h;
}
2021-06-20 12:25:46 +08:00
if (y != draw_area_h) {
lv_gpu_stm32_dma2d_blend(disp_buf_first, disp_w, blend_buf, opa, draw_area_w, draw_area_w, draw_area_h - y);
}
return;
}
2021-06-20 12:25:46 +08:00
#endif
lv_color_t last_dest_color = LV_COLOR_BLACK;
lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa);
uint16_t color_premult[3];
lv_color_premult(color, opa, color_premult);
lv_opa_t opa_inv = 255 - opa;
2021-06-20 12:25:46 +08:00
for (y = 0; y < draw_area_h; y++) {
for (x = 0; x < draw_area_w; x++) {
if (last_dest_color.full != disp_buf_first[x].full) {
last_dest_color = disp_buf_first[x];
#if LV_COLOR_SCREEN_TRANSP
2021-06-20 12:25:46 +08:00
if (disp->driver.screen_transp) {
lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, color, opa, &last_res_color,
&last_res_color.ch.alpha);
2021-06-20 12:25:46 +08:00
} else
#endif
{
last_res_color = lv_color_mix_premult(color_premult, disp_buf_first[x], opa_inv);
}
}
2021-06-20 12:25:46 +08:00
disp_buf_first[x] = last_res_color;
}
2021-06-20 12:25:46 +08:00
disp_buf_first += disp_w;
}
}
}
/*Masked*/
else {
/*DMA2D could be used here but it's much slower than software rendering*/
#if LV_USE_GPU_STM32_DMA2D && 0
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) > 240) {
lv_gpu_stm32_dma2d_fill_mask(disp_buf_first, disp_w, color, mask, opa, draw_area_w, draw_area_h);
return;
}
2021-06-20 12:25:46 +08:00
#endif
/*Buffer the result color to avoid recalculating the same color*/
lv_color_t last_dest_color;
lv_color_t last_res_color;
lv_opa_t last_mask = LV_OPA_TRANSP;
last_dest_color.full = disp_buf_first[0].full;
last_res_color.full = disp_buf_first[0].full;
int32_t x_end4 = draw_area_w - 4;
/*Only the mask matters*/
2021-06-20 12:25:46 +08:00
if (opa > LV_OPA_MAX) {
for (y = 0; y < draw_area_h; y++) {
const lv_opa_t *mask_tmp_x = mask;
#if 0
2021-06-20 12:25:46 +08:00
for(x = 0; x < draw_area_w; x++)
{
#if LV_COLOR_SCREEN_TRANSP
FILL_NORMAL_MASK_PX_SCR_TRANSP(x, color)
#else
FILL_NORMAL_MASK_PX(x, color)
#endif
}
2021-06-20 12:25:46 +08:00
#else
2021-06-20 12:25:46 +08:00
for (x = 0; x < draw_area_w && ((lv_uintptr_t)mask_tmp_x & 0x3); x++) {
#if LV_COLOR_SCREEN_TRANSP
FILL_NORMAL_MASK_PX_SCR_TRANSP(x, color)
#else
FILL_NORMAL_MASK_PX(x, color)
#endif
}
2021-06-20 12:25:46 +08:00
uint32_t *mask32 = (uint32_t *)mask_tmp_x;
for (; x <= x_end4; x += 4) {
if (*mask32) {
if ((*mask32) == 0xFFFFFFFF) {
disp_buf_first[x] = color;
disp_buf_first[x + 1] = color;
disp_buf_first[x + 2] = color;
disp_buf_first[x + 3] = color;
2021-06-20 12:25:46 +08:00
} else {
mask_tmp_x = (const lv_opa_t *)mask32;
#if LV_COLOR_SCREEN_TRANSP
FILL_NORMAL_MASK_PX_SCR_TRANSP(x, color)
FILL_NORMAL_MASK_PX_SCR_TRANSP(x + 1, color)
FILL_NORMAL_MASK_PX_SCR_TRANSP(x + 2, color)
FILL_NORMAL_MASK_PX_SCR_TRANSP(x + 3, color)
#else
FILL_NORMAL_MASK_PX(x, color)
FILL_NORMAL_MASK_PX(x + 1, color)
FILL_NORMAL_MASK_PX(x + 2, color)
FILL_NORMAL_MASK_PX(x + 3, color)
#endif
}
}
2021-06-20 12:25:46 +08:00
mask32++;
}
mask_tmp_x = (const lv_opa_t *)mask32;
2021-06-20 12:25:46 +08:00
for (; x < draw_area_w; x++) {
#if LV_COLOR_SCREEN_TRANSP
FILL_NORMAL_MASK_PX_SCR_TRANSP(x, color)
#else
FILL_NORMAL_MASK_PX(x, color)
#endif
}
2021-06-20 12:25:46 +08:00
#endif
disp_buf_first += disp_w;
mask += draw_area_w;
}
}
/*Handle opa and mask values too*/
else {
lv_opa_t opa_tmp = LV_OPA_TRANSP;
2021-06-20 12:25:46 +08:00
for (y = draw_area->y1; y <= draw_area->y2; y++) {
const lv_opa_t *mask_tmp_x = mask;
for (x = 0; x < draw_area_w; x++) {
if (*mask_tmp_x) {
if (*mask_tmp_x != last_mask)
opa_tmp = *mask_tmp_x == LV_OPA_COVER ? opa :
(uint32_t)((uint32_t)(*mask_tmp_x) * opa) >> 8;
if (*mask_tmp_x != last_mask || last_dest_color.full != disp_buf_first[x].full) {
#if LV_COLOR_SCREEN_TRANSP
2021-06-20 12:25:46 +08:00
if (disp->driver.screen_transp) {
lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, color, opa_tmp, &last_res_color,
&last_res_color.ch.alpha);
2021-06-20 12:25:46 +08:00
} else
#endif
{
2021-06-20 12:25:46 +08:00
if (opa_tmp == LV_OPA_COVER) {
last_res_color = color;
} else {
last_res_color = lv_color_mix(color, disp_buf_first[x], opa_tmp);
}
}
2021-06-20 12:25:46 +08:00
last_mask = *mask_tmp_x;
last_dest_color.full = disp_buf_first[x].full;
}
2021-06-20 12:25:46 +08:00
disp_buf_first[x] = last_res_color;
}
2021-06-20 12:25:46 +08:00
mask_tmp_x++;
}
2021-06-20 12:25:46 +08:00
disp_buf_first += disp_w;
mask += draw_area_w;
}
}
}
}
#if LV_USE_BLEND_MODES
/**
* Fill an area with a color but apply blending algorithms
* @param disp_area the current display area (destination area)
* @param disp_buf destination buffer
* @param draw_area fill this area (relative to `disp_area`)
* @param color fill color
* @param opa overall opacity in 0x00..0xff range
* @param mask a mask to apply on every pixel (uint8_t array with 0x00..0xff values).
* It fits into draw_area.
* @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask),
* LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent),
* LV_MASK_RES_CHANGED: the mask has mixed values
* @param mode blend mode from `lv_blend_mode_t`
*/
2021-06-20 12:25:46 +08:00
static void fill_blended(const lv_area_t *disp_area, lv_color_t *disp_buf, const lv_area_t *draw_area,
lv_color_t color, lv_opa_t opa,
2021-06-20 12:25:46 +08:00
const lv_opa_t *mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode)
{
/*Get the width of the `disp_area` it will be used to go to the next line*/
int32_t disp_w = lv_area_get_width(disp_area);
/*Create a temp. disp_buf which always point to current line to draw*/
2021-06-20 12:25:46 +08:00
lv_color_t *disp_buf_tmp = disp_buf + disp_w * draw_area->y1;
lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t);
2021-06-20 12:25:46 +08:00
switch (mode) {
case LV_BLEND_MODE_ADDITIVE:
blend_fp = color_blend_true_color_additive;
break;
2021-06-20 12:25:46 +08:00
case LV_BLEND_MODE_SUBTRACTIVE:
blend_fp = color_blend_true_color_subtractive;
break;
2021-06-20 12:25:46 +08:00
default:
LV_LOG_WARN("fill_blended: unsupported blend mode");
return;
}
int32_t x;
int32_t y;
/*Simple fill (maybe with opacity), no masking*/
2021-06-20 12:25:46 +08:00
if (mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
lv_color_t last_dest_color = LV_COLOR_BLACK;
lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa);
2021-06-20 12:25:46 +08:00
for (y = draw_area->y1; y <= draw_area->y2; y++) {
for (x = draw_area->x1; x <= draw_area->x2; x++) {
if (last_dest_color.full != disp_buf_tmp[x].full) {
last_dest_color = disp_buf_tmp[x];
last_res_color = blend_fp(color, disp_buf_tmp[x], opa);
}
2021-06-20 12:25:46 +08:00
disp_buf_tmp[x] = last_res_color;
}
2021-06-20 12:25:46 +08:00
disp_buf_tmp += disp_w;
}
}
/*Masked*/
else {
/*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
int32_t draw_area_w = lv_area_get_width(draw_area);
/* The mask is relative to the clipped area.
* In the cycles below mask will be indexed from `draw_area.x1`
* but it corresponds to zero index. So prepare `mask_tmp` accordingly. */
2021-06-20 12:25:46 +08:00
const lv_opa_t *mask_tmp = mask - draw_area->x1;
/*Buffer the result color to avoid recalculating the same color*/
lv_color_t last_dest_color;
lv_color_t last_res_color;
lv_opa_t last_mask = LV_OPA_TRANSP;
last_dest_color.full = disp_buf_tmp[0].full;
last_res_color.full = disp_buf_tmp[0].full;
2021-06-20 12:25:46 +08:00
for (y = draw_area->y1; y <= draw_area->y2; y++) {
for (x = draw_area->x1; x <= draw_area->x2; x++) {
if (mask_tmp[x] == 0) {
continue;
}
if (mask_tmp[x] != last_mask || last_dest_color.full != disp_buf_tmp[x].full) {
lv_opa_t opa_tmp = mask_tmp[x] >= LV_OPA_MAX ? opa : (uint32_t)((uint32_t)mask_tmp[x] * opa) >> 8;
last_res_color = blend_fp(color, disp_buf_tmp[x], opa_tmp);
last_mask = mask_tmp[x];
last_dest_color.full = disp_buf_tmp[x].full;
}
2021-06-20 12:25:46 +08:00
disp_buf_tmp[x] = last_res_color;
}
2021-06-20 12:25:46 +08:00
disp_buf_tmp += disp_w;
mask_tmp += draw_area_w;
}
}
}
#endif
2021-06-20 12:25:46 +08:00
static void map_set_px(const lv_area_t *disp_area, lv_color_t *disp_buf, const lv_area_t *draw_area,
const lv_area_t *map_area, const lv_color_t *map_buf, lv_opa_t opa,
const lv_opa_t *mask, lv_draw_mask_res_t mask_res)
{
2021-06-20 12:25:46 +08:00
lv_disp_t *disp = _lv_refr_get_disp_refreshing();
/*Get the width of the `disp_area` it will be used to go to the next line*/
int32_t disp_w = lv_area_get_width(disp_area);
/*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
int32_t draw_area_w = lv_area_get_width(draw_area);
/*Get the width of the `mask_area` it will be used to go to the next line*/
int32_t map_w = lv_area_get_width(map_area);
/*Create a temp. map_buf which always point to current line to draw*/
2021-06-20 12:25:46 +08:00
const lv_color_t *map_buf_tmp = map_buf + map_w * (draw_area->y1 - (map_area->y1 - disp_area->y1));
map_buf_tmp += (draw_area->x1 - (map_area->x1 - disp_area->x1));
map_buf_tmp -= draw_area->x1;
int32_t x;
int32_t y;
2021-06-20 12:25:46 +08:00
if (mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
for (y = draw_area->y1; y <= draw_area->y2; y++) {
for (x = draw_area->x1; x <= draw_area->x2; x++) {
disp->driver.set_px_cb(&disp->driver, (void *)disp_buf, disp_w, x, y, map_buf_tmp[x], opa);
}
2021-06-20 12:25:46 +08:00
map_buf_tmp += map_w;
}
2021-06-20 12:25:46 +08:00
} else {
/* The mask is relative to the clipped area.
* In the cycles below mask will be indexed from `draw_area.x1`
* but it corresponds to zero index. So prepare `mask_tmp` accordingly. */
2021-06-20 12:25:46 +08:00
const lv_opa_t *mask_tmp = mask - draw_area->x1;
2021-06-20 12:25:46 +08:00
for (y = draw_area->y1; y <= draw_area->y2; y++) {
for (x = draw_area->x1; x <= draw_area->x2; x++) {
if (mask_tmp[x]) {
disp->driver.set_px_cb(&disp->driver, (void *)disp_buf, disp_w, x, y, map_buf_tmp[x],
(uint32_t)((uint32_t)opa * mask_tmp[x]) >> 8);
}
}
2021-06-20 12:25:46 +08:00
mask_tmp += draw_area_w;
map_buf_tmp += map_w;
}
}
}
/**
* Copy an image to an area
* @param disp_area the current display area (destination area)
* @param disp_buf destination buffer
* @param map_area coordinates of the map (image) to copy. (absolute coordinates)
* @param map_buf the pixel of the image
* @param opa overall opacity in 0x00..0xff range
* @param mask a mask to apply on every pixel (uint8_t array with 0x00..0xff values).
* It fits into draw_area.
* @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask),
* LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent),
* LV_MASK_RES_CHANGED: the mask has mixed values
*/
2021-06-20 12:25:46 +08:00
LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t *disp_area, lv_color_t *disp_buf,
const lv_area_t *draw_area,
const lv_area_t *map_area, const lv_color_t *map_buf, lv_opa_t opa,
const lv_opa_t *mask, lv_draw_mask_res_t mask_res)
{
/*Get the width of the `disp_area` it will be used to go to the next line*/
int32_t disp_w = lv_area_get_width(disp_area);
int32_t draw_area_w = lv_area_get_width(draw_area);
int32_t draw_area_h = lv_area_get_height(draw_area);
/*Get the width of the `mask_area` it will be used to go to the next line*/
int32_t map_w = lv_area_get_width(map_area);
/*Create a temp. disp_buf which always point to first pixel to draw*/
2021-06-20 12:25:46 +08:00
lv_color_t *disp_buf_first = disp_buf + disp_w * draw_area->y1 + draw_area->x1;
/*Create a temp. map_buf which always point to first pixel to draw from the map*/
2021-06-20 12:25:46 +08:00
const lv_color_t *map_buf_first = map_buf + map_w * (draw_area->y1 - (map_area->y1 - disp_area->y1));
map_buf_first += (draw_area->x1 - (map_area->x1 - disp_area->x1));
#if LV_COLOR_SCREEN_TRANSP || LV_USE_GPU
2021-06-20 12:25:46 +08:00
lv_disp_t *disp = _lv_refr_get_disp_refreshing();
#endif
int32_t x;
int32_t y;
/*Simple fill (maybe with opacity), no masking*/
2021-06-20 12:25:46 +08:00
if (mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
#if LV_USE_GPU
2021-06-20 12:25:46 +08:00
if (disp->driver.gpu_blend_cb && (lv_area_get_size(draw_area) > GPU_SIZE_LIMIT)) {
for (y = draw_area->y1; y <= draw_area->y2; y++) {
disp->driver.gpu_blend_cb(&disp->driver, disp_buf_first, map_buf_first, draw_area_w, opa);
disp_buf_first += disp_w;
map_buf_first += map_w;
}
2021-06-20 12:25:46 +08:00
return;
}
2021-06-20 12:25:46 +08:00
#endif
2021-06-20 12:25:46 +08:00
if (opa > LV_OPA_MAX) {
#if LV_USE_GPU_NXP_PXP
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) >= LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT) {
lv_gpu_nxp_pxp_blit(disp_buf_first, disp_w, map_buf_first, map_w, draw_area_w, draw_area_h, opa);
return;
}
2021-06-20 12:25:46 +08:00
#elif (LV_USE_GPU_NXP_VG_LITE)
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) >= LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT) {
lv_gpu_nxp_vglite_blit_info_t blit;
blit.src = map_buf;
blit.src_width = draw_area_w;
blit.src_height = draw_area_h;
blit.src_stride = lv_area_get_width(map_area) * sizeof(lv_color_t);
blit.src_area.x1 = (draw_area->x1 - (map_area->x1 - disp_area->x1));
blit.src_area.y1 = (draw_area->y1 - (map_area->y1 - disp_area->y1));
blit.src_area.x2 = blit.src_area.x1 + draw_area_w;
blit.src_area.y2 = blit.src_area.y1 + draw_area_h;
blit.dst = disp_buf;
blit.dst_width = lv_area_get_width(disp_area);
blit.dst_height = lv_area_get_height(disp_area);
blit.dst_stride = lv_area_get_width(disp_area) * sizeof(lv_color_t);
blit.dst_area.x1 = draw_area->x1;
blit.dst_area.y1 = draw_area->y1;
blit.dst_area.x2 = blit.dst_area.x1 + draw_area_w;
blit.dst_area.y2 = blit.dst_area.y1 + draw_area_h;
blit.opa = opa;
2021-06-20 12:25:46 +08:00
if (lv_gpu_nxp_vglite_blit(&blit) == LV_RES_OK) {
return;
}
2021-06-20 12:25:46 +08:00
/* Fall down to SW render in case of error */
}
2021-06-20 12:25:46 +08:00
#elif LV_USE_GPU_STM32_DMA2D
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) >= 240) {
lv_gpu_stm32_dma2d_copy(disp_buf_first, disp_w, map_buf_first, map_w, draw_area_w, draw_area_h);
return;
}
2021-06-20 12:25:46 +08:00
#endif
/*Software rendering*/
2021-06-20 12:25:46 +08:00
for (y = 0; y < draw_area_h; y++) {
_lv_memcpy(disp_buf_first, map_buf_first, draw_area_w * sizeof(lv_color_t));
disp_buf_first += disp_w;
map_buf_first += map_w;
}
2021-06-20 12:25:46 +08:00
} else {
#if LV_USE_GPU_NXP_PXP
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) >= LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT) {
lv_gpu_nxp_pxp_blit(disp_buf_first, disp_w, map_buf_first, map_w, draw_area_w, draw_area_h, opa);
return;
}
2021-06-20 12:25:46 +08:00
#elif (LV_USE_GPU_NXP_VG_LITE)
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) >= LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT) {
lv_gpu_nxp_vglite_blit_info_t blit;
blit.src = map_buf;
blit.src_width = lv_area_get_width(map_area);
blit.src_height = lv_area_get_height(map_area);
blit.src_stride = lv_area_get_width(map_area) * sizeof(lv_color_t);
blit.src_area.x1 = (draw_area->x1 - (map_area->x1 - disp_area->x1));
blit.src_area.y1 = (draw_area->y1 - (map_area->y1 - disp_area->y1));
blit.src_area.x2 = blit.src_area.x1 + draw_area_w;
blit.src_area.y2 = blit.src_area.y1 + draw_area_h;
blit.dst = disp_buf;
blit.dst_width = lv_area_get_width(disp_area);
blit.dst_height = lv_area_get_height(disp_area);
blit.dst_stride = lv_area_get_width(disp_area) * sizeof(lv_color_t);
blit.dst_area.x1 = draw_area->x1;
blit.dst_area.y1 = draw_area->y1;
blit.dst_area.x2 = blit.dst_area.x1 + draw_area_w;
blit.dst_area.y2 = blit.dst_area.y1 + draw_area_h;
blit.opa = opa;
2021-06-20 12:25:46 +08:00
if (lv_gpu_nxp_vglite_blit(&blit) == LV_RES_OK) {
return;
}
2021-06-20 12:25:46 +08:00
/* Fall down to SW render in case of error */
}
2021-06-20 12:25:46 +08:00
#elif LV_USE_GPU_STM32_DMA2D
2021-06-20 12:25:46 +08:00
if (lv_area_get_size(draw_area) >= 240) {
lv_gpu_stm32_dma2d_blend(disp_buf_first, disp_w, map_buf_first, opa, map_w, draw_area_w, draw_area_h);
return;
}
2021-06-20 12:25:46 +08:00
#endif
/*Software rendering*/
2021-06-20 12:25:46 +08:00
for (y = 0; y < draw_area_h; y++) {
for (x = 0; x < draw_area_w; x++) {
#if LV_COLOR_SCREEN_TRANSP
2021-06-20 12:25:46 +08:00
if (disp->driver.screen_transp) {
lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, map_buf_first[x], opa, &disp_buf_first[x],
&disp_buf_first[x].ch.alpha);
2021-06-20 12:25:46 +08:00
} else
#endif
{
disp_buf_first[x] = lv_color_mix(map_buf_first[x], disp_buf_first[x], opa);
}
}
2021-06-20 12:25:46 +08:00
disp_buf_first += disp_w;
map_buf_first += map_w;
}
}
}
/*Masked*/
else {
/*Only the mask matters*/
2021-06-20 12:25:46 +08:00
if (opa > LV_OPA_MAX) {
/*Go to the first pixel of the row */
int32_t x_end4 = draw_area_w - 4;
2021-06-20 12:25:46 +08:00
for (y = 0; y < draw_area_h; y++) {
const lv_opa_t *mask_tmp_x = mask;
#if 0
2021-06-20 12:25:46 +08:00
for(x = 0; x < draw_area_w; x++)
{
MAP_NORMAL_MASK_PX(x);
}
2021-06-20 12:25:46 +08:00
#else
2021-06-20 12:25:46 +08:00
for (x = 0; x < draw_area_w && ((lv_uintptr_t)mask_tmp_x & 0x3); x++) {
#if LV_COLOR_SCREEN_TRANSP
MAP_NORMAL_MASK_PX_SCR_TRANSP(x)
#else
MAP_NORMAL_MASK_PX(x)
#endif
}
2021-06-20 12:25:46 +08:00
uint32_t *mask32 = (uint32_t *)mask_tmp_x;
for (; x < x_end4; x += 4) {
if (*mask32) {
if ((*mask32) == 0xFFFFFFFF) {
disp_buf_first[x] = map_buf_first[x];
disp_buf_first[x + 1] = map_buf_first[x + 1];
disp_buf_first[x + 2] = map_buf_first[x + 2];
disp_buf_first[x + 3] = map_buf_first[x + 3];
2021-06-20 12:25:46 +08:00
} else {
mask_tmp_x = (const lv_opa_t *)mask32;
#if LV_COLOR_SCREEN_TRANSP
MAP_NORMAL_MASK_PX_SCR_TRANSP(x)
MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 1)
MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 2)
MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 3)
#else
MAP_NORMAL_MASK_PX(x)
MAP_NORMAL_MASK_PX(x + 1)
MAP_NORMAL_MASK_PX(x + 2)
MAP_NORMAL_MASK_PX(x + 3)
#endif
}
}
2021-06-20 12:25:46 +08:00
mask32++;
}
mask_tmp_x = (const lv_opa_t *)mask32;
2021-06-20 12:25:46 +08:00
for (; x < draw_area_w; x++) {
#if LV_COLOR_SCREEN_TRANSP
MAP_NORMAL_MASK_PX_SCR_TRANSP(x)
#else
MAP_NORMAL_MASK_PX(x)
#endif
}
2021-06-20 12:25:46 +08:00
#endif
disp_buf_first += disp_w;
mask += draw_area_w;
map_buf_first += map_w;
}
}
/*Handle opa and mask values too*/
else {
2021-06-20 12:25:46 +08:00
for (y = 0; y < draw_area_h; y++) {
for (x = 0; x < draw_area_w; x++) {
if (mask[x]) {
lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8);
#if LV_COLOR_SCREEN_TRANSP
2021-06-20 12:25:46 +08:00
if (disp->driver.screen_transp) {
lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, map_buf_first[x], opa_tmp, &disp_buf_first[x],
&disp_buf_first[x].ch.alpha);
2021-06-20 12:25:46 +08:00
} else
#endif
{
disp_buf_first[x] = lv_color_mix(map_buf_first[x], disp_buf_first[x], opa_tmp);
}
}
}
2021-06-20 12:25:46 +08:00
disp_buf_first += disp_w;
mask += draw_area_w;
map_buf_first += map_w;
}
}
}
}
#if LV_USE_BLEND_MODES
2021-06-20 12:25:46 +08:00
static void map_blended(const lv_area_t *disp_area, lv_color_t *disp_buf, const lv_area_t *draw_area,
const lv_area_t *map_area, const lv_color_t *map_buf, lv_opa_t opa,
const lv_opa_t *mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode)
{
/*Get the width of the `disp_area` it will be used to go to the next line*/
int32_t disp_w = lv_area_get_width(disp_area);
/*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
int32_t draw_area_w = lv_area_get_width(draw_area);
/*Get the width of the `mask_area` it will be used to go to the next line*/
int32_t map_w = lv_area_get_width(map_area);
/*Create a temp. disp_buf which always point to current line to draw*/
2021-06-20 12:25:46 +08:00
lv_color_t *disp_buf_tmp = disp_buf + disp_w * draw_area->y1;
/*Create a temp. map_buf which always point to current line to draw*/
2021-06-20 12:25:46 +08:00
const lv_color_t *map_buf_tmp = map_buf + map_w * (draw_area->y1 - (map_area->y1 - disp_area->y1));
lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t);
2021-06-20 12:25:46 +08:00
switch (mode) {
case LV_BLEND_MODE_ADDITIVE:
blend_fp = color_blend_true_color_additive;
break;
2021-06-20 12:25:46 +08:00
case LV_BLEND_MODE_SUBTRACTIVE:
blend_fp = color_blend_true_color_subtractive;
break;
2021-06-20 12:25:46 +08:00
default:
LV_LOG_WARN("fill_blended: unsupported blend mode");
return;
}
int32_t x;
int32_t y;
/*Simple fill (maybe with opacity), no masking*/
2021-06-20 12:25:46 +08:00
if (mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
/*Go to the first px of the row*/
map_buf_tmp += (draw_area->x1 - (map_area->x1 - disp_area->x1));
/*The map will be indexed from `draw_area->x1` so compensate it.*/
map_buf_tmp -= draw_area->x1;
2021-06-20 12:25:46 +08:00
for (y = draw_area->y1; y <= draw_area->y2; y++) {
for (x = draw_area->x1; x <= draw_area->x2; x++) {
disp_buf_tmp[x] = blend_fp(map_buf_tmp[x], disp_buf_tmp[x], opa);
}
2021-06-20 12:25:46 +08:00
disp_buf_tmp += disp_w;
map_buf_tmp += map_w;
}
}
/*Masked*/
else {
/* The mask is relative to the clipped area.
* In the cycles below mask will be indexed from `draw_area.x1`
* but it corresponds to zero index. So prepare `mask_tmp` accordingly. */
2021-06-20 12:25:46 +08:00
const lv_opa_t *mask_tmp = mask - draw_area->x1;
map_buf_tmp -= draw_area->x1;
2021-06-20 12:25:46 +08:00
for (y = draw_area->y1; y <= draw_area->y2; y++) {
for (x = draw_area->x1; x <= draw_area->x2; x++) {
if (mask_tmp[x] == 0) {
continue;
}
lv_opa_t opa_tmp = mask_tmp[x] >= LV_OPA_MAX ? opa : ((opa * mask_tmp[x]) >> 8);
disp_buf_tmp[x] = blend_fp(map_buf_tmp[x], disp_buf_tmp[x], opa_tmp);
}
2021-06-20 12:25:46 +08:00
disp_buf_tmp += disp_w;
mask_tmp += draw_area_w;
map_buf_tmp += map_w;
}
}
}
static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa)
{
2021-06-20 12:25:46 +08:00
if (opa <= LV_OPA_MIN) {
return bg;
}
uint32_t tmp;
#if LV_COLOR_DEPTH == 1
tmp = bg.full + fg.full;
fg.full = LV_MATH_MIN(tmp, 1);
#else
tmp = bg.ch.red + fg.ch.red;
#if LV_COLOR_DEPTH == 8
fg.ch.red = LV_MATH_MIN(tmp, 7);
#elif LV_COLOR_DEPTH == 16
fg.ch.red = LV_MATH_MIN(tmp, 31);
#elif LV_COLOR_DEPTH == 32
fg.ch.red = LV_MATH_MIN(tmp, 255);
#endif
#if LV_COLOR_DEPTH == 8
fg.ch.green = LV_MATH_MIN(tmp, 7);
#elif LV_COLOR_DEPTH == 16
#if LV_COLOR_16_SWAP == 0
tmp = bg.ch.green + fg.ch.green;
fg.ch.green = LV_MATH_MIN(tmp, 63);
#else
tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l;
tmp = LV_MATH_MIN(tmp, 63);
fg.ch.green_h = tmp >> 3;
fg.ch.green_l = tmp & 0x7;
#endif
#elif LV_COLOR_DEPTH == 32
fg.ch.green = LV_MATH_MIN(tmp, 255);
#endif
tmp = bg.ch.blue + fg.ch.blue;
#if LV_COLOR_DEPTH == 8
fg.ch.blue = LV_MATH_MIN(tmp, 4);
#elif LV_COLOR_DEPTH == 16
fg.ch.blue = LV_MATH_MIN(tmp, 31);
#elif LV_COLOR_DEPTH == 32
fg.ch.blue = LV_MATH_MIN(tmp, 255);
#endif
#endif
2021-06-20 12:25:46 +08:00
if (opa == LV_OPA_COVER) {
return fg;
}
return lv_color_mix(fg, bg, opa);
}
static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa)
{
2021-06-20 12:25:46 +08:00
if (opa <= LV_OPA_MIN) {
return bg;
}
int32_t tmp;
tmp = bg.ch.red - fg.ch.red;
fg.ch.red = LV_MATH_MAX(tmp, 0);
#if LV_COLOR_16_SWAP == 0
tmp = bg.ch.green - fg.ch.green;
fg.ch.green = LV_MATH_MAX(tmp, 0);
#else
tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l;
tmp = LV_MATH_MAX(tmp, 0);
fg.ch.green_h = tmp >> 3;
fg.ch.green_l = tmp & 0x7;
#endif
tmp = bg.ch.blue - fg.ch.blue;
fg.ch.blue = LV_MATH_MAX(tmp, 0);
2021-06-20 12:25:46 +08:00
if (opa == LV_OPA_COVER) {
return fg;
}
return lv_color_mix(fg, bg, opa);
}
#endif