1 /**
2  * @file lv_bmp.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../../lvgl.h"
10 #if LV_USE_BMP
11 
12 #include <string.h>
13 
14 /*********************
15  *      DEFINES
16  *********************/
17 
18 /**********************
19  *      TYPEDEFS
20  **********************/
21 
22 typedef struct {
23     lv_fs_file_t f;
24     unsigned int px_offset;
25     int px_width;
26     int px_height;
27     unsigned int bpp;
28     int row_size_bytes;
29 } bmp_dsc_t;
30 
31 /**********************
32  *  STATIC PROTOTYPES
33  **********************/
34 static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
35 static lv_res_t decoder_open(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
36 
37 static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc,
38                                   lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
39 
40 static void decoder_close(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
41 
42 /**********************
43  *  STATIC VARIABLES
44  **********************/
45 
46 /**********************
47  *      MACROS
48  **********************/
49 
50 /**********************
51  *   GLOBAL FUNCTIONS
52  **********************/
lv_bmp_init(void)53 void lv_bmp_init(void)
54 {
55     lv_img_decoder_t * dec = lv_img_decoder_create();
56     lv_img_decoder_set_info_cb(dec, decoder_info);
57     lv_img_decoder_set_open_cb(dec, decoder_open);
58     lv_img_decoder_set_read_line_cb(dec, decoder_read_line);
59     lv_img_decoder_set_close_cb(dec, decoder_close);
60 }
61 
62 /**********************
63  *   STATIC FUNCTIONS
64  **********************/
65 
66 /**
67  * Get info about a PNG image
68  * @param src can be file name or pointer to a C array
69  * @param header store the info here
70  * @return LV_RES_OK: no error; LV_RES_INV: can't get the info
71  */
decoder_info(lv_img_decoder_t * decoder,const void * src,lv_img_header_t * header)72 static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
73 {
74     LV_UNUSED(decoder);
75 
76     lv_img_src_t src_type = lv_img_src_get_type(src);          /*Get the source type*/
77 
78     /*If it's a BMP file...*/
79     if(src_type == LV_IMG_SRC_FILE) {
80         const char * fn = src;
81         if(strcmp(lv_fs_get_ext(fn), "bmp") == 0) {              /*Check the extension*/
82             /*Save the data in the header*/
83             lv_fs_file_t f;
84             lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD);
85             if(res != LV_FS_RES_OK) return LV_RES_INV;
86             uint8_t headers[54];
87 
88             lv_fs_read(&f, headers, 54, NULL);
89             uint32_t w;
90             uint32_t h;
91             memcpy(&w, headers + 18, 4);
92             memcpy(&h, headers + 22, 4);
93             header->w = w;
94             header->h = h;
95             header->always_zero = 0;
96             lv_fs_close(&f);
97 #if LV_COLOR_DEPTH == 32
98             uint16_t bpp;
99             memcpy(&bpp, headers + 28, 2);
100             header->cf = bpp == 32 ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR;
101 #else
102             header->cf = LV_IMG_CF_TRUE_COLOR;
103 #endif
104             return LV_RES_OK;
105         }
106     }
107     /* BMP file as data not supported for simplicity.
108      * Convert them to LVGL compatible C arrays directly. */
109     else if(src_type == LV_IMG_SRC_VARIABLE) {
110         return LV_RES_INV;
111     }
112 
113     return LV_RES_INV;         /*If didn't succeeded earlier then it's an error*/
114 }
115 
116 /**
117  * Open a PNG image and return the decided image
118  * @param src can be file name or pointer to a C array
119  * @param style style of the image object (unused now but certain formats might use it)
120  * @return pointer to the decoded image or `LV_IMG_DECODER_OPEN_FAIL` if failed
121  */
decoder_open(lv_img_decoder_t * decoder,lv_img_decoder_dsc_t * dsc)122 static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
123 {
124     LV_UNUSED(decoder);
125 
126     /*If it's a PNG file...*/
127     if(dsc->src_type == LV_IMG_SRC_FILE) {
128         const char * fn = dsc->src;
129 
130         if(strcmp(lv_fs_get_ext(fn), "bmp") != 0) {
131             return LV_RES_INV;       /*Check the extension*/
132         }
133 
134         bmp_dsc_t b;
135         memset(&b, 0x00, sizeof(b));
136 
137         lv_fs_res_t res = lv_fs_open(&b.f, dsc->src, LV_FS_MODE_RD);
138         if(res == LV_RES_OK) return LV_RES_INV;
139 
140         uint8_t header[54];
141         lv_fs_read(&b.f, header, 54, NULL);
142 
143         if(0x42 != header[0] || 0x4d != header[1]) {
144             lv_fs_close(&b.f);
145             return LV_RES_INV;
146         }
147 
148         memcpy(&b.px_offset, header + 10, 4);
149         memcpy(&b.px_width, header + 18, 4);
150         memcpy(&b.px_height, header + 22, 4);
151         memcpy(&b.bpp, header + 28, 2);
152         b.row_size_bytes = ((b.bpp * b.px_width + 31) / 32) * 4;
153 
154         bool color_depth_error = false;
155         if(LV_COLOR_DEPTH == 32 && (b.bpp != 32 && b.bpp != 24)) {
156             LV_LOG_WARN("LV_COLOR_DEPTH == 32 but bpp is %d (should be 32 or 24)", b.bpp);
157             color_depth_error = true;
158         }
159         else if(LV_COLOR_DEPTH == 16 && b.bpp != 16) {
160             LV_LOG_WARN("LV_COLOR_DEPTH == 16 but bpp is %d (should be 16)", b.bpp);
161             color_depth_error = true;
162         }
163         else if(LV_COLOR_DEPTH == 8 && b.bpp != 8) {
164             LV_LOG_WARN("LV_COLOR_DEPTH == 8 but bpp is %d (should be 8)", b.bpp);
165             color_depth_error = true;
166         }
167 
168         if(color_depth_error) {
169             dsc->error_msg = "Color depth mismatch";
170             lv_fs_close(&b.f);
171             return LV_RES_INV;
172         }
173 
174         dsc->user_data = lv_mem_alloc(sizeof(bmp_dsc_t));
175         LV_ASSERT_MALLOC(dsc->user_data);
176         if(dsc->user_data == NULL) return LV_RES_INV;
177         memcpy(dsc->user_data, &b, sizeof(b));
178 
179         dsc->img_data = NULL;
180         return LV_RES_OK;
181     }
182     /* BMP file as data not supported for simplicity.
183      * Convert them to LVGL compatible C arrays directly. */
184     else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
185         return LV_RES_INV;
186     }
187 
188     return LV_RES_INV;    /*If not returned earlier then it failed*/
189 }
190 
decoder_read_line(lv_img_decoder_t * decoder,lv_img_decoder_dsc_t * dsc,lv_coord_t x,lv_coord_t y,lv_coord_t len,uint8_t * buf)191 static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc,
192                                   lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
193 {
194     LV_UNUSED(decoder);
195 
196     bmp_dsc_t * b = dsc->user_data;
197     y = (b->px_height - 1) - y; /*BMP images are stored upside down*/
198     uint32_t p = b->px_offset + b->row_size_bytes * y;
199     p += x * (b->bpp / 8);
200     lv_fs_seek(&b->f, p, LV_FS_SEEK_SET);
201     lv_fs_read(&b->f, buf, len * (b->bpp / 8), NULL);
202 
203 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 1
204     for(unsigned int i = 0; i < len * (b->bpp / 8); i += 2) {
205         buf[i] = buf[i] ^ buf[i + 1];
206         buf[i + 1] = buf[i] ^ buf[i + 1];
207         buf[i] = buf[i] ^ buf[i + 1];
208     }
209 
210 #elif LV_COLOR_DEPTH == 32
211     if(b->bpp == 32) {
212         lv_coord_t i;
213         for(i = 0; i < len; i++) {
214             uint8_t b0 = buf[i * 4];
215             uint8_t b1 = buf[i * 4 + 1];
216             uint8_t b2 = buf[i * 4 + 2];
217             uint8_t b3 = buf[i * 4 + 3];
218             lv_color32_t * c = (lv_color32_t *)&buf[i * 4];
219             c->ch.red = b2;
220             c->ch.green = b1;
221             c->ch.blue = b0;
222             c->ch.alpha = b3;
223         }
224     }
225     if(b->bpp == 24) {
226         lv_coord_t i;
227 
228         for(i = len - 1; i >= 0; i--) {
229             uint8_t * t = &buf[i * 3];
230             lv_color32_t * c = (lv_color32_t *)&buf[i * 4];
231             c->ch.red = t[2];
232             c->ch.green = t[1];
233             c->ch.blue = t[0];
234             c->ch.alpha = 0xff;
235         }
236     }
237 #endif
238 
239     return LV_RES_OK;
240 }
241 
242 /**
243  * Free the allocated resources
244  */
decoder_close(lv_img_decoder_t * decoder,lv_img_decoder_dsc_t * dsc)245 static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
246 {
247     LV_UNUSED(decoder);
248     bmp_dsc_t * b = dsc->user_data;
249     lv_fs_close(&b->f);
250     lv_mem_free(dsc->user_data);
251 
252 }
253 
254 #endif /*LV_USE_BMP*/
255