Skip to content

Commit

Permalink
Merge pull request #674 from marsman7/master
Browse files Browse the repository at this point in the history
Add QR-code object
  • Loading branch information
fvanroie authored Mar 24, 2024
2 parents d09452e + 6a819f5 commit 7dbf527
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 12 deletions.
160 changes: 152 additions & 8 deletions lib/lv_lib_qrcode/lv_qrcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* DEFINES
*********************/
#define QR_SIZE 150
#define LV_OBJX_NAME "lv_qrcode"

/**********************
* TYPEDEFS
Expand Down Expand Up @@ -47,18 +48,39 @@
*/
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("qrcode create started");

/*Create a basic object*/
lv_obj_t* new_qrcode = lv_canvas_create(parent, NULL);
LV_ASSERT_MEM(new_qrcode);
if(new_qrcode == NULL) return NULL;

/*Extend the canvas ext attr to qrcode ext attr*/
lv_qrcode_ext_t * ext = lv_obj_allocate_ext_attr(new_qrcode, sizeof(lv_qrcode_ext_t));
LV_ASSERT_MEM(ext);
if(ext == NULL) {
lv_obj_del(new_qrcode);
return NULL;
}

ext->text = NULL;
ext->static_txt = 0;
ext->dot.tmp_ptr = NULL;
ext->dot_tmp_alloc = 0;

/*Allocate QR bitmap buffer*/
uint32_t buf_size = LV_CANVAS_BUF_SIZE_INDEXED_1BIT(size, size);
uint8_t* buf = lv_mem_alloc(buf_size);
LV_ASSERT_MEM(buf);
if(buf == NULL) return NULL;

lv_obj_t* canvas = lv_canvas_create(parent, NULL);
lv_canvas_set_buffer(new_qrcode, buf, size, size, LV_IMG_CF_INDEXED_1BIT);
lv_canvas_set_palette(new_qrcode, 0, dark_color);
lv_canvas_set_palette(new_qrcode, 1, light_color);

lv_canvas_set_buffer(canvas, buf, size, size, LV_IMG_CF_INDEXED_1BIT);
lv_canvas_set_palette(canvas, 0, dark_color);
lv_canvas_set_palette(canvas, 1, light_color);
LV_LOG_INFO("qrcode create ready");

return canvas;
return new_qrcode;

// lv_img_dsc_t * img_buf = lv_img_buf_alloc(20, 20, LV_IMG_CF_TRUE_COLOR);
// if(img_buf == NULL) {
Expand Down Expand Up @@ -97,6 +119,8 @@ lv_res_t lv_qrcode_update(lv_obj_t* qrcode, const void* data, uint32_t data_len)
lv_canvas_fill_bg(qrcode, c, 0);
// lv_canvas_zoom();

LV_LOG_INFO("Update QR-code text with length : %d", data_len);

if(data_len > qrcodegen_BUFFER_LEN_MAX) return LV_RES_INV;

uint8_t qr0[qrcodegen_BUFFER_LEN_MAX];
Expand All @@ -106,21 +130,29 @@ lv_res_t lv_qrcode_update(lv_obj_t* qrcode, const void* data, uint32_t data_len)
bool ok = qrcodegen_encodeBinary(data_tmp, data_len, qr0, qrcodegen_Ecc_MEDIUM, qrcodegen_VERSION_MIN,
qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);

if(!ok) return LV_RES_INV;
if(!ok) {
LV_LOG_WARN("QR-code encoding error");
return LV_RES_INV;
}

lv_coord_t obj_w = lv_obj_get_width(qrcode);
int qr_size = qrcodegen_getSize(qr0);
int scale = obj_w / qr_size;
int qr_size = qrcodegen_getSize(qr0); // Number of vertical QR blocks
int scale = obj_w / (qr_size + 2); // +2 guaranteed a minimum of 1 block margin all round
int scaled = qr_size * scale;
int margin = (obj_w - scaled) / 2;

LV_LOG_INFO("Update QR-code data : obj_w[%d] QR moduls[%d] scale factor[%d]", obj_w, qr_size, scale);

/*Expand the qr encodet binary to canvas size*/
for(int y = 0; y < scaled; y++) {
for(int x = 0; x < scaled; x++) {
c.full = qrcodegen_getModule(qr0, x / scale, y / scale) ? 0 : 1;
lv_canvas_set_px(qrcode, x + margin, y + margin, c);
}
}

qrcode->signal_cb(qrcode, LV_SIGNAL_CLEANUP, NULL);

return LV_RES_OK;
}

Expand Down Expand Up @@ -168,6 +200,118 @@ lv_res_t lv_qrcode_update2(lv_obj_t* qrcode, const void* data, uint32_t data_len
return LV_RES_OK;
}

/**
* Set the data of a QR code object
* @param qrcode pointer to aQ code object
* @param text data to display, '\0' terminated character string. NULL to refresh with the current text.
* @return LV_RES_OK: if no error; LV_RES_INV: on error
*/
lv_res_t lv_qrcode_set_text(lv_obj_t * qrcode, const void * text)
{
LV_ASSERT_OBJ(qrcode, LV_OBJX_NAME);

lv_qrcode_ext_t * ext = lv_obj_get_ext_attr(qrcode);

/*If text is NULL then just refresh with the current text */
if(text == NULL) text = ext->text;

LV_ASSERT_STR(text);

if(ext->text == text && ext->static_txt == 0) {
/*If set its own text then reallocate it (maybe its size changed)*/
ext->text = lv_mem_realloc(ext->text, strlen(ext->text) + 1);

LV_ASSERT_MEM(ext->text);
if(ext->text == NULL) return LV_RES_INV;
}
else {
/*Free the old text*/
if(ext->text != NULL && ext->static_txt == 0) {
lv_mem_free(ext->text);
ext->text = NULL;
}

/*Get the size of the text*/
size_t len = strlen(text) + 1;

/*Allocate space for the new text*/
ext->text = lv_mem_alloc(len);
LV_ASSERT_MEM(ext->text);
if(ext->text == NULL) return LV_RES_INV;
strcpy(ext->text, text);

/*Now the text is dynamically allocated*/
ext->static_txt = 0;
}

return lv_qrcode_update(qrcode, ext->text, strlen(ext->text));
}

/**
* Set the data of a QR code object
* @param qrcode pointer to aQ code object
* @param text data to display, '\0' terminated character string. NULL to refresh with the current text.
* @return LV_RES_OK: if no error; LV_RES_INV: on error
*/
lv_res_t lv_qrcode_set_text_static(lv_obj_t * qrcode, const void * text)
{
LV_ASSERT_OBJ(qrcode, LV_OBJX_NAME);

lv_qrcode_ext_t * ext = lv_obj_get_ext_attr(qrcode);
if(ext->static_txt == 0 && ext->text != NULL) {
lv_mem_free(ext->text);
ext->text = NULL;
}

if(text != NULL) {
ext->static_txt = 1;
ext->text = (char *)text;
}

return lv_qrcode_update(qrcode, text, strlen(text));
}

/**
* Get the text of a qrcode
* @param qrcode pointer to a qrcode object
* @return the text of the qrcode
*/
char * lv_qrcode_get_text(const lv_obj_t * qrcode)
{
LV_ASSERT_OBJ(qrcode, LV_OBJX_NAME);

lv_qrcode_ext_t * ext = lv_obj_get_ext_attr(qrcode);

return ext->text;
}

/**
* Set the data of a QR code object
* @param qrcode pointer to aQ code object
* @param size width and height of the QR code
* @return LV_RES_OK: if no error; LV_RES_INV: on error
*/
lv_res_t lv_qrcode_set_size(lv_obj_t * qrcode, lv_coord_t size)
{
LV_ASSERT_OBJ(qrcode, LV_OBJX_NAME);

lv_qrcode_ext_t * ext = lv_obj_get_ext_attr(qrcode);

/*Reallocate QR bitmap buffer*/
uint32_t new_buf_size = LV_CANVAS_BUF_SIZE_INDEXED_1BIT(size, size);
uint8_t* buf = lv_mem_realloc((void *)ext->canvas.dsc.data, new_buf_size);
LV_ASSERT_MEM(buf);
if(buf == NULL) return LV_RES_INV;

lv_canvas_set_buffer(qrcode, buf, size, size, LV_IMG_CF_INDEXED_1BIT);

lv_qrcode_update(qrcode, ext->text, strlen(ext->text));

// qrcode->signal_cb(qrcode, LV_SIGNAL_CLEANUP, NULL);

return LV_RES_OK;
}

/**
* Delete a QR code object
* @param qrcode pointer to a QR code obejct
Expand Down
55 changes: 54 additions & 1 deletion lib/lv_lib_qrcode/lv_qrcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,28 @@ extern "C" {
* TYPEDEFS
**********************/

/** Data of qrcode*/
typedef struct {
/*Data of canvas, copyed from lv_canvas_ext_t*/
lv_canvas_ext_t canvas;
// lv_img_ext_t img; /*Ext. of ancestor*/
// lv_img_dsc_t dsc;

/*Inherited from 'base_obj' so no inherited ext.*/ /*Ext. of ancestor*/
/*New data for this type */
char * text; /*Text of the label*/

union {
char * tmp_ptr; /* Pointer to the allocated memory containing the character which are replaced by dots (Handled
by the library)*/
char tmp[4]; /* Directly store the characters if <=4 characters */
} dot;

uint8_t static_txt : 1; /*Flag to indicate the text is static*/
uint8_t dot_tmp_alloc : 1; /*True if dot_tmp has been allocated. False if dot_tmp directly holds up to 4 bytes of
characters */
} lv_qrcode_ext_t;

/**********************
* GLOBAL PROTOTYPES
**********************/
Expand All @@ -39,13 +61,44 @@ lv_obj_t * lv_qrcode_create(lv_obj_t * parent, lv_coord_t size, lv_color_t dark_

/**
* Set the data of a QR code object
* @param qrcode pointer to aQ code object
* @param qrcode pointer to QR 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);

/**
* Set the data of a QR code object
* @param qrcode pointer to QR code object
* @param text data to display, '\0' terminated character string. NULL to refresh with the current text.
* @return LV_RES_OK: if no error; LV_RES_INV: on error
*/
lv_res_t lv_qrcode_set_text(lv_obj_t * qrcode, const void * text);

/**
* Set the data of a QR code object
* @param qrcode pointer to QR code object
* @param text data to display, '\0' terminated character string. NULL to refresh with the current text.
* @return LV_RES_OK: if no error; LV_RES_INV: on error
*/
lv_res_t lv_qrcode_set_text_static(lv_obj_t * qrcode, const void * text);

/**
* Get the text of a qrcode
* @param qrcode pointer to a qrcode object
* @return the text of the qrcode
*/
char * lv_qrcode_get_text(const lv_obj_t * qrcode);

/**
* Set the data of a QR code object
* @param qrcode pointer to QR code object
* @param size width and height of the QR code
* @return LV_RES_OK: if no error; LV_RES_INV: on error
*/
lv_res_t lv_qrcode_set_size(lv_obj_t * qrcode, lv_coord_t size);

/**
* Delete a QR code object
* @param qrcode pointer to a QR code object
Expand Down
10 changes: 10 additions & 0 deletions lib/lv_lib_qrcode/qrcodegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ static const int PENALTY_N4 = 10;
bool qrcodegen_encodeText(const char* text, uint8_t tempBuffer[], uint8_t qrcode[], enum qrcodegen_Ecc ecl,
int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl)
{
/* preventing crashes */
if (maxVersion > qrcodegen_VERSION_MAX_LIMIT) {
qrcode[0] = 0; // Set size to invalid value for safety
return false;
}

size_t textLen = strlen(text);
if(textLen == 0)
Expand Down Expand Up @@ -172,6 +177,11 @@ bool qrcodegen_encodeText(const char* text, uint8_t tempBuffer[], uint8_t qrcode
bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], enum qrcodegen_Ecc ecl,
int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl)
{
/* preventing crashes */
if (maxVersion > qrcodegen_VERSION_MAX_LIMIT) {
qrcode[0] = 0; // Set size to invalid value for safety
return false;
}

struct qrcodegen_Segment seg;
seg.mode = qrcodegen_Mode_BYTE;
Expand Down
19 changes: 17 additions & 2 deletions lib/lv_lib_qrcode/qrcodegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,23 @@ struct qrcodegen_Segment

/*---- Macro constants and functions ----*/

#define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard
#define qrcodegen_VERSION_MAX 3 // The maximum version number supported in the QR Code Model 2 standard
/* Version number supported in the QR Code Model 2 standard
* higher version numbers can process longer texts but require more memory and computing time.
*
* VERSION_MAX = 3 : max text length to encode 42 alphanumeric characters
* VERSION_MAX = 5 : max text length to encode 85 alphanumeric characters
* VERSION_MAX = 7 : max text length to encode 122 alphanumeric characters
* VERSION_MAX = 9 : max text length to encode 180 alphanumeric characters
* VERSION_MAX = 11 : max text length to encode 251 alphanumeric characters
* VERSION_MAX = 15 : max text length to encode 412 alphanumeric characters
* and so on
* 25 is the highest posible version, see below
*/
#define qrcodegen_VERSION_MIN 1 // The minimum version number
#ifndef qrcodegen_VERSION_MAX
#define qrcodegen_VERSION_MAX 7 // The maximum version number
#endif
#define qrcodegen_VERSION_MAX_LIMIT 25

// Calculates the number of bytes needed to store any QR Code up to and including the given version number,
// as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];'
Expand Down
19 changes: 18 additions & 1 deletion src/hasp/hasp_attribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
#endif
/*** Image Improvement ***/

#if HASP_USE_QRCODE > 0
#include "lv_qrcode.h"
#endif

LV_FONT_DECLARE(unscii_8_icon);
extern const char** btnmatrix_default_map; // memory pointer to lvgl default btnmatrix map
extern const char* msgbox_default_map[]; // memory pointer to lvgl default btnmatrix map
Expand Down Expand Up @@ -394,6 +398,7 @@ static void hasp_attribute_get_part_state_new(lv_obj_t* obj, const char* attr_in
case LV_HASP_IMGBTN:
case LV_HASP_OBJECT:
case LV_HASP_TAB:
case LV_HASP_QRCODE:
part = LV_BTN_PART_MAIN;
break;

Expand Down Expand Up @@ -1752,7 +1757,8 @@ static hasp_attribute_type_t attribute_common_text(lv_obj_t* obj, uint16_t attr_
#if LV_USE_WIN != 0
{LV_HASP_WINDOW, ATTR_TEXT, lv_win_set_title, lv_win_get_title},
#endif
{LV_HASP_MSGBOX, ATTR_TEXT, my_msgbox_set_text, lv_msgbox_get_text}
{LV_HASP_MSGBOX, ATTR_TEXT, my_msgbox_set_text, lv_msgbox_get_text},
{LV_HASP_QRCODE, ATTR_TEXT, my_qrcode_set_text, my_qrcode_get_text}
};

for(int i = 0; i < sizeof(list) / sizeof(list[0]); i++) {
Expand Down Expand Up @@ -2446,6 +2452,16 @@ static hasp_attribute_type_t attribute_common_int(lv_obj_t* obj, uint16_t attr_h
val = lv_obj_get_ext_click_pad_top(obj);
break; // attribute_found

case ATTR_SIZE:
if(obj_check_type(obj, LV_HASP_QRCODE)) {
if(update) {
lv_qrcode_set_size(obj, val);
} else {
val = lv_obj_get_width(obj);
}
}
break;

default:
return HASP_ATTR_TYPE_NOT_FOUND; // attribute_not found
}
Expand Down Expand Up @@ -2648,6 +2664,7 @@ void hasp_process_obj_attribute(lv_obj_t* obj, const char* attribute, const char
case ATTR_OPACITY:
case ATTR_EXT_CLICK_H:
case ATTR_EXT_CLICK_V:
case ATTR_SIZE:
val = strtol(payload, nullptr, DEC);
ret = attribute_common_int(obj, attr_hash, val, update);
break;
Expand Down
Loading

0 comments on commit 7dbf527

Please sign in to comment.