1 /**
2  * @file lv_qrcode.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../core/lv_obj_class_private.h"
10 #include "lv_qrcode_private.h"
11 
12 #if LV_USE_QRCODE
13 
14 #include "qrcodegen.h"
15 
16 /*********************
17  *      DEFINES
18  *********************/
19 #define MY_CLASS (&lv_qrcode_class)
20 
21 /**********************
22  *      TYPEDEFS
23  **********************/
24 
25 /**********************
26  *  STATIC PROTOTYPES
27  **********************/
28 static void lv_qrcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
29 static void lv_qrcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
30 
31 /**********************
32  *  STATIC VARIABLES
33  **********************/
34 
35 const lv_obj_class_t lv_qrcode_class = {
36     .constructor_cb = lv_qrcode_constructor,
37     .destructor_cb = lv_qrcode_destructor,
38     .instance_size = sizeof(lv_qrcode_t),
39     .base_class = &lv_canvas_class,
40     .name = "qrcode",
41 };
42 
43 /**********************
44  *      MACROS
45  **********************/
46 
47 /**********************
48  *   GLOBAL FUNCTIONS
49  **********************/
50 
lv_qrcode_create(lv_obj_t * parent)51 lv_obj_t * lv_qrcode_create(lv_obj_t * parent)
52 {
53     LV_LOG_INFO("begin");
54     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
55     lv_obj_class_init_obj(obj);
56     return obj;
57 }
58 
lv_qrcode_set_size(lv_obj_t * obj,int32_t size)59 void lv_qrcode_set_size(lv_obj_t * obj, int32_t size)
60 {
61     LV_ASSERT_OBJ(obj, MY_CLASS);
62 
63     lv_draw_buf_t * old_buf = lv_canvas_get_draw_buf(obj);
64     lv_draw_buf_t * new_buf = lv_draw_buf_create(size, size, LV_COLOR_FORMAT_I1, LV_STRIDE_AUTO);
65     if(new_buf == NULL) {
66         LV_LOG_ERROR("malloc failed for canvas buffer");
67         return;
68     }
69 
70     lv_canvas_set_draw_buf(obj, new_buf);
71     LV_LOG_INFO("set canvas buffer: %p, size = %d", (void *)new_buf, (int)size);
72 
73     /*Clear canvas buffer*/
74     lv_draw_buf_clear(new_buf, NULL);
75 
76     if(old_buf != NULL) lv_draw_buf_destroy(old_buf);
77 }
78 
lv_qrcode_set_dark_color(lv_obj_t * obj,lv_color_t color)79 void lv_qrcode_set_dark_color(lv_obj_t * obj, lv_color_t color)
80 {
81     LV_ASSERT_OBJ(obj, MY_CLASS);
82     lv_qrcode_t * qrcode = (lv_qrcode_t *)obj;
83     qrcode->dark_color = color;
84 }
85 
lv_qrcode_set_light_color(lv_obj_t * obj,lv_color_t color)86 void lv_qrcode_set_light_color(lv_obj_t * obj, lv_color_t color)
87 {
88     LV_ASSERT_OBJ(obj, MY_CLASS);
89     lv_qrcode_t * qrcode = (lv_qrcode_t *)obj;
90     qrcode->light_color = color;
91 }
92 
lv_qrcode_update(lv_obj_t * obj,const void * data,uint32_t data_len)93 lv_result_t lv_qrcode_update(lv_obj_t * obj, const void * data, uint32_t data_len)
94 {
95     LV_ASSERT_OBJ(obj, MY_CLASS);
96     lv_qrcode_t * qrcode = (lv_qrcode_t *)obj;
97 
98     lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(obj);
99     if(draw_buf == NULL) {
100         LV_LOG_ERROR("canvas draw buffer is NULL");
101         return LV_RESULT_INVALID;
102     }
103 
104     lv_draw_buf_clear(draw_buf, NULL);
105     lv_canvas_set_palette(obj, 0, lv_color_to_32(qrcode->light_color, LV_OPA_COVER));
106     lv_canvas_set_palette(obj, 1, lv_color_to_32(qrcode->dark_color, LV_OPA_COVER));
107     lv_image_cache_drop(draw_buf);
108 
109     lv_obj_invalidate(obj);
110 
111     if(data_len > qrcodegen_BUFFER_LEN_MAX) return LV_RESULT_INVALID;
112 
113     int32_t qr_version = qrcodegen_getMinFitVersion(qrcodegen_Ecc_MEDIUM, data_len);
114     if(qr_version <= 0) return LV_RESULT_INVALID;
115     int32_t qr_size = qrcodegen_version2size(qr_version);
116     if(qr_size <= 0) return LV_RESULT_INVALID;
117     int32_t scale = draw_buf->header.w / qr_size;
118     if(scale <= 0) return LV_RESULT_INVALID;
119 
120     /* Pick the largest QR code that still maintains scale. */
121     for(int32_t i = qr_version + 1; i < qrcodegen_VERSION_MAX; i++) {
122         if(qrcodegen_version2size(i) * scale > draw_buf->header.w)
123             break;
124 
125         qr_version = i;
126     }
127     qr_size = qrcodegen_version2size(qr_version);
128 
129     uint8_t * qr0 = lv_malloc(qrcodegen_BUFFER_LEN_FOR_VERSION(qr_version));
130     LV_ASSERT_MALLOC(qr0);
131     uint8_t * data_tmp = lv_malloc(qrcodegen_BUFFER_LEN_FOR_VERSION(qr_version));
132     LV_ASSERT_MALLOC(data_tmp);
133     lv_memcpy(data_tmp, data, data_len);
134 
135     bool ok = qrcodegen_encodeBinary(data_tmp, data_len,
136                                      qr0, qrcodegen_Ecc_MEDIUM,
137                                      qr_version, qr_version,
138                                      qrcodegen_Mask_AUTO, true);
139 
140     if(!ok) {
141         lv_free(qr0);
142         lv_free(data_tmp);
143         return LV_RESULT_INVALID;
144     }
145 
146     /* Temporarily disable invalidation to improve the efficiency of lv_canvas_set_px */
147     lv_display_enable_invalidation(lv_obj_get_display(obj), false);
148 
149     int32_t obj_w = draw_buf->header.w;
150     qr_size = qrcodegen_getSize(qr0);
151     scale = obj_w / qr_size;
152     int scaled = qr_size * scale;
153     int margin = (obj_w - scaled) / 2;
154     uint8_t * buf_u8 = (uint8_t *)draw_buf->data + 8;    /*+8 skip the palette*/
155     lv_color_t c = lv_color_hex(1);
156 
157     /* Copy the qr code canvas:
158      * A simple `lv_canvas_set_px` would work but it's slow for so many pixels.
159      * So buffer 1 byte (8 px) from the qr code and set it in the canvas image */
160     uint32_t row_byte_cnt = draw_buf->header.stride;
161     int y;
162     for(y = margin; y < scaled + margin; y += scale) {
163         uint8_t b = 0;
164         uint8_t p = 0;
165         bool aligned = false;
166         int x;
167         for(x = margin; x < scaled + margin; x++) {
168             bool a = qrcodegen_getModule(qr0, (x - margin) / scale, (y - margin) / scale);
169 
170             if(aligned == false && (x & 0x7) == 0) aligned = true;
171 
172             if(aligned == false) {
173                 if(a) {
174                     lv_canvas_set_px(obj, x, y, c, LV_OPA_COVER);
175                 }
176             }
177             else {
178                 if(!a) b |= (1 << (7 - p));
179                 p++;
180                 if(p == 8) {
181                     uint32_t px = row_byte_cnt * y + (x >> 3);
182                     buf_u8[px] = ~b;
183                     b = 0;
184                     p = 0;
185                 }
186             }
187         }
188 
189         /*Process the last byte of the row*/
190         if(p) {
191             /*Make the rest of the bits white*/
192             b |= (1 << (8 - p)) - 1;
193 
194             uint32_t px = row_byte_cnt * y + (x >> 3);
195             buf_u8[px] = ~b;
196         }
197 
198         /*The Qr is probably scaled so simply to the repeated rows*/
199         int s;
200         const uint8_t * row_ori = buf_u8 + row_byte_cnt * y;
201         for(s = 1; s < scale; s++) {
202             lv_memcpy((uint8_t *)buf_u8 + row_byte_cnt * (y + s), row_ori, row_byte_cnt);
203         }
204     }
205 
206     /* invalidate the canvas to refresh it */
207     lv_display_enable_invalidation(lv_obj_get_display(obj), true);
208 
209     lv_free(qr0);
210     lv_free(data_tmp);
211     return LV_RESULT_OK;
212 }
213 
214 /**********************
215  *   STATIC FUNCTIONS
216  **********************/
217 
lv_qrcode_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)218 static void lv_qrcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
219 {
220     LV_UNUSED(class_p);
221 
222     /*Set default size*/
223     lv_qrcode_set_size(obj, LV_DPI_DEF);
224 
225     /*Set default color*/
226     lv_qrcode_set_dark_color(obj, lv_color_black());
227     lv_qrcode_set_light_color(obj, lv_color_white());
228 }
229 
lv_qrcode_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)230 static void lv_qrcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
231 {
232     LV_UNUSED(class_p);
233 
234     lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(obj);
235     if(draw_buf == NULL) return;
236     lv_image_cache_drop(draw_buf);
237 
238     /*@fixme destroy buffer in cache free_cb.*/
239     lv_draw_buf_destroy(draw_buf);
240 }
241 
242 #endif /*LV_USE_QRCODE*/
243