/** * @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 || dsc->header.cf == LV_IMG_CF_RGB565A8) { 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: case LV_IMG_CF_RGB565A8: 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; } } /** * 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; } lv_point_t p[4] = { {0, 0}, {w, 0}, {0, h}, {w, h}, }; lv_point_transform(&p[0], angle, zoom, pivot); lv_point_transform(&p[1], angle, zoom, pivot); lv_point_transform(&p[2], angle, zoom, pivot); lv_point_transform(&p[3], angle, zoom, pivot); res->x1 = LV_MIN4(p[0].x, p[1].x, p[2].x, p[3].x) - 2; res->x2 = LV_MAX4(p[0].x, p[1].x, p[2].x, p[3].x) + 2; res->y1 = LV_MIN4(p[0].y, p[1].y, p[2].y, p[3].y) - 2; res->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y) + 2; #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 } /********************** * STATIC FUNCTIONS **********************/