216 lines
6.2 KiB
C
216 lines
6.2 KiB
C
|
/**
|
||
|
* @file lv_qrcode.c
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*********************
|
||
|
* INCLUDES
|
||
|
*********************/
|
||
|
#include "lv_qrcode.h"
|
||
|
#if LV_USE_QRCODE
|
||
|
|
||
|
#include "qrcodegen.h"
|
||
|
|
||
|
/*********************
|
||
|
* DEFINES
|
||
|
*********************/
|
||
|
#define MY_CLASS &lv_qrcode_class
|
||
|
|
||
|
/**********************
|
||
|
* TYPEDEFS
|
||
|
**********************/
|
||
|
|
||
|
/**********************
|
||
|
* STATIC PROTOTYPES
|
||
|
**********************/
|
||
|
static void lv_qrcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
|
||
|
static void lv_qrcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
|
||
|
|
||
|
/**********************
|
||
|
* STATIC VARIABLES
|
||
|
**********************/
|
||
|
|
||
|
const lv_obj_class_t lv_qrcode_class = {
|
||
|
.constructor_cb = lv_qrcode_constructor,
|
||
|
.destructor_cb = lv_qrcode_destructor,
|
||
|
.base_class = &lv_canvas_class
|
||
|
};
|
||
|
|
||
|
static lv_coord_t size_param;
|
||
|
static lv_color_t dark_color_param;
|
||
|
static lv_color_t light_color_param;
|
||
|
|
||
|
/**********************
|
||
|
* MACROS
|
||
|
**********************/
|
||
|
|
||
|
/**********************
|
||
|
* GLOBAL FUNCTIONS
|
||
|
**********************/
|
||
|
|
||
|
/**
|
||
|
* Create an empty QR code (an `lv_canvas`) object.
|
||
|
* @param parent point to an object where to create the QR code
|
||
|
* @param size width and height of the QR code
|
||
|
* @param dark_color dark color of the QR code
|
||
|
* @param light_color light color of the QR code
|
||
|
* @return pointer to the created QR code object
|
||
|
*/
|
||
|
lv_obj_t * lv_qrcode_create(lv_obj_t * parent, lv_coord_t size, lv_color_t dark_color, lv_color_t light_color)
|
||
|
{
|
||
|
LV_LOG_INFO("begin");
|
||
|
size_param = size;
|
||
|
light_color_param = light_color;
|
||
|
dark_color_param = dark_color;
|
||
|
|
||
|
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
|
||
|
lv_obj_class_init_obj(obj);
|
||
|
return obj;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the data of a QR code object
|
||
|
* @param qrcode pointer to aQ code object
|
||
|
* @param data data to display
|
||
|
* @param data_len length of data in bytes
|
||
|
* @return LV_RES_OK: if no error; LV_RES_INV: on error
|
||
|
*/
|
||
|
lv_res_t lv_qrcode_update(lv_obj_t * qrcode, const void * data, uint32_t data_len)
|
||
|
{
|
||
|
lv_color_t c;
|
||
|
c.full = 1;
|
||
|
lv_canvas_fill_bg(qrcode, c, LV_OPA_COVER);
|
||
|
|
||
|
if(data_len > qrcodegen_BUFFER_LEN_MAX) return LV_RES_INV;
|
||
|
|
||
|
lv_img_dsc_t * imgdsc = lv_canvas_get_img(qrcode);
|
||
|
|
||
|
int32_t qr_version = qrcodegen_getMinFitVersion(qrcodegen_Ecc_MEDIUM, data_len);
|
||
|
if(qr_version <= 0) return LV_RES_INV;
|
||
|
int32_t qr_size = qrcodegen_version2size(qr_version);
|
||
|
if(qr_size <= 0) return LV_RES_INV;
|
||
|
int32_t scale = imgdsc->header.w / qr_size;
|
||
|
if(scale <= 0) return LV_RES_INV;
|
||
|
int32_t remain = imgdsc->header.w % qr_size;
|
||
|
|
||
|
/* The qr version is incremented by four point */
|
||
|
uint32_t version_extend = remain / (scale << 2);
|
||
|
if(version_extend && qr_version < qrcodegen_VERSION_MAX) {
|
||
|
qr_version = qr_version + version_extend > qrcodegen_VERSION_MAX ?
|
||
|
qrcodegen_VERSION_MAX : qr_version + version_extend;
|
||
|
}
|
||
|
|
||
|
uint8_t * qr0 = lv_mem_alloc(qrcodegen_BUFFER_LEN_FOR_VERSION(qr_version));
|
||
|
LV_ASSERT_MALLOC(qr0);
|
||
|
uint8_t * data_tmp = lv_mem_alloc(qrcodegen_BUFFER_LEN_FOR_VERSION(qr_version));
|
||
|
LV_ASSERT_MALLOC(data_tmp);
|
||
|
lv_memcpy(data_tmp, data, data_len);
|
||
|
|
||
|
bool ok = qrcodegen_encodeBinary(data_tmp, data_len,
|
||
|
qr0, qrcodegen_Ecc_MEDIUM,
|
||
|
qr_version, qr_version,
|
||
|
qrcodegen_Mask_AUTO, true);
|
||
|
|
||
|
if(!ok) {
|
||
|
lv_mem_free(qr0);
|
||
|
lv_mem_free(data_tmp);
|
||
|
return LV_RES_INV;
|
||
|
}
|
||
|
|
||
|
lv_coord_t obj_w = imgdsc->header.w;
|
||
|
qr_size = qrcodegen_getSize(qr0);
|
||
|
scale = obj_w / qr_size;
|
||
|
int scaled = qr_size * scale;
|
||
|
int margin = (obj_w - scaled) / 2;
|
||
|
uint8_t * buf_u8 = (uint8_t *)imgdsc->data + 8; /*+8 skip the palette*/
|
||
|
|
||
|
/* Copy the qr code canvas:
|
||
|
* A simple `lv_canvas_set_px` would work but it's slow for so many pixels.
|
||
|
* So buffer 1 byte (8 px) from the qr code and set it in the canvas image */
|
||
|
uint32_t row_byte_cnt = (imgdsc->header.w + 7) >> 3;
|
||
|
int y;
|
||
|
for(y = margin; y < scaled + margin; y += scale) {
|
||
|
uint8_t b = 0;
|
||
|
uint8_t p = 0;
|
||
|
bool aligned = false;
|
||
|
int x;
|
||
|
for(x = margin; x < scaled + margin; x++) {
|
||
|
bool a = qrcodegen_getModule(qr0, (x - margin) / scale, (y - margin) / scale);
|
||
|
|
||
|
if(aligned == false && (x & 0x7) == 0) aligned = true;
|
||
|
|
||
|
if(aligned == false) {
|
||
|
c.full = a ? 0 : 1;
|
||
|
lv_canvas_set_px_color(qrcode, x, y, c);
|
||
|
}
|
||
|
else {
|
||
|
if(!a) b |= (1 << (7 - p));
|
||
|
p++;
|
||
|
if(p == 8) {
|
||
|
uint32_t px = row_byte_cnt * y + (x >> 3);
|
||
|
buf_u8[px] = b;
|
||
|
b = 0;
|
||
|
p = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*Process the last byte of the row*/
|
||
|
if(p) {
|
||
|
/*Make the rest of the bits white*/
|
||
|
b |= (1 << (8 - p)) - 1;
|
||
|
|
||
|
uint32_t px = row_byte_cnt * y + (x >> 3);
|
||
|
buf_u8[px] = b;
|
||
|
}
|
||
|
|
||
|
/*The Qr is probably scaled so simply to the repeated rows*/
|
||
|
int s;
|
||
|
const uint8_t * row_ori = buf_u8 + row_byte_cnt * y;
|
||
|
for(s = 1; s < scale; s++) {
|
||
|
lv_memcpy((uint8_t *)buf_u8 + row_byte_cnt * (y + s), row_ori, row_byte_cnt);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lv_mem_free(qr0);
|
||
|
lv_mem_free(data_tmp);
|
||
|
return LV_RES_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
void lv_qrcode_delete(lv_obj_t * qrcode)
|
||
|
{
|
||
|
lv_obj_del(qrcode);
|
||
|
}
|
||
|
|
||
|
/**********************
|
||
|
* STATIC FUNCTIONS
|
||
|
**********************/
|
||
|
|
||
|
static void lv_qrcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
|
||
|
{
|
||
|
LV_UNUSED(class_p);
|
||
|
|
||
|
uint32_t buf_size = LV_CANVAS_BUF_SIZE_INDEXED_1BIT(size_param, size_param);
|
||
|
uint8_t * buf = lv_mem_alloc(buf_size);
|
||
|
LV_ASSERT_MALLOC(buf);
|
||
|
if(buf == NULL) return;
|
||
|
|
||
|
lv_canvas_set_buffer(obj, buf, size_param, size_param, LV_IMG_CF_INDEXED_1BIT);
|
||
|
lv_canvas_set_palette(obj, 0, dark_color_param);
|
||
|
lv_canvas_set_palette(obj, 1, light_color_param);
|
||
|
}
|
||
|
|
||
|
static void lv_qrcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
|
||
|
{
|
||
|
LV_UNUSED(class_p);
|
||
|
|
||
|
lv_img_dsc_t * img = lv_canvas_get_img(obj);
|
||
|
lv_img_cache_invalidate_src(img);
|
||
|
lv_mem_free((void *)img->data);
|
||
|
img->data = NULL;
|
||
|
}
|
||
|
|
||
|
#endif /*LV_USE_QRCODE*/
|