1 /**
2 * @file lv_qrcode.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_qrcode.h"
10 #if LV_USE_QRCODE
11
12 #include "qrcodegen.h"
13
14 /*********************
15 * DEFINES
16 *********************/
17 #define MY_CLASS &lv_qrcode_class
18
19 /**********************
20 * TYPEDEFS
21 **********************/
22
23 /**********************
24 * STATIC PROTOTYPES
25 **********************/
26 static void lv_qrcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
27 static void lv_qrcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
28
29 /**********************
30 * STATIC VARIABLES
31 **********************/
32
33 const lv_obj_class_t lv_qrcode_class = {
34 .constructor_cb = lv_qrcode_constructor,
35 .destructor_cb = lv_qrcode_destructor,
36 .base_class = &lv_canvas_class
37 };
38
39 static lv_coord_t size_param;
40 static lv_color_t dark_color_param;
41 static lv_color_t light_color_param;
42
43 /**********************
44 * MACROS
45 **********************/
46
47 /**********************
48 * GLOBAL FUNCTIONS
49 **********************/
50
51 /**
52 * Create an empty QR code (an `lv_canvas`) object.
53 * @param parent point to an object where to create the QR code
54 * @param size width and height of the QR code
55 * @param dark_color dark color of the QR code
56 * @param light_color light color of the QR code
57 * @return pointer to the created QR code object
58 */
lv_qrcode_create(lv_obj_t * parent,lv_coord_t size,lv_color_t dark_color,lv_color_t light_color)59 lv_obj_t * lv_qrcode_create(lv_obj_t * parent, lv_coord_t size, lv_color_t dark_color, lv_color_t light_color)
60 {
61 LV_LOG_INFO("begin");
62 size_param = size;
63 light_color_param = light_color;
64 dark_color_param = dark_color;
65
66 lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
67 lv_obj_class_init_obj(obj);
68 return obj;
69
70 }
71
72 /**
73 * Set the data of a QR code object
74 * @param qrcode pointer to aQ code object
75 * @param data data to display
76 * @param data_len length of data in bytes
77 * @return LV_RES_OK: if no error; LV_RES_INV: on error
78 */
lv_qrcode_update(lv_obj_t * qrcode,const void * data,uint32_t data_len)79 lv_res_t lv_qrcode_update(lv_obj_t * qrcode, const void * data, uint32_t data_len)
80 {
81 lv_color_t c;
82 c.full = 1;
83 lv_canvas_fill_bg(qrcode, c, LV_OPA_COVER);
84
85 if(data_len > qrcodegen_BUFFER_LEN_MAX) return LV_RES_INV;
86
87 lv_img_dsc_t * imgdsc = lv_canvas_get_img(qrcode);
88
89 int32_t qr_version = qrcodegen_getMinFitVersion(qrcodegen_Ecc_MEDIUM, data_len);
90 if(qr_version <= 0) return LV_RES_INV;
91 int32_t qr_size = qrcodegen_version2size(qr_version);
92 if(qr_size <= 0) return LV_RES_INV;
93 int32_t scale = imgdsc->header.w / qr_size;
94 if(scale <= 0) return LV_RES_INV;
95 int32_t remain = imgdsc->header.w % qr_size;
96
97 /* The qr version is incremented by four point */
98 uint32_t version_extend = remain / (scale << 2);
99 if(version_extend && qr_version < qrcodegen_VERSION_MAX) {
100 qr_version = qr_version + version_extend > qrcodegen_VERSION_MAX ?
101 qrcodegen_VERSION_MAX : qr_version + version_extend;
102 }
103
104 uint8_t * qr0 = lv_mem_alloc(qrcodegen_BUFFER_LEN_FOR_VERSION(qr_version));
105 LV_ASSERT_MALLOC(qr0);
106 uint8_t * data_tmp = lv_mem_alloc(qrcodegen_BUFFER_LEN_FOR_VERSION(qr_version));
107 LV_ASSERT_MALLOC(data_tmp);
108 lv_memcpy(data_tmp, data, data_len);
109
110 bool ok = qrcodegen_encodeBinary(data_tmp, data_len,
111 qr0, qrcodegen_Ecc_MEDIUM,
112 qr_version, qr_version,
113 qrcodegen_Mask_AUTO, true);
114
115 if(!ok) {
116 lv_mem_free(qr0);
117 lv_mem_free(data_tmp);
118 return LV_RES_INV;
119 }
120
121 lv_coord_t obj_w = imgdsc->header.w;
122 qr_size = qrcodegen_getSize(qr0);
123 scale = obj_w / qr_size;
124 int scaled = qr_size * scale;
125 int margin = (obj_w - scaled) / 2;
126 uint8_t * buf_u8 = (uint8_t *)imgdsc->data + 8; /*+8 skip the palette*/
127
128 /* Copy the qr code canvas:
129 * A simple `lv_canvas_set_px` would work but it's slow for so many pixels.
130 * So buffer 1 byte (8 px) from the qr code and set it in the canvas image */
131 uint32_t row_byte_cnt = (imgdsc->header.w + 7) >> 3;
132 int y;
133 for(y = margin; y < scaled + margin; y += scale) {
134 uint8_t b = 0;
135 uint8_t p = 0;
136 bool aligned = false;
137 int x;
138 for(x = margin; x < scaled + margin; x++) {
139 bool a = qrcodegen_getModule(qr0, (x - margin) / scale, (y - margin) / scale);
140
141 if(aligned == false && (x & 0x7) == 0) aligned = true;
142
143 if(aligned == false) {
144 c.full = a ? 0 : 1;
145 lv_canvas_set_px_color(qrcode, x, y, c);
146 }
147 else {
148 if(!a) b |= (1 << (7 - p));
149 p++;
150 if(p == 8) {
151 uint32_t px = row_byte_cnt * y + (x >> 3);
152 buf_u8[px] = b;
153 b = 0;
154 p = 0;
155 }
156 }
157 }
158
159 /*Process the last byte of the row*/
160 if(p) {
161 /*Make the rest of the bits white*/
162 b |= (1 << (8 - p)) - 1;
163
164 uint32_t px = row_byte_cnt * y + (x >> 3);
165 buf_u8[px] = b;
166 }
167
168 /*The Qr is probably scaled so simply to the repeated rows*/
169 int s;
170 const uint8_t * row_ori = buf_u8 + row_byte_cnt * y;
171 for(s = 1; s < scale; s++) {
172 lv_memcpy((uint8_t *)buf_u8 + row_byte_cnt * (y + s), row_ori, row_byte_cnt);
173 }
174 }
175
176 lv_mem_free(qr0);
177 lv_mem_free(data_tmp);
178 return LV_RES_OK;
179 }
180
181
lv_qrcode_delete(lv_obj_t * qrcode)182 void lv_qrcode_delete(lv_obj_t * qrcode)
183 {
184 lv_obj_del(qrcode);
185 }
186
187 /**********************
188 * STATIC FUNCTIONS
189 **********************/
190
lv_qrcode_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)191 static void lv_qrcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
192 {
193 LV_UNUSED(class_p);
194
195 uint32_t buf_size = LV_CANVAS_BUF_SIZE_INDEXED_1BIT(size_param, size_param);
196 uint8_t * buf = lv_mem_alloc(buf_size);
197 LV_ASSERT_MALLOC(buf);
198 if(buf == NULL) return;
199
200 lv_canvas_set_buffer(obj, buf, size_param, size_param, LV_IMG_CF_INDEXED_1BIT);
201 lv_canvas_set_palette(obj, 0, dark_color_param);
202 lv_canvas_set_palette(obj, 1, light_color_param);
203 }
204
lv_qrcode_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)205 static void lv_qrcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
206 {
207 LV_UNUSED(class_p);
208
209 lv_img_dsc_t * img = lv_canvas_get_img(obj);
210 lv_img_cache_invalidate_src(img);
211 lv_mem_free((void *)img->data);
212 img->data = NULL;
213 }
214
215 #endif /*LV_USE_QRCODE*/
216