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