1 /**
2 * @file lv_sjpg.c
3 *
4 */
5
6 /*----------------------------------------------------------------------------------------------------------------------------------
7 / Added normal JPG support [7/10/2020]
8 / ----------
9 / SJPEG is a custom created modified JPEG file format for small embedded platforms.
10 / It will contain multiple JPEG fragments all embedded into a single file with a custom header.
11 / This makes JPEG decoding easier using any JPEG library. Overall file size will be almost
12 / similar to the parent jpeg file. We can generate sjpeg from any jpeg using a python script
13 / provided along with this project.
14 / (by vinodstanur | 2020 )
15 / SJPEG FILE STRUCTURE
16 / --------------------------------------------------------------------------------------------------------------------------------
17 / Bytes | Value |
18 / --------------------------------------------------------------------------------------------------------------------------------
19 /
20 / 0 - 7 | "_SJPG__" followed by '\0'
21 /
22 / 8 - 13 | "V1.00" followed by '\0' [VERSION OF SJPG FILE for future compatibiliby]
23 /
24 / 14 - 15 | X_RESOLUTION (width) [little endian]
25 /
26 / 16 - 17 | Y_RESOLUTION (height) [little endian]
27 /
28 / 18 - 19 | TOTAL_FRAMES inside sjpeg [little endian]
29 /
30 / 20 - 21 | JPEG BLOCK WIDTH (16 normally) [little endian]
31 /
32 / 22 - [(TOTAL_FRAMES*2 )] | SIZE OF EACH JPEG SPLIT FRAGMENTS (FRAME_INFO_ARRAY)
33 /
34 / SJPEG data | Each JPEG frame can be extracted from SJPEG data by parsing the FRAME_INFO_ARRAY one time.
35 /
36 /----------------------------------------------------------------------------------------------------------------------------------
37 / JPEG DECODER
38 / ------------
39 / We are using TJpgDec - Tiny JPEG Decompressor library from ELM-CHAN for decoding each split-jpeg fragments.
40 / The tjpgd.c and tjpgd.h is not modified and those are used as it is. So if any update comes for the tiny-jpeg,
41 / just replace those files with updated files.
42 /---------------------------------------------------------------------------------------------------------------------------------*/
43
44 /*********************
45 * INCLUDES
46 *********************/
47
48 #include "../../../lvgl.h"
49 #if LV_USE_SJPG
50
51 #include "tjpgd.h"
52 #include "lv_sjpg.h"
53 #include "../../../misc/lv_fs.h"
54
55 /*********************
56 * DEFINES
57 *********************/
58 #define TJPGD_WORKBUFF_SIZE 4096 //Recommended by TJPGD libray
59
60 //NEVER EDIT THESE OFFSET VALUES
61 #define SJPEG_VERSION_OFFSET 8
62 #define SJPEG_X_RES_OFFSET 14
63 #define SJPEG_y_RES_OFFSET 16
64 #define SJPEG_TOTAL_FRAMES_OFFSET 18
65 #define SJPEG_BLOCK_WIDTH_OFFSET 20
66 #define SJPEG_FRAME_INFO_ARRAY_OFFSET 22
67
68 /**********************
69 * TYPEDEFS
70 **********************/
71
72 enum io_source_type {
73 SJPEG_IO_SOURCE_C_ARRAY,
74 SJPEG_IO_SOURCE_DISK,
75 };
76
77 typedef struct {
78 enum io_source_type type;
79 lv_fs_file_t lv_file;
80 uint8_t * img_cache_buff;
81 int img_cache_x_res;
82 int img_cache_y_res;
83 uint8_t * raw_sjpg_data; //Used when type==SJPEG_IO_SOURCE_C_ARRAY.
84 uint32_t raw_sjpg_data_size; //Num bytes pointed to by raw_sjpg_data.
85 uint32_t raw_sjpg_data_next_read_pos; //Used for all types.
86 } io_source_t;
87
88 typedef struct {
89 uint8_t * sjpeg_data;
90 uint32_t sjpeg_data_size;
91 int sjpeg_x_res;
92 int sjpeg_y_res;
93 int sjpeg_total_frames;
94 int sjpeg_single_frame_height;
95 int sjpeg_cache_frame_index;
96 uint8_t ** frame_base_array; //to save base address of each split frames upto sjpeg_total_frames.
97 int * frame_base_offset; //to save base offset for fseek
98 uint8_t * frame_cache;
99 uint8_t * workb; //JPG work buffer for jpeg library
100 JDEC * tjpeg_jd;
101 io_source_t io;
102 } SJPEG;
103
104 /**********************
105 * STATIC PROTOTYPES
106 **********************/
107 static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
108 static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
109 static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
110 lv_coord_t len, uint8_t * buf);
111 static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
112 static size_t input_func(JDEC * jd, uint8_t * buff, size_t ndata);
113 static int is_jpg(const uint8_t * raw_data, size_t len);
114 static void lv_sjpg_cleanup(SJPEG * sjpeg);
115 static void lv_sjpg_free(SJPEG * sjpeg);
116
117 /**********************
118 * STATIC VARIABLES
119 **********************/
120
121 /**********************
122 * MACROS
123 **********************/
124
125 /**********************
126 * GLOBAL FUNCTIONS
127 **********************/
lv_split_jpeg_init(void)128 void lv_split_jpeg_init(void)
129 {
130 lv_img_decoder_t * dec = lv_img_decoder_create();
131 lv_img_decoder_set_info_cb(dec, decoder_info);
132 lv_img_decoder_set_open_cb(dec, decoder_open);
133 lv_img_decoder_set_close_cb(dec, decoder_close);
134 lv_img_decoder_set_read_line_cb(dec, decoder_read_line);
135 }
136
137 /**********************
138 * STATIC FUNCTIONS
139 **********************/
140 /**
141 * Get info about an SJPG / JPG image
142 * @param decoder pointer to the decoder where this function belongs
143 * @param src can be file name or pointer to a C array
144 * @param header store the info here
145 * @return LV_RES_OK: no error; LV_RES_INV: can't get the info
146 */
decoder_info(lv_img_decoder_t * decoder,const void * src,lv_img_header_t * header)147 static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
148 {
149 LV_UNUSED(decoder);
150
151 /*Check whether the type `src` is known by the decoder*/
152 /* Read the SJPG/JPG header and find `width` and `height` */
153
154 lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/
155
156 lv_res_t ret = LV_RES_OK;
157
158 if(src_type == LV_IMG_SRC_VARIABLE) {
159 const lv_img_dsc_t * img_dsc = src;
160 uint8_t * raw_sjpeg_data = (uint8_t *)img_dsc->data;
161 const uint32_t raw_sjpeg_data_size = img_dsc->data_size;
162
163 if(!strncmp((char *)raw_sjpeg_data, "_SJPG__", strlen("_SJPG__"))) {
164
165 raw_sjpeg_data += 14; //seek to res info ... refer sjpeg format
166 header->always_zero = 0;
167 header->cf = LV_IMG_CF_RAW;
168
169 header->w = *raw_sjpeg_data++;
170 header->w |= *raw_sjpeg_data++ << 8;
171
172 header->h = *raw_sjpeg_data++;
173 header->h |= *raw_sjpeg_data++ << 8;
174
175 return ret;
176
177 }
178 else if(is_jpg(raw_sjpeg_data, raw_sjpeg_data_size) == true) {
179 header->always_zero = 0;
180 header->cf = LV_IMG_CF_RAW;
181
182 uint8_t * workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
183 if(!workb_temp) return LV_RES_INV;
184
185 io_source_t io_source_temp;
186 io_source_temp.type = SJPEG_IO_SOURCE_C_ARRAY;
187 io_source_temp.raw_sjpg_data = raw_sjpeg_data;
188 io_source_temp.raw_sjpg_data_size = raw_sjpeg_data_size;
189 io_source_temp.raw_sjpg_data_next_read_pos = 0;
190
191 JDEC jd_tmp;
192
193 JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
194 if(rc == JDR_OK) {
195 header->w = jd_tmp.width;
196 header->h = jd_tmp.height;
197
198 }
199 else {
200 ret = LV_RES_INV;
201 goto end;
202 }
203
204 end:
205 lv_mem_free(workb_temp);
206
207 return ret;
208
209 }
210 }
211 else if(src_type == LV_IMG_SRC_FILE) {
212 const char * fn = src;
213 if(strcmp(lv_fs_get_ext(fn), "sjpg") == 0) {
214
215 uint8_t buff[22];
216 memset(buff, 0, sizeof(buff));
217
218 lv_fs_file_t file;
219 lv_fs_res_t res = lv_fs_open(&file, fn, LV_FS_MODE_RD);
220 if(res != LV_FS_RES_OK) return 78;
221
222 uint32_t rn;
223 res = lv_fs_read(&file, buff, 8, &rn);
224 if(res != LV_FS_RES_OK || rn != 8) {
225 lv_fs_close(&file);
226 return LV_RES_INV;
227 }
228
229 if(strcmp((char *)buff, "_SJPG__") == 0) {
230 lv_fs_seek(&file, 14, LV_FS_SEEK_SET);
231 res = lv_fs_read(&file, buff, 4, &rn);
232 if(res != LV_FS_RES_OK || rn != 4) {
233 lv_fs_close(&file);
234 return LV_RES_INV;
235 }
236 header->always_zero = 0;
237 header->cf = LV_IMG_CF_RAW;
238 uint8_t * raw_sjpeg_data = buff;
239 header->w = *raw_sjpeg_data++;
240 header->w |= *raw_sjpeg_data++ << 8;
241 header->h = *raw_sjpeg_data++;
242 header->h |= *raw_sjpeg_data++ << 8;
243 lv_fs_close(&file);
244 return LV_RES_OK;
245
246 }
247 }
248 else if(strcmp(lv_fs_get_ext(fn), "jpg") == 0) {
249 lv_fs_file_t file;
250 lv_fs_res_t res = lv_fs_open(&file, fn, LV_FS_MODE_RD);
251 if(res != LV_FS_RES_OK) return 78;
252
253 uint8_t * workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
254 if(!workb_temp) {
255 lv_fs_close(&file);
256 return LV_RES_INV;
257 }
258
259 io_source_t io_source_temp;
260 io_source_temp.type = SJPEG_IO_SOURCE_DISK;
261 io_source_temp.raw_sjpg_data_next_read_pos = 0;
262 io_source_temp.img_cache_buff = NULL;
263 io_source_temp.lv_file = file;
264 JDEC jd_tmp;
265
266 JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
267 lv_mem_free(workb_temp);
268 lv_fs_close(&file);
269
270 if(rc == JDR_OK) {
271 header->always_zero = 0;
272 header->cf = LV_IMG_CF_RAW;
273 header->w = jd_tmp.width;
274 header->h = jd_tmp.height;
275 return LV_RES_OK;
276 }
277 }
278 }
279 return LV_RES_INV;
280 }
281
img_data_cb(JDEC * jd,void * data,JRECT * rect)282 static int img_data_cb(JDEC * jd, void * data, JRECT * rect)
283 {
284 io_source_t * io = jd->device;
285 uint8_t * cache = io->img_cache_buff;
286 const int xres = io->img_cache_x_res;
287 uint8_t * buf = data;
288 const int INPUT_PIXEL_SIZE = 3;
289 const int row_width = rect->right - rect->left + 1; // Row width in pixels.
290 const int row_size = row_width * INPUT_PIXEL_SIZE; // Row size (bytes).
291
292 for(int y = rect->top; y <= rect->bottom; y++) {
293 int row_offset = y * xres * INPUT_PIXEL_SIZE + rect->left * INPUT_PIXEL_SIZE;
294 memcpy(cache + row_offset, buf, row_size);
295 buf += row_size;
296 }
297
298 return 1;
299 }
300
input_func(JDEC * jd,uint8_t * buff,size_t ndata)301 static size_t input_func(JDEC * jd, uint8_t * buff, size_t ndata)
302 {
303 io_source_t * io = jd->device;
304
305 if(!io) return 0;
306
307 if(io->type == SJPEG_IO_SOURCE_C_ARRAY) {
308 const uint32_t bytes_left = io->raw_sjpg_data_size - io->raw_sjpg_data_next_read_pos;
309 const uint32_t to_read = ndata <= bytes_left ? (uint32_t)ndata : bytes_left;
310 if(to_read == 0)
311 return 0;
312 if(buff) {
313 memcpy(buff, io->raw_sjpg_data + io->raw_sjpg_data_next_read_pos, to_read);
314 }
315 io->raw_sjpg_data_next_read_pos += to_read;
316 return to_read;
317 }
318 else if(io->type == SJPEG_IO_SOURCE_DISK) {
319
320 lv_fs_file_t * lv_file_p = &(io->lv_file);
321
322 if(buff) {
323 uint32_t rn = 0;
324 lv_fs_read(lv_file_p, buff, (uint32_t)ndata, &rn);
325 return rn;
326 }
327 else {
328 uint32_t pos;
329 lv_fs_tell(lv_file_p, &pos);
330 lv_fs_seek(lv_file_p, (uint32_t)(ndata + pos), LV_FS_SEEK_SET);
331 return ndata;
332 }
333 }
334 return 0;
335 }
336
337 /**
338 * Open SJPG image and return the decided image
339 * @param decoder pointer to the decoder where this function belongs
340 * @param dsc pointer to a descriptor which describes this decoding session
341 * @return LV_RES_OK: no error; LV_RES_INV: can't get the info
342 */
decoder_open(lv_img_decoder_t * decoder,lv_img_decoder_dsc_t * dsc)343 static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
344 {
345 LV_UNUSED(decoder);
346 lv_res_t lv_ret = LV_RES_OK;
347
348 if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
349 uint8_t * data;
350 SJPEG * sjpeg = (SJPEG *) dsc->user_data;
351 const uint32_t raw_sjpeg_data_size = ((lv_img_dsc_t *)dsc->src)->data_size;
352 if(sjpeg == NULL) {
353 sjpeg = lv_mem_alloc(sizeof(SJPEG));
354 if(!sjpeg) return LV_RES_INV;
355
356 memset(sjpeg, 0, sizeof(SJPEG));
357
358 dsc->user_data = sjpeg;
359 sjpeg->sjpeg_data = (uint8_t *)((lv_img_dsc_t *)(dsc->src))->data;
360 sjpeg->sjpeg_data_size = ((lv_img_dsc_t *)(dsc->src))->data_size;
361 }
362
363 if(!strncmp((char *) sjpeg->sjpeg_data, "_SJPG__", strlen("_SJPG__"))) {
364
365 data = sjpeg->sjpeg_data;
366 data += 14;
367
368 sjpeg->sjpeg_x_res = *data++;
369 sjpeg->sjpeg_x_res |= *data++ << 8;
370
371 sjpeg->sjpeg_y_res = *data++;
372 sjpeg->sjpeg_y_res |= *data++ << 8;
373
374 sjpeg->sjpeg_total_frames = *data++;
375 sjpeg->sjpeg_total_frames |= *data++ << 8;
376
377 sjpeg->sjpeg_single_frame_height = *data++;
378 sjpeg->sjpeg_single_frame_height |= *data++ << 8;
379
380 sjpeg->frame_base_array = lv_mem_alloc(sizeof(uint8_t *) * sjpeg->sjpeg_total_frames);
381 if(! sjpeg->frame_base_array) {
382 lv_sjpg_cleanup(sjpeg);
383 sjpeg = NULL;
384 return LV_RES_INV;
385 }
386
387 sjpeg->frame_base_offset = NULL;
388
389 uint8_t * img_frame_base = data + sjpeg->sjpeg_total_frames * 2;
390 sjpeg->frame_base_array[0] = img_frame_base;
391
392 for(int i = 1; i < sjpeg->sjpeg_total_frames; i++) {
393 int offset = *data++;
394 offset |= *data++ << 8;
395 sjpeg->frame_base_array[i] = sjpeg->frame_base_array[i - 1] + offset;
396 }
397 sjpeg->sjpeg_cache_frame_index = -1;
398 sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3/*2*/);
399 if(! sjpeg->frame_cache) {
400 lv_sjpg_cleanup(sjpeg);
401 sjpeg = NULL;
402 return LV_RES_INV;
403 }
404 sjpeg->io.img_cache_buff = sjpeg->frame_cache;
405 sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
406 sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
407 if(! sjpeg->workb) {
408 lv_sjpg_cleanup(sjpeg);
409 sjpeg = NULL;
410 return LV_RES_INV;
411 }
412
413 sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC));
414 if(! sjpeg->tjpeg_jd) {
415 lv_sjpg_cleanup(sjpeg);
416 sjpeg = NULL;
417 return LV_RES_INV;
418 }
419 sjpeg->io.type = SJPEG_IO_SOURCE_C_ARRAY;
420 sjpeg->io.lv_file.file_d = NULL;
421 dsc->img_data = NULL;
422 return lv_ret;
423 }
424 else if(is_jpg(sjpeg->sjpeg_data, raw_sjpeg_data_size) == true) {
425
426 uint8_t * workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
427 if(! workb_temp) {
428 lv_sjpg_cleanup(sjpeg);
429 sjpeg = NULL;
430 return LV_RES_INV;
431 }
432 io_source_t io_source_temp;
433 io_source_temp.type = SJPEG_IO_SOURCE_C_ARRAY;
434 io_source_temp.raw_sjpg_data = sjpeg->sjpeg_data;
435 io_source_temp.raw_sjpg_data_size = sjpeg->sjpeg_data_size;
436 io_source_temp.raw_sjpg_data_next_read_pos = 0;
437
438 JDEC jd_tmp;
439 JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
440 lv_mem_free(workb_temp);
441
442 if(rc == JDR_OK) {
443 sjpeg->sjpeg_x_res = jd_tmp.width;
444 sjpeg->sjpeg_y_res = jd_tmp.height;
445 sjpeg->sjpeg_total_frames = 1;
446 sjpeg->sjpeg_single_frame_height = jd_tmp.height;
447
448 sjpeg->frame_base_array = lv_mem_alloc(sizeof(uint8_t *) * sjpeg->sjpeg_total_frames);
449 if(! sjpeg->frame_base_array) {
450 lv_sjpg_cleanup(sjpeg);
451 sjpeg = NULL;
452 return LV_RES_INV;
453 }
454 sjpeg->frame_base_offset = NULL;
455
456 uint8_t * img_frame_base = sjpeg->sjpeg_data;
457 sjpeg->frame_base_array[0] = img_frame_base;
458
459 sjpeg->sjpeg_cache_frame_index = -1;
460 sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3);
461 if(! sjpeg->frame_cache) {
462 lv_sjpg_cleanup(sjpeg);
463 sjpeg = NULL;
464 return LV_RES_INV;
465 }
466
467 sjpeg->io.img_cache_buff = sjpeg->frame_cache;
468 sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
469 sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
470 if(! sjpeg->workb) {
471 lv_sjpg_cleanup(sjpeg);
472 sjpeg = NULL;
473 return LV_RES_INV;
474 }
475
476 sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC));
477 if(! sjpeg->tjpeg_jd) {
478 lv_sjpg_cleanup(sjpeg);
479 sjpeg = NULL;
480 return LV_RES_INV;
481 }
482
483 sjpeg->io.type = SJPEG_IO_SOURCE_C_ARRAY;
484 sjpeg->io.lv_file.file_d = NULL;
485 dsc->img_data = NULL;
486 return lv_ret;
487 }
488 else {
489 lv_ret = LV_RES_INV;
490 goto end;
491 }
492
493 end:
494 lv_mem_free(workb_temp);
495
496 return lv_ret;
497 }
498 }
499 else if(dsc->src_type == LV_IMG_SRC_FILE) {
500 /* If all fine, then the file will be kept open */
501 const char * fn = dsc->src;
502 uint8_t * data;
503
504 if(strcmp(lv_fs_get_ext(fn), "sjpg") == 0) {
505
506 uint8_t buff[22];
507 memset(buff, 0, sizeof(buff));
508
509 lv_fs_file_t lv_file;
510 lv_fs_res_t res = lv_fs_open(&lv_file, fn, LV_FS_MODE_RD);
511 if(res != LV_FS_RES_OK) {
512 return 78;
513 }
514
515 uint32_t rn;
516 res = lv_fs_read(&lv_file, buff, 22, &rn);
517 if(res != LV_FS_RES_OK || rn != 22) {
518 lv_fs_close(&lv_file);
519 return LV_RES_INV;
520 }
521
522 if(strcmp((char *)buff, "_SJPG__") == 0) {
523
524 SJPEG * sjpeg = (SJPEG *) dsc->user_data;
525 if(sjpeg == NULL) {
526 sjpeg = lv_mem_alloc(sizeof(SJPEG));
527
528 if(! sjpeg) {
529 lv_fs_close(&lv_file);
530 return LV_RES_INV;
531 }
532 memset(sjpeg, 0, sizeof(SJPEG));
533
534 dsc->user_data = sjpeg;
535 sjpeg->sjpeg_data = (uint8_t *)((lv_img_dsc_t *)(dsc->src))->data;
536 sjpeg->sjpeg_data_size = ((lv_img_dsc_t *)(dsc->src))->data_size;
537 }
538 data = buff;
539 data += 14;
540
541 sjpeg->sjpeg_x_res = *data++;
542 sjpeg->sjpeg_x_res |= *data++ << 8;
543
544 sjpeg->sjpeg_y_res = *data++;
545 sjpeg->sjpeg_y_res |= *data++ << 8;
546
547 sjpeg->sjpeg_total_frames = *data++;
548 sjpeg->sjpeg_total_frames |= *data++ << 8;
549
550 sjpeg->sjpeg_single_frame_height = *data++;
551 sjpeg->sjpeg_single_frame_height |= *data++ << 8;
552
553 sjpeg->frame_base_array = NULL;//lv_mem_alloc( sizeof(uint8_t *) * sjpeg->sjpeg_total_frames );
554 sjpeg->frame_base_offset = lv_mem_alloc(sizeof(int) * sjpeg->sjpeg_total_frames);
555 if(! sjpeg->frame_base_offset) {
556 lv_fs_close(&lv_file);
557 lv_sjpg_cleanup(sjpeg);
558 return LV_RES_INV;
559 }
560 int img_frame_start_offset = (SJPEG_FRAME_INFO_ARRAY_OFFSET + sjpeg->sjpeg_total_frames * 2);
561 sjpeg->frame_base_offset[0] = img_frame_start_offset; //pointer used to save integer for now...
562
563 for(int i = 1; i < sjpeg->sjpeg_total_frames; i++) {
564 res = lv_fs_read(&lv_file, buff, 2, &rn);
565 if(res != LV_FS_RES_OK || rn != 2) {
566 lv_fs_close(&lv_file);
567 return LV_RES_INV;
568 }
569
570 data = buff;
571 int offset = *data++;
572 offset |= *data++ << 8;
573 sjpeg->frame_base_offset[i] = sjpeg->frame_base_offset[i - 1] + offset;
574 }
575
576 sjpeg->sjpeg_cache_frame_index = -1; //INVALID AT BEGINNING for a forced compare mismatch at first time.
577 sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3);
578 if(! sjpeg->frame_cache) {
579 lv_fs_close(&lv_file);
580 lv_sjpg_cleanup(sjpeg);
581 return LV_RES_INV;
582 }
583 sjpeg->io.img_cache_buff = sjpeg->frame_cache;
584 sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
585 sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
586 if(! sjpeg->workb) {
587 lv_fs_close(&lv_file);
588 lv_sjpg_cleanup(sjpeg);
589 return LV_RES_INV;
590 }
591
592 sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC));
593 if(! sjpeg->tjpeg_jd) {
594 lv_fs_close(&lv_file);
595 lv_sjpg_cleanup(sjpeg);
596 return LV_RES_INV;
597 }
598
599 sjpeg->io.type = SJPEG_IO_SOURCE_DISK;
600 sjpeg->io.lv_file = lv_file;
601 dsc->img_data = NULL;
602 return LV_RES_OK;
603 }
604 }
605 else if(strcmp(lv_fs_get_ext(fn), "jpg") == 0) {
606
607 lv_fs_file_t lv_file;
608 lv_fs_res_t res = lv_fs_open(&lv_file, fn, LV_FS_MODE_RD);
609 if(res != LV_FS_RES_OK) {
610 return LV_RES_INV;
611 }
612
613 SJPEG * sjpeg = (SJPEG *) dsc->user_data;
614 if(sjpeg == NULL) {
615 sjpeg = lv_mem_alloc(sizeof(SJPEG));
616 if(! sjpeg) {
617 lv_fs_close(&lv_file);
618 return LV_RES_INV;
619 }
620
621 memset(sjpeg, 0, sizeof(SJPEG));
622 dsc->user_data = sjpeg;
623 sjpeg->sjpeg_data = (uint8_t *)((lv_img_dsc_t *)(dsc->src))->data;
624 sjpeg->sjpeg_data_size = ((lv_img_dsc_t *)(dsc->src))->data_size;
625 }
626
627 uint8_t * workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
628 if(! workb_temp) {
629 lv_fs_close(&lv_file);
630 lv_sjpg_cleanup(sjpeg);
631 return LV_RES_INV;
632 }
633
634 io_source_t io_source_temp;
635 io_source_temp.type = SJPEG_IO_SOURCE_DISK;
636 io_source_temp.raw_sjpg_data_next_read_pos = 0;
637 io_source_temp.img_cache_buff = NULL;
638 io_source_temp.lv_file = lv_file;
639
640 JDEC jd_tmp;
641
642 JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
643
644 lv_mem_free(workb_temp);
645
646 if(rc == JDR_OK) {
647 sjpeg->sjpeg_x_res = jd_tmp.width;
648 sjpeg->sjpeg_y_res = jd_tmp.height;
649 sjpeg->sjpeg_total_frames = 1;
650 sjpeg->sjpeg_single_frame_height = jd_tmp.height;
651
652 sjpeg->frame_base_array = NULL;
653 sjpeg->frame_base_offset = lv_mem_alloc(sizeof(uint8_t *) * sjpeg->sjpeg_total_frames);
654 if(! sjpeg->frame_base_offset) {
655 lv_fs_close(&lv_file);
656 lv_sjpg_cleanup(sjpeg);
657 return LV_RES_INV;
658 }
659
660 int img_frame_start_offset = 0;
661 sjpeg->frame_base_offset[0] = img_frame_start_offset;
662
663 sjpeg->sjpeg_cache_frame_index = -1;
664 sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3);
665 if(! sjpeg->frame_cache) {
666 lv_fs_close(&lv_file);
667 lv_sjpg_cleanup(sjpeg);
668 return LV_RES_INV;
669 }
670
671 sjpeg->io.img_cache_buff = sjpeg->frame_cache;
672 sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
673 sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
674 if(! sjpeg->workb) {
675 lv_fs_close(&lv_file);
676 lv_sjpg_cleanup(sjpeg);
677 return LV_RES_INV;
678 }
679
680 sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC));
681 if(! sjpeg->tjpeg_jd) {
682 lv_fs_close(&lv_file);
683 lv_sjpg_cleanup(sjpeg);
684 return LV_RES_INV;
685 }
686
687 sjpeg->io.type = SJPEG_IO_SOURCE_DISK;
688 sjpeg->io.lv_file = lv_file;
689 dsc->img_data = NULL;
690 return LV_RES_OK;
691
692 }
693 else {
694 if(dsc->user_data) lv_mem_free(dsc->user_data);
695 lv_fs_close(&lv_file);
696 return LV_RES_INV;
697 }
698 }
699 }
700
701 return LV_RES_INV;
702 }
703
704 /**
705 * Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
706 * Required only if the "open" function can't open the whole decoded pixel array. (dsc->img_data == NULL)
707 * @param decoder pointer to the decoder the function associated with
708 * @param dsc pointer to decoder descriptor
709 * @param x start x coordinate
710 * @param y start y coordinate
711 * @param len number of pixels to decode
712 * @param buf a buffer to store the decoded pixels
713 * @return LV_RES_OK: ok; LV_RES_INV: failed
714 */
715
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)716 static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
717 lv_coord_t len, uint8_t * buf)
718 {
719 LV_UNUSED(decoder);
720 if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
721 SJPEG * sjpeg = (SJPEG *) dsc->user_data;
722 JRESULT rc;
723
724 int sjpeg_req_frame_index = y / sjpeg->sjpeg_single_frame_height;
725
726 /*If line not from cache, refresh cache */
727 if(sjpeg_req_frame_index != sjpeg->sjpeg_cache_frame_index) {
728 sjpeg->io.raw_sjpg_data = sjpeg->frame_base_array[ sjpeg_req_frame_index ];
729 if(sjpeg_req_frame_index == (sjpeg->sjpeg_total_frames - 1)) {
730 /*This is the last frame. */
731 const uint32_t frame_offset = (uint32_t)(sjpeg->io.raw_sjpg_data - sjpeg->sjpeg_data);
732 sjpeg->io.raw_sjpg_data_size = sjpeg->sjpeg_data_size - frame_offset;
733 }
734 else {
735 sjpeg->io.raw_sjpg_data_size =
736 (uint32_t)(sjpeg->frame_base_array[sjpeg_req_frame_index + 1] - sjpeg->io.raw_sjpg_data);
737 }
738 sjpeg->io.raw_sjpg_data_next_read_pos = 0;
739 rc = jd_prepare(sjpeg->tjpeg_jd, input_func, sjpeg->workb, (size_t)TJPGD_WORKBUFF_SIZE, &(sjpeg->io));
740 if(rc != JDR_OK) return LV_RES_INV;
741 rc = jd_decomp(sjpeg->tjpeg_jd, img_data_cb, 0);
742 if(rc != JDR_OK) return LV_RES_INV;
743 sjpeg->sjpeg_cache_frame_index = sjpeg_req_frame_index;
744 }
745
746 int offset = 0;
747 uint8_t * cache = (uint8_t *)sjpeg->frame_cache + x * 3 + (y % sjpeg->sjpeg_single_frame_height) * sjpeg->sjpeg_x_res *
748 3;
749
750 #if LV_COLOR_DEPTH == 32
751 for(int i = 0; i < len; i++) {
752 buf[offset + 3] = 0xff;
753 buf[offset + 2] = *cache++;
754 buf[offset + 1] = *cache++;
755 buf[offset + 0] = *cache++;
756 offset += 4;
757 }
758
759 #elif LV_COLOR_DEPTH == 16
760
761 for(int i = 0; i < len; i++) {
762 uint16_t col_16bit = (*cache++ & 0xf8) << 8;
763 col_16bit |= (*cache++ & 0xFC) << 3;
764 col_16bit |= (*cache++ >> 3);
765 #if LV_BIG_ENDIAN_SYSTEM == 1 || LV_COLOR_16_SWAP == 1
766 buf[offset++] = col_16bit >> 8;
767 buf[offset++] = col_16bit & 0xff;
768 #else
769 buf[offset++] = col_16bit & 0xff;
770 buf[offset++] = col_16bit >> 8;
771 #endif // LV_BIG_ENDIAN_SYSTEM
772 }
773
774 #elif LV_COLOR_DEPTH == 8
775
776 for(int i = 0; i < len; i++) {
777 uint8_t col_8bit = (*cache++ & 0xC0);
778 col_8bit |= (*cache++ & 0xe0) >> 2;
779 col_8bit |= (*cache++ & 0xe0) >> 5;
780 buf[offset++] = col_8bit;
781 }
782 #else
783 #error Unsupported LV_COLOR_DEPTH
784
785 #endif // LV_COLOR_DEPTH
786 return LV_RES_OK;
787 }
788 else if(dsc->src_type == LV_IMG_SRC_FILE) {
789 SJPEG * sjpeg = (SJPEG *) dsc->user_data;
790 JRESULT rc;
791 int sjpeg_req_frame_index = y / sjpeg->sjpeg_single_frame_height;
792
793 lv_fs_file_t * lv_file_p = &(sjpeg->io.lv_file);
794 if(!lv_file_p) goto end;
795
796 /*If line not from cache, refresh cache */
797 if(sjpeg_req_frame_index != sjpeg->sjpeg_cache_frame_index) {
798 sjpeg->io.raw_sjpg_data_next_read_pos = (int)(sjpeg->frame_base_offset [ sjpeg_req_frame_index ]);
799 lv_fs_seek(&(sjpeg->io.lv_file), sjpeg->io.raw_sjpg_data_next_read_pos, LV_FS_SEEK_SET);
800
801 rc = jd_prepare(sjpeg->tjpeg_jd, input_func, sjpeg->workb, (size_t)TJPGD_WORKBUFF_SIZE, &(sjpeg->io));
802 if(rc != JDR_OK) return LV_RES_INV;
803
804 rc = jd_decomp(sjpeg->tjpeg_jd, img_data_cb, 0);
805 if(rc != JDR_OK) return LV_RES_INV;
806
807 sjpeg->sjpeg_cache_frame_index = sjpeg_req_frame_index;
808 }
809
810 int offset = 0;
811 uint8_t * cache = (uint8_t *)sjpeg->frame_cache + x * 3 + (y % sjpeg->sjpeg_single_frame_height) * sjpeg->sjpeg_x_res *
812 3;
813
814 #if LV_COLOR_DEPTH == 32
815 for(int i = 0; i < len; i++) {
816 buf[offset + 3] = 0xff;
817 buf[offset + 2] = *cache++;
818 buf[offset + 1] = *cache++;
819 buf[offset + 0] = *cache++;
820 offset += 4;
821 }
822 #elif LV_COLOR_DEPTH == 16
823
824 for(int i = 0; i < len; i++) {
825 uint16_t col_8bit = (*cache++ & 0xf8) << 8;
826 col_8bit |= (*cache++ & 0xFC) << 3;
827 col_8bit |= (*cache++ >> 3);
828 #if LV_BIG_ENDIAN_SYSTEM == 1 || LV_COLOR_16_SWAP == 1
829 buf[offset++] = col_8bit >> 8;
830 buf[offset++] = col_8bit & 0xff;
831 #else
832 buf[offset++] = col_8bit & 0xff;
833 buf[offset++] = col_8bit >> 8;
834 #endif // LV_BIG_ENDIAN_SYSTEM
835 }
836
837 #elif LV_COLOR_DEPTH == 8
838
839 for(int i = 0; i < len; i++) {
840 uint8_t col_8bit = (*cache++ & 0xC0);
841 col_8bit |= (*cache++ & 0xe0) >> 2;
842 col_8bit |= (*cache++ & 0xe0) >> 5;
843 buf[offset++] = col_8bit;
844 }
845
846 #else
847 #error Unsupported LV_COLOR_DEPTH
848
849 #endif // LV_COLOR_DEPTH
850
851 return LV_RES_OK;
852 }
853 end:
854 return LV_RES_INV;
855 }
856
857 /**
858 * Free the allocated resources
859 * @param decoder pointer to the decoder where this function belongs
860 * @param dsc pointer to a descriptor which describes this decoding session
861 */
decoder_close(lv_img_decoder_t * decoder,lv_img_decoder_dsc_t * dsc)862 static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
863 {
864 LV_UNUSED(decoder);
865 /*Free all allocated data*/
866 SJPEG * sjpeg = (SJPEG *) dsc->user_data;
867 if(!sjpeg) return;
868
869 switch(dsc->src_type) {
870 case LV_IMG_SRC_FILE:
871 if(sjpeg->io.lv_file.file_d) {
872 lv_fs_close(&(sjpeg->io.lv_file));
873 }
874 lv_sjpg_cleanup(sjpeg);
875 break;
876
877 case LV_IMG_SRC_VARIABLE:
878 lv_sjpg_cleanup(sjpeg);
879 break;
880
881 default:
882 ;
883 }
884 }
885
is_jpg(const uint8_t * raw_data,size_t len)886 static int is_jpg(const uint8_t * raw_data, size_t len)
887 {
888 const uint8_t jpg_signature[] = {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46};
889 if(len < sizeof(jpg_signature)) return false;
890 return memcmp(jpg_signature, raw_data, sizeof(jpg_signature)) == 0;
891 }
892
lv_sjpg_free(SJPEG * sjpeg)893 static void lv_sjpg_free(SJPEG * sjpeg)
894 {
895 if(sjpeg->frame_cache) lv_mem_free(sjpeg->frame_cache);
896 if(sjpeg->frame_base_array) lv_mem_free(sjpeg->frame_base_array);
897 if(sjpeg->frame_base_offset) lv_mem_free(sjpeg->frame_base_offset);
898 if(sjpeg->tjpeg_jd) lv_mem_free(sjpeg->tjpeg_jd);
899 if(sjpeg->workb) lv_mem_free(sjpeg->workb);
900 }
901
lv_sjpg_cleanup(SJPEG * sjpeg)902 static void lv_sjpg_cleanup(SJPEG * sjpeg)
903 {
904 if(! sjpeg) return;
905
906 lv_sjpg_free(sjpeg);
907 lv_mem_free(sjpeg);
908 }
909
910 #endif /*LV_USE_SJPG*/
911