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
89 typedef struct {
90 uint8_t * sjpeg_data;
91 uint32_t sjpeg_data_size;
92 int sjpeg_x_res;
93 int sjpeg_y_res;
94 int sjpeg_total_frames;
95 int sjpeg_single_frame_height;
96 int sjpeg_cache_frame_index;
97 uint8_t ** frame_base_array; //to save base address of each split frames upto sjpeg_total_frames.
98 int * frame_base_offset; //to save base offset for fseek
99 uint8_t * frame_cache;
100 uint8_t * workb; //JPG work buffer for jpeg library
101 JDEC * tjpeg_jd;
102 io_source_t io;
103 } SJPEG;
104
105 /**********************
106 * STATIC PROTOTYPES
107 **********************/
108 static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
109 static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
110 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,
111 lv_coord_t len, uint8_t * buf);
112 static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc);
113 static size_t input_func(JDEC * jd, uint8_t * buff, size_t ndata);
114 static int is_jpg(const uint8_t * raw_data);
115 static void lv_sjpg_cleanup(SJPEG * sjpeg);
116 static void lv_sjpg_free(SJPEG * sjpeg);
117
118 /**********************
119 * STATIC VARIABLES
120 **********************/
121
122 /**********************
123 * MACROS
124 **********************/
125
126 /**********************
127 * GLOBAL FUNCTIONS
128 **********************/
lv_split_jpeg_init(void)129 void lv_split_jpeg_init(void)
130 {
131 lv_img_decoder_t * dec = lv_img_decoder_create();
132 lv_img_decoder_set_info_cb(dec, decoder_info);
133 lv_img_decoder_set_open_cb(dec, decoder_open);
134 lv_img_decoder_set_close_cb(dec, decoder_close);
135 lv_img_decoder_set_read_line_cb(dec, decoder_read_line);
136 }
137
138 /**********************
139 * STATIC FUNCTIONS
140 **********************/
141 /**
142 * Get info about an SJPG / JPG image
143 * @param decoder pointer to the decoder where this function belongs
144 * @param src can be file name or pointer to a C array
145 * @param header store the info here
146 * @return LV_RES_OK: no error; LV_RES_INV: can't get the info
147 */
decoder_info(lv_img_decoder_t * decoder,const void * src,lv_img_header_t * header)148 static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
149 {
150 LV_UNUSED(decoder);
151
152 /*Check whether the type `src` is known by the decoder*/
153 /* Read the SJPG/JPG header and find `width` and `height` */
154
155 lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/
156
157 lv_res_t ret = LV_RES_OK;
158
159 if(src_type == LV_IMG_SRC_VARIABLE) {
160 uint8_t * raw_sjpeg_data = (uint8_t *)((lv_img_dsc_t *)src)->data;
161 const uint32_t raw_sjpeg_data_size = ((lv_img_dsc_t *)src)->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) == 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(&fn[strlen(fn) - 5], ".sjpg")) {
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(&fn[strlen(fn) - 4], ".jpg")) {
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 if(sjpeg == NULL) {
352 sjpeg = lv_mem_alloc(sizeof(SJPEG));
353 if(!sjpeg) return LV_RES_INV;
354
355 memset(sjpeg, 0, sizeof(SJPEG));
356
357 dsc->user_data = sjpeg;
358 sjpeg->sjpeg_data = (uint8_t *)((lv_img_dsc_t *)(dsc->src))->data;
359 sjpeg->sjpeg_data_size = ((lv_img_dsc_t *)(dsc->src))->data_size;
360 }
361
362 if(!strncmp((char *) sjpeg->sjpeg_data, "_SJPG__", strlen("_SJPG__"))) {
363
364 data = sjpeg->sjpeg_data;
365 data += 14;
366
367 sjpeg->sjpeg_x_res = *data++;
368 sjpeg->sjpeg_x_res |= *data++ << 8;
369
370 sjpeg->sjpeg_y_res = *data++;
371 sjpeg->sjpeg_y_res |= *data++ << 8;
372
373 sjpeg->sjpeg_total_frames = *data++;
374 sjpeg->sjpeg_total_frames |= *data++ << 8;
375
376 sjpeg->sjpeg_single_frame_height = *data++;
377 sjpeg->sjpeg_single_frame_height |= *data++ << 8;
378
379 sjpeg->frame_base_array = lv_mem_alloc(sizeof(uint8_t *) * sjpeg->sjpeg_total_frames);
380 if(! sjpeg->frame_base_array) {
381 lv_sjpg_cleanup(sjpeg);
382 sjpeg = NULL;
383 return LV_RES_INV;
384 }
385
386 sjpeg->frame_base_offset = NULL;
387
388 uint8_t * img_frame_base = data + sjpeg->sjpeg_total_frames * 2;
389 sjpeg->frame_base_array[0] = img_frame_base;
390
391 for(int i = 1; i < sjpeg->sjpeg_total_frames; i++) {
392 int offset = *data++;
393 offset |= *data++ << 8;
394 sjpeg->frame_base_array[i] = sjpeg->frame_base_array[i - 1] + offset;
395 }
396 sjpeg->sjpeg_cache_frame_index = -1;
397 sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3/*2*/);
398 if(! sjpeg->frame_cache) {
399 lv_sjpg_cleanup(sjpeg);
400 sjpeg = NULL;
401 return LV_RES_INV;
402 }
403 sjpeg->io.img_cache_buff = sjpeg->frame_cache;
404 sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
405 sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
406 if(! sjpeg->workb) {
407 lv_sjpg_cleanup(sjpeg);
408 sjpeg = NULL;
409 return LV_RES_INV;
410 }
411
412 sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC));
413 if(! sjpeg->tjpeg_jd) {
414 lv_sjpg_cleanup(sjpeg);
415 sjpeg = NULL;
416 return LV_RES_INV;
417 }
418 sjpeg->io.type = SJPEG_IO_SOURCE_C_ARRAY;
419 sjpeg->io.lv_file.file_d = NULL;
420 dsc->img_data = NULL;
421 return lv_ret;
422 }
423
424 else if(is_jpg(sjpeg->sjpeg_data) == 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
443 if(rc == JDR_OK) {
444 sjpeg->sjpeg_x_res = jd_tmp.width;
445 sjpeg->sjpeg_y_res = jd_tmp.height;
446 sjpeg->sjpeg_total_frames = 1;
447 sjpeg->sjpeg_single_frame_height = jd_tmp.height;
448
449 sjpeg->frame_base_array = lv_mem_alloc(sizeof(uint8_t *) * sjpeg->sjpeg_total_frames);
450 if(! sjpeg->frame_base_array) {
451 lv_sjpg_cleanup(sjpeg);
452 sjpeg = NULL;
453 return LV_RES_INV;
454 }
455 sjpeg->frame_base_offset = NULL;
456
457 uint8_t * img_frame_base = sjpeg->sjpeg_data;
458 sjpeg->frame_base_array[0] = img_frame_base;
459
460 sjpeg->sjpeg_cache_frame_index = -1;
461 sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3);
462 if(! sjpeg->frame_cache) {
463 lv_sjpg_cleanup(sjpeg);
464 sjpeg = NULL;
465 return LV_RES_INV;
466 }
467
468 sjpeg->io.img_cache_buff = sjpeg->frame_cache;
469 sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
470 sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
471 if(! sjpeg->workb) {
472 lv_sjpg_cleanup(sjpeg);
473 sjpeg = NULL;
474 return LV_RES_INV;
475 }
476
477 sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC));
478 if(! sjpeg->tjpeg_jd) {
479 lv_sjpg_cleanup(sjpeg);
480 sjpeg = NULL;
481 return LV_RES_INV;
482 }
483
484 sjpeg->io.type = SJPEG_IO_SOURCE_C_ARRAY;
485 sjpeg->io.lv_file.file_d = NULL;
486 dsc->img_data = NULL;
487 return lv_ret;
488 }
489 else {
490 lv_ret = LV_RES_INV;
491 goto end;
492 }
493
494 end:
495 lv_mem_free(workb_temp);
496
497 return lv_ret;
498 }
499 }
500 else if(dsc->src_type == LV_IMG_SRC_FILE) {
501 /* If all fine, then the file will be kept open */
502 const char * fn = dsc->src;
503 uint8_t * data;
504
505 if(!strcmp(&fn[strlen(fn) - 5], ".sjpg")) {
506
507 uint8_t buff[22];
508 memset(buff, 0, sizeof(buff));
509
510
511 lv_fs_file_t lv_file;
512 lv_fs_res_t res = lv_fs_open(&lv_file, fn, LV_FS_MODE_RD);
513 if(res != LV_FS_RES_OK) {
514 return 78;
515 }
516
517
518 uint32_t rn;
519 res = lv_fs_read(&lv_file, buff, 22, &rn);
520 if(res != LV_FS_RES_OK || rn != 22) {
521 lv_fs_close(&lv_file);
522 return LV_RES_INV;
523 }
524
525 if(strcmp((char *)buff, "_SJPG__") == 0) {
526
527 SJPEG * sjpeg = (SJPEG *) dsc->user_data;
528 if(sjpeg == NULL) {
529 sjpeg = lv_mem_alloc(sizeof(SJPEG));
530
531 if(! sjpeg) {
532 lv_fs_close(&lv_file);
533 return LV_RES_INV;
534 }
535 memset(sjpeg, 0, sizeof(SJPEG));
536
537 dsc->user_data = sjpeg;
538 sjpeg->sjpeg_data = (uint8_t *)((lv_img_dsc_t *)(dsc->src))->data;
539 sjpeg->sjpeg_data_size = ((lv_img_dsc_t *)(dsc->src))->data_size;
540 }
541 data = buff;
542 data += 14;
543
544 sjpeg->sjpeg_x_res = *data++;
545 sjpeg->sjpeg_x_res |= *data++ << 8;
546
547 sjpeg->sjpeg_y_res = *data++;
548 sjpeg->sjpeg_y_res |= *data++ << 8;
549
550 sjpeg->sjpeg_total_frames = *data++;
551 sjpeg->sjpeg_total_frames |= *data++ << 8;
552
553 sjpeg->sjpeg_single_frame_height = *data++;
554 sjpeg->sjpeg_single_frame_height |= *data++ << 8;
555
556 sjpeg->frame_base_array = NULL;//lv_mem_alloc( sizeof(uint8_t *) * sjpeg->sjpeg_total_frames );
557 sjpeg->frame_base_offset = lv_mem_alloc(sizeof(int) * sjpeg->sjpeg_total_frames);
558 if(! sjpeg->frame_base_offset) {
559 lv_fs_close(&lv_file);
560 lv_sjpg_cleanup(sjpeg);
561 return LV_RES_INV;
562 }
563 int img_frame_start_offset = (SJPEG_FRAME_INFO_ARRAY_OFFSET + sjpeg->sjpeg_total_frames * 2);
564 sjpeg->frame_base_offset[0] = img_frame_start_offset; //pointer used to save integer for now...
565
566 for(int i = 1; i < sjpeg->sjpeg_total_frames; i++) {
567 res = lv_fs_read(&lv_file, buff, 2, &rn);
568 if(res != LV_FS_RES_OK || rn != 2) {
569 lv_fs_close(&lv_file);
570 return LV_RES_INV;
571 }
572
573 data = buff;
574 int offset = *data++;
575 offset |= *data++ << 8;
576 sjpeg->frame_base_offset[i] = sjpeg->frame_base_offset[i - 1] + offset;
577 }
578
579 sjpeg->sjpeg_cache_frame_index = -1; //INVALID AT BEGINNING for a forced compare mismatch at first time.
580 sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3);
581 if(! sjpeg->frame_cache) {
582 lv_fs_close(&lv_file);
583 lv_sjpg_cleanup(sjpeg);
584 return LV_RES_INV;
585 }
586 sjpeg->io.img_cache_buff = sjpeg->frame_cache;
587 sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
588 sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
589 if(! sjpeg->workb) {
590 lv_fs_close(&lv_file);
591 lv_sjpg_cleanup(sjpeg);
592 return LV_RES_INV;
593 }
594
595 sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC));
596 if(! sjpeg->tjpeg_jd) {
597 lv_fs_close(&lv_file);
598 lv_sjpg_cleanup(sjpeg);
599 return LV_RES_INV;
600 }
601
602 sjpeg->io.type = SJPEG_IO_SOURCE_DISK;
603 sjpeg->io.lv_file = lv_file;
604 dsc->img_data = NULL;
605 return LV_RES_OK;
606 }
607 }
608 else if(!strcmp(&fn[strlen(fn) - 4], ".jpg")) {
609
610 lv_fs_file_t lv_file;
611 lv_fs_res_t res = lv_fs_open(&lv_file, fn, LV_FS_MODE_RD);
612 if(res != LV_FS_RES_OK) {
613 return LV_RES_INV;
614 }
615
616 SJPEG * sjpeg = (SJPEG *) dsc->user_data;
617 if(sjpeg == NULL) {
618 sjpeg = lv_mem_alloc(sizeof(SJPEG));
619 if(! sjpeg) {
620 lv_fs_close(&lv_file);
621 return LV_RES_INV;
622 }
623
624 memset(sjpeg, 0, sizeof(SJPEG));
625 dsc->user_data = sjpeg;
626 sjpeg->sjpeg_data = (uint8_t *)((lv_img_dsc_t *)(dsc->src))->data;
627 sjpeg->sjpeg_data_size = ((lv_img_dsc_t *)(dsc->src))->data_size;
628 }
629
630 uint8_t * workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
631 if(! workb_temp) {
632 lv_fs_close(&lv_file);
633 lv_sjpg_cleanup(sjpeg);
634 return LV_RES_INV;
635 }
636
637 io_source_t io_source_temp;
638 io_source_temp.type = SJPEG_IO_SOURCE_DISK;
639 io_source_temp.raw_sjpg_data_next_read_pos = 0;
640 io_source_temp.img_cache_buff = NULL;
641 io_source_temp.lv_file = lv_file;
642
643 JDEC jd_tmp;
644
645 JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
646
647 lv_mem_free(workb_temp);
648
649
650 if(rc == JDR_OK) {
651 sjpeg->sjpeg_x_res = jd_tmp.width;
652 sjpeg->sjpeg_y_res = jd_tmp.height;
653 sjpeg->sjpeg_total_frames = 1;
654 sjpeg->sjpeg_single_frame_height = jd_tmp.height;
655
656 sjpeg->frame_base_array = NULL;
657 sjpeg->frame_base_offset = lv_mem_alloc(sizeof(uint8_t *) * sjpeg->sjpeg_total_frames);
658 if(! sjpeg->frame_base_offset) {
659 lv_fs_close(&lv_file);
660 lv_sjpg_cleanup(sjpeg);
661 return LV_RES_INV;
662 }
663
664 int img_frame_start_offset = 0;
665 sjpeg->frame_base_offset[0] = img_frame_start_offset;
666
667 sjpeg->sjpeg_cache_frame_index = -1;
668 sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3);
669 if(! sjpeg->frame_cache) {
670 lv_fs_close(&lv_file);
671 lv_sjpg_cleanup(sjpeg);
672 return LV_RES_INV;
673 }
674
675 sjpeg->io.img_cache_buff = sjpeg->frame_cache;
676 sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
677 sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
678 if(! sjpeg->workb) {
679 lv_fs_close(&lv_file);
680 lv_sjpg_cleanup(sjpeg);
681 return LV_RES_INV;
682 }
683
684 sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC));
685 if(! sjpeg->tjpeg_jd) {
686 lv_fs_close(&lv_file);
687 lv_sjpg_cleanup(sjpeg);
688 return LV_RES_INV;
689 }
690
691 sjpeg->io.type = SJPEG_IO_SOURCE_DISK;
692 sjpeg->io.lv_file = lv_file;
693 dsc->img_data = NULL;
694 return LV_RES_OK;
695
696 }
697 else {
698 if(dsc->user_data) lv_mem_free(dsc->user_data);
699 lv_fs_close(&lv_file);
700 return LV_RES_INV;
701 }
702 }
703 }
704
705 return LV_RES_INV;
706 }
707
708 /**
709 * Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
710 * Required only if the "open" function can't open the whole decoded pixel array. (dsc->img_data == NULL)
711 * @param decoder pointer to the decoder the function associated with
712 * @param dsc pointer to decoder descriptor
713 * @param x start x coordinate
714 * @param y start y coordinate
715 * @param len number of pixels to decode
716 * @param buf a buffer to store the decoded pixels
717 * @return LV_RES_OK: ok; LV_RES_INV: failed
718 */
719
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)720 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,
721 lv_coord_t len, uint8_t * buf)
722 {
723 LV_UNUSED(decoder);
724 if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
725 SJPEG * sjpeg = (SJPEG *) dsc->user_data;
726 JRESULT rc;
727
728 int sjpeg_req_frame_index = y / sjpeg->sjpeg_single_frame_height;
729
730 /*If line not from cache, refresh cache */
731 if(sjpeg_req_frame_index != sjpeg->sjpeg_cache_frame_index) {
732 sjpeg->io.raw_sjpg_data = sjpeg->frame_base_array[ sjpeg_req_frame_index ];
733 if(sjpeg_req_frame_index == (sjpeg->sjpeg_total_frames - 1)) {
734 /*This is the last frame. */
735 const uint32_t frame_offset = (uint32_t)(sjpeg->io.raw_sjpg_data - sjpeg->sjpeg_data);
736 sjpeg->io.raw_sjpg_data_size = sjpeg->sjpeg_data_size - frame_offset;
737 }
738 else {
739 sjpeg->io.raw_sjpg_data_size =
740 (uint32_t)(sjpeg->frame_base_array[sjpeg_req_frame_index + 1] - sjpeg->io.raw_sjpg_data);
741 }
742 sjpeg->io.raw_sjpg_data_next_read_pos = 0;
743 rc = jd_prepare(sjpeg->tjpeg_jd, input_func, sjpeg->workb, (size_t)TJPGD_WORKBUFF_SIZE, &(sjpeg->io));
744 if(rc != JDR_OK) return LV_RES_INV;
745 rc = jd_decomp(sjpeg->tjpeg_jd, img_data_cb, 0);
746 if(rc != JDR_OK) return LV_RES_INV;
747 sjpeg->sjpeg_cache_frame_index = sjpeg_req_frame_index;
748 }
749
750 int offset = 0;
751 uint8_t * cache = (uint8_t *)sjpeg->frame_cache + x * 3 + (y % sjpeg->sjpeg_single_frame_height) * sjpeg->sjpeg_x_res *
752 3;
753
754 #if LV_COLOR_DEPTH == 32
755 for(int i = 0; i < len; i++) {
756 buf[offset + 3] = 0xff;
757 buf[offset + 2] = *cache++;
758 buf[offset + 1] = *cache++;
759 buf[offset + 0] = *cache++;
760 offset += 4;
761 }
762
763 #elif LV_COLOR_DEPTH == 16
764
765 for(int i = 0; i < len; i++) {
766 uint16_t col_16bit = (*cache++ & 0xf8) << 8;
767 col_16bit |= (*cache++ & 0xFC) << 3;
768 col_16bit |= (*cache++ >> 3);
769 #if LV_BIG_ENDIAN_SYSTEM == 1
770 buf[offset++] = col_16bit >> 8;
771 buf[offset++] = col_16bit & 0xff;
772 #else
773 buf[offset++] = col_16bit & 0xff;
774 buf[offset++] = col_16bit >> 8;
775 #endif // LV_BIG_ENDIAN_SYSTEM
776 }
777
778 #elif LV_COLOR_DEPTH == 8
779
780 for(int i = 0; i < len; i++) {
781 uint8_t col_8bit = (*cache++ & 0xC0);
782 col_8bit |= (*cache++ & 0xe0) >> 2;
783 col_8bit |= (*cache++ & 0xe0) >> 5;
784 buf[offset++] = col_8bit;
785 }
786 #else
787 #error Unsupported LV_COLOR_DEPTH
788
789
790 #endif // LV_COLOR_DEPTH
791 return LV_RES_OK;
792 }
793 else if(dsc->src_type == LV_IMG_SRC_FILE) {
794 SJPEG * sjpeg = (SJPEG *) dsc->user_data;
795 JRESULT rc;
796 int sjpeg_req_frame_index = y / sjpeg->sjpeg_single_frame_height;
797
798 lv_fs_file_t * lv_file_p = &(sjpeg->io.lv_file);
799 if(!lv_file_p) goto end;
800
801 /*If line not from cache, refresh cache */
802 if(sjpeg_req_frame_index != sjpeg->sjpeg_cache_frame_index) {
803 sjpeg->io.raw_sjpg_data_next_read_pos = (int)(sjpeg->frame_base_offset [ sjpeg_req_frame_index ]);
804 lv_fs_seek(&(sjpeg->io.lv_file), sjpeg->io.raw_sjpg_data_next_read_pos, LV_FS_SEEK_SET);
805
806 rc = jd_prepare(sjpeg->tjpeg_jd, input_func, sjpeg->workb, (size_t)TJPGD_WORKBUFF_SIZE, &(sjpeg->io));
807 if(rc != JDR_OK) return LV_RES_INV;
808
809 rc = jd_decomp(sjpeg->tjpeg_jd, img_data_cb, 0);
810 if(rc != JDR_OK) return LV_RES_INV;
811
812 sjpeg->sjpeg_cache_frame_index = sjpeg_req_frame_index;
813 }
814
815 int offset = 0;
816 uint8_t * cache = (uint8_t *)sjpeg->frame_cache + x * 3 + (y % sjpeg->sjpeg_single_frame_height) * sjpeg->sjpeg_x_res *
817 3;
818
819 #if LV_COLOR_DEPTH == 32
820 for(int i = 0; i < len; i++) {
821 buf[offset + 3] = 0xff;
822 buf[offset + 2] = *cache++;
823 buf[offset + 1] = *cache++;
824 buf[offset + 0] = *cache++;
825 offset += 4;
826 }
827 #elif LV_COLOR_DEPTH == 16
828
829 for(int i = 0; i < len; i++) {
830 uint16_t col_8bit = (*cache++ & 0xf8) << 8;
831 col_8bit |= (*cache++ & 0xFC) << 3;
832 col_8bit |= (*cache++ >> 3);
833 #if LV_BIG_ENDIAN_SYSTEM == 1
834 buf[offset++] = col_8bit >> 8;
835 buf[offset++] = col_8bit & 0xff;
836 #else
837 buf[offset++] = col_8bit & 0xff;
838 buf[offset++] = col_8bit >> 8;
839 #endif // LV_BIG_ENDIAN_SYSTEM
840 }
841
842 #elif LV_COLOR_DEPTH == 8
843
844 for(int i = 0; i < len; i++) {
845 uint8_t col_8bit = (*cache++ & 0xC0);
846 col_8bit |= (*cache++ & 0xe0) >> 2;
847 col_8bit |= (*cache++ & 0xe0) >> 5;
848 buf[offset++] = col_8bit;
849 }
850
851 #else
852 #error Unsupported LV_COLOR_DEPTH
853
854
855 #endif // LV_COLOR_DEPTH
856
857 return LV_RES_OK;
858 }
859 end:
860 return LV_RES_INV;
861 }
862
863 /**
864 * Free the allocated resources
865 * @param decoder pointer to the decoder where this function belongs
866 * @param dsc pointer to a descriptor which describes this decoding session
867 */
decoder_close(lv_img_decoder_t * decoder,lv_img_decoder_dsc_t * dsc)868 static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
869 {
870 LV_UNUSED(decoder);
871 /*Free all allocated data*/
872 SJPEG * sjpeg = (SJPEG *) dsc->user_data;
873 if(!sjpeg) return;
874
875 switch(dsc->src_type) {
876 case LV_IMG_SRC_FILE:
877 if(sjpeg->io.lv_file.file_d) {
878 lv_fs_close(&(sjpeg->io.lv_file));
879 }
880 lv_sjpg_cleanup(sjpeg);
881 break;
882
883 case LV_IMG_SRC_VARIABLE:
884 lv_sjpg_cleanup(sjpeg);
885 break;
886
887 default:
888 ;
889 }
890 }
891
is_jpg(const uint8_t * raw_data)892 static int is_jpg(const uint8_t * raw_data)
893 {
894 const uint8_t jpg_signature[] = {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46};
895 return memcmp(jpg_signature, raw_data, sizeof(jpg_signature)) == 0;
896 }
897
lv_sjpg_free(SJPEG * sjpeg)898 static void lv_sjpg_free(SJPEG * sjpeg)
899 {
900 if(sjpeg->frame_cache) lv_mem_free(sjpeg->frame_cache);
901 if(sjpeg->frame_base_array) lv_mem_free(sjpeg->frame_base_array);
902 if(sjpeg->frame_base_offset) lv_mem_free(sjpeg->frame_base_offset);
903 if(sjpeg->tjpeg_jd) lv_mem_free(sjpeg->tjpeg_jd);
904 if(sjpeg->workb) lv_mem_free(sjpeg->workb);
905 }
906
lv_sjpg_cleanup(SJPEG * sjpeg)907 static void lv_sjpg_cleanup(SJPEG * sjpeg)
908 {
909 if(! sjpeg) return;
910
911 lv_sjpg_free(sjpeg);
912 lv_mem_free(sjpeg);
913 }
914
915 #endif /*LV_USE_SJPG*/
916