diff --git a/src/lv_draw/lv_img_buf.c b/src/lv_draw/lv_img_buf.c index 0e405fd41..32334f6aa 100644 --- a/src/lv_draw/lv_img_buf.c +++ b/src/lv_draw/lv_img_buf.c @@ -44,27 +44,30 @@ * @param y x coordinate of the point to get * @param color the color of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` this color is used. * Not used in other cases. + * @param safe true: check out of bounds * @return color of the point */ -lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color) +lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color, bool safe) { + if(safe) { + if(x >= dsc->header.w) { + x = dsc->header.w - 1; + LV_LOG_WARN("lv_img_buf_get_px_color: x is too large"); + } else if(x < 0) { + x = 0; + LV_LOG_WARN("lv_img_buf_get_px_color: x is < 0"); + } + + if(y >= dsc->header.h) { + y = dsc->header.h - 1; + LV_LOG_WARN("lv_img_buf_get_px_color: y is too large"); + } else if(y < 0) { + y = 0; + LV_LOG_WARN("lv_img_buf_get_px_color: y is < 0"); + } + } + lv_color_t p_color = LV_COLOR_BLACK; - if(x >= dsc->header.w) { - x = dsc->header.w - 1; - LV_LOG_WARN("lv_canvas_get_px: x is too large (out of canvas)"); - } else if(x < 0) { - x = 0; - LV_LOG_WARN("lv_canvas_get_px: x is < 0 (out of canvas)"); - } - - if(y >= dsc->header.h) { - y = dsc->header.h - 1; - LV_LOG_WARN("lv_canvas_get_px: y is too large (out of canvas)"); - } else if(y < 0) { - y = 0; - LV_LOG_WARN("lv_canvas_get_px: y is < 0 (out of canvas)"); - } - uint8_t * buf_u8 = (uint8_t *)dsc->data; if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED || @@ -121,25 +124,28 @@ lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t * @param dsc pointer to an image descriptor * @param x x coordinate of the point to set * @param y x coordinate of the point to set + * @param safe true: check out of bounds * @return alpha value of the point */ -lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y) +lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, bool safe) { - if(x >= dsc->header.w) { - x = dsc->header.w - 1; - LV_LOG_WARN("lv_canvas_get_px: x is too large (out of canvas)"); - } else if(x < 0) { - x = 0; - LV_LOG_WARN("lv_canvas_get_px: x is < 0 (out of canvas)"); - } + if(safe) { + if(x >= dsc->header.w) { + x = dsc->header.w - 1; + LV_LOG_WARN("lv_img_buf_get_px_alpha: x is too large"); + } else if(x < 0) { + x = 0; + LV_LOG_WARN("lv_img_buf_get_px_alpha: x is < 0"); + } - if(y >= dsc->header.h) { - y = dsc->header.h - 1; - LV_LOG_WARN("lv_canvas_get_px: y is too large (out of canvas)"); - } else if(y < 0) { - y = 0; - LV_LOG_WARN("lv_canvas_get_px: y is < 0 (out of canvas)"); - } + if(y >= dsc->header.h) { + y = dsc->header.h - 1; + LV_LOG_WARN("lv_img_buf_get_px_alpha: y is too large"); + } else if(y < 0) { + y = 0; + LV_LOG_WARN("lv_img_buf_get_px_alpha: y is < 0"); + } + } uint8_t * buf_u8 = (uint8_t *)dsc->data; @@ -195,9 +201,28 @@ lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y) * @param x x coordinate of the point to set * @param y x coordinate of the point to set * @param c color of the point + * @param safe true: check out of bounds */ -void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c) +void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c, bool safe) { + if(safe) { + if(x >= dsc->header.w) { + x = dsc->header.w - 1; + LV_LOG_WARN("lv_img_buf_set_px_color: x is too large"); + } else if(x < 0) { + x = 0; + LV_LOG_WARN("lv_img_buf_set_px_color: x is < 0"); + } + + if(y >= dsc->header.h) { + y = dsc->header.h - 1; + LV_LOG_WARN("lv_img_buf_set_px_color: y is too large"); + } else if(y < 0) { + y = 0; + LV_LOG_WARN("lv_img_buf_set_px_color: y is < 0"); + } + } + uint8_t * buf_u8 = (uint8_t *)dsc->data; if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { @@ -256,9 +281,28 @@ void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_ * @param x x coordinate of the point to set * @param y x coordinate of the point to set * @param opa the desired opacity + * @param safe true: check out of bounds */ -void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa) +void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa, bool safe) { + if(safe) { + if(x >= dsc->header.w) { + x = dsc->header.w - 1; + LV_LOG_WARN("lv_img_buf_set_px_alpha: x is too large"); + } else if(x < 0) { + x = 0; + LV_LOG_WARN("lv_img_buf_set_px_alpha: x is < 0"); + } + + if(y >= dsc->header.h) { + y = dsc->header.h - 1; + LV_LOG_WARN("lv_img_buf_set_px_alpha: y is too large"); + } else if(y < 0) { + y = 0; + LV_LOG_WARN("lv_img_buf_set_px_alpha: y is < 0"); + } + } + uint8_t * buf_u8 = (uint8_t *)dsc->data; if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { @@ -304,200 +348,6 @@ void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_ } } - -void lv_img_buf_rotate_init(lv_img_rotate_dsc_t * dsc, int16_t angle, const void * src, lv_coord_t src_w, lv_coord_t src_h, - lv_img_cf_t cf, lv_coord_t pivot_x, lv_coord_t pivot_y, lv_color_t color) -{ - memset(dsc, 0x00, sizeof(lv_img_rotate_dsc_t)); - - dsc->angle = angle; - dsc->src = src; - dsc->src_w = src_w; - dsc->src_h = src_h; - dsc->cf = cf; - dsc->color = color; - dsc->pivot_x = pivot_x; - dsc->pivot_y = pivot_y; - dsc->pivot_x_256 = pivot_x * 256; - dsc->pivot_y_256 = pivot_y * 256; - dsc->sinma = lv_trigo_sin(-angle); - dsc->cosma = lv_trigo_sin(-angle + 90); - - dsc->chroma_keyed = lv_img_cf_is_chroma_keyed(cf) ? 1 : 0; - dsc->has_alpha = lv_img_cf_has_alpha(cf) ? 1 : 0; - if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_ALPHA || cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { - dsc->native_color = 1; - } - - dsc->img_dsc.data = src; - dsc->img_dsc.header.always_zero = 0; - dsc->img_dsc.header.cf = cf; - dsc->img_dsc.header.w = src_w; - dsc->img_dsc.header.h = src_h; - - dsc->res_opa = LV_OPA_COVER; -} - -bool lv_img_buf_get_px_rotated(lv_img_rotate_dsc_t * dsc, lv_coord_t x, lv_coord_t y) -{ - const uint8_t * src_u8 = dsc->src; - - /*Get the target point relative coordinates to the pivot*/ - int32_t xt = x - dsc->pivot_x; - int32_t yt = y - dsc->pivot_y; - - /*Get the source pixel from the upscaled image*/ - int32_t xs = ((dsc->cosma * xt - dsc->sinma * yt) >> (LV_TRIGO_SHIFT - 8)) + dsc->pivot_x_256; - int32_t ys = ((dsc->sinma * xt + dsc->cosma * yt) >> (LV_TRIGO_SHIFT - 8)) + dsc->pivot_y_256; - - /*Get the integer part of the source pixel*/ - int xs_int = xs >> 8; - int ys_int = ys >> 8; - - if(xs_int >= dsc->src_w) return false; - else if(xs_int < 0) return false; - - if(ys_int >= dsc->src_h) return false; - else if(ys_int < 0) return false; - - /*Get the fractional part of the source pixel*/ - int xs_fract = xs & 0xff; - int ys_fract = ys & 0xff; - - /* If the fractional < 0x70 mix the source pixel with the left/top pixel - * If the fractional > 0x90 mix the source pixel with the right/bottom pixel - * In the 0x70..0x90 range use the unchanged source pixel */ - - lv_color_t c_dest_int; - lv_opa_t opa_dest_int = 0; - - uint8_t px_size; - uint32_t px; - if(dsc->native_color) { - if(dsc->has_alpha == 0) { - px_size = LV_COLOR_SIZE >> 3; - - px = dsc->src_w * ys_int * px_size + xs_int * px_size; - memcpy(&c_dest_int, &src_u8[px], px_size); - } else { - px_size = LV_IMG_PX_SIZE_ALPHA_BYTE; - px = dsc->src_w * ys_int * px_size + xs_int * px_size; - memcpy(&c_dest_int, &src_u8[px], px_size - 1); - opa_dest_int = src_u8[px + px_size - 1]; - } - } else { - px = 0; /*unused*/ - px_size = 0; /*unused*/ - c_dest_int = lv_img_buf_get_px_color(&dsc->img_dsc, x, y, dsc->color); - opa_dest_int = lv_img_buf_get_px_alpha(&dsc->img_dsc, x, y); - } - - - if(dsc->chroma_keyed) { - lv_color_t ct = LV_COLOR_TRANSP; - if(c_dest_int.full == ct.full) return false; - } - - /*Get the mixture of the original source and the neightboor pixels in both directions*/ - lv_color_t c_x_dest; - lv_color_t c_y_dest; - lv_opa_t opa_x_dest = 0; - lv_opa_t opa_y_dest = 0; - - int32_t xn; /*x neightboor*/ - lv_opa_t xr; /*x mix ratio*/ - lv_color_t c_dest_xn; - lv_opa_t opa_dest_xn = 0; - - if(xs_fract < 0x70) { - xn = xs_int - 1; - if(xn < 0) return false; - - xr = xs_fract + 0x80; - - if(dsc->native_color) { - memcpy(&c_dest_xn, &src_u8[px - px_size], sizeof(lv_color_t)); - if(dsc->has_alpha) opa_dest_xn = src_u8[px - 1]; - } else { - c_dest_xn = lv_img_buf_get_px_color(&dsc->img_dsc, xn, y, dsc->color); - if(dsc->has_alpha) opa_dest_xn = lv_img_buf_get_px_alpha(&dsc->img_dsc, xn, y); - } - - c_x_dest = lv_color_mix(c_dest_int, c_dest_xn, xr); - if(dsc->has_alpha) opa_x_dest = (opa_dest_int * xr + (opa_dest_xn * (255 - xr))) >> 8; - - } else if(xs_fract > 0x90) { - xn = xs_int + 1; - if(xn >= dsc->src_w) return false; - - xr = (0xFF - xs_fract) + 0x80; - - if(dsc->native_color) { - memcpy(&c_dest_xn, &src_u8[px + px_size], sizeof(lv_color_t)); - if(dsc->has_alpha) opa_dest_xn = src_u8[px + 2 * px_size - 1]; - } else { - c_dest_xn = lv_img_buf_get_px_color(&dsc->img_dsc, xn, y, dsc->color); - if(dsc->has_alpha) opa_dest_xn = lv_img_buf_get_px_alpha(&dsc->img_dsc, xn, y); - } - - c_x_dest = lv_color_mix(c_dest_int, c_dest_xn, xr); - if(dsc->has_alpha) opa_x_dest = (opa_dest_int * xr + (opa_dest_xn * (255 - xr))) >> 8; - - } else { - c_x_dest.full = c_dest_int.full; - opa_x_dest = opa_dest_int; - } - - - int32_t yn; /*x neightboor*/ - lv_opa_t yr; /*x mix ratio*/ - lv_color_t c_dest_yn; - lv_opa_t opa_dest_yn = 0; - - if(ys_fract < 0x70) { - yn = ys_int - 1; - if(yn < 0) return false; - - yr = ys_fract + 0x80; - - if(dsc->native_color) { - memcpy(&c_dest_yn, &src_u8[px - px_size * dsc->src_w], sizeof(lv_color_t)); - if(dsc->has_alpha) opa_dest_yn = src_u8[px - px_size * dsc->src_w + px_size- 1]; - } else { - c_dest_yn = lv_img_buf_get_px_color(&dsc->img_dsc, yn, y, dsc->color); - if(dsc->has_alpha) opa_dest_yn = lv_img_buf_get_px_alpha(&dsc->img_dsc, yn, y); - } - - c_y_dest = lv_color_mix(c_dest_int, c_dest_yn, yr); - if(dsc->has_alpha) opa_y_dest = (opa_dest_int * yr + (opa_dest_yn * (255 - yr))) >> 8; - - } else if(ys_fract > 0x90) { - yn = ys_int + 1; - if(yn >= dsc->src_h) return false; - - yr = (0xFF - ys_fract) + 0x80; - - if(dsc->native_color) { - memcpy(&c_dest_yn, &src_u8[px + px_size * dsc->src_w], sizeof(lv_color_t)); - if(dsc->has_alpha) opa_dest_yn = src_u8[px + px_size * dsc->src_w + 2 * px_size - 1]; - } else { - c_dest_yn = lv_img_buf_get_px_color(&dsc->img_dsc, yn, y, dsc->color); - if(dsc->has_alpha) opa_dest_yn = lv_img_buf_get_px_alpha(&dsc->img_dsc, yn, y); - } - - c_y_dest = lv_color_mix(c_dest_int, c_dest_yn, yr); - if(dsc->has_alpha) opa_y_dest = (opa_dest_int * yr + (opa_dest_yn * (255 - yr))) >> 8; - } else { - c_y_dest.full = c_dest_int.full; - opa_y_dest = opa_dest_int; - } - - dsc->res_color = lv_color_mix(c_x_dest, c_y_dest, LV_OPA_50); - if(dsc->has_alpha) dsc->res_opa = (opa_x_dest + opa_y_dest) >> 1; - - return true; -} - /** * Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8` * @param dsc pointer to an image descriptor @@ -600,6 +450,219 @@ uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf) } } +/** + * Initialize a descriptor to rotate an image + * @param dsc pointer to an `lv_img_rotate_dsc_t` variable + * @param angle angle to rotate + * @param src image source (array of pixels) + * @param src_w width of the image to rotate + * @param src_h height of the image to rotate + * @param cf color format of the image to rotate + * @param pivot_x pivot x + * @param pivot_y pivot y + * @param color a color used for `LV_IMG_CF_INDEXED_1/2/4/8BIT` color formats + */ +void lv_img_buf_rotate_init(lv_img_rotate_dsc_t * dsc, int16_t angle, const void * src, lv_coord_t src_w, lv_coord_t src_h, + lv_img_cf_t cf, lv_coord_t pivot_x, lv_coord_t pivot_y, lv_color_t color) +{ + memset(dsc, 0x00, sizeof(lv_img_rotate_dsc_t)); + + dsc->angle = angle; + dsc->src = src; + dsc->src_w = src_w; + dsc->src_h = src_h; + dsc->cf = cf; + dsc->color = color; + dsc->pivot_x = pivot_x; + dsc->pivot_y = pivot_y; + dsc->pivot_x_256 = pivot_x * 256; + dsc->pivot_y_256 = pivot_y * 256; + dsc->sinma = lv_trigo_sin(-angle); + dsc->cosma = lv_trigo_sin(-angle + 90); + + dsc->chroma_keyed = lv_img_cf_is_chroma_keyed(cf) ? 1 : 0; + dsc->has_alpha = lv_img_cf_has_alpha(cf) ? 1 : 0; + if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_ALPHA || cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { + dsc->native_color = 1; + } + + dsc->img_dsc.data = src; + dsc->img_dsc.header.always_zero = 0; + dsc->img_dsc.header.cf = cf; + dsc->img_dsc.header.w = src_w; + dsc->img_dsc.header.h = src_h; + + dsc->res_opa = LV_OPA_COVER; +} + +/** + * Get which color and opa would come to a pixel if it were rotated + * @param dsc a descriptor initialized by `lv_img_buf_rotate_init` + * @param x the coordinate which color and opa should be get + * @param y the coordinate which color and opa should be get + * @return true: there is valid pixel on these x/y coordinates; false: the rotated pixel was out of the image + * @note the result is written back to `dsc->res_color` and `dsc->res_opa` + */ +bool lv_img_buf_get_px_rotated(lv_img_rotate_dsc_t * dsc, lv_coord_t x, lv_coord_t y) +{ + const uint8_t * src_u8 = dsc->src; + + /*Get the target point relative coordinates to the pivot*/ + int32_t xt = x - dsc->pivot_x; + int32_t yt = y - dsc->pivot_y; + + /*Get the source pixel from the upscaled image*/ + int32_t xs = ((dsc->cosma * xt - dsc->sinma * yt) >> (LV_TRIGO_SHIFT - 8)) + dsc->pivot_x_256; + int32_t ys = ((dsc->sinma * xt + dsc->cosma * yt) >> (LV_TRIGO_SHIFT - 8)) + dsc->pivot_y_256; + + /*Get the integer part of the source pixel*/ + int xs_int = xs >> 8; + int ys_int = ys >> 8; + + if(xs_int >= dsc->src_w) return false; + else if(xs_int < 0) return false; + + if(ys_int >= dsc->src_h) return false; + else if(ys_int < 0) return false; + + /*Get the fractional part of the source pixel*/ + int xs_fract = xs & 0xff; + int ys_fract = ys & 0xff; + + /* If the fractional < 0x70 mix the source pixel with the left/top pixel + * If the fractional > 0x90 mix the source pixel with the right/bottom pixel + * In the 0x70..0x90 range use the unchanged source pixel */ + + lv_color_t c_dest_int; + lv_opa_t opa_dest_int = 0; + + uint8_t px_size; + uint32_t px; + if(dsc->native_color) { + if(dsc->has_alpha == 0) { + px_size = LV_COLOR_SIZE >> 3; + + px = dsc->src_w * ys_int * px_size + xs_int * px_size; + memcpy(&c_dest_int, &src_u8[px], px_size); + } else { + px_size = LV_IMG_PX_SIZE_ALPHA_BYTE; + px = dsc->src_w * ys_int * px_size + xs_int * px_size; + memcpy(&c_dest_int, &src_u8[px], px_size - 1); + opa_dest_int = src_u8[px + px_size - 1]; + } + } else { + px = 0; /*unused*/ + px_size = 0; /*unused*/ + c_dest_int = lv_img_buf_get_px_color(&dsc->img_dsc, x, y, dsc->color, false); + opa_dest_int = lv_img_buf_get_px_alpha(&dsc->img_dsc, x, y, false); + } + + + if(dsc->chroma_keyed) { + lv_color_t ct = LV_COLOR_TRANSP; + if(c_dest_int.full == ct.full) return false; + } + + /*Get the mixture of the original source and the neightboor pixels in both directions*/ + lv_color_t c_x_dest; + lv_color_t c_y_dest; + lv_opa_t opa_x_dest = 0; + lv_opa_t opa_y_dest = 0; + + int32_t xn; /*x neightboor*/ + lv_opa_t xr; /*x mix ratio*/ + lv_color_t c_dest_xn; + lv_opa_t opa_dest_xn = 0; + + if(xs_fract < 0x70) { + xn = xs_int - 1; + if(xn < 0) return false; + + xr = xs_fract + 0x80; + + if(dsc->native_color) { + memcpy(&c_dest_xn, &src_u8[px - px_size], sizeof(lv_color_t)); + if(dsc->has_alpha) opa_dest_xn = src_u8[px - 1]; + } else { + c_dest_xn = lv_img_buf_get_px_color(&dsc->img_dsc, xn, y, dsc->color, false); + if(dsc->has_alpha) opa_dest_xn = lv_img_buf_get_px_alpha(&dsc->img_dsc, xn, y, false); + } + + c_x_dest = lv_color_mix(c_dest_int, c_dest_xn, xr); + if(dsc->has_alpha) opa_x_dest = (opa_dest_int * xr + (opa_dest_xn * (255 - xr))) >> 8; + + } else if(xs_fract > 0x90) { + xn = xs_int + 1; + if(xn >= dsc->src_w) return false; + + xr = (0xFF - xs_fract) + 0x80; + + if(dsc->native_color) { + memcpy(&c_dest_xn, &src_u8[px + px_size], sizeof(lv_color_t)); + if(dsc->has_alpha) opa_dest_xn = src_u8[px + 2 * px_size - 1]; + } else { + c_dest_xn = lv_img_buf_get_px_color(&dsc->img_dsc, xn, y, dsc->color, false); + if(dsc->has_alpha) opa_dest_xn = lv_img_buf_get_px_alpha(&dsc->img_dsc, xn, y, false); + } + + c_x_dest = lv_color_mix(c_dest_int, c_dest_xn, xr); + if(dsc->has_alpha) opa_x_dest = (opa_dest_int * xr + (opa_dest_xn * (255 - xr))) >> 8; + + } else { + c_x_dest.full = c_dest_int.full; + opa_x_dest = opa_dest_int; + } + + + int32_t yn; /*x neightboor*/ + lv_opa_t yr; /*x mix ratio*/ + lv_color_t c_dest_yn; + lv_opa_t opa_dest_yn = 0; + + if(ys_fract < 0x70) { + yn = ys_int - 1; + if(yn < 0) return false; + + yr = ys_fract + 0x80; + + if(dsc->native_color) { + memcpy(&c_dest_yn, &src_u8[px - px_size * dsc->src_w], sizeof(lv_color_t)); + if(dsc->has_alpha) opa_dest_yn = src_u8[px - px_size * dsc->src_w + px_size- 1]; + } else { + c_dest_yn = lv_img_buf_get_px_color(&dsc->img_dsc, yn, y, dsc->color, false); + if(dsc->has_alpha) opa_dest_yn = lv_img_buf_get_px_alpha(&dsc->img_dsc, yn, y, false); + } + + c_y_dest = lv_color_mix(c_dest_int, c_dest_yn, yr); + if(dsc->has_alpha) opa_y_dest = (opa_dest_int * yr + (opa_dest_yn * (255 - yr))) >> 8; + + } else if(ys_fract > 0x90) { + yn = ys_int + 1; + if(yn >= dsc->src_h) return false; + + yr = (0xFF - ys_fract) + 0x80; + + if(dsc->native_color) { + memcpy(&c_dest_yn, &src_u8[px + px_size * dsc->src_w], sizeof(lv_color_t)); + if(dsc->has_alpha) opa_dest_yn = src_u8[px + px_size * dsc->src_w + 2 * px_size - 1]; + } else { + c_dest_yn = lv_img_buf_get_px_color(&dsc->img_dsc, yn, y, dsc->color, false); + if(dsc->has_alpha) opa_dest_yn = lv_img_buf_get_px_alpha(&dsc->img_dsc, yn, y, false); + } + + c_y_dest = lv_color_mix(c_dest_int, c_dest_yn, yr); + if(dsc->has_alpha) opa_y_dest = (opa_dest_int * yr + (opa_dest_yn * (255 - yr))) >> 8; + } else { + c_y_dest.full = c_dest_int.full; + opa_y_dest = opa_dest_int; + } + + dsc->res_color = lv_color_mix(c_x_dest, c_y_dest, LV_OPA_50); + if(dsc->has_alpha) dsc->res_opa = (opa_x_dest + opa_y_dest) >> 1; + + return true; +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/lv_draw/lv_img_buf.h b/src/lv_draw/lv_img_buf.h index 6a2f89822..41f33af5b 100644 --- a/src/lv_draw/lv_img_buf.h +++ b/src/lv_draw/lv_img_buf.h @@ -170,25 +170,20 @@ lv_img_dsc_t *lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf); * @param y x coordinate of the point to get * @param color the color of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` this color is used. * Not used in other cases. + * @param safe true: check out of bounds * @return color of the point */ -lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color); - +lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color, bool safe); /** * Get the alpha value of an image's pixel * @param dsc pointer to an image descriptor * @param x x coordinate of the point to set * @param y x coordinate of the point to set + * @param safe true: check out of bounds * @return alpha value of the point */ -lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y); - - -void lv_img_buf_rotate_init(lv_img_rotate_dsc_t * dsc, int16_t angle, const void * src, lv_coord_t src_w, lv_coord_t src_h, - lv_img_cf_t cf, lv_coord_t pivot_x, lv_coord_t pivot_y, lv_color_t color); - -bool lv_img_buf_get_px_rotated(lv_img_rotate_dsc_t * dsc, lv_coord_t x, lv_coord_t y); +lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, bool safe); /** * Set the color of a pixel of an image. The alpha channel won't be affected. @@ -196,8 +191,9 @@ bool lv_img_buf_get_px_rotated(lv_img_rotate_dsc_t * dsc, lv_coord_t x, lv_coord * @param x x coordinate of the point to set * @param y x coordinate of the point to set * @param c color of the point + * @param safe true: check out of bounds */ -void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c); +void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c, bool safe); /** * Set the alpha value of a pixel of an image. The color won't be affected @@ -205,8 +201,9 @@ void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_ * @param x x coordinate of the point to set * @param y x coordinate of the point to set * @param opa the desired opacity + * @param safe true: check out of bounds */ -void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa); +void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa, bool safe); /** * Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8` @@ -235,6 +232,30 @@ void lv_img_buf_free(lv_img_dsc_t *dsc); */ uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf); +/** + * Initialize a descriptor to rotate an image + * @param dsc pointer to an `lv_img_rotate_dsc_t` variable + * @param angle angle to rotate + * @param src image source (array of pixels) + * @param src_w width of the image to rotate + * @param src_h height of the image to rotate + * @param cf color format of the image to rotate + * @param pivot_x pivot x + * @param pivot_y pivot y + * @param color a color used for `LV_IMG_CF_INDEXED_1/2/4/8BIT` color formats + */ +void lv_img_buf_rotate_init(lv_img_rotate_dsc_t * dsc, int16_t angle, const void * src, lv_coord_t src_w, lv_coord_t src_h, + lv_img_cf_t cf, lv_coord_t pivot_x, lv_coord_t pivot_y, lv_color_t color); + +/** + * Get which color and opa would come to a pixel if it were rotated + * @param dsc a descriptor initialized by `lv_img_buf_rotate_init` + * @param x the coordinate which color and opa should be get + * @param y the coordinate which color and opa should be get + * @return true: there is valid pixel on these x/y coordinates; false: the rotated pixel was out of the image + * @note the result is written back to `dsc->res_color` and `dsc->res_opa` + */ +bool lv_img_buf_get_px_rotated(lv_img_rotate_dsc_t * dsc, lv_coord_t x, lv_coord_t y); /********************** diff --git a/src/lv_objx/lv_canvas.c b/src/lv_objx/lv_canvas.c index 5eabfd8c7..92a1a470e 100644 --- a/src/lv_objx/lv_canvas.c +++ b/src/lv_objx/lv_canvas.c @@ -159,7 +159,7 @@ void lv_canvas_set_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas); - lv_img_buf_set_px_color(&ext->dsc, x, y, c); + lv_img_buf_set_px_color(&ext->dsc, x, y, c, true); lv_obj_invalidate(canvas); } @@ -218,7 +218,7 @@ lv_color_t lv_canvas_get_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y) if(style == NULL) style = &lv_style_scr; - return lv_img_buf_get_px_color(&ext->dsc, x, y, style->image.color); + return lv_img_buf_get_px_color(&ext->dsc, x, y, style->image.color, true); } /** @@ -333,30 +333,30 @@ void lv_canvas_rotate(lv_obj_t * canvas, lv_img_dsc_t * img, int16_t angle, lv_c if(x + offset_x >= 0 && x + offset_x < dest_width && y + offset_y >= 0 && y + offset_y < dest_height) { /*If the image has no alpha channel just simple set the result color on the canvas*/ if(lv_img_cf_has_alpha(img->header.cf) == false) { - lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.res_color); + lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.res_color, false); } else { - lv_color_t bg_color = lv_img_buf_get_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, style->image.color); + lv_color_t bg_color = lv_img_buf_get_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, style->image.color, false); /*If the canvas has no alpha but the image has mix the image's color with * canvas*/ if(lv_img_cf_has_alpha(ext_dst->dsc.header.cf) == false) { if(dsc.res_opa < LV_OPA_MAX) dsc.res_color = lv_color_mix(dsc.res_color, bg_color, dsc.res_opa); - lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.res_color); + lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.res_color, false); } /*Both the image and canvas has alpha channel. Some extra calculation is required*/ else { - lv_opa_t bg_opa = lv_img_buf_get_px_alpha(&ext_dst->dsc, x + offset_x, y + offset_y); + lv_opa_t bg_opa = lv_img_buf_get_px_alpha(&ext_dst->dsc, x + offset_x, y + offset_y, false); /* Pick the foreground if it's fully opaque or the Background is fully * transparent*/ if(dsc.res_opa >= LV_OPA_MAX || bg_opa <= LV_OPA_MIN) { - lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.res_color); - lv_img_buf_set_px_alpha(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.res_opa); + lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.res_color, false); + lv_img_buf_set_px_alpha(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.res_opa, false); } /*Opaque background: use simple mix*/ else if(bg_opa >= LV_OPA_MAX) { lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, - lv_color_mix(dsc.res_color, bg_color, dsc.res_opa)); + lv_color_mix(dsc.res_color, bg_color, dsc.res_opa), false); } /*Both colors have alpha. Expensive calculation need to be applied*/ else { @@ -370,8 +370,8 @@ void lv_canvas_rotate(lv_obj_t * canvas, lv_img_dsc_t * img, int16_t angle, lv_c lv_opa_t ratio = (uint16_t)((uint16_t)dsc.res_opa * 255) / opa_res_2; lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, - lv_color_mix(dsc.res_color, bg_color, ratio)); - lv_img_buf_set_px_alpha(&ext_dst->dsc, x + offset_x, y + offset_y, opa_res_2); + lv_color_mix(dsc.res_color, bg_color, ratio), false); + lv_img_buf_set_px_alpha(&ext_dst->dsc, x + offset_x, y + offset_y, opa_res_2, false); } } } @@ -393,6 +393,7 @@ void lv_canvas_blur_hor(lv_obj_t * canvas, uint16_t r) LV_ASSERT_OBJ(canvas, LV_OBJX_NAME); lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas); + const lv_style_t * style = lv_canvas_get_style(canvas, LV_CANVAS_STYLE_MAIN); uint16_t r_back = r / 2; uint16_t r_front = r / 2; @@ -426,8 +427,8 @@ void lv_canvas_blur_hor(lv_obj_t * canvas, uint16_t r) for(x = -r_back; x <= r_front; x++) { x_safe = x < 0 ? 0 : x; - c = lv_img_buf_get_px_color(&line_img, x_safe, 0, LV_COLOR_RED); - opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0); + c = lv_img_buf_get_px_color(&line_img, x_safe, 0, style->image.color, false); + opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0, false); rsum += c.ch.red; gsum += c.ch.green; @@ -436,19 +437,21 @@ void lv_canvas_blur_hor(lv_obj_t * canvas, uint16_t r) } for(x = 0; x < ext->dsc.header.w; x++) { - c.ch.red = rsum / r; - c.ch.green = gsum / r; - c.ch.blue = bsum / r; - opa = asum / r; - lv_img_buf_set_px_color(&ext->dsc, x, y, c); - lv_img_buf_set_px_alpha(&ext->dsc, x, y, opa); + if(asum) { + c.ch.red = rsum / r; + c.ch.green = gsum / r; + c.ch.blue = bsum / r; + opa = asum / r; + lv_img_buf_set_px_color(&ext->dsc, x, y, c, false); + lv_img_buf_set_px_alpha(&ext->dsc, x, y, opa, false); + } x_safe = x - r_back; x_safe = x_safe < 0 ? 0 : x_safe; - c = lv_img_buf_get_px_color(&line_img, x_safe, 0, LV_COLOR_RED); - opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0); + c = lv_img_buf_get_px_color(&line_img, x_safe, 0, style->image.color, false); + opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0, false); rsum -= c.ch.red; gsum -= c.ch.green; @@ -457,8 +460,8 @@ void lv_canvas_blur_hor(lv_obj_t * canvas, uint16_t r) x_safe = x + 1 + r_front; x_safe = x_safe > ext->dsc.header.w - 1 ? ext->dsc.header.w - 1 : x_safe; - c = lv_img_buf_get_px_color(&line_img, x_safe, 0, LV_COLOR_RED); - opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0); + c = lv_img_buf_get_px_color(&line_img, x_safe, 0, LV_COLOR_RED, false); + opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0, false); rsum += c.ch.red; gsum += c.ch.green; @@ -479,6 +482,7 @@ void lv_canvas_blur_ver(lv_obj_t * canvas, uint16_t r) LV_ASSERT_OBJ(canvas, LV_OBJX_NAME); lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas); + const lv_style_t * style = lv_canvas_get_style(canvas, LV_CANVAS_STYLE_MAIN); uint16_t r_back = r / 2; uint16_t r_front = r / 2; @@ -509,11 +513,11 @@ void lv_canvas_blur_ver(lv_obj_t * canvas, uint16_t r) for(y = -r_back; y <= r_front; y++) { y_safe = y < 0 ? 0 : y; - c = lv_img_buf_get_px_color(&ext->dsc, x, y_safe, LV_COLOR_RED); - opa = lv_img_buf_get_px_alpha(&ext->dsc, x, y_safe); + c = lv_img_buf_get_px_color(&ext->dsc, x, y_safe, style->image.color, false); + opa = lv_img_buf_get_px_alpha(&ext->dsc, x, y_safe, false); - lv_img_buf_set_px_color(&line_img, 0, y_safe, c); - lv_img_buf_set_px_alpha(&line_img, 0, y_safe, opa); + lv_img_buf_set_px_color(&line_img, 0, y_safe, c, false); + lv_img_buf_set_px_alpha(&line_img, 0, y_safe, opa, false); rsum += c.ch.red; gsum += c.ch.green; @@ -522,18 +526,20 @@ void lv_canvas_blur_ver(lv_obj_t * canvas, uint16_t r) } for(y = 0; y < ext->dsc.header.h; y++) { - c.ch.red = rsum / r; - c.ch.green = gsum / r; - c.ch.blue = bsum / r; - opa = asum / r; + if(opa) { + c.ch.red = rsum / r; + c.ch.green = gsum / r; + c.ch.blue = bsum / r; + opa = asum / r; - lv_img_buf_set_px_color(&ext->dsc, x, y, c); - lv_img_buf_set_px_alpha(&ext->dsc, x, y, opa); + lv_img_buf_set_px_color(&ext->dsc, x, y, c, false); + lv_img_buf_set_px_alpha(&ext->dsc, x, y, opa, false); + } y_safe = y - r_back; y_safe = y_safe < 0 ? 0 : y_safe; - c = lv_img_buf_get_px_color(&line_img, 0, y_safe, LV_COLOR_RED); - opa = lv_img_buf_get_px_alpha(&line_img, 0, y_safe); + c = lv_img_buf_get_px_color(&line_img, 0, y_safe, style->image.color, false); + opa = lv_img_buf_get_px_alpha(&line_img, 0, y_safe, false); rsum -= c.ch.red; gsum -= c.ch.green; @@ -543,11 +549,11 @@ void lv_canvas_blur_ver(lv_obj_t * canvas, uint16_t r) y_safe = y + 1 + r_front; y_safe = y_safe > ext->dsc.header.h - 1 ? ext->dsc.header.h - 1 : y_safe; - c = lv_img_buf_get_px_color(&ext->dsc, x, y_safe, LV_COLOR_RED); - opa = lv_img_buf_get_px_alpha(&ext->dsc, x, y_safe); + c = lv_img_buf_get_px_color(&ext->dsc, x, y_safe, style->image.color, false); + opa = lv_img_buf_get_px_alpha(&ext->dsc, x, y_safe, false); - lv_img_buf_set_px_color(&line_img, 0, y_safe, c); - lv_img_buf_set_px_alpha(&line_img, 0, y_safe, opa); + lv_img_buf_set_px_color(&line_img, 0, y_safe, c, false); + lv_img_buf_set_px_alpha(&line_img, 0, y_safe, opa, false); rsum += c.ch.red; gsum += c.ch.green; @@ -572,7 +578,7 @@ void lv_canvas_fill_bg(lv_obj_t * canvas, lv_color_t color) uint32_t y; for(y = 0; y < dsc->header.h; y++) { for(x = 0; x < dsc->header.w; x++) { - lv_img_buf_set_px_color(dsc, x, y, color); + lv_img_buf_set_px_color(dsc, x, y, color, false); } } } @@ -1066,11 +1072,11 @@ static void set_px_alpha_generic(lv_img_dsc_t * d, lv_coord_t x, lv_coord_t y, l uint8_t br = lv_color_brightness(color); if(opa < LV_OPA_MAX) { - uint8_t bg = lv_img_buf_get_px_alpha(d, x, y); - br = (uint16_t)((uint16_t)br * opa + (bg * (255 - opa))) >> 8; //LV_MATH_MIN(opa + bg, 0xFF); + uint8_t bg = lv_img_buf_get_px_alpha(d, x, y, false); + br = (uint16_t)((uint16_t)br * opa + (bg * (255 - opa))) >> 8; } - lv_img_buf_set_px_alpha(d, x, y, br); + lv_img_buf_set_px_alpha(d, x, y, br, false); } @@ -1087,8 +1093,8 @@ static void set_px_true_color_alpha(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_ d.header.w = buf_w; d.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA; - lv_color_t bg_color = lv_img_buf_get_px_color(&d, x, y, LV_COLOR_BLACK); - lv_opa_t bg_opa = lv_img_buf_get_px_alpha(&d, x, y); + lv_color_t bg_color = lv_img_buf_get_px_color(&d, x, y, LV_COLOR_BLACK, false); + lv_opa_t bg_opa = lv_img_buf_get_px_alpha(&d, x, y, false); lv_opa_t res_opa; lv_color_t res_color; @@ -1096,8 +1102,8 @@ static void set_px_true_color_alpha(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_ lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &res_opa); - lv_img_buf_set_px_alpha(&d, x, y, res_opa); - lv_img_buf_set_px_color(&d, x, y, res_color); + lv_img_buf_set_px_alpha(&d, x, y, res_opa, false); + lv_img_buf_set_px_color(&d, x, y, res_color, false); } #endif