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