1 /**
2  * @file lv_bmp.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../draw/lv_image_decoder_private.h"
10 #include "../../../lvgl.h"
11 #if LV_USE_BMP
12 
13 #include <string.h>
14 #include "../../core/lv_global.h"
15 
16 /*********************
17  *      DEFINES
18  *********************/
19 
20 #define DECODER_NAME    "BMP"
21 
22 #define image_cache_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers)
23 
24 /**********************
25  *      TYPEDEFS
26  **********************/
27 
28 typedef struct {
29     lv_fs_file_t f;
30     unsigned int px_offset;
31     int px_width;
32     int px_height;
33     unsigned int bpp;
34     int row_size_bytes;
35 } bmp_dsc_t;
36 
37 /**********************
38  *  STATIC PROTOTYPES
39  **********************/
40 static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * src, lv_image_header_t * header);
41 static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
42 
43 static lv_result_t decoder_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc,
44                                     const lv_area_t * full_area, lv_area_t * decoded_area);
45 
46 static void decoder_close(lv_image_decoder_t * dec, lv_image_decoder_dsc_t * dsc);
47 
48 /**********************
49  *  STATIC VARIABLES
50  **********************/
51 
52 /**********************
53  *      MACROS
54  **********************/
55 
56 /**********************
57  *   GLOBAL FUNCTIONS
58  **********************/
lv_bmp_init(void)59 void lv_bmp_init(void)
60 {
61     lv_image_decoder_t * dec = lv_image_decoder_create();
62     lv_image_decoder_set_info_cb(dec, decoder_info);
63     lv_image_decoder_set_open_cb(dec, decoder_open);
64     lv_image_decoder_set_get_area_cb(dec, decoder_get_area);
65     lv_image_decoder_set_close_cb(dec, decoder_close);
66 
67     dec->name = DECODER_NAME;
68 }
69 
lv_bmp_deinit(void)70 void lv_bmp_deinit(void)
71 {
72     lv_image_decoder_t * dec = NULL;
73     while((dec = lv_image_decoder_get_next(dec)) != NULL) {
74         if(dec->info_cb == decoder_info) {
75             lv_image_decoder_delete(dec);
76             break;
77         }
78     }
79 }
80 
81 /**********************
82  *   STATIC FUNCTIONS
83  **********************/
84 
85 /**
86  * Get info about a BMP image
87  * @param dsc image descriptor containing the source and type of the image and other info.
88  * @param header store the info here
89  * @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info
90  */
decoder_info(lv_image_decoder_t * decoder,lv_image_decoder_dsc_t * dsc,lv_image_header_t * header)91 static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_image_header_t * header)
92 {
93     LV_UNUSED(decoder);
94 
95     const void * src = dsc->src;
96     lv_image_src_t src_type = dsc->src_type;          /*Get the source type*/
97 
98     /*If it's a BMP file...*/
99     if(src_type == LV_IMAGE_SRC_FILE) {
100         const char * fn = src;
101         if(lv_strcmp(lv_fs_get_ext(fn), "bmp") == 0) {              /*Check the extension*/
102             /*Save the data in the header*/
103             uint8_t headers[54];
104 
105             lv_fs_read(&dsc->file, headers, 54, NULL);
106             uint32_t w;
107             uint32_t h;
108             lv_memcpy(&w, headers + 18, 4);
109             lv_memcpy(&h, headers + 22, 4);
110             header->w = w;
111             header->h = h;
112 
113             uint16_t bpp;
114             lv_memcpy(&bpp, headers + 28, 2);
115             switch(bpp) {
116                 case 16:
117                     header->cf = LV_COLOR_FORMAT_RGB565;
118                     break;
119                 case 24:
120                     header->cf = LV_COLOR_FORMAT_RGB888;
121                     break;
122                 case 32:
123                     header->cf = LV_COLOR_FORMAT_ARGB8888;
124                     break;
125                 default:
126                     LV_LOG_WARN("Not supported bpp: %d", bpp);
127                     return LV_RESULT_OK;
128             }
129             return LV_RESULT_OK;
130         }
131     }
132     /* BMP file as data not supported for simplicity.
133      * Convert them to LVGL compatible C arrays directly. */
134     else if(src_type == LV_IMAGE_SRC_VARIABLE) {
135         return LV_RESULT_INVALID;
136     }
137 
138     return LV_RESULT_INVALID;         /*If didn't succeeded earlier then it's an error*/
139 }
140 
141 /**
142  * Open a BMP image and return the decided image
143  * @param decoder pointer to the decoder
144  * @param dsc     pointer to the decoder descriptor
145  * @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
146  */
decoder_open(lv_image_decoder_t * decoder,lv_image_decoder_dsc_t * dsc)147 static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
148 {
149     LV_UNUSED(decoder);
150 
151     /*If it's a BMP file...*/
152     if(dsc->src_type == LV_IMAGE_SRC_FILE) {
153         const char * fn = dsc->src;
154 
155         if(lv_strcmp(lv_fs_get_ext(fn), "bmp") != 0) {
156             return LV_RESULT_INVALID;       /*Check the extension*/
157         }
158 
159         bmp_dsc_t b;
160         lv_memset(&b, 0x00, sizeof(b));
161 
162         lv_fs_res_t res = lv_fs_open(&b.f, dsc->src, LV_FS_MODE_RD);
163         if(res != LV_FS_RES_OK) return LV_RESULT_INVALID;
164 
165         uint8_t header[54];
166         lv_fs_read(&b.f, header, 54, NULL);
167 
168         if(0x42 != header[0] || 0x4d != header[1]) {
169             lv_fs_close(&b.f);
170             return LV_RESULT_INVALID;
171         }
172 
173         lv_memcpy(&b.px_offset, header + 10, 4);
174         lv_memcpy(&b.px_width, header + 18, 4);
175         lv_memcpy(&b.px_height, header + 22, 4);
176         lv_memcpy(&b.bpp, header + 28, 2);
177         b.row_size_bytes = ((b.bpp * b.px_width + 31) / 32) * 4;
178 
179         dsc->user_data = lv_malloc(sizeof(bmp_dsc_t));
180         LV_ASSERT_MALLOC(dsc->user_data);
181         if(dsc->user_data == NULL) return LV_RESULT_INVALID;
182         lv_memcpy(dsc->user_data, &b, sizeof(b));
183         return LV_RESULT_OK;
184     }
185     /* BMP file as data not supported for simplicity.
186      * Convert them to LVGL compatible C arrays directly. */
187     else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
188         return LV_RESULT_INVALID;
189     }
190 
191     return LV_RESULT_INVALID;    /*If not returned earlier then it failed*/
192 }
193 
decoder_get_area(lv_image_decoder_t * decoder,lv_image_decoder_dsc_t * dsc,const lv_area_t * full_area,lv_area_t * decoded_area)194 static lv_result_t decoder_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc,
195                                     const lv_area_t * full_area, lv_area_t * decoded_area)
196 {
197     LV_UNUSED(decoder);
198     bmp_dsc_t * b = dsc->user_data;
199     lv_draw_buf_t * decoded = (void *)dsc->decoded;
200 
201     if(decoded_area->y1 == LV_COORD_MIN) {
202         *decoded_area = *full_area;
203         decoded_area->y2 = decoded_area->y1;
204         int32_t w_px = lv_area_get_width(full_area);
205         lv_draw_buf_t * reshaped = lv_draw_buf_reshape(decoded, dsc->header.cf, w_px, 1, LV_STRIDE_AUTO);
206         if(reshaped == NULL) {
207             if(decoded != NULL) {
208                 lv_draw_buf_destroy(decoded);
209                 decoded = NULL;
210                 dsc->decoded = NULL;
211             }
212             decoded = lv_draw_buf_create_ex(image_cache_draw_buf_handlers, w_px, 1, dsc->header.cf, LV_STRIDE_AUTO);
213             if(decoded == NULL) return LV_RESULT_INVALID;
214         }
215         else {
216             decoded = reshaped;
217         }
218         dsc->decoded = decoded;
219     }
220     else {
221         decoded_area->y1++;
222         decoded_area->y2++;
223     }
224 
225     if(decoded_area->y1 > full_area->y2) {
226         return LV_RESULT_INVALID;
227     }
228     else {
229         int32_t y = (b->px_height - 1) - (decoded_area->y1); /*BMP images are stored upside down*/
230         uint32_t p = b->px_offset + b->row_size_bytes * y;
231         p += (decoded_area->x1) * (b->bpp / 8);
232         lv_fs_seek(&b->f, p, LV_FS_SEEK_SET);
233         uint32_t line_width_byte = lv_area_get_width(full_area) * (b->bpp / 8);
234         lv_fs_read(&b->f, decoded->data, line_width_byte, NULL);
235 
236         return LV_RESULT_OK;
237     }
238 }
239 
240 /**
241  * Free the allocated resources
242  */
decoder_close(lv_image_decoder_t * decoder,lv_image_decoder_dsc_t * dsc)243 static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
244 {
245     LV_UNUSED(decoder);
246     bmp_dsc_t * b = dsc->user_data;
247     lv_fs_close(&b->f);
248     lv_free(dsc->user_data);
249     if(dsc->decoded) lv_draw_buf_destroy((void *)dsc->decoded);
250 
251 }
252 
253 #endif /*LV_USE_BMP*/
254