1 /**
2  * @file lv_img_buf.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include <stddef.h>
10 #include <string.h>
11 #include "lv_img_buf.h"
12 #include "lv_draw_img.h"
13 #include "../misc/lv_math.h"
14 #include "../misc/lv_log.h"
15 #include "../misc/lv_mem.h"
16 
17 /*********************
18  *      DEFINES
19  *********************/
20 
21 /**********************
22  *      TYPEDEFS
23  **********************/
24 
25 /**********************
26  *  STATIC PROTOTYPES
27  **********************/
28 
29 /**********************
30  *  STATIC VARIABLES
31  **********************/
32 
33 /**********************
34  *      MACROS
35  **********************/
36 
37 /**********************
38  *   GLOBAL FUNCTIONS
39  **********************/
40 
41 /**
42  * Get the color of an image's pixel
43  * @param dsc an image descriptor
44  * @param x x coordinate of the point to get
45  * @param y x coordinate of the point to get
46  * @param color the color of the image. In case of `LV_IMG_CF_ALPHA_1/2/4/8` this color is used.
47  * Not used in other cases.
48  * @param safe true: check out of bounds
49  * @return color of the point
50  */
lv_img_buf_get_px_color(lv_img_dsc_t * dsc,lv_coord_t x,lv_coord_t y,lv_color_t color)51 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)
52 {
53     lv_color_t p_color = lv_color_black();
54     uint8_t * buf_u8 = (uint8_t *)dsc->data;
55 
56     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED ||
57        dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA || dsc->header.cf == LV_IMG_CF_RGB565A8) {
58         uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
59         uint32_t px     = dsc->header.w * y * px_size + x * px_size;
60         lv_memcpy_small(&p_color, &buf_u8[px], sizeof(lv_color_t));
61 #if LV_COLOR_SIZE == 32
62         p_color.ch.alpha = 0xFF; /*Only the color should be get so use a default alpha value*/
63 #endif
64     }
65     else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
66         buf_u8 += 4 * 2;
67         uint8_t bit = x & 0x7;
68         x           = x >> 3;
69 
70         /*Get the current pixel.
71          *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
72          *so the possible real width are 8, 16, 24 ...*/
73         uint32_t px  = ((dsc->header.w + 7) >> 3) * y + x;
74         p_color.full = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
75     }
76     else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
77         buf_u8 += 4 * 4;
78         uint8_t bit = (x & 0x3) * 2;
79         x           = x >> 2;
80 
81         /*Get the current pixel.
82          *dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
83          *so the possible real width are 4, 8, 12 ...*/
84         uint32_t px  = ((dsc->header.w + 3) >> 2) * y + x;
85         p_color.full = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
86     }
87     else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
88         buf_u8 += 4 * 16;
89         uint8_t bit = (x & 0x1) * 4;
90         x           = x >> 1;
91 
92         /*Get the current pixel.
93          *dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
94          *so the possible real width are 2, 4, 6 ...*/
95         uint32_t px  = ((dsc->header.w + 1) >> 1) * y + x;
96         p_color.full = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
97     }
98     else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
99         buf_u8 += 4 * 256;
100         uint32_t px  = dsc->header.w * y + x;
101         p_color.full = buf_u8[px];
102     }
103     else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
104             dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
105         p_color = color;
106     }
107     return p_color;
108 }
109 
110 /**
111  * Get the alpha value of an image's pixel
112  * @param dsc pointer to an image descriptor
113  * @param x x coordinate of the point to set
114  * @param y x coordinate of the point to set
115  * @param safe true: check out of bounds
116  * @return alpha value of the point
117  */
lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc,lv_coord_t x,lv_coord_t y)118 lv_opa_t lv_img_buf_get_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y)
119 {
120     uint8_t * buf_u8 = (uint8_t *)dsc->data;
121 
122     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
123         uint32_t px = dsc->header.w * y * LV_IMG_PX_SIZE_ALPHA_BYTE + x * LV_IMG_PX_SIZE_ALPHA_BYTE;
124         return buf_u8[px + LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
125     }
126     else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
127         uint8_t bit = x & 0x7;
128         x           = x >> 3;
129 
130         /*Get the current pixel.
131          *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
132          *so the possible real width are 8 ,16, 24 ...*/
133         uint32_t px    = ((dsc->header.w + 7) >> 3) * y + x;
134         uint8_t px_opa = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
135         return px_opa ? LV_OPA_TRANSP : LV_OPA_COVER;
136     }
137     else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
138         const uint8_t opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
139 
140         uint8_t bit = (x & 0x3) * 2;
141         x           = x >> 2;
142 
143         /*Get the current pixel.
144          *dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
145          *so the possible real width are 4 ,8, 12 ...*/
146         uint32_t px    = ((dsc->header.w + 3) >> 2) * y + x;
147         uint8_t px_opa = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
148         return opa_table[px_opa];
149     }
150     else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
151         const uint8_t opa_table[16] = {0,  17, 34,  51, /*Opacity mapping with bpp = 4*/
152                                        68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
153                                       };
154 
155         uint8_t bit = (x & 0x1) * 4;
156         x           = x >> 1;
157 
158         /*Get the current pixel.
159          *dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
160          *so the possible real width are 2 ,4, 6 ...*/
161         uint32_t px    = ((dsc->header.w + 1) >> 1) * y + x;
162         uint8_t px_opa = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
163         return opa_table[px_opa];
164     }
165     else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
166         uint32_t px = dsc->header.w * y + x;
167         return buf_u8[px];
168     }
169 
170     return LV_OPA_COVER;
171 }
172 
173 /**
174  * Set the alpha value of a pixel of an image. The color won't be affected
175  * @param dsc pointer to an image descriptor
176  * @param x x coordinate of the point to set
177  * @param y x coordinate of the point to set
178  * @param opa the desired opacity
179  * @param safe true: check out of bounds
180  */
lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc,lv_coord_t x,lv_coord_t y,lv_opa_t opa)181 void lv_img_buf_set_px_alpha(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa)
182 {
183     uint8_t * buf_u8 = (uint8_t *)dsc->data;
184 
185     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
186         uint8_t px_size          = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
187         uint32_t px              = dsc->header.w * y * px_size + x * px_size;
188         buf_u8[px + px_size - 1] = opa;
189     }
190     else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
191         opa         = opa >> 7; /*opa -> [0,1]*/
192         uint8_t bit = x & 0x7;
193         x           = x >> 3;
194 
195         /*Get the current pixel.
196          *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
197          *so the possible real width are 8 ,16, 24 ...*/
198         uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
199         buf_u8[px]  = buf_u8[px] & ~(1 << (7 - bit));
200         buf_u8[px]  = buf_u8[px] | ((opa & 0x1) << (7 - bit));
201     }
202     else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
203         opa         = opa >> 6; /*opa -> [0,3]*/
204         uint8_t bit = (x & 0x3) * 2;
205         x           = x >> 2;
206 
207         /*Get the current pixel.
208          *dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
209          *so the possible real width are 4 ,8, 12 ...*/
210         uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
211         buf_u8[px]  = buf_u8[px] & ~(3 << (6 - bit));
212         buf_u8[px]  = buf_u8[px] | ((opa & 0x3) << (6 - bit));
213     }
214     else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
215         opa         = opa >> 4; /*opa -> [0,15]*/
216         uint8_t bit = (x & 0x1) * 4;
217         x           = x >> 1;
218 
219         /*Get the current pixel.
220          *dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
221          *so the possible real width are 2 ,4, 6 ...*/
222         uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
223         buf_u8[px]  = buf_u8[px] & ~(0xF << (4 - bit));
224         buf_u8[px]  = buf_u8[px] | ((opa & 0xF) << (4 - bit));
225     }
226     else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
227         uint32_t px = dsc->header.w * y + x;
228         buf_u8[px]  = opa;
229     }
230 }
231 
232 /**
233  * Set the color of a pixel of an image. The alpha channel won't be affected.
234  * @param dsc pointer to an image descriptor
235  * @param x x coordinate of the point to set
236  * @param y x coordinate of the point to set
237  * @param c color of the point
238  * @param safe true: check out of bounds
239  */
lv_img_buf_set_px_color(lv_img_dsc_t * dsc,lv_coord_t x,lv_coord_t y,lv_color_t c)240 void lv_img_buf_set_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c)
241 {
242     uint8_t * buf_u8 = (uint8_t *)dsc->data;
243 
244     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
245         uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
246         uint32_t px     = dsc->header.w * y * px_size + x * px_size;
247         lv_memcpy_small(&buf_u8[px], &c, px_size);
248     }
249     else if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
250         uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
251         uint32_t px     = dsc->header.w * y * px_size + x * px_size;
252         lv_memcpy_small(&buf_u8[px], &c, px_size - 1); /*-1 to not overwrite the alpha value*/
253     }
254     else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
255         buf_u8 += sizeof(lv_color32_t) * 2; /*Skip the palette*/
256 
257         uint8_t bit = x & 0x7;
258         x           = x >> 3;
259 
260         /*Get the current pixel.
261          *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
262          *so the possible real width are 8 ,16, 24 ...*/
263         uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
264         buf_u8[px]  = buf_u8[px] & ~(1 << (7 - bit));
265         buf_u8[px]  = buf_u8[px] | ((c.full & 0x1) << (7 - bit));
266     }
267     else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
268         buf_u8 += sizeof(lv_color32_t) * 4; /*Skip the palette*/
269         uint8_t bit = (x & 0x3) * 2;
270         x           = x >> 2;
271 
272         /*Get the current pixel.
273          *dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
274          *so the possible real width are 4, 8 ,12 ...*/
275         uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
276 
277         buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
278         buf_u8[px] = buf_u8[px] | ((c.full & 0x3) << (6 - bit));
279     }
280     else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
281         buf_u8 += sizeof(lv_color32_t) * 16; /*Skip the palette*/
282         uint8_t bit = (x & 0x1) * 4;
283         x           = x >> 1;
284 
285         /*Get the current pixel.
286          *dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
287          *so the possible real width are 2 ,4, 6 ...*/
288         uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
289         buf_u8[px]  = buf_u8[px] & ~(0xF << (4 - bit));
290         buf_u8[px]  = buf_u8[px] | ((c.full & 0xF) << (4 - bit));
291     }
292     else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
293         buf_u8 += sizeof(lv_color32_t) * 256; /*Skip the palette*/
294         uint32_t px = dsc->header.w * y + x;
295         buf_u8[px]  = c.full;
296     }
297 }
298 
299 /**
300  * Set the palette color of an indexed image. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
301  * @param dsc pointer to an image descriptor
302  * @param id the palette color to set:
303  *   - for `LV_IMG_CF_INDEXED1`: 0..1
304  *   - for `LV_IMG_CF_INDEXED2`: 0..3
305  *   - for `LV_IMG_CF_INDEXED4`: 0..15
306  *   - for `LV_IMG_CF_INDEXED8`: 0..255
307  * @param c the color to set
308  */
lv_img_buf_set_palette(lv_img_dsc_t * dsc,uint8_t id,lv_color_t c)309 void lv_img_buf_set_palette(lv_img_dsc_t * dsc, uint8_t id, lv_color_t c)
310 {
311     if((dsc->header.cf == LV_IMG_CF_ALPHA_1BIT && id > 1) || (dsc->header.cf == LV_IMG_CF_ALPHA_2BIT && id > 3) ||
312        (dsc->header.cf == LV_IMG_CF_ALPHA_4BIT && id > 15) || (dsc->header.cf == LV_IMG_CF_ALPHA_8BIT)) {
313         LV_LOG_WARN("lv_img_buf_set_px_alpha: invalid 'id'");
314         return;
315     }
316 
317     lv_color32_t c32;
318     c32.full      = lv_color_to32(c);
319     uint8_t * buf = (uint8_t *)dsc->data;
320     lv_memcpy_small(&buf[id * sizeof(c32)], &c32, sizeof(c32));
321 }
322 
323 /**
324  * Allocate an image buffer in RAM
325  * @param w width of image
326  * @param h height of image
327  * @param cf a color format (`LV_IMG_CF_...`)
328  * @return an allocated image, or NULL on failure
329  */
lv_img_buf_alloc(lv_coord_t w,lv_coord_t h,lv_img_cf_t cf)330 lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
331 {
332     /*Allocate image descriptor*/
333     lv_img_dsc_t * dsc = lv_mem_alloc(sizeof(lv_img_dsc_t));
334     if(dsc == NULL)
335         return NULL;
336 
337     lv_memset_00(dsc, sizeof(lv_img_dsc_t));
338 
339     /*Get image data size*/
340     dsc->data_size = lv_img_buf_get_img_size(w, h, cf);
341     if(dsc->data_size == 0) {
342         lv_mem_free(dsc);
343         return NULL;
344     }
345 
346     /*Allocate raw buffer*/
347     dsc->data = lv_mem_alloc(dsc->data_size);
348     if(dsc->data == NULL) {
349         lv_mem_free(dsc);
350         return NULL;
351     }
352     lv_memset_00((uint8_t *)dsc->data, dsc->data_size);
353 
354     /*Fill in header*/
355     dsc->header.always_zero = 0;
356     dsc->header.w = w;
357     dsc->header.h = h;
358     dsc->header.cf = cf;
359     return dsc;
360 }
361 
362 /**
363  * Free an allocated image buffer
364  * @param dsc image buffer to free
365  */
lv_img_buf_free(lv_img_dsc_t * dsc)366 void lv_img_buf_free(lv_img_dsc_t * dsc)
367 {
368     if(dsc != NULL) {
369         if(dsc->data != NULL)
370             lv_mem_free((void *)dsc->data);
371 
372         lv_mem_free(dsc);
373     }
374 }
375 
376 /**
377  * Get the memory consumption of a raw bitmap, given color format and dimensions.
378  * @param w width
379  * @param h height
380  * @param cf color format
381  * @return size in bytes
382  */
lv_img_buf_get_img_size(lv_coord_t w,lv_coord_t h,lv_img_cf_t cf)383 uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
384 {
385     switch(cf) {
386         case LV_IMG_CF_TRUE_COLOR:
387             return LV_IMG_BUF_SIZE_TRUE_COLOR(w, h);
388         case LV_IMG_CF_TRUE_COLOR_ALPHA:
389         case LV_IMG_CF_RGB565A8:
390             return LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h);
391         case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
392             return LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h);
393         case LV_IMG_CF_ALPHA_1BIT:
394             return LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h);
395         case LV_IMG_CF_ALPHA_2BIT:
396             return LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h);
397         case LV_IMG_CF_ALPHA_4BIT:
398             return LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h);
399         case LV_IMG_CF_ALPHA_8BIT:
400             return LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h);
401         case LV_IMG_CF_INDEXED_1BIT:
402             return LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h);
403         case LV_IMG_CF_INDEXED_2BIT:
404             return LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h);
405         case LV_IMG_CF_INDEXED_4BIT:
406             return LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h);
407         case LV_IMG_CF_INDEXED_8BIT:
408             return LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h);
409         default:
410             return 0;
411     }
412 }
413 
414 /**
415  * Get the area of a rectangle if its rotated and scaled
416  * @param res store the coordinates here
417  * @param w width of the rectangle to transform
418  * @param h height of the rectangle to transform
419  * @param angle angle of rotation
420  * @param zoom zoom, (256 no zoom)
421  * @param pivot x,y pivot coordinates of rotation
422  */
_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)423 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,
424                                       const lv_point_t * pivot)
425 {
426 #if LV_DRAW_COMPLEX
427     if(angle == 0 && zoom == LV_IMG_ZOOM_NONE) {
428         res->x1 = 0;
429         res->y1 = 0;
430         res->x2 = w - 1;
431         res->y2 = h - 1;
432         return;
433     }
434 
435     lv_point_t p[4] = {
436         {0, 0},
437         {w, 0},
438         {0, h},
439         {w, h},
440     };
441     lv_point_transform(&p[0], angle, zoom, pivot);
442     lv_point_transform(&p[1], angle, zoom, pivot);
443     lv_point_transform(&p[2], angle, zoom, pivot);
444     lv_point_transform(&p[3], angle, zoom, pivot);
445     res->x1 = LV_MIN4(p[0].x, p[1].x, p[2].x, p[3].x) - 2;
446     res->x2 = LV_MAX4(p[0].x, p[1].x, p[2].x, p[3].x) + 2;
447     res->y1 = LV_MIN4(p[0].y, p[1].y, p[2].y, p[3].y) - 2;
448     res->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y) + 2;
449 
450 #else
451     LV_UNUSED(angle);
452     LV_UNUSED(zoom);
453     LV_UNUSED(pivot);
454     res->x1 = 0;
455     res->y1 = 0;
456     res->x2 = w - 1;
457     res->y2 = h - 1;
458 #endif
459 }
460 
461 /**********************
462  *   STATIC FUNCTIONS
463  **********************/
464