1 /**
2  * @file lv_barcode.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../core/lv_obj_class_private.h"
10 #include "lv_barcode_private.h"
11 #include "../../lvgl.h"
12 
13 #if LV_USE_BARCODE
14 
15 #include "code128.h"
16 
17 /*********************
18  *      DEFINES
19  *********************/
20 #define MY_CLASS (&lv_barcode_class)
21 
22 /**********************
23  *      TYPEDEFS
24  **********************/
25 
26 /**********************
27  *  STATIC PROTOTYPES
28  **********************/
29 static void lv_barcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
30 static void lv_barcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
31 static bool lv_barcode_change_buf_size(lv_obj_t * obj, int32_t w, int32_t h);
32 static void lv_barcode_clear(lv_obj_t * obj);
33 
34 /**********************
35  *  STATIC VARIABLES
36  **********************/
37 
38 const lv_obj_class_t lv_barcode_class = {
39     .constructor_cb = lv_barcode_constructor,
40     .destructor_cb = lv_barcode_destructor,
41     .width_def = LV_SIZE_CONTENT,
42     .instance_size = sizeof(lv_barcode_t),
43     .base_class = &lv_canvas_class,
44     .name = "barcode",
45 };
46 
47 /**********************
48  *      MACROS
49  **********************/
50 
51 /**********************
52  *   GLOBAL FUNCTIONS
53  **********************/
54 
lv_barcode_create(lv_obj_t * parent)55 lv_obj_t * lv_barcode_create(lv_obj_t * parent)
56 {
57     LV_LOG_INFO("begin");
58     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
59     lv_obj_class_init_obj(obj);
60     return obj;
61 }
62 
lv_barcode_set_dark_color(lv_obj_t * obj,lv_color_t color)63 void lv_barcode_set_dark_color(lv_obj_t * obj, lv_color_t color)
64 {
65     LV_ASSERT_OBJ(obj, MY_CLASS);
66 
67     lv_barcode_t * barcode = (lv_barcode_t *)obj;
68     barcode->dark_color = color;
69 }
70 
lv_barcode_set_light_color(lv_obj_t * obj,lv_color_t color)71 void lv_barcode_set_light_color(lv_obj_t * obj, lv_color_t color)
72 {
73     LV_ASSERT_OBJ(obj, MY_CLASS);
74 
75     lv_barcode_t * barcode = (lv_barcode_t *)obj;
76     barcode->light_color = color;
77 }
78 
lv_barcode_set_scale(lv_obj_t * obj,uint16_t scale)79 void lv_barcode_set_scale(lv_obj_t * obj, uint16_t scale)
80 {
81     LV_ASSERT_OBJ(obj, MY_CLASS);
82 
83     if(scale == 0) {
84         scale = 1;
85     }
86 
87     lv_barcode_t * barcode = (lv_barcode_t *)obj;
88     barcode->scale = scale;
89 }
90 
lv_barcode_set_direction(lv_obj_t * obj,lv_dir_t direction)91 void lv_barcode_set_direction(lv_obj_t * obj, lv_dir_t direction)
92 {
93     LV_ASSERT_OBJ(obj, MY_CLASS);
94 
95     lv_barcode_t * barcode = (lv_barcode_t *)obj;
96     barcode->direction = direction;
97 }
98 
lv_barcode_set_tiled(lv_obj_t * obj,bool tiled)99 void lv_barcode_set_tiled(lv_obj_t * obj, bool tiled)
100 {
101     LV_ASSERT_OBJ(obj, MY_CLASS);
102 
103     lv_barcode_t * barcode = (lv_barcode_t *)obj;
104     barcode->tiled = tiled;
105     lv_image_set_inner_align(obj, tiled ? LV_IMAGE_ALIGN_TILE : LV_IMAGE_ALIGN_DEFAULT);
106 }
107 
lv_barcode_update(lv_obj_t * obj,const char * data)108 lv_result_t lv_barcode_update(lv_obj_t * obj, const char * data)
109 {
110     LV_ASSERT_OBJ(obj, MY_CLASS);
111     LV_ASSERT_NULL(data);
112 
113     if(data == NULL || lv_strlen(data) == 0) {
114         LV_LOG_WARN("data is empty");
115         lv_barcode_clear(obj);
116         return LV_RESULT_INVALID;
117     }
118 
119     size_t len = code128_estimate_len(data);
120     LV_LOG_INFO("data: %s, len = %zu", data, len);
121 
122     char * out_buf = lv_malloc(len);
123     LV_ASSERT_MALLOC(out_buf);
124     if(!out_buf) {
125         LV_LOG_ERROR("malloc failed for out_buf");
126         lv_barcode_clear(obj);
127         return LV_RESULT_INVALID;
128     }
129 
130     int32_t barcode_w = (int32_t) code128_encode_gs1(data, out_buf, len);
131     LV_LOG_INFO("barcode width = %" LV_PRId32, barcode_w);
132 
133     lv_barcode_t * barcode = (lv_barcode_t *)obj;
134     LV_ASSERT(barcode->scale > 0);
135     uint16_t scale = barcode->scale;
136 
137     int32_t buf_w;
138     int32_t buf_h;
139 
140     if(barcode->tiled) {
141         buf_w = (barcode->direction == LV_DIR_HOR) ? barcode_w * scale : 1;
142         buf_h = (barcode->direction == LV_DIR_VER) ? barcode_w * scale : 1;
143     }
144     else {
145         lv_obj_update_layout(obj);
146         buf_w = (barcode->direction == LV_DIR_HOR) ? barcode_w * scale : lv_obj_get_width(obj);
147         buf_h = (barcode->direction == LV_DIR_VER) ? barcode_w * scale : lv_obj_get_height(obj);
148     }
149 
150     if(!lv_barcode_change_buf_size(obj, buf_w, buf_h)) {
151         lv_barcode_clear(obj);
152         lv_free(out_buf);
153         return LV_RESULT_INVALID;
154     }
155 
156     /* Temporarily disable invalidation to improve the efficiency of lv_canvas_set_px */
157     lv_display_enable_invalidation(lv_obj_get_display(obj), false);
158 
159     lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(obj);
160     uint32_t stride = draw_buf->header.stride;
161     const lv_color_t color = lv_color_hex(1);
162 
163     /* Clear the canvas */
164     lv_draw_buf_clear(draw_buf, NULL);
165 
166     /* Set the palette */
167     lv_canvas_set_palette(obj, 0, lv_color_to_32(barcode->light_color, LV_OPA_COVER));
168     lv_canvas_set_palette(obj, 1, lv_color_to_32(barcode->dark_color, LV_OPA_COVER));
169 
170     for(int32_t x = 0; x < barcode_w; x++) {
171         /*skip empty data*/
172         if(out_buf[x] == 0) {
173             continue;
174         }
175 
176         for(uint16_t i = 0; i < scale; i++) {
177             int32_t offset = x * scale + i;
178             if(barcode->direction == LV_DIR_HOR) {
179                 lv_canvas_set_px(obj, offset, 0, color, LV_OPA_COVER);
180             }
181             else { /*LV_DIR_VER*/
182                 if(barcode->tiled) {
183                     lv_canvas_set_px(obj, 0, offset, color, LV_OPA_COVER);
184                 }
185                 else {
186                     uint8_t * dest = lv_draw_buf_goto_xy(draw_buf, 0, offset);
187                     lv_memset(dest, 0xFF, stride);
188                 }
189             }
190         }
191     }
192 
193     /* Copy pixels by row */
194     if(!barcode->tiled && barcode->direction == LV_DIR_HOR && buf_h > 1) {
195         /* Skip the first row */
196         int32_t h = buf_h - 1;
197         const uint8_t * src = lv_draw_buf_goto_xy(draw_buf, 0, 0);
198         uint8_t * dest = lv_draw_buf_goto_xy(draw_buf, 0, 1);
199         while(h--) {
200             lv_memcpy(dest, src, stride);
201             dest += stride;
202         }
203     }
204 
205     /* invalidate the canvas to refresh it */
206     lv_display_enable_invalidation(lv_obj_get_display(obj), true);
207     lv_obj_invalidate(obj);
208 
209     lv_free(out_buf);
210 
211     return LV_RESULT_OK;
212 }
213 
lv_barcode_get_dark_color(lv_obj_t * obj)214 lv_color_t lv_barcode_get_dark_color(lv_obj_t * obj)
215 {
216     LV_ASSERT_OBJ(obj, MY_CLASS);
217 
218     lv_barcode_t * barcode = (lv_barcode_t *)obj;
219     return barcode->dark_color;
220 }
221 
lv_barcode_get_light_color(lv_obj_t * obj)222 lv_color_t lv_barcode_get_light_color(lv_obj_t * obj)
223 {
224     LV_ASSERT_OBJ(obj, MY_CLASS);
225 
226     lv_barcode_t * barcode = (lv_barcode_t *)obj;
227     return barcode->light_color;
228 }
229 
lv_barcode_get_scale(lv_obj_t * obj)230 uint16_t lv_barcode_get_scale(lv_obj_t * obj)
231 {
232     LV_ASSERT_OBJ(obj, MY_CLASS);
233 
234     lv_barcode_t * barcode = (lv_barcode_t *)obj;
235     return barcode->scale;
236 }
237 
238 /**********************
239  *   STATIC FUNCTIONS
240  **********************/
241 
lv_barcode_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)242 static void lv_barcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
243 {
244     LV_UNUSED(class_p);
245 
246     lv_barcode_t * barcode = (lv_barcode_t *)obj;
247     barcode->dark_color = lv_color_black();
248     barcode->light_color = lv_color_white();
249     barcode->scale = 1;
250     barcode->direction = LV_DIR_HOR;
251     lv_image_set_inner_align(obj, LV_IMAGE_ALIGN_DEFAULT);
252 }
253 
lv_barcode_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)254 static void lv_barcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
255 {
256     LV_UNUSED(class_p);
257 
258     lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(obj);
259     if(draw_buf == NULL) return;
260     lv_image_cache_drop(draw_buf);
261 
262     /*@fixme destroy buffer in cache free_cb.*/
263     lv_draw_buf_destroy(draw_buf);
264 }
265 
lv_barcode_change_buf_size(lv_obj_t * obj,int32_t w,int32_t h)266 static bool lv_barcode_change_buf_size(lv_obj_t * obj, int32_t w, int32_t h)
267 {
268     LV_ASSERT_NULL(obj);
269     if(w <= 0 || h <= 0) {
270         LV_LOG_WARN("invalid size: %" LV_PRId32 " x %" LV_PRId32, w, h);
271         return false;
272     }
273 
274     lv_draw_buf_t * old_buf = lv_canvas_get_draw_buf(obj);
275     lv_draw_buf_t * new_buf = lv_draw_buf_create(w, h, LV_COLOR_FORMAT_I1, LV_STRIDE_AUTO);
276     if(new_buf == NULL) {
277         LV_LOG_ERROR("malloc failed for canvas buffer");
278         return false;
279     }
280 
281     lv_canvas_set_draw_buf(obj, new_buf);
282     LV_LOG_INFO("set canvas buffer: %p, width = %" LV_PRId32, (void *)new_buf, w);
283 
284     if(old_buf != NULL) lv_draw_buf_destroy(old_buf);
285     return true;
286 }
287 
lv_barcode_clear(lv_obj_t * obj)288 static void lv_barcode_clear(lv_obj_t * obj)
289 {
290     lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(obj);
291     if(!draw_buf) {
292         return;
293     }
294 
295     lv_draw_buf_clear(draw_buf, NULL);
296     lv_image_cache_drop(draw_buf);
297     lv_obj_invalidate(obj);
298 }
299 
300 #endif /*LV_USE_BARCODE*/
301