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 
lv_img_buf_get_px_color(const lv_img_dsc_t * dsc,lv_coord_t x,lv_coord_t y,lv_color_t color)41 lv_color_t lv_img_buf_get_px_color(const lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t color)
42 {
43     lv_color_t p_color = lv_color_black();
44     uint8_t * buf_u8 = (uint8_t *)dsc->data;
45 
46     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED ||
47        dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA || dsc->header.cf == LV_IMG_CF_RGB565A8) {
48         uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
49         uint32_t px     = dsc->header.w * y * px_size + x * px_size;
50         lv_memcpy_small(&p_color, &buf_u8[px], sizeof(lv_color_t));
51 #if LV_COLOR_SIZE == 32
52         p_color.ch.alpha = 0xFF; /*Only the color should be get so use a default alpha value*/
53 #endif
54     }
55     else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
56         buf_u8 += 4 * 2;
57         uint8_t bit = x & 0x7;
58         x           = x >> 3;
59 
60         /*Get the current pixel.
61          *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
62          *so the possible real width are 8, 16, 24 ...*/
63         uint32_t px  = ((dsc->header.w + 7) >> 3) * y + x;
64         p_color.full = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
65     }
66     else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
67         buf_u8 += 4 * 4;
68         uint8_t bit = (x & 0x3) * 2;
69         x           = x >> 2;
70 
71         /*Get the current pixel.
72          *dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
73          *so the possible real width are 4, 8, 12 ...*/
74         uint32_t px  = ((dsc->header.w + 3) >> 2) * y + x;
75         p_color.full = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
76     }
77     else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
78         buf_u8 += 4 * 16;
79         uint8_t bit = (x & 0x1) * 4;
80         x           = x >> 1;
81 
82         /*Get the current pixel.
83          *dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
84          *so the possible real width are 2, 4, 6 ...*/
85         uint32_t px  = ((dsc->header.w + 1) >> 1) * y + x;
86         p_color.full = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
87     }
88     else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
89         buf_u8 += 4 * 256;
90         uint32_t px  = dsc->header.w * y + x;
91         p_color.full = buf_u8[px];
92     }
93     else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
94             dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
95         p_color = color;
96     }
97     return p_color;
98 }
99 
lv_img_buf_get_px_alpha(const lv_img_dsc_t * dsc,lv_coord_t x,lv_coord_t y)100 lv_opa_t lv_img_buf_get_px_alpha(const lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y)
101 {
102     uint8_t * buf_u8 = (uint8_t *)dsc->data;
103 
104     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
105         uint32_t px = dsc->header.w * y * LV_IMG_PX_SIZE_ALPHA_BYTE + x * LV_IMG_PX_SIZE_ALPHA_BYTE;
106         return buf_u8[px + LV_IMG_PX_SIZE_ALPHA_BYTE - 1];
107     }
108     else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
109         uint8_t bit = x & 0x7;
110         x           = x >> 3;
111 
112         /*Get the current pixel.
113          *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
114          *so the possible real width are 8 ,16, 24 ...*/
115         uint32_t px    = ((dsc->header.w + 7) >> 3) * y + x;
116         uint8_t px_opa = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
117         return px_opa ? LV_OPA_TRANSP : LV_OPA_COVER;
118     }
119     else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
120         const uint8_t opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
121 
122         uint8_t bit = (x & 0x3) * 2;
123         x           = x >> 2;
124 
125         /*Get the current pixel.
126          *dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
127          *so the possible real width are 4 ,8, 12 ...*/
128         uint32_t px    = ((dsc->header.w + 3) >> 2) * y + x;
129         uint8_t px_opa = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
130         return opa_table[px_opa];
131     }
132     else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
133         const uint8_t opa_table[16] = {0,  17, 34,  51, /*Opacity mapping with bpp = 4*/
134                                        68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
135                                       };
136 
137         uint8_t bit = (x & 0x1) * 4;
138         x           = x >> 1;
139 
140         /*Get the current pixel.
141          *dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
142          *so the possible real width are 2 ,4, 6 ...*/
143         uint32_t px    = ((dsc->header.w + 1) >> 1) * y + x;
144         uint8_t px_opa = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
145         return opa_table[px_opa];
146     }
147     else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
148         uint32_t px = dsc->header.w * y + x;
149         return buf_u8[px];
150     }
151 
152     return LV_OPA_COVER;
153 }
154 
lv_img_buf_set_px_alpha(const lv_img_dsc_t * dsc,lv_coord_t x,lv_coord_t y,lv_opa_t opa)155 void lv_img_buf_set_px_alpha(const lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_opa_t opa)
156 {
157     uint8_t * buf_u8 = (uint8_t *)dsc->data;
158 
159     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
160         uint8_t px_size          = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
161         uint32_t px              = dsc->header.w * y * px_size + x * px_size;
162         buf_u8[px + px_size - 1] = opa;
163     }
164     else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
165         opa         = opa >> 7; /*opa -> [0,1]*/
166         uint8_t bit = x & 0x7;
167         x           = x >> 3;
168 
169         /*Get the current pixel.
170          *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
171          *so the possible real width are 8 ,16, 24 ...*/
172         uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
173         buf_u8[px]  = buf_u8[px] & ~(1 << (7 - bit));
174         buf_u8[px]  = buf_u8[px] | ((opa & 0x1) << (7 - bit));
175     }
176     else if(dsc->header.cf == LV_IMG_CF_ALPHA_2BIT) {
177         opa         = opa >> 6; /*opa -> [0,3]*/
178         uint8_t bit = (x & 0x3) * 2;
179         x           = x >> 2;
180 
181         /*Get the current pixel.
182          *dsc->header.w + 4 means rounding up to 8 because the lines are byte aligned
183          *so the possible real width are 4 ,8, 12 ...*/
184         uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
185         buf_u8[px]  = buf_u8[px] & ~(3 << (6 - bit));
186         buf_u8[px]  = buf_u8[px] | ((opa & 0x3) << (6 - bit));
187     }
188     else if(dsc->header.cf == LV_IMG_CF_ALPHA_4BIT) {
189         opa         = opa >> 4; /*opa -> [0,15]*/
190         uint8_t bit = (x & 0x1) * 4;
191         x           = x >> 1;
192 
193         /*Get the current pixel.
194          *dsc->header.w + 1 means rounding up to 8 because the lines are byte aligned
195          *so the possible real width are 2 ,4, 6 ...*/
196         uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
197         buf_u8[px]  = buf_u8[px] & ~(0xF << (4 - bit));
198         buf_u8[px]  = buf_u8[px] | ((opa & 0xF) << (4 - bit));
199     }
200     else if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
201         uint32_t px = dsc->header.w * y + x;
202         buf_u8[px]  = opa;
203     }
204 }
205 
lv_img_buf_set_px_color(const lv_img_dsc_t * dsc,lv_coord_t x,lv_coord_t y,lv_color_t c)206 void lv_img_buf_set_px_color(const lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_color_t c)
207 {
208     uint8_t * buf_u8 = (uint8_t *)dsc->data;
209 
210     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
211         uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
212         uint32_t px     = dsc->header.w * y * px_size + x * px_size;
213         lv_memcpy_small(&buf_u8[px], &c, px_size);
214     }
215     else if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
216         uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3;
217         uint32_t px     = dsc->header.w * y * px_size + x * px_size;
218         lv_memcpy_small(&buf_u8[px], &c, px_size - 1); /*-1 to not overwrite the alpha value*/
219     }
220     else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
221         buf_u8 += sizeof(lv_color32_t) * 2; /*Skip the palette*/
222 
223         uint8_t bit = x & 0x7;
224         x           = x >> 3;
225 
226         /*Get the current pixel.
227          *dsc->header.w + 7 means rounding up to 8 because the lines are byte aligned
228          *so the possible real width are 8 ,16, 24 ...*/
229         uint32_t px = ((dsc->header.w + 7) >> 3) * y + x;
230         buf_u8[px]  = buf_u8[px] & ~(1 << (7 - bit));
231         buf_u8[px]  = buf_u8[px] | ((c.full & 0x1) << (7 - bit));
232     }
233     else if(dsc->header.cf == LV_IMG_CF_INDEXED_2BIT) {
234         buf_u8 += sizeof(lv_color32_t) * 4; /*Skip the palette*/
235         uint8_t bit = (x & 0x3) * 2;
236         x           = x >> 2;
237 
238         /*Get the current pixel.
239          *dsc->header.w + 3 means rounding up to 4 because the lines are byte aligned
240          *so the possible real width are 4, 8 ,12 ...*/
241         uint32_t px = ((dsc->header.w + 3) >> 2) * y + x;
242 
243         buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
244         buf_u8[px] = buf_u8[px] | ((c.full & 0x3) << (6 - bit));
245     }
246     else if(dsc->header.cf == LV_IMG_CF_INDEXED_4BIT) {
247         buf_u8 += sizeof(lv_color32_t) * 16; /*Skip the palette*/
248         uint8_t bit = (x & 0x1) * 4;
249         x           = x >> 1;
250 
251         /*Get the current pixel.
252          *dsc->header.w + 1 means rounding up to 2 because the lines are byte aligned
253          *so the possible real width are 2 ,4, 6 ...*/
254         uint32_t px = ((dsc->header.w + 1) >> 1) * y + x;
255         buf_u8[px]  = buf_u8[px] & ~(0xF << (4 - bit));
256         buf_u8[px]  = buf_u8[px] | ((c.full & 0xF) << (4 - bit));
257     }
258     else if(dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
259         buf_u8 += sizeof(lv_color32_t) * 256; /*Skip the palette*/
260         uint32_t px = dsc->header.w * y + x;
261         buf_u8[px]  = c.full;
262     }
263 }
264 
lv_img_buf_set_palette(const lv_img_dsc_t * dsc,uint8_t id,lv_color_t c)265 void lv_img_buf_set_palette(const lv_img_dsc_t * dsc, uint8_t id, lv_color_t c)
266 {
267     if((dsc->header.cf == LV_IMG_CF_ALPHA_1BIT && id > 1) || (dsc->header.cf == LV_IMG_CF_ALPHA_2BIT && id > 3) ||
268        (dsc->header.cf == LV_IMG_CF_ALPHA_4BIT && id > 15) || (dsc->header.cf == LV_IMG_CF_ALPHA_8BIT)) {
269         LV_LOG_WARN("lv_img_buf_set_px_alpha: invalid 'id'");
270         return;
271     }
272 
273     lv_color32_t c32;
274     c32.full      = lv_color_to32(c);
275     uint8_t * buf = (uint8_t *)dsc->data;
276     lv_memcpy_small(&buf[id * sizeof(c32)], &c32, sizeof(c32));
277 }
278 
lv_img_buf_alloc(lv_coord_t w,lv_coord_t h,lv_img_cf_t cf)279 lv_img_dsc_t * lv_img_buf_alloc(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
280 {
281     /*Allocate image descriptor*/
282     lv_img_dsc_t * dsc = lv_mem_alloc(sizeof(lv_img_dsc_t));
283     if(dsc == NULL)
284         return NULL;
285 
286     lv_memset_00(dsc, sizeof(lv_img_dsc_t));
287 
288     /*Get image data size*/
289     dsc->data_size = lv_img_buf_get_img_size(w, h, cf);
290     if(dsc->data_size == 0) {
291         lv_mem_free(dsc);
292         return NULL;
293     }
294 
295     /*Allocate raw buffer*/
296     dsc->data = lv_mem_alloc(dsc->data_size);
297     if(dsc->data == NULL) {
298         lv_mem_free(dsc);
299         return NULL;
300     }
301     lv_memset_00((uint8_t *)dsc->data, dsc->data_size);
302 
303     /*Fill in header*/
304     dsc->header.always_zero = 0;
305     dsc->header.w = w;
306     dsc->header.h = h;
307     dsc->header.cf = cf;
308     return dsc;
309 }
310 
lv_img_buf_free(lv_img_dsc_t * dsc)311 void lv_img_buf_free(lv_img_dsc_t * dsc)
312 {
313     if(dsc != NULL) {
314         if(dsc->data != NULL)
315             lv_mem_free((void *)dsc->data);
316 
317         lv_mem_free(dsc);
318     }
319 }
320 
lv_img_buf_get_img_size(lv_coord_t w,lv_coord_t h,lv_img_cf_t cf)321 uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
322 {
323     switch(cf) {
324         case LV_IMG_CF_TRUE_COLOR:
325             return LV_IMG_BUF_SIZE_TRUE_COLOR(w, h);
326         case LV_IMG_CF_TRUE_COLOR_ALPHA:
327         case LV_IMG_CF_RGB565A8:
328             return LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h);
329         case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
330             return LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h);
331         case LV_IMG_CF_ALPHA_1BIT:
332             return LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h);
333         case LV_IMG_CF_ALPHA_2BIT:
334             return LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h);
335         case LV_IMG_CF_ALPHA_4BIT:
336             return LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h);
337         case LV_IMG_CF_ALPHA_8BIT:
338             return LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h);
339         case LV_IMG_CF_INDEXED_1BIT:
340             return LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h);
341         case LV_IMG_CF_INDEXED_2BIT:
342             return LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h);
343         case LV_IMG_CF_INDEXED_4BIT:
344             return LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h);
345         case LV_IMG_CF_INDEXED_8BIT:
346             return LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h);
347         default:
348             return 0;
349     }
350 }
351 
_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)352 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,
353                                       const lv_point_t * pivot)
354 {
355 #if LV_DRAW_COMPLEX
356     if(angle == 0 && zoom == LV_IMG_ZOOM_NONE) {
357         res->x1 = 0;
358         res->y1 = 0;
359         res->x2 = w - 1;
360         res->y2 = h - 1;
361         return;
362     }
363 
364     lv_point_t p[4] = {
365         {0, 0},
366         {w, 0},
367         {0, h},
368         {w, h},
369     };
370     lv_point_transform(&p[0], angle, zoom, pivot);
371     lv_point_transform(&p[1], angle, zoom, pivot);
372     lv_point_transform(&p[2], angle, zoom, pivot);
373     lv_point_transform(&p[3], angle, zoom, pivot);
374     res->x1 = LV_MIN4(p[0].x, p[1].x, p[2].x, p[3].x) - 2;
375     res->x2 = LV_MAX4(p[0].x, p[1].x, p[2].x, p[3].x) + 2;
376     res->y1 = LV_MIN4(p[0].y, p[1].y, p[2].y, p[3].y) - 2;
377     res->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y) + 2;
378 
379 #else
380     LV_UNUSED(angle);
381     LV_UNUSED(zoom);
382     LV_UNUSED(pivot);
383     res->x1 = 0;
384     res->y1 = 0;
385     res->x2 = w - 1;
386     res->y2 = h - 1;
387 #endif
388 }
389 
390 /**********************
391  *   STATIC FUNCTIONS
392  **********************/
393