/** * @file lv_draw_sw_blend.c * */ /********************* * INCLUDES *********************/ #include "lv_draw_sw.h" #include "../../misc/lv_math.h" #include "../../hal/lv_hal_disp.h" #include "../../core/lv_refr.h" /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ static void fill_set_px(lv_color_t * dest_buf, const lv_area_t * blend_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stide); static void /* LV_ATTRIBUTE_FAST_MEM */ fill_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride); #if LV_COLOR_SCREEN_TRANSP static void /* LV_ATTRIBUTE_FAST_MEM */ fill_argb(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride); #endif /*LV_COLOR_SCREEN_TRANSP*/ #if LV_DRAW_COMPLEX static void fill_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode); #endif /*LV_DRAW_COMPLEX*/ static void map_set_px(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride); static void /* LV_ATTRIBUTE_FAST_MEM */ map_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride); #if LV_COLOR_SCREEN_TRANSP static void /* LV_ATTRIBUTE_FAST_MEM */ map_argb(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode); #endif /*LV_COLOR_SCREEN_TRANSP*/ #if LV_DRAW_COMPLEX static void map_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_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); static inline lv_color_t color_blend_true_color_multiply(lv_color_t fg, lv_color_t bg, lv_opa_t opa); #endif /*LV_DRAW_COMPLEX*/ /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ #define FILL_NORMAL_MASK_PX(color) \ if(*mask == LV_OPA_COVER) *dest_buf = color; \ else *dest_buf = lv_color_mix(color, *dest_buf, *mask); \ mask++; \ dest_buf++; #define MAP_NORMAL_MASK_PX(x) \ if(*mask_tmp_x) { \ if(*mask_tmp_x == LV_OPA_COVER) dest_buf[x] = src_buf[x]; \ else dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], *mask_tmp_x); \ } \ mask_tmp_x++; /********************** * GLOBAL FUNCTIONS **********************/ void lv_draw_sw_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc) { /*Do not draw transparent things*/ if(dsc->opa <= LV_OPA_MIN) return; lv_area_t blend_area; if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return; if(draw_ctx->wait_for_finish) draw_ctx->wait_for_finish(draw_ctx); ((lv_draw_sw_ctx_t *)draw_ctx)->blend(draw_ctx, dsc); } void LV_ATTRIBUTE_FAST_MEM lv_draw_sw_blend_basic(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc) { lv_opa_t * mask; if(dsc->mask_buf == NULL) mask = NULL; if(dsc->mask_buf && dsc->mask_res == LV_DRAW_MASK_RES_TRANSP) return; else if(dsc->mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask = NULL; else mask = dsc->mask_buf; lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area); lv_area_t blend_area; if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return; lv_disp_t * disp = _lv_refr_get_disp_refreshing(); lv_color_t * dest_buf = draw_ctx->buf; if(disp->driver->set_px_cb == NULL) { if(disp->driver->screen_transp == 0) { dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1); } else { /*With LV_COLOR_DEPTH 16 it means ARGB8565 (3 bytes format)*/ uint8_t * dest_buf8 = (uint8_t *) dest_buf; dest_buf8 += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) * LV_IMG_PX_SIZE_ALPHA_BYTE; dest_buf8 += (blend_area.x1 - draw_ctx->buf_area->x1) * LV_IMG_PX_SIZE_ALPHA_BYTE; dest_buf = (lv_color_t *)dest_buf8; } } const lv_color_t * src_buf = dsc->src_buf; lv_coord_t src_stride; if(src_buf) { src_stride = lv_area_get_width(dsc->blend_area); src_buf += src_stride * (blend_area.y1 - dsc->blend_area->y1) + (blend_area.x1 - dsc->blend_area->x1); } else { src_stride = 0; } lv_coord_t mask_stride; if(mask) { /*Round the values in the mask if anti-aliasing is disabled*/ if(disp->driver->antialiasing == 0) { int32_t mask_size = lv_area_get_size(dsc->mask_area); int32_t i; for(i = 0; i < mask_size; i++) { mask[i] = mask[i] > 128 ? LV_OPA_COVER : LV_OPA_TRANSP; } } mask_stride = lv_area_get_width(dsc->mask_area); mask += mask_stride * (blend_area.y1 - dsc->mask_area->y1) + (blend_area.x1 - dsc->mask_area->x1); } else { mask_stride = 0; } lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); if(disp->driver->set_px_cb) { if(dsc->src_buf == NULL) { fill_set_px(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride); } else { map_set_px(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride); } } #if LV_COLOR_SCREEN_TRANSP else if(disp->driver->screen_transp) { if(dsc->src_buf == NULL) { fill_argb(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride); } else { map_argb(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride, dsc->blend_mode); } } #endif else if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) { if(dsc->src_buf == NULL) { fill_normal(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride); } else { map_normal(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride); } } else { #if LV_DRAW_COMPLEX if(dsc->src_buf == NULL) { fill_blended(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride, dsc->blend_mode); } else { map_blended(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride, dsc->blend_mode); } #endif } } /********************** * STATIC FUNCTIONS **********************/ static void fill_set_px(lv_color_t * dest_buf, const lv_area_t * blend_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stide) { lv_disp_t * disp = _lv_refr_get_disp_refreshing(); int32_t x; int32_t y; if(mask == NULL) { for(y = blend_area->y1; y <= blend_area->y2; y++) { for(x = blend_area->x1; x <= blend_area->x2; x++) { disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, x, y, color, opa); } } } else { int32_t w = lv_area_get_width(blend_area); int32_t h = lv_area_get_height(blend_area); for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { if(mask[x]) { disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, blend_area->x1 + x, blend_area->y1 + y, color, (uint32_t)((uint32_t)opa * mask[x]) >> 8); } } mask += mask_stide; } } } static LV_ATTRIBUTE_FAST_MEM void fill_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride) { int32_t w = lv_area_get_width(dest_area); int32_t h = lv_area_get_height(dest_area); int32_t x; int32_t y; /*No mask*/ if(mask == NULL) { if(opa >= LV_OPA_MAX) { for(y = 0; y < h; y++) { lv_color_fill(dest_buf, color, w); dest_buf += dest_stride; } } /*Has opacity*/ else { lv_color_t last_dest_color = lv_color_black(); lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa); #if LV_COLOR_MIX_ROUND_OFS == 0 && LV_COLOR_DEPTH == 16 /*lv_color_mix work with an optimized algorithm with 16 bit color depth. *However, it introduces some rounded error on opa. *Introduce the same error here too to make lv_color_premult produces the same result */ opa = (uint32_t)((uint32_t)opa + 4) >> 3; opa = opa << 3; #endif uint16_t color_premult[3]; lv_color_premult(color, opa, color_premult); lv_opa_t opa_inv = 255 - opa; for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { if(last_dest_color.full != dest_buf[x].full) { last_dest_color = dest_buf[x]; last_res_color = lv_color_mix_premult(color_premult, dest_buf[x], opa_inv); } dest_buf[x] = last_res_color; } dest_buf += dest_stride; } } } /*Masked*/ else { #if LV_COLOR_DEPTH == 16 uint32_t c32 = color.full + ((uint32_t)color.full << 16); #endif /*Only the mask matters*/ if(opa >= LV_OPA_MAX) { int32_t x_end4 = w - 4; for(y = 0; y < h; y++) { for(x = 0; x < w && ((lv_uintptr_t)(mask) & 0x3); x++) { FILL_NORMAL_MASK_PX(color) } for(; x <= x_end4; x += 4) { uint32_t mask32 = *((uint32_t *)mask); if(mask32 == 0xFFFFFFFF) { #if LV_COLOR_DEPTH == 16 if((lv_uintptr_t)dest_buf & 0x3) { *(dest_buf + 0) = color; uint32_t * d = (uint32_t *)(dest_buf + 1); *d = c32; *(dest_buf + 3) = color; } else { uint32_t * d = (uint32_t *)dest_buf; *d = c32; *(d + 1) = c32; } #else dest_buf[0] = color; dest_buf[1] = color; dest_buf[2] = color; dest_buf[3] = color; #endif dest_buf += 4; mask += 4; } else if(mask32) { FILL_NORMAL_MASK_PX(color) FILL_NORMAL_MASK_PX(color) FILL_NORMAL_MASK_PX(color) FILL_NORMAL_MASK_PX(color) } else { mask += 4; dest_buf += 4; } } for(; x < w ; x++) { FILL_NORMAL_MASK_PX(color) } dest_buf += (dest_stride - w); mask += (mask_stride - w); } } /*With opacity*/ else { /*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 = dest_buf[0].full; last_res_color.full = dest_buf[0].full; lv_opa_t opa_tmp = LV_OPA_TRANSP; for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { if(*mask) { if(*mask != last_mask) opa_tmp = *mask == LV_OPA_COVER ? opa : (uint32_t)((uint32_t)(*mask) * opa) >> 8; if(*mask != last_mask || last_dest_color.full != dest_buf[x].full) { if(opa_tmp == LV_OPA_COVER) last_res_color = color; else last_res_color = lv_color_mix(color, dest_buf[x], opa_tmp); last_mask = *mask; last_dest_color.full = dest_buf[x].full; } dest_buf[x] = last_res_color; } mask++; } dest_buf += dest_stride; mask += (mask_stride - w); } } } } #if LV_COLOR_SCREEN_TRANSP static inline void set_px_argb(uint8_t * buf, lv_color_t color, lv_opa_t opa) { lv_color_t bg_color; lv_color_t res_color; lv_opa_t bg_opa = buf[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; #if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 bg_color.full = buf[0]; lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &buf[1]); if(buf[1] <= LV_OPA_MIN) return; buf[0] = res_color.full; #elif LV_COLOR_DEPTH == 16 bg_color.full = buf[0] + (buf[1] << 8); lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &buf[2]); if(buf[2] <= LV_OPA_MIN) return; buf[0] = res_color.full & 0xff; buf[1] = res_color.full >> 8; #elif LV_COLOR_DEPTH == 32 bg_color = *((lv_color_t *)buf); lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &buf[3]); if(buf[3] <= LV_OPA_MIN) return; buf[0] = res_color.ch.blue; buf[1] = res_color.ch.green; buf[2] = res_color.ch.red; #endif } static inline void set_px_argb_blend(uint8_t * buf, lv_color_t color, lv_opa_t opa, lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t)) { static lv_color_t last_dest_color; static lv_color_t last_src_color; static lv_color_t last_res_color; static uint32_t last_opa = 0xffff; /*Set to an invalid value for first*/ lv_color_t bg_color; /*Get the BG color*/ #if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 if(buf[1] <= LV_OPA_MIN) return; bg_color.full = buf[0]; #elif LV_COLOR_DEPTH == 16 if(buf[2] <= LV_OPA_MIN) return; bg_color.full = buf[0] + (buf[1] << 8); #elif LV_COLOR_DEPTH == 32 if(buf[3] <= LV_OPA_MIN) return; bg_color = *((lv_color_t *)buf); #endif /*Get the result color*/ if(last_dest_color.full != bg_color.full || last_src_color.full != color.full || last_opa != opa) { last_dest_color = bg_color; last_src_color = color; last_opa = opa; last_res_color = blend_fp(last_src_color, last_dest_color, last_opa); } /*Set the result color*/ #if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 buf[0] = last_res_color.full; #elif LV_COLOR_DEPTH == 16 buf[0] = last_res_color.full & 0xff; buf[1] = last_res_color.full >> 8; #elif LV_COLOR_DEPTH == 32 buf[0] = last_res_color.ch.blue; buf[1] = last_res_color.ch.green; buf[2] = last_res_color.ch.red; #endif } static void LV_ATTRIBUTE_FAST_MEM fill_argb(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride) { uint8_t * dest_buf8 = (uint8_t *) dest_buf; int32_t w = lv_area_get_width(dest_area); int32_t h = lv_area_get_height(dest_area); int32_t x; int32_t y; uint8_t ctmp[LV_IMG_PX_SIZE_ALPHA_BYTE]; lv_memcpy(ctmp, &color, sizeof(lv_color_t)); ctmp[LV_IMG_PX_SIZE_ALPHA_BYTE - 1] = opa; /*No mask*/ if(mask == NULL) { if(opa >= LV_OPA_MAX) { for(x = 0; x < w; x++) { lv_memcpy(dest_buf8, ctmp, LV_IMG_PX_SIZE_ALPHA_BYTE); dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; } dest_buf8 += (dest_stride - w) * LV_IMG_PX_SIZE_ALPHA_BYTE; for(y = 1; y < h; y++) { lv_memcpy(dest_buf8, (uint8_t *) dest_buf, w * LV_IMG_PX_SIZE_ALPHA_BYTE); dest_buf8 += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; } } /*Has opacity*/ else { uint8_t * dest_buf8_row = dest_buf8; for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { set_px_argb(dest_buf8, color, opa); dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; } dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; dest_buf8 = dest_buf8_row; } } } /*Masked*/ else { /*Only the mask matters*/ if(opa >= LV_OPA_MAX) { uint8_t * dest_buf8_row = dest_buf8; for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { set_px_argb(dest_buf8, color, *mask); mask++; dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; } dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; dest_buf8 = dest_buf8_row; } } /*With opacity*/ else { /*Buffer the result color to avoid recalculating the same color*/ lv_opa_t last_mask = LV_OPA_TRANSP; lv_opa_t opa_tmp = LV_OPA_TRANSP; uint8_t * dest_buf8_row = dest_buf8; for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { if(*mask) { if(*mask != last_mask) opa_tmp = *mask == LV_OPA_COVER ? opa : (uint32_t)((uint32_t)(*mask) * opa) >> 8; set_px_argb(dest_buf8, color, opa_tmp); } dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; mask++; } dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; dest_buf8 = dest_buf8_row; mask += (mask_stride - w); } } } } #endif #if LV_DRAW_COMPLEX static void fill_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode) { int32_t w = lv_area_get_width(dest_area); int32_t h = lv_area_get_height(dest_area); int32_t x; int32_t y; lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t); switch(blend_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; case LV_BLEND_MODE_MULTIPLY: blend_fp = color_blend_true_color_multiply; break; default: LV_LOG_WARN("fill_blended: unsupported blend mode"); return; } /*Simple fill (maybe with opacity), no masking*/ if(mask == NULL) { lv_color_t last_dest_color = dest_buf[0]; lv_color_t last_res_color = blend_fp(color, dest_buf[0], opa); for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { if(last_dest_color.full != dest_buf[x].full) { last_dest_color = dest_buf[x]; last_res_color = blend_fp(color, dest_buf[x], opa); } dest_buf[x] = last_res_color; } dest_buf += dest_stride; } } /*Masked*/ else { /*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 = dest_buf[0]; lv_opa_t opa_tmp = mask[0] >= LV_OPA_MAX ? opa : (uint32_t)((uint32_t)mask[0] * opa) >> 8; last_res_color = blend_fp(color, last_dest_color, opa_tmp); for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { if(mask[x] == 0) continue; if(mask[x] != last_mask || last_dest_color.full != dest_buf[x].full) { opa_tmp = mask[x] >= LV_OPA_MAX ? opa : (uint32_t)((uint32_t)mask[x] * opa) >> 8; last_res_color = blend_fp(color, dest_buf[x], opa_tmp); last_mask = mask[x]; last_dest_color.full = dest_buf[x].full; } dest_buf[x] = last_res_color; } dest_buf += dest_stride; mask += mask_stride; } } } #endif static void map_set_px(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride) { lv_disp_t * disp = _lv_refr_get_disp_refreshing(); int32_t w = lv_area_get_width(dest_area); int32_t h = lv_area_get_height(dest_area); int32_t x; int32_t y; if(mask == NULL) { for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, dest_area->x1 + x, dest_area->y1 + y, src_buf[x], opa); } src_buf += src_stride; } } else { for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { if(mask[x]) { disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, dest_area->x1 + x, dest_area->y1 + y, src_buf[x], (uint32_t)((uint32_t)opa * mask[x]) >> 8); } } mask += mask_stride; src_buf += src_stride; } } } static void LV_ATTRIBUTE_FAST_MEM map_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride) { int32_t w = lv_area_get_width(dest_area); int32_t h = lv_area_get_height(dest_area); int32_t x; int32_t y; /*Simple fill (maybe with opacity), no masking*/ if(mask == NULL) { if(opa >= LV_OPA_MAX) { for(y = 0; y < h; y++) { lv_memcpy(dest_buf, src_buf, w * sizeof(lv_color_t)); dest_buf += dest_stride; src_buf += src_stride; } } else { for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], opa); } dest_buf += dest_stride; src_buf += src_stride; } } } /*Masked*/ else { /*Only the mask matters*/ if(opa > LV_OPA_MAX) { int32_t x_end4 = w - 4; for(y = 0; y < h; y++) { const lv_opa_t * mask_tmp_x = mask; #if 0 for(x = 0; x < w; x++) { MAP_NORMAL_MASK_PX(x); } #else for(x = 0; x < w && ((lv_uintptr_t)mask_tmp_x & 0x3); x++) { MAP_NORMAL_MASK_PX(x) } uint32_t * mask32 = (uint32_t *)mask_tmp_x; for(; x < x_end4; x += 4) { if(*mask32) { if((*mask32) == 0xFFFFFFFF) { dest_buf[x] = src_buf[x]; dest_buf[x + 1] = src_buf[x + 1]; dest_buf[x + 2] = src_buf[x + 2]; dest_buf[x + 3] = src_buf[x + 3]; } else { mask_tmp_x = (const lv_opa_t *)mask32; MAP_NORMAL_MASK_PX(x) MAP_NORMAL_MASK_PX(x + 1) MAP_NORMAL_MASK_PX(x + 2) MAP_NORMAL_MASK_PX(x + 3) } } mask32++; } mask_tmp_x = (const lv_opa_t *)mask32; for(; x < w ; x++) { MAP_NORMAL_MASK_PX(x) } #endif dest_buf += dest_stride; src_buf += src_stride; mask += mask_stride; } } /*Handle opa and mask values too*/ else { for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { if(mask[x]) { lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], opa_tmp); } } dest_buf += dest_stride; src_buf += src_stride; mask += mask_stride; } } } } #if LV_COLOR_SCREEN_TRANSP static void LV_ATTRIBUTE_FAST_MEM map_argb(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode) { uint8_t * dest_buf8 = (uint8_t *) dest_buf; int32_t w = lv_area_get_width(dest_area); int32_t h = lv_area_get_height(dest_area); int32_t x; int32_t y; lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t); switch(blend_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; case LV_BLEND_MODE_MULTIPLY: blend_fp = color_blend_true_color_multiply; break; default: blend_fp = NULL; } /*Simple fill (maybe with opacity), no masking*/ if(mask == NULL) { if(opa >= LV_OPA_MAX) { if(blend_fp == NULL && LV_COLOR_DEPTH == 32) { for(y = 0; y < h; y++) { lv_memcpy(dest_buf, src_buf, w * sizeof(lv_color_t)); dest_buf += dest_stride; src_buf += src_stride; } } else { uint8_t * dest_buf8_row = dest_buf8; for(y = 0; y < h; y++) { if(blend_fp == NULL) { for(x = 0; x < w; x++) { set_px_argb(dest_buf8, src_buf[x], LV_OPA_COVER); dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; } } else { for(x = 0; x < w; x++) { set_px_argb_blend(dest_buf8, src_buf[x], LV_OPA_COVER, blend_fp); dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; } } dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; dest_buf8 = dest_buf8_row; src_buf += src_stride; } } } /*No mask but opacity*/ else { uint8_t * dest_buf8_row = dest_buf8; for(y = 0; y < h; y++) { if(blend_fp == NULL) { for(x = 0; x < w; x++) { set_px_argb(dest_buf8, src_buf[x], opa); dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; } } else { for(x = 0; x < w; x++) { set_px_argb_blend(dest_buf8, src_buf[x], opa, blend_fp); dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; } } dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; dest_buf8 = dest_buf8_row; src_buf += src_stride; } } } /*Masked*/ else { /*Only the mask matters*/ if(opa > LV_OPA_MAX) { uint8_t * dest_buf8_row = dest_buf8; for(y = 0; y < h; y++) { if(blend_fp == NULL) { for(x = 0; x < w; x++) { set_px_argb(dest_buf8, src_buf[x], mask[x]); dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; } } else { for(x = 0; x < w; x++) { set_px_argb_blend(dest_buf8, src_buf[x], mask[x], blend_fp); dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; } } dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; dest_buf8 = dest_buf8_row; src_buf += src_stride; mask += mask_stride; } } /*Handle opa and mask values too*/ else { uint8_t * dest_buf8_row = dest_buf8; for(y = 0; y < h; y++) { if(blend_fp == NULL) { for(x = 0; x < w; x++) { if(mask[x]) { lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); set_px_argb(dest_buf8, src_buf[x], opa_tmp); } dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; } } else { for(x = 0; x < w; x++) { if(mask[x]) { lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); set_px_argb_blend(dest_buf8, src_buf[x], opa_tmp, blend_fp); } dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; } } dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; dest_buf8 = dest_buf8_row; src_buf += src_stride; mask += mask_stride; } } } } #endif #if LV_DRAW_COMPLEX static void map_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode) { int32_t w = lv_area_get_width(dest_area); int32_t h = lv_area_get_height(dest_area); int32_t x; int32_t y; lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t); switch(blend_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; case LV_BLEND_MODE_MULTIPLY: blend_fp = color_blend_true_color_multiply; break; default: LV_LOG_WARN("fill_blended: unsupported blend mode"); return; } lv_color_t last_dest_color; lv_color_t last_src_color; /*Simple fill (maybe with opacity), no masking*/ if(mask == NULL) { last_dest_color = dest_buf[0]; last_src_color = src_buf[0]; lv_color_t last_res_color = blend_fp(last_src_color, last_dest_color, opa); for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { if(last_src_color.full != src_buf[x].full || last_dest_color.full != dest_buf[x].full) { last_dest_color = dest_buf[x]; last_src_color = src_buf[x]; last_res_color = blend_fp(last_src_color, last_dest_color, opa); } dest_buf[x] = last_res_color; } dest_buf += dest_stride; src_buf += src_stride; } } /*Masked*/ else { last_dest_color = dest_buf[0]; last_src_color = src_buf[0]; lv_opa_t last_opa = mask[0] >= LV_OPA_MAX ? opa : ((opa * mask[0]) >> 8); lv_color_t last_res_color = blend_fp(last_src_color, last_dest_color, last_opa); for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { if(mask[x] == 0) continue; lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); if(last_src_color.full != src_buf[x].full || last_dest_color.full != dest_buf[x].full || last_opa != opa_tmp) { last_dest_color = dest_buf[x]; last_src_color = src_buf[x]; last_opa = opa_tmp; last_res_color = blend_fp(last_src_color, last_dest_color, last_opa); } dest_buf[x] = last_res_color; } dest_buf += dest_stride; src_buf += src_stride; mask += mask_stride; } } } static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa) { if(opa <= LV_OPA_MIN) return bg; uint32_t tmp; #if LV_COLOR_DEPTH == 1 tmp = bg.full + fg.full; fg.full = LV_MIN(tmp, 1); #else tmp = bg.ch.red + fg.ch.red; #if LV_COLOR_DEPTH == 8 fg.ch.red = LV_MIN(tmp, 7); #elif LV_COLOR_DEPTH == 16 fg.ch.red = LV_MIN(tmp, 31); #elif LV_COLOR_DEPTH == 32 fg.ch.red = LV_MIN(tmp, 255); #endif #if LV_COLOR_DEPTH == 8 tmp = bg.ch.green + fg.ch.green; fg.ch.green = LV_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_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_MIN(tmp, 63); fg.ch.green_h = tmp >> 3; fg.ch.green_l = tmp & 0x7; #endif #elif LV_COLOR_DEPTH == 32 tmp = bg.ch.green + fg.ch.green; fg.ch.green = LV_MIN(tmp, 255); #endif tmp = bg.ch.blue + fg.ch.blue; #if LV_COLOR_DEPTH == 8 fg.ch.blue = LV_MIN(tmp, 4); #elif LV_COLOR_DEPTH == 16 fg.ch.blue = LV_MIN(tmp, 31); #elif LV_COLOR_DEPTH == 32 fg.ch.blue = LV_MIN(tmp, 255); #endif #endif 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) { if(opa <= LV_OPA_MIN) return bg; int32_t tmp; tmp = bg.ch.red - fg.ch.red; fg.ch.red = LV_MAX(tmp, 0); #if LV_COLOR_16_SWAP == 0 tmp = bg.ch.green - fg.ch.green; fg.ch.green = LV_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_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_MAX(tmp, 0); if(opa == LV_OPA_COVER) return fg; return lv_color_mix(fg, bg, opa); } static inline lv_color_t color_blend_true_color_multiply(lv_color_t fg, lv_color_t bg, lv_opa_t opa) { if(opa <= LV_OPA_MIN) return bg; #if LV_COLOR_DEPTH == 32 fg.ch.red = (fg.ch.red * bg.ch.red) >> 8; fg.ch.green = (fg.ch.green * bg.ch.green) >> 8; fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 8; #elif LV_COLOR_DEPTH == 16 fg.ch.red = (fg.ch.red * bg.ch.red) >> 5; fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 5; LV_COLOR_SET_G(fg, (LV_COLOR_GET_G(fg) * LV_COLOR_GET_G(bg)) >> 6); #elif LV_COLOR_DEPTH == 8 fg.ch.red = (fg.ch.red * bg.ch.red) >> 3; fg.ch.green = (fg.ch.green * bg.ch.green) >> 3; fg.ch.blue = (fg.ch.blue * bg.ch.blue) >> 2; #endif if(opa == LV_OPA_COVER) return fg; return lv_color_mix(fg, bg, opa); } #endif