/** * @file lv_img_buf.c * */ /********************* * INCLUDES *********************/ #include #include #include "lv_img_buf.h" #include "lv_draw_img.h" #include "../misc/lv_math.h" #include "../misc/lv_log.h" #include "../misc/lv_mem.h" /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ /** * Get the color of an image's pixel * @param dsc an image descriptor * @param x x coordinate of the point to get * @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 p_color = lv_color_black(); 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 || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3; uint32_t px = dsc->header.w * y * px_size + x * px_size; lv_memcpy_small(&p_color, &buf_u8[px], sizeof(lv_color_t)); #if LV_COLOR_SIZE == 32 p_color.ch.alpha = 0xFF; /*Only the color should be get so use a default alpha value*/ #endif } else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) { buf_u8 += 4 * 2; uint8_t bit = x & 0x7; x = x >> 3; /*Get the current pixel. *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned *so the possible real width are 8, 16, 24 ...*/ uint32_t px = ((dsc->header.w + 7) >> 3) * y + x; p_color.full = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit); } else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) { buf_u8 += 4 * 4; uint8_t bit = (x & 0x3) * 2; x = x >> 2; /*Get the current pixel. *dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned *so the possible real width are 4, 8, 12 ...*/ uint32_t px = ((dsc->header.w + 3) >> 2) * y + x; p_color.full = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit); } else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) { buf_u8 += 4 * 16; uint8_t bit = (x & 0x1) * 4; x = x >> 1; /*Get the current pixel. *dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned *so the possible real width are 2, 4, 6 ...*/ uint32_t px = ((dsc->header.w + 1) >> 1) * y + x; p_color.full = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit); } else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) { buf_u8 += 4 * 256; uint32_t px = dsc->header.w * y + x; p_color.full = buf_u8[px]; } else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT || dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) { p_color = color; } return p_color; } /** * 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) { uint8_t * buf_u8 = (uint8_t *)dsc->data; if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { uint32_t px = dsc->header.w * y * LV_IMG_PX_SIZE_ALPHA_BYTE + x * LV_IMG_PX_SIZE_ALPHA_BYTE; return buf_u8[px + LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; } else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) { uint8_t bit = x & 0x7; x = x >> 3; /*Get the current pixel. *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned *so the possible real width are 8 ,16, 24 ...*/ uint32_t px = ((dsc->header.w + 7) >> 3) * y + x; uint8_t px_opa = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit); return px_opa ? LV_OPA_TRANSP : LV_OPA_COVER; } else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) { const uint8_t opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/ uint8_t bit = (x & 0x3) * 2; x = x >> 2; /*Get the current pixel. *dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned *so the possible real width are 4 ,8, 12 ...*/ uint32_t px = ((dsc->header.w + 3) >> 2) * y + x; uint8_t px_opa = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit); return opa_table[px_opa]; } else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) { const uint8_t opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/ 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255 }; uint8_t bit = (x & 0x1) * 4; x = x >> 1; /*Get the current pixel. *dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned *so the possible real width are 2 ,4, 6 ...*/ uint32_t px = ((dsc->header.w + 1) >> 1) * y + x; uint8_t px_opa = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit); return opa_table[px_opa]; } else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) { uint32_t px = dsc->header.w * y + x; return buf_u8[px]; } return LV_OPA_COVER; } /** * Set the alpha value of a pixel of an image. The color won't be affected * @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 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) { uint8_t * buf_u8 = (uint8_t *)dsc->data; if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3; uint32_t px = dsc->header.w * y * px_size + x * px_size; buf_u8[px + px_size - 1] = opa; } else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) { opa = opa >> 7; /*opa -> [0,1]*/ uint8_t bit = x & 0x7; x = x >> 3; /*Get the current pixel. *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned *so the possible real width are 8 ,16, 24 ...*/ uint32_t px = ((dsc->header.w + 7) >> 3) * y + x; buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit)); buf_u8[px] = buf_u8[px] | ((opa & 0x1) << (7 - bit)); } else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) { opa = opa >> 6; /*opa -> [0,3]*/ uint8_t bit = (x & 0x3) * 2; x = x >> 2; /*Get the current pixel. *dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned *so the possible real width are 4 ,8, 12 ...*/ uint32_t px = ((dsc->header.w + 3) >> 2) * y + x; buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit)); buf_u8[px] = buf_u8[px] | ((opa & 0x3) << (6 - bit)); } else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) { opa = opa >> 4; /*opa -> [0,15]*/ uint8_t bit = (x & 0x1) * 4; x = x >> 1; /*Get the current pixel. *dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned *so the possible real width are 2 ,4, 6 ...*/ uint32_t px = ((dsc->header.w + 1) >> 1) * y + x; buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit)); buf_u8[px] = buf_u8[px] | ((opa & 0xF) << (4 - bit)); } else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) { uint32_t px = dsc->header.w * y + x; buf_u8[px] = opa; } } /** * Set the color of a pixel of an image. The alpha channel won't be affected. * @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 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) { 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) { uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3; uint32_t px = dsc->header.w * y * px_size + x * px_size; lv_memcpy_small(&buf_u8[px], &c, px_size); } else if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3; uint32_t px = dsc->header.w * y * px_size + x * px_size; lv_memcpy_small(&buf_u8[px], &c, px_size - 1); /*-1 to not overwrite the alpha value*/ } else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) { buf_u8 += sizeof(lv_color32_t) * 2; /*Skip the palette*/ uint8_t bit = x & 0x7; x = x >> 3; /*Get the current pixel. *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned *so the possible real width are 8 ,16, 24 ...*/ uint32_t px = ((dsc->header.w + 7) >> 3) * y + x; buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit)); buf_u8[px] = buf_u8[px] | ((c.full & 0x1) << (7 - bit)); } else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) { buf_u8 += sizeof(lv_color32_t) * 4; /*Skip the palette*/ uint8_t bit = (x & 0x3) * 2; x = x >> 2; /*Get the current pixel. *dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned *so the possible real width are 4, 8 ,12 ...*/ uint32_t px = ((dsc->header.w + 3) >> 2) * y + x; buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit)); buf_u8[px] = buf_u8[px] | ((c.full & 0x3) << (6 - bit)); } else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) { buf_u8 += sizeof(lv_color32_t) * 16; /*Skip the palette*/ uint8_t bit = (x & 0x1) * 4; x = x >> 1; /*Get the current pixel. *dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned *so the possible real width are 2 ,4, 6 ...*/ uint32_t px = ((dsc->header.w + 1) >> 1) * y + x; buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit)); buf_u8[px] = buf_u8[px] | ((c.full & 0xF) << (4 - bit)); } else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) { buf_u8 += sizeof(lv_color32_t) * 256; /*Skip the palette*/ uint32_t px = dsc->header.w * y + x; buf_u8[px] = c.full; } } /** * 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 * @param id the palette color to set: * - for `LV_IMG_CF_INDEXED1`: 0..1 * - for `LV_IMG_CF_INDEXED2`: 0..3 * - for `LV_IMG_CF_INDEXED4`: 0..15 * - for `LV_IMG_CF_INDEXED8`: 0..255 * @param c the color to set */ void lv_img_buf_set_palette(lv_img_dsc_t * dsc, uint8_t id, lv_color_t c) { if((dsc->header.cf == LV_IMG_CF_ALPHA_1BIT && id > 1) || (dsc->header.cf == LV_IMG_CF_ALPHA_2BIT && id > 3) || (dsc->header.cf == LV_IMG_CF_ALPHA_4BIT && id > 15) || (dsc->header.cf == LV_IMG_CF_ALPHA_8BIT)) { LV_LOG_WARN("lv_img_buf_set_px_alpha: invalid 'id'"); return; } lv_color32_t c32; c32.full = lv_color_to32(c); uint8_t * buf = (uint8_t *)dsc->data; lv_memcpy_small(&buf[id * sizeof(c32)], &c32, sizeof(c32)); } /** * Allocate an image buffer in RAM * @param w width of image * @param h height of image * @param cf a color format (`LV_IMG_CF_...`) * @return an allocated image, or NULL on failure */ lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf) { /*Allocate image descriptor*/ lv_img_dsc_t * dsc = lv_mem_alloc(sizeof(lv_img_dsc_t)); if(dsc == NULL) return NULL; lv_memset_00(dsc, sizeof(lv_img_dsc_t)); /*Get image data size*/ dsc->data_size = lv_img_buf_get_img_size(w, h, cf); if(dsc->data_size == 0) { lv_mem_free(dsc); return NULL; } /*Allocate raw buffer*/ dsc->data = lv_mem_alloc(dsc->data_size); if(dsc->data == NULL) { lv_mem_free(dsc); return NULL; } lv_memset_00((uint8_t *)dsc->data, dsc->data_size); /*Fill in header*/ dsc->header.always_zero = 0; dsc->header.w = w; dsc->header.h = h; dsc->header.cf = cf; return dsc; } /** * Free an allocated image buffer * @param dsc image buffer to free */ void lv_img_buf_free(lv_img_dsc_t * dsc) { if(dsc != NULL) { if(dsc->data != NULL) lv_mem_free((void *)dsc->data); lv_mem_free(dsc); } } /** * Get the memory consumption of a raw bitmap, given color format and dimensions. * @param w width * @param h height * @param cf color format * @return size in bytes */ uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf) { switch(cf) { case LV_IMG_CF_TRUE_COLOR: return LV_IMG_BUF_SIZE_TRUE_COLOR(w, h); case LV_IMG_CF_TRUE_COLOR_ALPHA: return LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h); case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: return LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h); case LV_IMG_CF_ALPHA_1BIT: return LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h); case LV_IMG_CF_ALPHA_2BIT: return LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h); case LV_IMG_CF_ALPHA_4BIT: return LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h); case LV_IMG_CF_ALPHA_8BIT: return LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h); case LV_IMG_CF_INDEXED_1BIT: return LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h); case LV_IMG_CF_INDEXED_2BIT: return LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h); case LV_IMG_CF_INDEXED_4BIT: return LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h); case LV_IMG_CF_INDEXED_8BIT: return LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h); default: return 0; } } #if LV_DRAW_COMPLEX /** * Initialize a descriptor to transform an image * @param dsc pointer to an `lv_img_transform_dsc_t` variable whose `cfg` field is initialized */ void _lv_img_buf_transform_init(lv_img_transform_dsc_t * dsc) { dsc->tmp.pivot_x_256 = dsc->cfg.pivot_x * 256; dsc->tmp.pivot_y_256 = dsc->cfg.pivot_y * 256; int32_t angle_low = dsc->cfg.angle / 10; int32_t angle_high = angle_low + 1; int32_t angle_rem = dsc->cfg.angle - (angle_low * 10); int32_t s1 = lv_trigo_sin(-angle_low); int32_t s2 = lv_trigo_sin(-angle_high); int32_t c1 = lv_trigo_sin(-angle_low + 90); int32_t c2 = lv_trigo_sin(-angle_high + 90); dsc->tmp.sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10; dsc->tmp.cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10; /*Use smaller value to avoid overflow*/ dsc->tmp.sinma = dsc->tmp.sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT); dsc->tmp.cosma = dsc->tmp.cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT); dsc->tmp.chroma_keyed = lv_img_cf_is_chroma_keyed(dsc->cfg.cf) ? 1 : 0; dsc->tmp.has_alpha = lv_img_cf_has_alpha(dsc->cfg.cf) ? 1 : 0; if(dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR || dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR_ALPHA || dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { dsc->tmp.native_color = 1; } else { dsc->tmp.native_color = 0; } dsc->tmp.img_dsc.data = dsc->cfg.src; dsc->tmp.img_dsc.header.always_zero = 0; dsc->tmp.img_dsc.header.cf = dsc->cfg.cf; dsc->tmp.img_dsc.header.w = dsc->cfg.src_w; dsc->tmp.img_dsc.header.h = dsc->cfg.src_h; /*The inverse of the zoom will be sued during the transformation * + dsc->cfg.zoom / 2 for rounding*/ dsc->tmp.zoom_inv = (((256 * 256) << _LV_ZOOM_INV_UPSCALE) + dsc->cfg.zoom / 2) / dsc->cfg.zoom; dsc->res.opa = LV_OPA_COVER; dsc->res.color = dsc->cfg.color; } #endif /** * Get the area of a rectangle if its rotated and scaled * @param res store the coordinates here * @param w width of the rectangle to transform * @param h height of the rectangle to transform * @param angle angle of rotation * @param zoom zoom, (256 no zoom) * @param pivot x,y pivot coordinates of rotation */ void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t h, int16_t angle, uint16_t zoom, const lv_point_t * pivot) { #if LV_DRAW_COMPLEX if(angle == 0 && zoom == LV_IMG_ZOOM_NONE) { res->x1 = 0; res->y1 = 0; res->x2 = w - 1; res->y2 = h - 1; return; } res->x1 = (((int32_t)(-pivot->x) * zoom) >> 8) - 1; res->y1 = (((int32_t)(-pivot->y) * zoom) >> 8) - 1; res->x2 = (((int32_t)(w - pivot->x) * zoom) >> 8) + 2; res->y2 = (((int32_t)(h - pivot->y) * zoom) >> 8) + 2; if(angle == 0) { res->x1 += pivot->x; res->y1 += pivot->y; res->x2 += pivot->x; res->y2 += pivot->y; return; } int32_t angle_low = angle / 10; int32_t angle_high = angle_low + 1; int32_t angle_rem = angle - (angle_low * 10); int32_t s1 = lv_trigo_sin(angle_low); int32_t s2 = lv_trigo_sin(angle_high); int32_t c1 = lv_trigo_sin(angle_low + 90); int32_t c2 = lv_trigo_sin(angle_high + 90); int32_t sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10; int32_t cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10; /*Use smaller value to avoid overflow*/ sinma = sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT); cosma = cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT); lv_point_t lt; lv_point_t rt; lv_point_t lb; lv_point_t rb; lv_coord_t xt; lv_coord_t yt; xt = res->x1; yt = res->y1; lt.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x; lt.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y; xt = res->x2; yt = res->y1; rt.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x; rt.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y; xt = res->x1; yt = res->y2; lb.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x; lb.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y; xt = res->x2; yt = res->y2; rb.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x; rb.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y; res->x1 = LV_MIN4(lb.x, lt.x, rb.x, rt.x); res->x2 = LV_MAX4(lb.x, lt.x, rb.x, rt.x); res->y1 = LV_MIN4(lb.y, lt.y, rb.y, rt.y); res->y2 = LV_MAX4(lb.y, lt.y, rb.y, rt.y); #else LV_UNUSED(angle); LV_UNUSED(zoom); LV_UNUSED(pivot); res->x1 = 0; res->y1 = 0; res->x2 = w - 1; res->y2 = h - 1; #endif } #if LV_DRAW_COMPLEX /** * 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_transform(lv_img_transform_dsc_t * dsc, lv_coord_t x, lv_coord_t y) { const uint8_t * src_u8 = (const uint8_t *)dsc->cfg.src; /*Get the target point relative coordinates to the pivot*/ int32_t xt = x - dsc->cfg.pivot_x; int32_t yt = y - dsc->cfg.pivot_y; int32_t xs; int32_t ys; if(dsc->cfg.zoom == LV_IMG_ZOOM_NONE) { /*Get the source pixel from the upscaled image*/ xs = ((dsc->tmp.cosma * xt - dsc->tmp.sinma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT - 8)) + dsc->tmp.pivot_x_256; ys = ((dsc->tmp.sinma * xt + dsc->tmp.cosma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT - 8)) + dsc->tmp.pivot_y_256; } else if(dsc->cfg.angle == 0) { xt = (int32_t)((int32_t)xt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE; yt = (int32_t)((int32_t)yt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE; xs = xt + dsc->tmp.pivot_x_256; ys = yt + dsc->tmp.pivot_y_256; } else { xt = (int32_t)((int32_t)xt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE; yt = (int32_t)((int32_t)yt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE; xs = ((dsc->tmp.cosma * xt - dsc->tmp.sinma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT)) + dsc->tmp.pivot_x_256; ys = ((dsc->tmp.sinma * xt + dsc->tmp.cosma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT)) + dsc->tmp.pivot_y_256; } /*Get the integer part of the source pixel*/ int32_t xs_int = xs >> 8; int32_t ys_int = ys >> 8; if(xs_int >= dsc->cfg.src_w) return false; else if(xs_int < 0) return false; if(ys_int >= dsc->cfg.src_h) return false; else if(ys_int < 0) return false; uint8_t px_size; uint32_t pxi; if(dsc->tmp.native_color) { if(dsc->tmp.has_alpha == 0) { px_size = LV_COLOR_SIZE >> 3; pxi = dsc->cfg.src_w * ys_int * px_size + xs_int * px_size; lv_memcpy_small(&dsc->res.color, &src_u8[pxi], px_size); } else { px_size = LV_IMG_PX_SIZE_ALPHA_BYTE; pxi = dsc->cfg.src_w * ys_int * px_size + xs_int * px_size; lv_memcpy_small(&dsc->res.color, &src_u8[pxi], px_size - 1); dsc->res.opa = src_u8[pxi + px_size - 1]; } } else { pxi = 0; /*unused*/ px_size = 0; /*unused*/ dsc->res.color = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, xs_int, ys_int, dsc->cfg.color); dsc->res.opa = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, xs_int, ys_int); } if(dsc->tmp.chroma_keyed) { lv_color_t ct = LV_COLOR_CHROMA_KEY; if(dsc->res.color.full == ct.full) return false; } if(dsc->cfg.antialias == false) return true; dsc->tmp.xs = xs; dsc->tmp.ys = ys; dsc->tmp.xs_int = xs_int; dsc->tmp.ys_int = ys_int; dsc->tmp.pxi = pxi; dsc->tmp.px_size = px_size; bool ret; ret = _lv_img_buf_transform_anti_alias(dsc); return ret; } /** * Continue transformation by taking the neighbors into account * @param dsc pointer to the transformation descriptor */ bool _lv_img_buf_transform_anti_alias(lv_img_transform_dsc_t * dsc) { const uint8_t * src_u8 = dsc->cfg.src; /*Get the fractional part of the source pixel*/ int xs_fract = dsc->tmp.xs & 0xff; int ys_fract = dsc->tmp.ys & 0xff; int32_t xn; /*x neighbor*/ lv_opa_t xr; /*x mix ratio*/ if(xs_fract < 0x70) { xn = - 1; if(dsc->tmp.xs_int + xn < 0) xn = 0; xr = xs_fract + 0x80; } else if(xs_fract > 0x90) { xn = 1; if(dsc->tmp.xs_int + xn >= dsc->cfg.src_w) xn = 0; xr = (0xFF - xs_fract) + 0x80; } else { xn = 0; xr = 0xFF; } int32_t yn; /*x neighbor*/ lv_opa_t yr; /*x mix ratio*/ if(ys_fract < 0x70) { yn = - 1; if(dsc->tmp.ys_int + yn < 0) yn = 0; yr = ys_fract + 0x80; } else if(ys_fract > 0x90) { yn = 1; if(dsc->tmp.ys_int + yn >= dsc->cfg.src_h) yn = 0; yr = (0xFF - ys_fract) + 0x80; } else { yn = 0; yr = 0xFF; } lv_color_t c00 = dsc->res.color; lv_color_t c01; lv_color_t c10; lv_color_t c11; lv_opa_t a00 = dsc->res.opa; lv_opa_t a10 = 0; lv_opa_t a01 = 0; lv_opa_t a11 = 0; if(dsc->tmp.native_color) { lv_memcpy_small(&c01, &src_u8[dsc->tmp.pxi + dsc->tmp.px_size * xn], sizeof(lv_color_t)); lv_memcpy_small(&c10, &src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn], sizeof(lv_color_t)); lv_memcpy_small(&c11, &src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size * xn], sizeof(lv_color_t)); if(dsc->tmp.has_alpha) { a10 = src_u8[dsc->tmp.pxi + dsc->tmp.px_size * xn + dsc->tmp.px_size - 1]; a01 = src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size - 1]; a11 = src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size * xn + dsc->tmp.px_size - 1]; } } else { c01 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int, dsc->cfg.color); c10 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int, dsc->tmp.ys_int + yn, dsc->cfg.color); c11 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int + yn, dsc->cfg.color); if(dsc->tmp.has_alpha) { a10 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int); a01 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int, dsc->tmp.ys_int + yn); a11 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int + yn); } } lv_opa_t xr0 = xr; lv_opa_t xr1 = xr; if(dsc->tmp.has_alpha) { lv_opa_t a0 = (a00 * xr + (a10 * (255 - xr))) >> 8; lv_opa_t a1 = (a01 * xr + (a11 * (255 - xr))) >> 8; dsc->res.opa = (a0 * yr + (a1 * (255 - yr))) >> 8; if(a0 <= LV_OPA_MIN && a1 <= LV_OPA_MIN) return false; if(a0 <= LV_OPA_MIN) yr = LV_OPA_TRANSP; if(a1 <= LV_OPA_MIN) yr = LV_OPA_COVER; if(a00 <= LV_OPA_MIN) xr0 = LV_OPA_TRANSP; if(a10 <= LV_OPA_MIN) xr0 = LV_OPA_COVER; if(a01 <= LV_OPA_MIN) xr1 = LV_OPA_TRANSP; if(a11 <= LV_OPA_MIN) xr1 = LV_OPA_COVER; } else { xr0 = xr; xr1 = xr; dsc->res.opa = LV_OPA_COVER; } lv_color_t c0; if(xr0 == LV_OPA_TRANSP) c0 = c01; else if(xr0 == LV_OPA_COVER) c0 = c00; else c0 = lv_color_mix(c00, c01, xr0); lv_color_t c1; if(xr1 == LV_OPA_TRANSP) c1 = c11; else if(xr1 == LV_OPA_COVER) c1 = c10; else c1 = lv_color_mix(c10, c11, xr1); if(yr == LV_OPA_TRANSP) dsc->res.color = c1; else if(yr == LV_OPA_COVER) dsc->res.color = c0; else dsc->res.color = lv_color_mix(c0, c1, yr); return true; } #endif /********************** * STATIC FUNCTIONS **********************/