1 /**
2 * @file lv_img.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_img.h"
10 #if LV_USE_IMG != 0
11
12 /*Testing of dependencies*/
13 #if LV_USE_LABEL == 0
14 #error "lv_img: lv_label is required. Enable it in lv_conf.h (LV_USE_LABEL 1) "
15 #endif
16
17 #include "../lv_themes/lv_theme.h"
18 #include "../lv_draw/lv_img_decoder.h"
19 #include "../lv_misc/lv_fs.h"
20 #include "../lv_misc/lv_txt.h"
21 #include "../lv_misc/lv_log.h"
22
23 /*********************
24 * DEFINES
25 *********************/
26
27 /**********************
28 * TYPEDEFS
29 **********************/
30
31 /**********************
32 * STATIC PROTOTYPES
33 **********************/
34 static bool lv_img_design(lv_obj_t * img, const lv_area_t * mask, lv_design_mode_t mode);
35 static lv_res_t lv_img_signal(lv_obj_t * img, lv_signal_t sign, void * param);
36
37 /**********************
38 * STATIC VARIABLES
39 **********************/
40 static lv_signal_cb_t ancestor_signal;
41
42 /**********************
43 * MACROS
44 **********************/
45
46 /**********************
47 * GLOBAL FUNCTIONS
48 **********************/
49
50 /**
51 * Create an image objects
52 * @param par pointer to an object, it will be the parent of the new button
53 * @param copy pointer to a image object, if not NULL then the new object will be copied from it
54 * @return pointer to the created image
55 */
lv_img_create(lv_obj_t * par,const lv_obj_t * copy)56 lv_obj_t * lv_img_create(lv_obj_t * par, const lv_obj_t * copy)
57 {
58 LV_LOG_TRACE("image create started");
59
60 lv_obj_t * new_img = NULL;
61
62 /*Create a basic object*/
63 new_img = lv_obj_create(par, copy);
64 lv_mem_assert(new_img);
65 if(new_img == NULL) return NULL;
66
67 if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_img);
68
69 /*Extend the basic object to image object*/
70 lv_img_ext_t * ext = lv_obj_allocate_ext_attr(new_img, sizeof(lv_img_ext_t));
71 lv_mem_assert(ext);
72 if(ext == NULL) return NULL;
73
74 ext->src = NULL;
75 ext->src_type = LV_IMG_SRC_UNKNOWN;
76 ext->cf = LV_IMG_CF_UNKNOWN;
77 ext->w = lv_obj_get_width(new_img);
78 ext->h = lv_obj_get_height(new_img);
79 ext->auto_size = 1;
80 ext->offset.x = 0;
81 ext->offset.y = 0;
82
83 /*Init the new object*/
84 lv_obj_set_signal_cb(new_img, lv_img_signal);
85 lv_obj_set_design_cb(new_img, lv_img_design);
86
87 if(copy == NULL) {
88 lv_obj_set_click(new_img, false);
89 /* Enable auto size for non screens
90 * because image screens are wallpapers
91 * and must be screen sized*/
92 if(par != NULL) {
93 ext->auto_size = 1;
94 lv_obj_set_style(new_img, NULL); /*Inherit the style by default*/
95 } else {
96 ext->auto_size = 0;
97 lv_obj_set_style(new_img, &lv_style_plain); /*Set a style for screens*/
98 }
99 } else {
100 lv_img_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
101 ext->auto_size = copy_ext->auto_size;
102 lv_img_set_src(new_img, copy_ext->src);
103
104 /*Refresh the style with new signal function*/
105 lv_obj_refresh_style(new_img);
106 }
107
108 LV_LOG_INFO("image created");
109
110 return new_img;
111 }
112
113 /*=====================
114 * Setter functions
115 *====================*/
116
117 /**
118 * Set the pixel map to display by the image
119 * @param img pointer to an image object
120 * @param data the image data
121 */
lv_img_set_src(lv_obj_t * img,const void * src_img)122 void lv_img_set_src(lv_obj_t * img, const void * src_img)
123 {
124 lv_img_src_t src_type = lv_img_src_get_type(src_img);
125 lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
126
127 #if LV_USE_LOG && LV_LOG_LEVEL >= LV_LOG_LEVEL_INFO
128 switch(src_type) {
129 case LV_IMG_SRC_FILE: LV_LOG_TRACE("lv_img_set_src: `LV_IMG_SRC_FILE` type found"); break;
130 case LV_IMG_SRC_VARIABLE: LV_LOG_TRACE("lv_img_set_src: `LV_IMG_SRC_VARIABLE` type found"); break;
131 case LV_IMG_SRC_SYMBOL: LV_LOG_TRACE("lv_img_set_src: `LV_IMG_SRC_SYMBOL` type found"); break;
132 default: LV_LOG_WARN("lv_img_set_src: unknown type");
133 }
134 #endif
135
136 /*If the new source type is unknown free the memories of the old source*/
137 if(src_type == LV_IMG_SRC_UNKNOWN) {
138 LV_LOG_WARN("lv_img_set_src: unknown image type");
139 if(ext->src_type == LV_IMG_SRC_SYMBOL || ext->src_type == LV_IMG_SRC_FILE) {
140 lv_mem_free(ext->src);
141 }
142 ext->src = NULL;
143 ext->src_type = LV_IMG_SRC_UNKNOWN;
144 return;
145 }
146
147 lv_img_header_t header;
148 lv_img_decoder_get_info(src_img, &header);
149
150 /*Save the source*/
151 if(src_type == LV_IMG_SRC_VARIABLE) {
152 LV_LOG_INFO("lv_img_set_src: `LV_IMG_SRC_VARIABLE` type found");
153
154 /*If memory was allocated because of the previous `src_type` then free it*/
155 if(ext->src_type == LV_IMG_SRC_FILE || ext->src_type == LV_IMG_SRC_SYMBOL) {
156 lv_mem_free(ext->src);
157 }
158 ext->src = src_img;
159 } else if(src_type == LV_IMG_SRC_FILE || src_type == LV_IMG_SRC_SYMBOL) {
160 /* If the new and the old src are the same then it was only a refresh.*/
161 if(ext->src != src_img) {
162 /*If memory was allocated because of the previous `src_type` then free it*/
163 if(ext->src_type == LV_IMG_SRC_FILE || ext->src_type == LV_IMG_SRC_SYMBOL) {
164 lv_mem_free(ext->src);
165 }
166 char * new_str = lv_mem_alloc(strlen(src_img) + 1);
167 lv_mem_assert(new_str);
168 if(new_str == NULL) return;
169 strcpy(new_str, src_img);
170 ext->src = new_str;
171 }
172 }
173
174 if(src_type == LV_IMG_SRC_SYMBOL) {
175 /*`lv_img_dsc_get_info` couldn't set the with and height of a font so set it here*/
176 const lv_style_t * style = lv_img_get_style(img, LV_IMG_STYLE_MAIN);
177 lv_point_t size;
178 lv_txt_get_size(&size, src_img, style->text.font, style->text.letter_space, style->text.line_space,
179 LV_COORD_MAX, LV_TXT_FLAG_NONE);
180 header.w = size.x;
181 header.h = size.y;
182 }
183
184 ext->src_type = src_type;
185 ext->w = header.w;
186 ext->h = header.h;
187 ext->cf = header.cf;
188
189 if(lv_img_get_auto_size(img) != false) {
190 lv_obj_set_size(img, ext->w, ext->h);
191 }
192
193 lv_obj_invalidate(img);
194 }
195
196 /**
197 * Enable the auto size feature.
198 * If enabled the object size will be same as the picture size.
199 * @param img pointer to an image
200 * @param en true: auto size enable, false: auto size disable
201 */
lv_img_set_auto_size(lv_obj_t * img,bool en)202 void lv_img_set_auto_size(lv_obj_t * img, bool en)
203 {
204 lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
205
206 ext->auto_size = (en == false ? 0 : 1);
207 }
208
209 /**
210 * Set an offset for the source of an image.
211 * so the image will be displayed from the new origin.
212 * @param img pointer to an image
213 * @param x: the new offset along x axis.
214 */
lv_img_set_offset_x(lv_obj_t * img,lv_coord_t x)215 void lv_img_set_offset_x(lv_obj_t * img, lv_coord_t x)
216 {
217 lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
218
219 if(x < ext->w - 1) {
220 ext->offset.x = x;
221 lv_obj_invalidate(img);
222 }
223 }
224
225 /**
226 * Set an offset for the source of an image.
227 * so the image will be displayed from the new origin.
228 * @param img pointer to an image
229 * @param y: the new offset along y axis.
230 */
lv_img_set_offset_y(lv_obj_t * img,lv_coord_t y)231 void lv_img_set_offset_y(lv_obj_t * img, lv_coord_t y)
232 {
233 lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
234
235 if(y < ext->h - 1) {
236 ext->offset.y = y;
237 lv_obj_invalidate(img);
238 }
239 }
240
241 /*=====================
242 * Getter functions
243 *====================*/
244
245 /**
246 * Get the source of the image
247 * @param img pointer to an image object
248 * @return the image source (symbol, file name or C array)
249 */
lv_img_get_src(lv_obj_t * img)250 const void * lv_img_get_src(lv_obj_t * img)
251 {
252 lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
253
254 return ext->src;
255 }
256
257 /**
258 * Get the name of the file set for an image
259 * @param img pointer to an image
260 * @return file name
261 */
lv_img_get_file_name(const lv_obj_t * img)262 const char * lv_img_get_file_name(const lv_obj_t * img)
263 {
264 lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
265
266 if(ext->src_type == LV_IMG_SRC_FILE)
267 return ext->src;
268 else
269 return "";
270 }
271
272 /**
273 * Get the auto size enable attribute
274 * @param img pointer to an image
275 * @return true: auto size is enabled, false: auto size is disabled
276 */
lv_img_get_auto_size(const lv_obj_t * img)277 bool lv_img_get_auto_size(const lv_obj_t * img)
278 {
279 lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
280
281 return ext->auto_size == 0 ? false : true;
282 }
283
284 /**
285 * Get the offset.x attribute of the img object.
286 * @param img pointer to an image
287 * @return offset.x value.
288 */
lv_img_get_offset_x(lv_obj_t * img)289 lv_coord_t lv_img_get_offset_x(lv_obj_t * img)
290 {
291 lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
292
293 return ext->offset.x;
294 }
295
296 /**
297 * Get the offset.y attribute of the img object.
298 * @param img pointer to an image
299 * @return offset.y value.
300 */
lv_img_get_offset_y(lv_obj_t * img)301 lv_coord_t lv_img_get_offset_y(lv_obj_t * img)
302 {
303 lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
304
305 return ext->offset.y;
306 }
307
308 /**********************
309 * STATIC FUNCTIONS
310 **********************/
311
312 /**
313 * Handle the drawing related tasks of the images
314 * @param img pointer to an object
315 * @param mask the object will be drawn only in this area
316 * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
317 * (return 'true' if yes)
318 * LV_DESIGN_DRAW: draw the object (always return 'true')
319 * LV_DESIGN_DRAW_POST: drawing after every children are drawn
320 * @param return true/false, depends on 'mode'
321 */
lv_img_design(lv_obj_t * img,const lv_area_t * mask,lv_design_mode_t mode)322 static bool lv_img_design(lv_obj_t * img, const lv_area_t * mask, lv_design_mode_t mode)
323 {
324 const lv_style_t * style = lv_obj_get_style(img);
325 lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
326
327 if(mode == LV_DESIGN_COVER_CHK) {
328 bool cover = false;
329 if(ext->src_type == LV_IMG_SRC_UNKNOWN || ext->src_type == LV_IMG_SRC_SYMBOL) return false;
330
331 if(ext->cf == LV_IMG_CF_TRUE_COLOR || ext->cf == LV_IMG_CF_RAW) cover = lv_area_is_in(mask, &img->coords);
332
333 return cover;
334 } else if(mode == LV_DESIGN_DRAW_MAIN) {
335 if(ext->h == 0 || ext->w == 0) return true;
336 lv_area_t coords;
337 lv_opa_t opa_scale = lv_obj_get_opa_scale(img);
338
339 lv_obj_get_coords(img, &coords);
340
341 if(ext->src_type == LV_IMG_SRC_FILE || ext->src_type == LV_IMG_SRC_VARIABLE) {
342 coords.x1 -= ext->offset.x;
343 coords.y1 -= ext->offset.y;
344
345 LV_LOG_TRACE("lv_img_design: start to draw image");
346 lv_area_t cords_tmp;
347 cords_tmp.y1 = coords.y1;
348 cords_tmp.y2 = coords.y1 + ext->h - 1;
349
350 for(; cords_tmp.y1 < coords.y2; cords_tmp.y1 += ext->h, cords_tmp.y2 += ext->h) {
351 cords_tmp.x1 = coords.x1;
352 cords_tmp.x2 = coords.x1 + ext->w - 1;
353 for(; cords_tmp.x1 < coords.x2; cords_tmp.x1 += ext->w, cords_tmp.x2 += ext->w) {
354 lv_draw_img(&cords_tmp, mask, ext->src, style, opa_scale);
355 }
356 }
357 } else if(ext->src_type == LV_IMG_SRC_SYMBOL) {
358 LV_LOG_TRACE("lv_img_design: start to draw symbol");
359 lv_style_t style_mod;
360 lv_style_copy(&style_mod, style);
361 style_mod.text.color = style->image.color;
362 lv_draw_label(&coords, mask, &style_mod, opa_scale, ext->src, LV_TXT_FLAG_NONE, NULL, -1, -1, NULL);
363 } else {
364 /*Trigger the error handler of image drawer*/
365 LV_LOG_WARN("lv_img_design: image source type is unknown");
366 lv_draw_img(&img->coords, mask, NULL, style, opa_scale);
367 }
368 }
369
370 return true;
371 }
372
373 /**
374 * Signal function of the image
375 * @param img pointer to an image object
376 * @param sign a signal type from lv_signal_t enum
377 * @param param pointer to a signal specific variable
378 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
379 */
lv_img_signal(lv_obj_t * img,lv_signal_t sign,void * param)380 static lv_res_t lv_img_signal(lv_obj_t * img, lv_signal_t sign, void * param)
381 {
382 lv_res_t res;
383
384 /* Include the ancient signal function */
385 res = ancestor_signal(img, sign, param);
386 if(res != LV_RES_OK) return res;
387
388 lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
389 if(sign == LV_SIGNAL_CLEANUP) {
390 if(ext->src_type == LV_IMG_SRC_FILE || ext->src_type == LV_IMG_SRC_SYMBOL) {
391 lv_mem_free(ext->src);
392 ext->src = NULL;
393 ext->src_type = LV_IMG_SRC_UNKNOWN;
394 }
395 } else if(sign == LV_SIGNAL_STYLE_CHG) {
396 /*Refresh the file name to refresh the symbol text size*/
397 if(ext->src_type == LV_IMG_SRC_SYMBOL) {
398 lv_img_set_src(img, ext->src);
399 }
400 } else if(sign == LV_SIGNAL_GET_TYPE) {
401 lv_obj_type_t * buf = param;
402 uint8_t i;
403 for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
404 if(buf->type[i] == NULL) break;
405 }
406 buf->type[i] = "lv_img";
407 }
408
409 return res;
410 }
411
412 #endif
413