-
Notifications
You must be signed in to change notification settings - Fork 26
/
lv_png.c
256 lines (220 loc) · 8.35 KB
/
lv_png.c
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
/**
* @file lv_png.c
*
*/
/*********************
* INCLUDES
*********************/
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#include "lv_png.h"
#include "lodepng.h"
#include <stdlib.h>
#include <stdio.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
static lv_res_t decoder_open(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
static void decoder_close(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
static void convert_color_depth(uint8_t * img, uint32_t px_cnt);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lodepng_free(void* ptr);
/**
* Register the PNG decoder functions in LittlevGL
*/
void lv_png_init(void)
{
lv_img_decoder_t * dec = lv_img_decoder_create();
lv_img_decoder_set_info_cb(dec, decoder_info);
lv_img_decoder_set_open_cb(dec, decoder_open);
lv_img_decoder_set_close_cb(dec, decoder_close);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get info about a PNG image
* @param src can be file name or pointer to a C array
* @param header store the info here
* @return LV_RES_OK: no error; LV_RES_INV: can't get the info
*/
static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
{
(void) decoder; /*Unused*/
lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/
/*If it's a PNG file...*/
if(src_type == LV_IMG_SRC_FILE) {
const char * fn = src;
if(!strcmp(&fn[strlen(fn) - 3], "png")) { /*Check the extension*/
/* Read the width and height from the file. They have a constant location:
* [16..23]: width
* [24..27]: height
*/
uint32_t size[2];
#if LV_PNG_USE_LV_FILESYSTEM
lv_fs_file_t f;
lv_fs_res_t res = lv_fs_open(&f, fn, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) return LV_RES_INV;
lv_fs_seek(&f, 16, LV_FS_SEEK_SET);
uint32_t rn;
lv_fs_read(&f, &size, 8, &rn);
if(rn != 8) return LV_RES_INV;
lv_fs_close(&f);
#else
FILE* file;
file = fopen(fn, "rb" );
if(!file) return LV_RES_INV;
fseek(file, 16, SEEK_SET);
size_t rn = fread(size, 1 , 8, file);
fclose(file);
if(rn != 8) return LV_RES_INV;
#endif
/*Save the data in the header*/
header->always_zero = 0;
header->cf = LV_IMG_CF_RAW_ALPHA;
/*The width and height are stored in Big endian format so convert them to little endian*/
header->w = (lv_coord_t) ((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8);
header->h = (lv_coord_t) ((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8);
return LV_RES_OK;
}
}
/*If it's a PNG file in a C array...*/
else if(src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = src;
header->always_zero = 0;
header->cf = img_dsc->header.cf; /*Save the color format*/
header->w = img_dsc->header.w; /*Save the color width*/
header->h = img_dsc->header.h; /*Save the color height*/
return LV_RES_OK;
}
return LV_RES_INV; /*If didn't succeeded earlier then it's an error*/
}
/**
* Open a PNG image and return the decided image
* @param src can be file name or pointer to a C array
* @param style style of the image object (unused now but certain formats might use it)
* @return pointer to the decoded image or `LV_IMG_DECODER_OPEN_FAIL` if failed
*/
static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
(void) decoder; /*Unused*/
uint32_t error; /*For the return values of PNG decoder functions*/
uint8_t * img_data = NULL;
/*If it's a PNG file...*/
if(dsc->src_type == LV_IMG_SRC_FILE) {
const char * fn = dsc->src;
if(!strcmp(&fn[strlen(fn) - 3], "png")) { /*Check the extension*/
/*Load the PNG file into buffer. It's still compressed (not decoded)*/
unsigned char * png_data; /*Pointer to the loaded data. Same as the original file just loaded into the RAM*/
size_t png_data_size; /*Size of `png_data` in bytes*/
error = lodepng_load_file(&png_data, &png_data_size, fn); /*Load the file*/
if(error) {
printf("error %u: %s\n", error, lodepng_error_text(error));
return LV_RES_INV;
}
/*Decode the PNG image*/
uint32_t png_width; /*Will be the width of the decoded image*/
uint32_t png_height; /*Will be the width of the decoded image*/
/*Decode the loaded image in ARGB8888 */
error = lodepng_decode32(&img_data, &png_width, &png_height, png_data, png_data_size);
lodepng_free(png_data); /*Free the loaded file*/
if(error) {
if(img_data != NULL) {
lodepng_free(img_data);
}
printf("error %u: %s\n", error, lodepng_error_text(error));
return LV_RES_INV;
}
/*Convert the image to the system's color depth*/
convert_color_depth(img_data, png_width * png_height);
dsc->img_data = img_data;
return LV_RES_OK; /*The image is fully decoded. Return with its pointer*/
}
}
/*If it's a PNG file in a C array...*/
else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = dsc->src;
uint32_t png_width; /*No used, just required by he decoder*/
uint32_t png_height; /*No used, just required by he decoder*/
/*Decode the image in ARGB8888 */
error = lodepng_decode32(&img_data, &png_width, &png_height, img_dsc->data, img_dsc->data_size);
if(error) {
if(img_data != NULL) {
lodepng_free(img_data);
}
return LV_RES_INV;
}
/*Convert the image to the system's color depth*/
convert_color_depth(img_data, png_width * png_height);
dsc->img_data = img_data;
return LV_RES_OK; /*Return with its pointer*/
}
return LV_RES_INV; /*If not returned earlier then it failed*/
}
/**
* Free the allocated resources
*/
static void decoder_close(lv_img_decoder_t *decoder, lv_img_decoder_dsc_t *dsc)
{
(void)decoder; /*Unused*/
if (dsc->img_data) {
lodepng_free((uint8_t *)dsc->img_data);
dsc->img_data = NULL;
}
}
/**
* If the display is not in 32 bit format (ARGB888) then covert the image to the current color depth
* @param img the ARGB888 image
* @param px_cnt number of pixels in `img`
*/
static void convert_color_depth(uint8_t * img, uint32_t px_cnt)
{
#if LV_COLOR_DEPTH == 32
lv_color32_t * img_argb = (lv_color32_t*)img;
lv_color_t c;
lv_color_t * img_c = (lv_color_t *) img;
uint32_t i;
for(i = 0; i < px_cnt; i++) {
c = lv_color_make(img_argb[i].ch.red, img_argb[i].ch.green, img_argb[i].ch.blue);
img_c[i].ch.red = c.ch.blue;
img_c[i].ch.blue = c.ch.red;
}
#elif LV_COLOR_DEPTH == 16
lv_color32_t * img_argb = (lv_color32_t*)img;
lv_color_t c;
uint32_t i;
for(i = 0; i < px_cnt; i++) {
c = lv_color_make(img_argb[i].ch.blue, img_argb[i].ch.green, img_argb[i].ch.red);
img[i*3 + 2] = img_argb[i].ch.alpha;
img[i*3 + 1] = c.full >> 8;
img[i*3 + 0] = c.full & 0xFF;
}
#elif LV_COLOR_DEPTH == 8
lv_color32_t * img_argb = (lv_color32_t*)img;
lv_color_t c;
uint32_t i;
for(i = 0; i < px_cnt; i++) {
c = lv_color_make(img_argb[i].red, img_argb[i].green, img_argb[i].blue);
img[i*2 + 1] = img_argb[i].alpha;
img[i*2 + 0] = c.full
}
#endif
}