summaryrefslogtreecommitdiff
path: root/lib/lvgl/src/draw/lv_img_decoder.c
blob: 13050b8b7946cf4c5686d4b1361bebcaccdb1e6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
/**
 * @file lv_img_decoder.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_img_decoder.h"
#include "../misc/lv_assert.h"
#include "../draw/lv_draw_img.h"
#include "../misc/lv_ll.h"
#include "../misc/lv_gc.h"

/*********************
 *      DEFINES
 *********************/
#define CF_BUILT_IN_FIRST   LV_IMG_CF_TRUE_COLOR
#define CF_BUILT_IN_LAST    LV_IMG_CF_RGB565A8

/**********************
 *      TYPEDEFS
 **********************/

typedef struct {
    lv_fs_file_t f;
    lv_color_t * palette;
    lv_opa_t * opa;
} lv_img_decoder_built_in_data_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static lv_res_t lv_img_decoder_built_in_line_true_color(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
                                                        lv_coord_t len, uint8_t * buf);
static lv_res_t lv_img_decoder_built_in_line_alpha(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
                                                   lv_coord_t len, uint8_t * buf);
static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
                                                     lv_coord_t len, uint8_t * buf);

/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

/**
 * Initialize the image decoder module
 */
void _lv_img_decoder_init(void)
{
    _lv_ll_init(&LV_GC_ROOT(_lv_img_decoder_ll), sizeof(lv_img_decoder_t));

    lv_img_decoder_t * decoder;

    /*Create a decoder for the built in color format*/
    decoder = lv_img_decoder_create();
    LV_ASSERT_MALLOC(decoder);
    if(decoder == NULL) {
        LV_LOG_WARN("lv_img_decoder_init: out of memory");
        return;
    }

    lv_img_decoder_set_info_cb(decoder, lv_img_decoder_built_in_info);
    lv_img_decoder_set_open_cb(decoder, lv_img_decoder_built_in_open);
    lv_img_decoder_set_read_line_cb(decoder, lv_img_decoder_built_in_read_line);
    lv_img_decoder_set_close_cb(decoder, lv_img_decoder_built_in_close);
}

/**
 * Get information about an image.
 * Try the created image decoder one by one. Once one is able to get info that info will be used.
 * @param src the image source. E.g. file name or variable.
 * @param header the image info will be stored here
 * @return LV_RES_OK: success; LV_RES_INV: wasn't able to get info about the image
 */
lv_res_t lv_img_decoder_get_info(const void * src, lv_img_header_t * header)
{
    lv_memset_00(header, sizeof(lv_img_header_t));

    if(src == NULL) return LV_RES_INV;

    lv_img_src_t src_type = lv_img_src_get_type(src);
    if(src_type == LV_IMG_SRC_VARIABLE) {
        const lv_img_dsc_t * img_dsc = src;
        if(img_dsc->data == NULL) return LV_RES_INV;
    }

    lv_res_t res = LV_RES_INV;
    lv_img_decoder_t * d;
    _LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), d) {
        if(d->info_cb) {
            res = d->info_cb(d, src, header);
            if(res == LV_RES_OK) break;
        }
    }

    return res;
}

lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_color_t color, int32_t frame_id)
{
    lv_memset_00(dsc, sizeof(lv_img_decoder_dsc_t));

    if(src == NULL) return LV_RES_INV;
    lv_img_src_t src_type = lv_img_src_get_type(src);
    if(src_type == LV_IMG_SRC_VARIABLE) {
        const lv_img_dsc_t * img_dsc = src;
        if(img_dsc->data == NULL) return LV_RES_INV;
    }

    dsc->color    = color;
    dsc->src_type = src_type;
    dsc->frame_id = frame_id;

    if(dsc->src_type == LV_IMG_SRC_FILE) {
        size_t fnlen = strlen(src);
        dsc->src = lv_mem_alloc(fnlen + 1);
        LV_ASSERT_MALLOC(dsc->src);
        if(dsc->src == NULL) {
            LV_LOG_WARN("lv_img_decoder_open: out of memory");
            return LV_RES_INV;
        }
        strcpy((char *)dsc->src, src);
    }
    else {
        dsc->src = src;
    }

    lv_res_t res = LV_RES_INV;

    lv_img_decoder_t * decoder;
    _LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), decoder) {
        /*Info and Open callbacks are required*/
        if(decoder->info_cb == NULL || decoder->open_cb == NULL) continue;

        res = decoder->info_cb(decoder, src, &dsc->header);
        if(res != LV_RES_OK) continue;

        dsc->decoder = decoder;
        res = decoder->open_cb(decoder, dsc);

        /*Opened successfully. It is a good decoder for this image source*/
        if(res == LV_RES_OK) return res;

        /*Prepare for the next loop*/
        lv_memset_00(&dsc->header, sizeof(lv_img_header_t));

        dsc->error_msg = NULL;
        dsc->img_data  = NULL;
        dsc->user_data = NULL;
        dsc->time_to_open = 0;
    }

    if(dsc->src_type == LV_IMG_SRC_FILE)
        lv_mem_free((void *)dsc->src);

    return res;
}

/**
 * Read a line from an opened image
 * @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
 * @param x start X coordinate (from left)
 * @param y start Y coordinate (from top)
 * @param len number of pixels to read
 * @param buf store the data here
 * @return LV_RES_OK: success; LV_RES_INV: an error occurred
 */
lv_res_t lv_img_decoder_read_line(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
    lv_res_t res = LV_RES_INV;
    if(dsc->decoder->read_line_cb) res = dsc->decoder->read_line_cb(dsc->decoder, dsc, x, y, len, buf);

    return res;
}

/**
 * Close a decoding session
 * @param dsc pointer to `lv_img_decoder_dsc_t` used in `lv_img_decoder_open`
 */
void lv_img_decoder_close(lv_img_decoder_dsc_t * dsc)
{
    if(dsc->decoder) {
        if(dsc->decoder->close_cb) dsc->decoder->close_cb(dsc->decoder, dsc);

        if(dsc->src_type == LV_IMG_SRC_FILE) {
            lv_mem_free((void *)dsc->src);
            dsc->src = NULL;
        }
    }
}

/**
 * Create a new image decoder
 * @return pointer to the new image decoder
 */
lv_img_decoder_t * lv_img_decoder_create(void)
{
    lv_img_decoder_t * decoder;
    decoder = _lv_ll_ins_head(&LV_GC_ROOT(_lv_img_decoder_ll));
    LV_ASSERT_MALLOC(decoder);
    if(decoder == NULL) return NULL;

    lv_memset_00(decoder, sizeof(lv_img_decoder_t));

    return decoder;
}

/**
 * Delete an image decoder
 * @param decoder pointer to an image decoder
 */
void lv_img_decoder_delete(lv_img_decoder_t * decoder)
{
    _lv_ll_remove(&LV_GC_ROOT(_lv_img_decoder_ll), decoder);
    lv_mem_free(decoder);
}

/**
 * Set a callback to get information about the image
 * @param decoder pointer to an image decoder
 * @param info_cb a function to collect info about an image (fill an `lv_img_header_t` struct)
 */
void lv_img_decoder_set_info_cb(lv_img_decoder_t * decoder, lv_img_decoder_info_f_t info_cb)
{
    decoder->info_cb = info_cb;
}

/**
 * Set a callback to open an image
 * @param decoder pointer to an image decoder
 * @param open_cb a function to open an image
 */
void lv_img_decoder_set_open_cb(lv_img_decoder_t * decoder, lv_img_decoder_open_f_t open_cb)
{
    decoder->open_cb = open_cb;
}

/**
 * Set a callback to a decoded line of an image
 * @param decoder pointer to an image decoder
 * @param read_line_cb a function to read a line of an image
 */
void lv_img_decoder_set_read_line_cb(lv_img_decoder_t * decoder, lv_img_decoder_read_line_f_t read_line_cb)
{
    decoder->read_line_cb = read_line_cb;
}

/**
 * Set a callback to close a decoding session. E.g. close files and free other resources.
 * @param decoder pointer to an image decoder
 * @param close_cb a function to close a decoding session
 */
void lv_img_decoder_set_close_cb(lv_img_decoder_t * decoder, lv_img_decoder_close_f_t close_cb)
{
    decoder->close_cb = close_cb;
}

/**
 * Get info about a built-in image
 * @param decoder the decoder where this function belongs
 * @param src the image source: pointer to an `lv_img_dsc_t` variable, a file path or a symbol
 * @param header store the image data here
 * @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
 */
lv_res_t lv_img_decoder_built_in_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
{
    LV_UNUSED(decoder); /*Unused*/

    lv_img_src_t src_type = lv_img_src_get_type(src);
    if(src_type == LV_IMG_SRC_VARIABLE) {
        lv_img_cf_t cf = ((lv_img_dsc_t *)src)->header.cf;
        if(cf < CF_BUILT_IN_FIRST || cf > CF_BUILT_IN_LAST) return LV_RES_INV;

        header->w  = ((lv_img_dsc_t *)src)->header.w;
        header->h  = ((lv_img_dsc_t *)src)->header.h;
        header->cf = ((lv_img_dsc_t *)src)->header.cf;
    }
    else if(src_type == LV_IMG_SRC_FILE) {
        /*Support only "*.bin" files*/
        if(strcmp(lv_fs_get_ext(src), "bin")) return LV_RES_INV;

        lv_fs_file_t f;
        lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD);
        if(res == LV_FS_RES_OK) {
            uint32_t rn;
            res = lv_fs_read(&f, header, sizeof(lv_img_header_t), &rn);
            lv_fs_close(&f);
            if(res != LV_FS_RES_OK || rn != sizeof(lv_img_header_t)) {
                LV_LOG_WARN("Image get info get read file header");
                return LV_RES_INV;
            }
        }

        if(header->cf < CF_BUILT_IN_FIRST || header->cf > CF_BUILT_IN_LAST) return LV_RES_INV;
    }
    else if(src_type == LV_IMG_SRC_SYMBOL) {
        /*The size depend on the font but it is unknown here. It should be handled outside of the
         *function*/
        header->w = 1;
        header->h = 1;
        /*Symbols always have transparent parts. Important because of cover check in the draw
         *function. The actual value doesn't matter because lv_draw_label will draw it*/
        header->cf = LV_IMG_CF_ALPHA_1BIT;
    }
    else {
        LV_LOG_WARN("Image get info found unknown src type");
        return LV_RES_INV;
    }
    return LV_RES_OK;
}

/**
 * Open a built in image
 * @param decoder the decoder where this function belongs
 * @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
 * @return LV_RES_OK: the info is successfully stored in `header`; LV_RES_INV: unknown format or other error.
 */
lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
    /*Open the file if it's a file*/
    if(dsc->src_type == LV_IMG_SRC_FILE) {
        /*Support only "*.bin" files*/
        if(strcmp(lv_fs_get_ext(dsc->src), "bin")) return LV_RES_INV;

        lv_fs_file_t f;
        lv_fs_res_t res = lv_fs_open(&f, dsc->src, LV_FS_MODE_RD);
        if(res != LV_FS_RES_OK) {
            LV_LOG_WARN("Built-in image decoder can't open the file");
            return LV_RES_INV;
        }

        /*If the file was open successfully save the file descriptor*/
        if(dsc->user_data == NULL) {
            dsc->user_data = lv_mem_alloc(sizeof(lv_img_decoder_built_in_data_t));
            LV_ASSERT_MALLOC(dsc->user_data);
            if(dsc->user_data == NULL) {
                LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
                lv_fs_close(&f);
                return LV_RES_INV;
            }
            lv_memset_00(dsc->user_data, sizeof(lv_img_decoder_built_in_data_t));
        }

        lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
        lv_memcpy_small(&user_data->f, &f, sizeof(f));
    }
    else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
        /*The variables should have valid data*/
        if(((lv_img_dsc_t *)dsc->src)->data == NULL) {
            return LV_RES_INV;
        }
    }

    lv_img_cf_t cf = dsc->header.cf;
    /*Process true color formats*/
    if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
       cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED || cf == LV_IMG_CF_RGB565A8 ||
       cf == LV_IMG_CF_ALPHA_8BIT) {
        if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
            /*In case of uncompressed formats the image stored in the ROM/RAM.
             *So simply give its pointer*/
            dsc->img_data = ((lv_img_dsc_t *)dsc->src)->data;
            return LV_RES_OK;
        }
        else {
            /*If it's a file it need to be read line by line later*/
            return LV_RES_OK;
        }
    }
    /*Process indexed images. Build a palette*/
    else if(cf == LV_IMG_CF_INDEXED_1BIT || cf == LV_IMG_CF_INDEXED_2BIT || cf == LV_IMG_CF_INDEXED_4BIT ||
            cf == LV_IMG_CF_INDEXED_8BIT) {
        uint8_t px_size       = lv_img_cf_get_px_size(cf);
        uint32_t palette_size = 1 << px_size;

        /*Allocate the palette*/
        if(dsc->user_data == NULL) {
            dsc->user_data = lv_mem_alloc(sizeof(lv_img_decoder_built_in_data_t));
            LV_ASSERT_MALLOC(dsc->user_data);
            if(dsc->user_data == NULL) {
                LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
                return LV_RES_INV;
            }
            lv_memset_00(dsc->user_data, sizeof(lv_img_decoder_built_in_data_t));
        }

        lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
        user_data->palette                         = lv_mem_alloc(palette_size * sizeof(lv_color_t));
        LV_ASSERT_MALLOC(user_data->palette);
        user_data->opa                             = lv_mem_alloc(palette_size * sizeof(lv_opa_t));
        LV_ASSERT_MALLOC(user_data->opa);
        if(user_data->palette == NULL || user_data->opa == NULL) {
            LV_LOG_ERROR("img_decoder_built_in_open: out of memory");
            lv_img_decoder_built_in_close(decoder, dsc);
            return LV_RES_INV;
        }

        if(dsc->src_type == LV_IMG_SRC_FILE) {
            /*Read the palette from file*/
            lv_fs_seek(&user_data->f, 4, LV_FS_SEEK_SET); /*Skip the header*/
            lv_color32_t cur_color;
            uint32_t i;
            for(i = 0; i < palette_size; i++) {
                lv_fs_read(&user_data->f, &cur_color, sizeof(lv_color32_t), NULL);
                user_data->palette[i] = lv_color_make(cur_color.ch.red, cur_color.ch.green, cur_color.ch.blue);
                user_data->opa[i]     = cur_color.ch.alpha;
            }
        }
        else {
            /*The palette begins in the beginning of the image data. Just point to it.*/
            lv_color32_t * palette_p = (lv_color32_t *)((lv_img_dsc_t *)dsc->src)->data;

            uint32_t i;
            for(i = 0; i < palette_size; i++) {
                user_data->palette[i] = lv_color_make(palette_p[i].ch.red, palette_p[i].ch.green, palette_p[i].ch.blue);
                user_data->opa[i]     = palette_p[i].ch.alpha;
            }
        }

        return LV_RES_OK;
    }
    /*Alpha indexed images.*/
    else if(cf == LV_IMG_CF_ALPHA_1BIT || cf == LV_IMG_CF_ALPHA_2BIT || cf == LV_IMG_CF_ALPHA_4BIT) {
        return LV_RES_OK; /*Nothing to process*/
    }
    /*Unknown format. Can't decode it.*/
    else {
        /*Free the potentially allocated memories*/
        lv_img_decoder_built_in_close(decoder, dsc);

        LV_LOG_WARN("Image decoder open: unknown color format");
        return LV_RES_INV;
    }
}

/**
 * Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
 * Required only if the "open" function can't return with the whole decoded pixel array.
 * @param decoder pointer to the decoder the function associated with
 * @param dsc pointer to decoder descriptor
 * @param x start x coordinate
 * @param y start y coordinate
 * @param len number of pixels to decode
 * @param buf a buffer to store the decoded pixels
 * @return LV_RES_OK: ok; LV_RES_INV: failed
 */
lv_res_t lv_img_decoder_built_in_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)
{
    LV_UNUSED(decoder); /*Unused*/

    lv_res_t res = LV_RES_INV;

    if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
       dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
        /*For TRUE_COLOR images read line required only for files.
         *For variables the image data was returned in `open`*/
        if(dsc->src_type == LV_IMG_SRC_FILE) {
            res = lv_img_decoder_built_in_line_true_color(dsc, x, y, len, buf);
        }
    }
    else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT || dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
            dsc->header.cf == LV_IMG_CF_ALPHA_4BIT || dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
        res = lv_img_decoder_built_in_line_alpha(dsc, x, y, len, buf);
    }
    else if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT || dsc->header.cf == LV_IMG_CF_INDEXED_2BIT ||
            dsc->header.cf == LV_IMG_CF_INDEXED_4BIT || dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
        res = lv_img_decoder_built_in_line_indexed(dsc, x, y, len, buf);
    }
    else {
        LV_LOG_WARN("Built-in image decoder read not supports the color format");
        return LV_RES_INV;
    }

    return res;
}

/**
 * Close the pending decoding. Free resources etc.
 * @param decoder pointer to the decoder the function associated with
 * @param dsc pointer to decoder descriptor
 */
void lv_img_decoder_built_in_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
    LV_UNUSED(decoder); /*Unused*/

    lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
    if(user_data) {
        if(dsc->src_type == LV_IMG_SRC_FILE) {
            lv_fs_close(&user_data->f);
        }
        if(user_data->palette) lv_mem_free(user_data->palette);
        if(user_data->opa) lv_mem_free(user_data->opa);

        lv_mem_free(user_data);
        dsc->user_data = NULL;
    }
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

static lv_res_t lv_img_decoder_built_in_line_true_color(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
                                                        lv_coord_t len, uint8_t * buf)
{
    lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
    lv_fs_res_t res;
    uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);

    uint32_t pos = ((y * dsc->header.w + x) * px_size) >> 3;
    pos += 4; /*Skip the header*/
    res = lv_fs_seek(&user_data->f, pos, LV_FS_SEEK_SET);
    if(res != LV_FS_RES_OK) {
        LV_LOG_WARN("Built-in image decoder seek failed");
        return LV_RES_INV;
    }
    uint32_t btr = len * (px_size >> 3);
    uint32_t br  = 0;
    res = lv_fs_read(&user_data->f, buf, btr, &br);
    if(res != LV_FS_RES_OK || btr != br) {
        LV_LOG_WARN("Built-in image decoder read failed");
        return LV_RES_INV;
    }

    return LV_RES_OK;
}

static lv_res_t lv_img_decoder_built_in_line_alpha(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
                                                   lv_coord_t len, uint8_t * buf)
{
    const lv_opa_t alpha1_opa_table[2]  = {0, 255};          /*Opacity mapping with bpp = 1 (Just for compatibility)*/
    const lv_opa_t alpha2_opa_table[4]  = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
    const lv_opa_t alpha4_opa_table[16] = {0,  17, 34,  51,  /*Opacity mapping with bpp = 4*/
                                           68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255
                                          };

    /*Simply fill the buffer with the color. Later only the alpha value will be modified.*/
    lv_color_t bg_color = dsc->color;
    lv_coord_t i;
    for(i = 0; i < len; i++) {
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
        buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full;
#elif LV_COLOR_DEPTH == 16
        /*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
        buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full & 0xFF;
        buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (bg_color.full >> 8) & 0xFF;
#elif LV_COLOR_DEPTH == 32
        *((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = bg_color.full;
#else
#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
#endif
    }

    const lv_opa_t * opa_table = NULL;
    uint8_t px_size            = lv_img_cf_get_px_size(dsc->header.cf);
    uint16_t mask              = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/

    lv_coord_t w = 0;
    uint32_t ofs = 0;
    int8_t pos   = 0;
    switch(dsc->header.cf) {
        case LV_IMG_CF_ALPHA_1BIT:
            w = (dsc->header.w + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
            ofs += w * y + (x >> 3); /*First pixel*/
            pos = 7 - (x & 0x7);
            opa_table = alpha1_opa_table;
            break;
        case LV_IMG_CF_ALPHA_2BIT:
            w = (dsc->header.w + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
            ofs += w * y + (x >> 2); /*First pixel*/
            pos = 6 - (x & 0x3) * 2;
            opa_table = alpha2_opa_table;
            break;
        case LV_IMG_CF_ALPHA_4BIT:
            w = (dsc->header.w + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
            ofs += w * y + (x >> 1); /*First pixel*/
            pos = 4 - (x & 0x1) * 4;
            opa_table = alpha4_opa_table;
            break;
        case LV_IMG_CF_ALPHA_8BIT:
            w = dsc->header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
            ofs += w * y + x;  /*First pixel*/
            pos = 0;
            break;
    }

    lv_img_decoder_built_in_data_t * user_data = dsc->user_data;
    uint8_t * fs_buf = lv_mem_buf_get(w);
    if(fs_buf == NULL) return LV_RES_INV;

    const uint8_t * data_tmp = NULL;
    if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
        const lv_img_dsc_t * img_dsc = dsc->src;

        data_tmp = img_dsc->data + ofs;
    }
    else {
        lv_fs_seek(&user_data->f, ofs + 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
        lv_fs_read(&user_data->f, fs_buf, w, NULL);
        data_tmp = fs_buf;
    }

    for(i = 0; i < len; i++) {
        uint8_t val_act = (*data_tmp >> pos) & mask;

        buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] =
            dsc->header.cf == LV_IMG_CF_ALPHA_8BIT ? val_act : opa_table[val_act];

        pos -= px_size;
        if(pos < 0) {
            pos = 8 - px_size;
            data_tmp++;
        }
    }
    lv_mem_buf_release(fs_buf);
    return LV_RES_OK;
}

static lv_res_t lv_img_decoder_built_in_line_indexed(lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y,
                                                     lv_coord_t len, uint8_t * buf)
{
    uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
    uint16_t mask   = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/

    lv_coord_t w = 0;
    int8_t pos   = 0;
    uint32_t ofs = 0;
    switch(dsc->header.cf) {
        case LV_IMG_CF_INDEXED_1BIT:
            w = (dsc->header.w + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
            ofs += w * y + (x >> 3); /*First pixel*/
            ofs += 8;                /*Skip the palette*/
            pos = 7 - (x & 0x7);
            break;
        case LV_IMG_CF_INDEXED_2BIT:
            w = (dsc->header.w + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
            ofs += w * y + (x >> 2); /*First pixel*/
            ofs += 16;               /*Skip the palette*/
            pos = 6 - (x & 0x3) * 2;
            break;
        case LV_IMG_CF_INDEXED_4BIT:
            w = (dsc->header.w + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
            ofs += w * y + (x >> 1); /*First pixel*/
            ofs += 64;               /*Skip the palette*/
            pos = 4 - (x & 0x1) * 4;
            break;
        case LV_IMG_CF_INDEXED_8BIT:
            w = dsc->header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
            ofs += w * y + x;  /*First pixel*/
            ofs += 1024;       /*Skip the palette*/
            pos = 0;
            break;
    }

    lv_img_decoder_built_in_data_t * user_data = dsc->user_data;

    uint8_t * fs_buf = lv_mem_buf_get(w);
    if(fs_buf == NULL) return LV_RES_INV;
    const uint8_t * data_tmp = NULL;
    if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
        const lv_img_dsc_t * img_dsc = dsc->src;
        data_tmp                     = img_dsc->data + ofs;
    }
    else {
        lv_fs_seek(&user_data->f, ofs + 4, LV_FS_SEEK_SET); /*+4 to skip the header*/
        lv_fs_read(&user_data->f, fs_buf, w, NULL);
        data_tmp = fs_buf;
    }

    lv_coord_t i;
    for(i = 0; i < len; i++) {
        uint8_t val_act = (*data_tmp >> pos) & mask;

        lv_color_t color = user_data->palette[val_act];
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
        buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full;
#elif LV_COLOR_DEPTH == 16
        /*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
        buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = color.full & 0xFF;
        buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (color.full >> 8) & 0xFF;
#elif LV_COLOR_DEPTH == 32
        *((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = color.full;
#else
#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
#endif
        buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] = user_data->opa[val_act];

        pos -= px_size;
        if(pos < 0) {
            pos = 8 - px_size;
            data_tmp++;
        }
    }
    lv_mem_buf_release(fs_buf);
    return LV_RES_OK;
}