1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00
lvgl/src/lv_draw/lv_draw_blend.c

811 lines
31 KiB
C
Raw Normal View History

2019-08-21 15:44:35 +02:00
/**
2019-09-06 12:24:15 +02:00
* @file lv_draw_blend.c
2019-08-21 15:44:35 +02:00
*
*/
/*********************
* INCLUDES
*********************/
2019-09-06 12:24:15 +02:00
#include "lv_draw_blend.h"
2019-09-06 09:25:08 +02:00
#include "lv_img_decoder.h"
2019-08-24 15:59:19 +02:00
#include "../lv_misc/lv_math.h"
#include "../lv_hal/lv_hal_disp.h"
#include "../lv_core/lv_refr.h"
2019-08-21 15:44:35 +02:00
/*********************
* DEFINES
*********************/
2019-08-24 15:59:19 +02:00
#define FILL_DIRECT_LEN 32
#define FILL_DIRECT_MASK 0x1F
2019-09-07 01:23:55 +02:00
#define GPU_WIDTH_LIMIT 32
2019-08-21 15:44:35 +02:00
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
2019-09-09 05:53:40 +02:00
static void fill_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area,
2020-02-26 19:48:27 +01:00
lv_color_t color, lv_opa_t opa,
const lv_opa_t * mask, lv_draw_mask_res_t mask_res);
2019-09-06 09:25:08 +02:00
2019-09-09 05:53:40 +02:00
static void fill_normal(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area,
2020-02-26 19:48:27 +01:00
lv_color_t color, lv_opa_t opa,
const lv_opa_t * mask, lv_draw_mask_res_t mask_res);
2019-09-09 05:53:40 +02:00
static void fill_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area,
2020-02-26 19:48:27 +01:00
lv_color_t color, lv_opa_t opa,
const lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode);
2019-09-06 09:25:08 +02:00
2019-09-10 05:36:11 +02:00
static void map_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area,
2020-02-26 19:48:27 +01:00
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);
2019-09-10 05:36:11 +02:00
2019-09-09 05:53:40 +02:00
static void map_normal(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area,
2020-02-26 19:48:27 +01:00
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);
2019-09-06 09:25:08 +02:00
2019-09-10 06:13:08 +02:00
static void map_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area,
2020-02-26 19:48:27 +01:00
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);
2019-09-10 06:13:08 +02:00
2019-12-03 06:56:48 +01:00
static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa);
2019-09-06 09:25:08 +02:00
static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa);
2019-08-21 15:44:35 +02:00
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
2019-08-23 10:53:38 +02:00
/**
*
* @param disp_area
* @param clip_area already truncated to disp_arae
* @param fill_area
* @param disp_buf
* @param cf
* @param color
* @param mask
* @param mask_res
* @param opa
* @param mode
*/
void lv_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area,
2020-02-26 19:48:27 +01:00
lv_color_t color, lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa,
lv_blend_mode_t mode)
2019-08-23 10:53:38 +02:00
{
/*Do not draw transparent things*/
if(opa < LV_OPA_MIN) return;
2019-09-06 19:53:39 +02:00
if(mask_res == LV_DRAW_MASK_RES_FULL_TRANSP) return;
2019-08-23 10:53:38 +02: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;
2019-08-23 10:53:38 +02:00
/* 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);
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;
2019-09-14 11:46:25 +02:00
/*Round the values in the mask if anti-aliasing is disabled*/
2020-02-27 15:07:18 +01:00
#if LV_ANTIALIAS
2020-02-26 20:15:29 +01:00
if(mask && disp->driver.antialiasing == 0)
#else
if(mask)
#endif
{
2020-02-05 23:17:19 +01:00
int32_t mask_w = lv_area_get_width(&draw_area);
int32_t i;
2020-02-26 19:48:27 +01:00
for(i = 0; i < mask_w; i++) mask[i] = mask[i] > 128 ? LV_OPA_COVER : LV_OPA_TRANSP;
2019-09-14 11:46:25 +02:00
}
if(disp->driver.set_px_cb) {
2019-09-09 05:53:40 +02:00
fill_set_px(disp_area, disp_buf, &draw_area, color, opa, mask, mask_res);
}
else if(mode == LV_BLEND_MODE_NORMAL) {
2019-09-09 05:53:40 +02:00
fill_normal(disp_area, disp_buf, &draw_area, color, opa, mask, mask_res);
2019-09-06 09:25:08 +02:00
}
else {
2019-09-09 05:53:40 +02:00
fill_blended(disp_area, disp_buf, &draw_area, color, opa, mask, mask_res, mode);
2019-09-06 09:25:08 +02:00
}
}
void lv_blend_map(const lv_area_t * clip_area, const lv_area_t * map_area, const lv_color_t * map_buf,
2020-02-26 19:48:27 +01:00
lv_opa_t * mask, lv_draw_mask_res_t mask_res,
lv_opa_t opa, lv_blend_mode_t mode)
2019-09-06 09:25:08 +02:00
{
/*Do not draw transparent things*/
if(opa < LV_OPA_MIN) return;
2019-09-06 19:53:39 +02:00
if(mask_res == LV_DRAW_MASK_RES_FULL_TRANSP) return;
2019-09-06 09:25:08 +02:00
/* 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);
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;
/* 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;
2019-09-14 11:46:25 +02:00
/*Round the values in the mask if anti-aliasing is disabled*/
2020-02-27 15:07:18 +01:00
#if LV_ANTIALIAS
2020-02-26 20:15:29 +01:00
if(mask && disp->driver.antialiasing == 0)
#else
if(mask)
#endif
{
2020-02-05 23:17:19 +01:00
int32_t mask_w = lv_area_get_width(&draw_area);
int32_t i;
2020-02-26 19:48:27 +01:00
for(i = 0; i < mask_w; i++) mask[i] = mask[i] > 128 ? LV_OPA_COVER : LV_OPA_TRANSP;
2019-09-14 11:46:25 +02:00
}
2019-09-10 05:36:11 +02:00
if(disp->driver.set_px_cb) {
map_set_px(disp_area, disp_buf, &draw_area, map_area, map_buf, opa, mask, mask_res);
}
else if(mode == LV_BLEND_MODE_NORMAL) {
2019-09-09 05:53:40 +02:00
map_normal(disp_area, disp_buf, &draw_area, map_area, map_buf, opa, mask, mask_res);
2020-02-26 19:48:27 +01:00
}
else {
2019-09-10 06:13:08 +02:00
map_blended(disp_area, disp_buf, &draw_area, map_area, map_buf, opa, mask, mask_res, mode);
2019-09-09 05:53:40 +02:00
}
}
2019-08-23 10:53:38 +02:00
2019-09-06 09:25:08 +02:00
2019-09-09 05:53:40 +02:00
/**********************
* STATIC FUNCTIONS
**********************/
2019-09-09 05:53:40 +02:00
static void fill_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area,
2020-02-26 19:48:27 +01:00
lv_color_t color, lv_opa_t opa,
const lv_opa_t * mask, lv_draw_mask_res_t mask_res)
2019-09-09 05:53:40 +02: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*/
2020-02-05 23:17:19 +01:00
int32_t disp_w = lv_area_get_width(disp_area);
2020-02-05 23:17:19 +01:00
int32_t x;
int32_t y;
2019-08-23 10:53:38 +02:00
2019-09-06 19:53:39 +02:00
if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
2019-09-09 05:53:40 +02:00
for(y = draw_area->y1; y <= draw_area->y2; y++) {
for(x = draw_area->x1; x <= draw_area->x2; x++) {
2020-02-26 19:48:27 +01:00
disp->driver.set_px_cb(&disp->driver, (void *)disp_buf, disp_w, x, y, color, opa);
2019-09-06 09:25:08 +02:00
}
}
2020-02-26 19:48:27 +01:00
}
else {
2019-09-06 09:25:08 +02:00
/* 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. */
2019-09-09 05:53:40 +02:00
const lv_opa_t * mask_tmp = mask - draw_area->x1;
2019-09-06 09:25:08 +02:00
2019-09-09 05:53:40 +02:00
/*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
2020-02-05 23:17:19 +01:00
int32_t draw_area_w = lv_area_get_width(draw_area);
2019-09-06 09:25:08 +02:00
2019-09-09 05:53:40 +02:00
for(y = draw_area->y1; y <= draw_area->y2; y++) {
for(x = draw_area->x1; x <= draw_area->x2; x++) {
2020-02-26 19:48:27 +01:00
disp->driver.set_px_cb(&disp->driver, (void *)disp_buf, disp_w, x, y, color,
(uint32_t)((uint32_t)opa * mask_tmp[x]) >> 8);
2019-09-06 09:25:08 +02:00
}
2019-09-09 05:53:40 +02:00
mask_tmp += draw_area_w;
2019-09-06 09:25:08 +02:00
}
}
}
2019-09-09 05:53:40 +02:00
static void fill_normal(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area,
2020-02-26 19:48:27 +01:00
lv_color_t color, lv_opa_t opa,
const lv_opa_t * mask, lv_draw_mask_res_t mask_res)
2019-09-06 09:25:08 +02:00
{
2020-01-27 13:29:12 +01:00
#if LV_USE_GPU
2019-09-07 01:23:55 +02:00
lv_disp_t * disp = lv_refr_get_disp_refreshing();
2020-01-27 13:29:12 +01:00
#endif
2019-09-07 01:23:55 +02:00
2019-09-06 09:25:08 +02:00
/*Get the width of the `disp_area` it will be used to go to the next line*/
2020-02-05 23:17:19 +01:00
int32_t disp_w = lv_area_get_width(disp_area);
2019-09-06 09:25:08 +02:00
2019-09-07 01:23:55 +02:00
/*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
2020-02-05 23:17:19 +01:00
int32_t draw_area_w = lv_area_get_width(draw_area);
2019-09-07 01:23:55 +02:00
2019-09-06 09:25:08 +02:00
/*Create a temp. disp_buf which always point to current line to draw*/
lv_color_t * disp_buf_tmp = disp_buf + disp_w * draw_area->y1;
2020-02-05 23:17:19 +01:00
int32_t x;
int32_t y;
2019-09-06 09:25:08 +02:00
/*Simple fill (maybe with opacity), no masking*/
2019-09-06 19:53:39 +02:00
if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
2019-09-06 09:25:08 +02:00
if(opa > LV_OPA_MAX) {
2019-08-24 15:59:19 +02:00
lv_color_t * disp_buf_tmp_ori = disp_buf_tmp;
2019-09-07 01:23:55 +02:00
#if LV_USE_GPU
if(disp->driver.gpu_fill_cb && draw_area_w > GPU_WIDTH_LIMIT) {
disp->driver.gpu_fill_cb(&disp->driver, disp_buf, disp_w, draw_area, color);
return;
}
#endif
2019-08-24 15:59:19 +02:00
/*Fill the first line. Use `memcpy` because it's faster then simple value assignment*/
/*Set the first pixels manually*/
2020-03-10 10:41:39 +01:00
int32_t fill_end = draw_area->x1 + FILL_DIRECT_LEN + (draw_area_w & FILL_DIRECT_MASK) - 1;
2020-02-26 19:48:27 +01:00
int32_t direct_fill_end = LV_MATH_MIN(draw_area->x2,
2020-03-10 10:41:39 +01:00
fill_end);
2019-09-06 09:25:08 +02:00
for(x = draw_area->x1; x <= direct_fill_end ; x++) {
2019-08-24 15:59:19 +02:00
disp_buf_tmp[x].full = color.full;
}
2019-09-06 09:25:08 +02:00
for(; x <= draw_area->x2; x += FILL_DIRECT_LEN) {
memcpy(&disp_buf_tmp[x], &disp_buf_tmp[draw_area->x1], FILL_DIRECT_LEN * sizeof(lv_color_t));
2019-08-24 15:59:19 +02:00
}
disp_buf_tmp += disp_w;
2019-09-06 09:25:08 +02:00
for(y = draw_area->y1 + 1; y <= draw_area->y2; y++) {
memcpy(&disp_buf_tmp[draw_area->x1], &disp_buf_tmp_ori[draw_area->x1], draw_area_w * sizeof(lv_color_t));
2019-08-23 10:53:38 +02:00
disp_buf_tmp += disp_w;
}
}
else {
2019-09-07 01:23:55 +02:00
#if LV_USE_GPU
if(disp->driver.gpu_blend_cb && draw_area_w > GPU_WIDTH_LIMIT) {
static lv_color_t blend_buf[LV_HOR_RES_MAX];
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_tmp[draw_area->x1], blend_buf, draw_area_w, opa);
disp_buf_tmp += disp_w;
}
return;
}
#endif
2019-08-27 07:05:51 +02:00
lv_color_t last_dest_color = LV_COLOR_BLACK;
lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa);
2019-09-06 09:25:08 +02: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];
2019-09-07 01:23:55 +02:00
#if LV_COLOR_SCREEN_TRANSP
if(disp->driver.screen_transp) {
2020-02-26 19:48:27 +01:00
lv_color_mix_with_alpha(disp_buf_tmp[x], disp_buf_tmp[x].ch.alpha, color, opa, &last_res_color,
&last_res_color.ch.alpha);
}
else
2019-09-07 01:23:55 +02:00
#endif
{
last_res_color = lv_color_mix(color, disp_buf_tmp[x], opa);
}
2019-09-06 09:25:08 +02:00
}
disp_buf_tmp[x] = last_res_color;
}
disp_buf_tmp += disp_w;
}
2019-08-23 10:53:38 +02:00
}
}
/*Masked*/
else {
2019-08-24 15:59:19 +02:00
/* 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. */
2019-09-06 09:25:08 +02:00
const lv_opa_t * mask_tmp = mask - draw_area->x1;
2019-08-23 10:53:38 +02:00
/*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;
/*Only the mask matters*/
if(opa > LV_OPA_MAX) {
2019-09-06 09:25:08 +02: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;
2020-02-26 19:48:27 +01:00
if(mask_tmp[x] != last_mask || last_dest_color.full != disp_buf_tmp[x].full) {
2019-09-07 01:23:55 +02:00
#if LV_COLOR_SCREEN_TRANSP
if(disp->driver.screen_transp) {
2020-02-26 19:48:27 +01:00
lv_color_mix_with_alpha(disp_buf_tmp[x], disp_buf_tmp[x].ch.alpha, color, mask_tmp[x], &last_res_color,
&last_res_color.ch.alpha);
}
else
2019-09-07 01:23:55 +02:00
#endif
{
2020-02-05 23:19:32 +01:00
if(mask_tmp[x] == LV_OPA_COVER) last_res_color = color;
else if(mask_tmp[x] == LV_OPA_TRANSP) last_res_color = disp_buf_tmp[x];
else if(disp_buf_tmp[x].full == color.full) last_res_color = color;
2019-09-07 01:23:55 +02:00
else last_res_color = lv_color_mix(color, disp_buf_tmp[x], mask_tmp[x]);
}
2019-08-23 10:53:38 +02:00
last_mask = mask_tmp[x];
last_dest_color.full = disp_buf_tmp[x].full;
}
disp_buf_tmp[x] = last_res_color;
2019-09-06 09:25:08 +02:00
}
disp_buf_tmp += disp_w;
mask_tmp += draw_area_w;
2019-08-23 10:53:38 +02:00
}
}
/*Handle opa and mask values too*/
else {
2019-09-06 09:25:08 +02: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;
2019-08-24 15:59:19 +02:00
if(mask_tmp[x] != last_mask || last_dest_color.full != disp_buf_tmp[x].full) {
2020-02-05 23:19:32 +01:00
lv_opa_t opa_tmp = mask_tmp[x] == LV_OPA_COVER ? opa : (uint32_t)((uint32_t)mask_tmp[x] * opa) >> 8;
2019-09-07 01:23:55 +02:00
#if LV_COLOR_SCREEN_TRANSP
if(disp->driver.screen_transp) {
2020-02-26 19:48:27 +01:00
lv_color_mix_with_alpha(disp_buf_tmp[x], disp_buf_tmp[x].ch.alpha, color, opa_tmp, &last_res_color,
&last_res_color.ch.alpha);
}
else
2019-09-07 01:23:55 +02:00
#endif
{
2020-02-05 23:19:32 +01:00
if(opa_tmp == LV_OPA_COVER) last_res_color = lv_color_mix(color, disp_buf_tmp[x], mask_tmp[x]);
else if(opa_tmp == LV_OPA_TRANSP) last_res_color = disp_buf_tmp[x];
2020-02-26 19:48:27 +01:00
else last_res_color = lv_color_mix(color, disp_buf_tmp[x], opa_tmp);
2019-09-07 01:23:55 +02:00
}
2019-08-26 05:06:49 +02:00
last_mask = mask_tmp[x];
2019-08-23 10:53:38 +02:00
last_dest_color.full = disp_buf_tmp[x].full;
}
disp_buf_tmp[x] = last_res_color;
2019-09-06 09:25:08 +02:00
}
disp_buf_tmp += disp_w;
mask_tmp += draw_area_w;
2019-08-23 10:53:38 +02:00
}
}
}
}
2019-09-09 05:53:40 +02:00
static void fill_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area,
2020-02-26 19:48:27 +01:00
lv_color_t color, 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*/
2020-02-05 23:17:19 +01:00
int32_t disp_w = lv_area_get_width(disp_area);
/*Create a temp. disp_buf which always point to current line to draw*/
2019-09-06 09:25:08 +02: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);
2020-02-26 19:48:27 +01:00
switch(mode) {
case LV_BLEND_MODE_ADDITIVE:
blend_fp = color_blend_true_color_additive;
break;
case LV_BLEND_MODE_SUBTRACTIVE:
blend_fp = color_blend_true_color_subtractive;
break;
default:
LV_LOG_WARN("fill_blended: unsupported blend mode");
return;
2019-09-06 09:25:08 +02:00
}
2020-02-05 23:17:19 +01:00
int32_t x;
int32_t y;
/*Simple fill (maybe with opacity), no masking*/
2019-09-06 19:53:39 +02:00
if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
2019-09-06 09:25:08 +02:00
lv_color_t last_dest_color = LV_COLOR_BLACK;
lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa);
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);
}
disp_buf_tmp[x] = last_res_color;
}
2019-09-06 09:25:08 +02:00
disp_buf_tmp += disp_w;
}
2019-09-06 09:25:08 +02:00
}
/*Masked*/
else {
/*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
2020-02-05 23:17:19 +01:00
int32_t draw_area_w = lv_area_get_width(draw_area);
2019-09-06 09:25:08 +02:00
/* 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. */
const lv_opa_t * mask_tmp = mask - draw_area->x1;
2019-09-06 09:25:08 +02:00
/*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;
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) {
2020-02-05 23:17:19 +01:00
lv_opa_t opa_tmp = mask_tmp[x] >= LV_OPA_MAX ? opa : (uint32_t)((uint32_t)mask_tmp[x] * opa) >> 8;
2019-09-06 09:25:08 +02:00
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;
}
disp_buf_tmp[x] = last_res_color;
}
disp_buf_tmp += disp_w;
mask_tmp += draw_area_w;
}
}
2019-09-06 09:25:08 +02:00
}
2019-09-07 01:23:55 +02:00
2019-09-10 05:36:11 +02:00
static void map_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area,
2020-02-26 19:48:27 +01:00
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)
2019-09-10 05:36:11 +02: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*/
2020-02-05 23:17:19 +01:00
int32_t disp_w = lv_area_get_width(disp_area);
2019-09-10 05:36:11 +02:00
/*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
2020-02-05 23:17:19 +01:00
int32_t draw_area_w = lv_area_get_width(draw_area);
2019-09-10 05:36:11 +02:00
/*Get the width of the `mask_area` it will be used to go to the next line*/
2020-02-05 23:17:19 +01:00
int32_t map_w = lv_area_get_width(map_area);
2019-09-10 05:36:11 +02:00
/*Create a temp. map_buf which always point to current line to draw*/
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;
2020-02-05 23:17:19 +01:00
int32_t x;
int32_t y;
2019-09-10 05:36:11 +02: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++) {
2020-02-26 19:48:27 +01:00
disp->driver.set_px_cb(&disp->driver, (void *)disp_buf, disp_w, x, y, map_buf_tmp[x], opa);
2019-09-10 05:36:11 +02:00
}
map_buf_tmp += map_w;
}
2020-02-26 19:48:27 +01:00
}
else {
2019-09-10 05:36:11 +02:00
/* 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. */
const lv_opa_t * mask_tmp = mask - draw_area->x1;
for(y = draw_area->y1; y <= draw_area->y2; y++) {
for(x = draw_area->x1; x <= draw_area->x2; x++) {
2020-02-26 19:48:27 +01:00
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);
2019-09-10 05:36:11 +02:00
}
mask_tmp += draw_area_w;
map_buf_tmp += map_w;
}
}
}
2019-09-09 05:53:40 +02:00
static void map_normal(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area,
2020-02-26 19:48:27 +01:00
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)
2019-09-09 05:53:40 +02:00
{
/*Get the width of the `disp_area` it will be used to go to the next line*/
2020-02-05 23:17:19 +01:00
int32_t disp_w = lv_area_get_width(disp_area);
2019-09-09 05:53:40 +02:00
/*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
2020-02-05 23:17:19 +01:00
int32_t draw_area_w = lv_area_get_width(draw_area);
2019-09-09 05:53:40 +02:00
/*Get the width of the `mask_area` it will be used to go to the next line*/
2020-02-05 23:17:19 +01:00
int32_t map_w = lv_area_get_width(map_area);
2019-09-09 05:53:40 +02:00
/*Create a temp. disp_buf which always point to current line to draw*/
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*/
const lv_color_t * map_buf_tmp = map_buf + map_w * (draw_area->y1 - (map_area->y1 - disp_area->y1));
#if LV_COLOR_SCREEN_TRANSP
lv_opa_t opa_composed;
#endif
#if LV_COLOR_SCREEN_TRANSP || LV_USE_GPU
lv_disp_t * disp = lv_refr_get_disp_refreshing();
#endif
2019-09-09 05:53:40 +02:00
2020-02-05 23:17:19 +01:00
int32_t x;
int32_t y;
2019-09-09 05:53:40 +02:00
/*Simple fill (maybe with opacity), no masking*/
if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
2019-09-10 05:36:11 +02:00
/*Go to the first px of the row*/
2019-09-09 05:53:40 +02:00
map_buf_tmp += (draw_area->x1 - (map_area->x1 - disp_area->x1));
#if LV_USE_GPU
if(disp->driver.gpu_blend_cb &&
2020-02-26 19:48:27 +01:00
((draw_area_w > GPU_WIDTH_LIMIT * 4 && opa == LV_OPA_COVER) ||
(draw_area_w > GPU_WIDTH_LIMIT && opa != LV_OPA_COVER))) {
2019-09-09 05:53:40 +02:00
for(y = draw_area->y1; y <= draw_area->y2; y++) {
disp->driver.gpu_blend_cb(&disp->driver, &disp_buf_tmp[draw_area->x1], map_buf_tmp, draw_area_w, opa);
disp_buf_tmp += disp_w;
map_buf_tmp += map_w;
}
return;
}
#endif
if(opa > LV_OPA_MAX) {
for(y = draw_area->y1; y <= draw_area->y2; y++) {
memcpy(&disp_buf_tmp[draw_area->x1], map_buf_tmp, draw_area_w * sizeof(lv_color_t));
disp_buf_tmp += disp_w;
map_buf_tmp += map_w;
}
}
else {
/*The map will be indexed from `draw_area->x1` so compensate it.*/
map_buf_tmp -= draw_area->x1;
for(y = draw_area->y1; y <= draw_area->y2; y++) {
for(x = draw_area->x1; x <= draw_area->x2; x++) {
#if LV_COLOR_SCREEN_TRANSP
if(disp->driver.screen_transp) {
2020-02-26 19:48:27 +01:00
lv_color_mix_with_alpha(disp_buf_tmp[x], disp_buf_tmp[x].ch.alpha, map_buf_tmp[x], opa, &disp_buf_tmp[x],
&disp_buf_tmp[x].ch.alpha);
}
else
2019-09-09 05:53:40 +02:00
#endif
{
disp_buf_tmp[x] = lv_color_mix(map_buf_tmp[x], disp_buf_tmp[x], opa);
}
}
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. */
const lv_opa_t * mask_tmp = mask - draw_area->x1;
/*Buffer the result color to avoid recalculating the same color*/
2019-11-08 23:15:48 +01:00
lv_color_t res_color;
res_color.full = disp_buf_tmp[0].full;
2019-09-09 05:53:40 +02:00
/*Only the mask matters*/
if(opa > LV_OPA_MAX) {
2019-09-10 05:36:11 +02:00
/*Go to the first pixel of the row */
map_buf_tmp += (draw_area->x1 - (map_area->x1 - disp_area->x1));
map_buf_tmp -= draw_area->x1;
2019-09-09 05:53:40 +02:00
for(y = draw_area->y1; y <= draw_area->y2; y++) {
for(x = draw_area->x1; x <= draw_area->x2; x++) {
2019-11-08 23:15:48 +01:00
if(mask_tmp[x] < LV_OPA_MIN) continue;
2019-09-09 05:53:40 +02:00
#if LV_COLOR_SCREEN_TRANSP
2020-02-26 19:48:27 +01:00
if(disp->driver.screen_transp) {
lv_color_mix_with_alpha(disp_buf_tmp[x], disp_buf_tmp[x].ch.alpha, map_buf_tmp[x], mask_tmp[x], &res_color,
&opa_composed);
res_color.ch.alpha = opa_composed;
}
else
2019-09-09 05:53:40 +02:00
#endif
2020-02-26 19:48:27 +01:00
{
if(mask_tmp[x] > LV_OPA_MAX) res_color = map_buf_tmp[x];
else res_color = lv_color_mix(map_buf_tmp[x], disp_buf_tmp[x], mask_tmp[x]);
}
2019-11-08 23:15:48 +01:00
disp_buf_tmp[x] = res_color;//lv_color_mix(map_buf_tmp[x], disp_buf_tmp[x], mask_tmp[x]);
2019-09-09 05:53:40 +02:00
}
disp_buf_tmp += disp_w;
mask_tmp += draw_area_w;
map_buf_tmp += map_w;
}
}
/*Handle opa and mask values too*/
else {
map_buf_tmp -= draw_area->x1;
for(y = draw_area->y1; y <= draw_area->y2; y++) {
for(x = draw_area->x1; x <= draw_area->x2; x++) {
2019-09-10 05:36:11 +02:00
if(mask_tmp[x] == 0) continue;
lv_opa_t opa_tmp = mask_tmp[x] >= LV_OPA_MAX ? opa : ((opa * mask_tmp[x]) >> 8);
2019-09-09 05:53:40 +02:00
#if LV_COLOR_SCREEN_TRANSP
if(disp->driver.screen_transp) {
2020-02-26 19:48:27 +01:00
lv_color_mix_with_alpha(disp_buf_tmp[x], disp_buf_tmp[x].ch.alpha, map_buf_tmp[x], opa_tmp, &disp_buf_tmp[x],
&disp_buf_tmp[x].ch.alpha);
}
else
2019-09-09 05:53:40 +02:00
#endif
{
disp_buf_tmp[x] = lv_color_mix(map_buf_tmp[x], disp_buf_tmp[x], opa_tmp);
}
}
disp_buf_tmp += disp_w;
mask_tmp += draw_area_w;
map_buf_tmp += map_w;
}
}
}
}
2019-09-10 06:13:08 +02:00
static void map_blended(const lv_area_t * disp_area, lv_color_t * disp_buf, const lv_area_t * draw_area,
2020-02-26 19:48:27 +01:00
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)
2019-09-10 06:13:08 +02:00
{
/*Get the width of the `disp_area` it will be used to go to the next line*/
2020-02-05 23:17:19 +01:00
int32_t disp_w = lv_area_get_width(disp_area);
2019-09-10 06:13:08 +02:00
/*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
2020-02-05 23:17:19 +01:00
int32_t draw_area_w = lv_area_get_width(draw_area);
2019-09-10 06:13:08 +02:00
/*Get the width of the `mask_area` it will be used to go to the next line*/
2020-02-05 23:17:19 +01:00
int32_t map_w = lv_area_get_width(map_area);
2019-09-10 06:13:08 +02:00
/*Create a temp. disp_buf which always point to current line to draw*/
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*/
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);
2020-02-26 19:48:27 +01:00
switch(mode) {
case LV_BLEND_MODE_ADDITIVE:
blend_fp = color_blend_true_color_additive;
break;
case LV_BLEND_MODE_SUBTRACTIVE:
blend_fp = color_blend_true_color_subtractive;
break;
default:
LV_LOG_WARN("fill_blended: unsupported blend mode");
return;
2019-09-10 06:13:08 +02:00
}
2020-02-05 23:17:19 +01:00
int32_t x;
int32_t y;
2019-09-10 06:13:08 +02:00
/*Simple fill (maybe with opacity), no masking*/
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;
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);
}
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. */
const lv_opa_t * mask_tmp = mask - draw_area->x1;
map_buf_tmp -= draw_area->x1;
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);
}
disp_buf_tmp += disp_w;
mask_tmp += draw_area_w;
map_buf_tmp += map_w;
}
}
}
2019-09-06 09:25:08 +02:00
static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa)
{
2019-09-06 09:25:08 +02:00
if(opa <= LV_OPA_MIN) return bg;
2020-02-05 23:17:19 +01:00
uint32_t tmp;
2019-12-26 03:37:28 +01:00
#if LV_COLOR_DEPTH == 1
tmp = bg.full + fg.full;
fg.full = LV_MATH_MIN(tmp, 1);
#else
2019-09-06 09:25:08 +02:00
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
2019-10-30 06:00:13 +01:00
tmp = bg.ch.green + fg.ch.green;
2019-09-06 09:25:08 +02:00
fg.ch.green = LV_MATH_MIN(tmp, 63);
#else
2019-10-30 06:00:13 +01:00
tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l;
2019-09-06 09:25:08 +02:00
tmp = LV_MATH_MIN(tmp, 63);
fg.ch.green_h = tmp >> 3;
fg.ch.green_l = tmp & 0x7;
#endif
2019-10-30 06:00:13 +01:00
2019-09-06 09:25:08 +02:00
#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);
2019-12-26 03:37:28 +01:00
#endif
2019-09-06 09:25:08 +02:00
#endif
if(opa == LV_OPA_COVER) return fg;
return lv_color_mix(fg, bg, opa);
}
2019-08-23 10:53:38 +02:00
2019-09-06 09:25:08 +02:00
static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa)
{
2019-08-23 10:53:38 +02:00
2019-09-06 09:25:08 +02:00
if(opa <= LV_OPA_MIN) return bg;
2020-02-05 23:17:19 +01:00
int32_t tmp;
2019-09-06 09:25:08 +02:00
tmp = bg.ch.red - fg.ch.red;
fg.ch.red = LV_MATH_MAX(tmp, 0);
#if LV_COLOR_16_SWAP == 0
2019-10-30 06:00:13 +01:00
tmp = bg.ch.green - fg.ch.green;
2019-09-06 09:25:08 +02:00
fg.ch.green = LV_MATH_MAX(tmp, 0);
#else
2019-10-30 06:00:13 +01:00
tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l;
2019-09-06 09:25:08 +02:00
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);
if(opa == LV_OPA_COVER) return fg;
return lv_color_mix(fg, bg, opa);
}