From 12b6a9b5be12375ce130f8b5db92552f2dc83857 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Sun, 8 Oct 2023 23:08:08 +0200 Subject: [PATCH] Add qrio.QRDecoder.find() to locate codes without decoding Fix #8452 --- shared-bindings/qrio/QRDecoder.c | 51 +++++++++++++++++++++----- shared-bindings/qrio/QRInfo.c | 63 ++++++++++++++++++++++++++++++++ shared-bindings/qrio/QRInfo.h | 1 + shared-module/qrio/QRDecoder.c | 40 +++++++++++++++++--- shared-module/qrio/QRDecoder.h | 1 + 5 files changed, 141 insertions(+), 15 deletions(-) diff --git a/shared-bindings/qrio/QRDecoder.c b/shared-bindings/qrio/QRDecoder.c index e8cabade33..f58335d459 100644 --- a/shared-bindings/qrio/QRDecoder.c +++ b/shared-bindings/qrio/QRDecoder.c @@ -56,6 +56,19 @@ STATIC mp_obj_t qrio_qrdecoder_make_new(const mp_obj_type_t *type, size_t n_args return self; } + +STATIC void verify_buffer_size(qrio_qrdecoder_obj_t *self, mp_obj_t *buffer, size_t len, qrio_pixel_policy_t policy) { + int width = shared_module_qrio_qrdecoder_get_width(self); + int height = shared_module_qrio_qrdecoder_get_height(self); + + // verify that the buffer is big enough + int sz = width * height; + if (policy != QRIO_EVERY_BYTE) { + sz *= 2; + } + mp_get_index(mp_obj_get_type(*buffer), len, MP_OBJ_NEW_SMALL_INT(sz - 1), false); +} + //| def decode( //| self, buffer: ReadableBuffer, pixel_policy: PixelPolicy = PixelPolicy.EVERY_BYTE //| ) -> List[QRInfo]: @@ -73,22 +86,39 @@ STATIC mp_obj_t qrio_qrdecoder_decode(size_t n_args, const mp_obj_t *pos_args, m mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); - - int width = shared_module_qrio_qrdecoder_get_width(self); - int height = shared_module_qrio_qrdecoder_get_height(self); - - // verify that the buffer is big enough - int sz = width * height; qrio_pixel_policy_t policy = cp_enum_value(&qrio_pixel_policy_type, args[ARG_pixel_policy].u_obj, MP_QSTR_pixel_policy); - if (policy != QRIO_EVERY_BYTE) { - sz *= 2; - } - mp_get_index(mp_obj_get_type(args[ARG_buffer].u_obj), bufinfo.len, MP_OBJ_NEW_SMALL_INT(sz - 1), false); + verify_buffer_size(self, &args[ARG_buffer].u_obj, bufinfo.len, policy); return shared_module_qrio_qrdecoder_decode(self, &bufinfo, policy); } MP_DEFINE_CONST_FUN_OBJ_KW(qrio_qrdecoder_decode_obj, 1, qrio_qrdecoder_decode); + +//| def find( +//| self, buffer: ReadableBuffer, pixel_policy: PixelPolicy = PixelPolicy.EVERY_BYTE +//| ) -> List[QRPosition]: +//| """Find all visible QR codes from the given image. The size of the buffer must be at least ``length``×``width`` bytes for `EVERY_BYTE`, and 2×``length``×``width`` bytes for `EVEN_BYTES` or `ODD_BYTES`.""" +STATIC mp_obj_t qrio_qrdecoder_find(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + qrio_qrdecoder_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_buffer, ARG_pixel_policy }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_int = 0} }, + { MP_QSTR_pixel_policy, MP_ARG_OBJ, {.u_obj = MP_ROM_PTR((mp_obj_t *)&qrio_pixel_policy_EVERY_BYTE_obj)} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); + qrio_pixel_policy_t policy = cp_enum_value(&qrio_pixel_policy_type, args[ARG_pixel_policy].u_obj, MP_QSTR_pixel_policy); + verify_buffer_size(self, &args[ARG_buffer].u_obj, bufinfo.len, policy); + + return shared_module_qrio_qrdecoder_find(self, &bufinfo, policy); +} +MP_DEFINE_CONST_FUN_OBJ_KW(qrio_qrdecoder_find_obj, 1, qrio_qrdecoder_find); + + //| width: int //| """The width of image the decoder expects""" STATIC mp_obj_t qrio_qrdecoder_get_width(mp_obj_t self_in) { @@ -135,6 +165,7 @@ STATIC const mp_rom_map_elem_t qrio_qrdecoder_locals_table[] = { { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&qrio_qrdecoder_width_obj) }, { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&qrio_qrdecoder_height_obj) }, { MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&qrio_qrdecoder_decode_obj) }, + { MP_ROM_QSTR(MP_QSTR_find), MP_ROM_PTR(&qrio_qrdecoder_find_obj) }, }; STATIC MP_DEFINE_CONST_DICT(qrio_qrdecoder_locals, qrio_qrdecoder_locals_table); diff --git a/shared-bindings/qrio/QRInfo.c b/shared-bindings/qrio/QRInfo.c index d53e638f81..f4d99746cc 100644 --- a/shared-bindings/qrio/QRInfo.c +++ b/shared-bindings/qrio/QRInfo.c @@ -63,3 +63,66 @@ const mp_obj_namedtuple_type_t qrio_qrinfo_type_obj = { MP_QSTR_data_type, }, }; + +//| class QRPosition: +//| """Information about a non-decoded QR code""" +//| +//| top_left_x: int +//| """X coordinate of the top left corner""" +//| +//| top_left_y: int +//| """Y coordinate of the top left corner""" +//| +//| top_right_x: int +//| """X coordinate of the top right corner""" +//| +//| top_right_y: int +//| """Y coordinate of the top right corner""" +//| +//| bottom_right_x: int +//| """X coordinate of the bottom right corner""" +//| +//| bottom_right_y: int +//| """Y coordinate of the bottom right corner""" +//| +//| bottom_left_x: int +//| """X coordinate of the bottom left corner""" +//| +//| bottom_left_y: int +//| """Y coordinate of the bottom left corner""" +//| +//| size: int +//| """The number of bits the code contains""" +//| + +const mp_obj_namedtuple_type_t qrio_qrposition_type_obj = { + .base = { + .base = { + .type = &mp_type_type + }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_QRPosition, + .print = namedtuple_print, + .parent = &mp_type_tuple, + .make_new = namedtuple_make_new, + .attr = namedtuple_attr, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = mp_obj_tuple_unary_op, + .binary_op = mp_obj_tuple_binary_op, + .subscr = mp_obj_tuple_subscr, + .getiter = mp_obj_tuple_getiter, + ), + }, + .n_fields = 9, + .fields = { + MP_QSTR_top_left_x, + MP_QSTR_top_left_y, + MP_QSTR_top_right_x, + MP_QSTR_top_right_y, + MP_QSTR_bottom_right_x, + MP_QSTR_bottom_right_y, + MP_QSTR_bottom_left_x, + MP_QSTR_bottom_left_y, + MP_QSTR_size, + }, +}; diff --git a/shared-bindings/qrio/QRInfo.h b/shared-bindings/qrio/QRInfo.h index 956cb42735..6c58743379 100644 --- a/shared-bindings/qrio/QRInfo.h +++ b/shared-bindings/qrio/QRInfo.h @@ -29,3 +29,4 @@ #include "py/objnamedtuple.h" extern const mp_obj_namedtuple_type_t qrio_qrinfo_type_obj; +extern const mp_obj_namedtuple_type_t qrio_qrposition_type_obj; diff --git a/shared-module/qrio/QRDecoder.c b/shared-module/qrio/QRDecoder.c index f7b25362e4..b98fe63735 100644 --- a/shared-module/qrio/QRDecoder.c +++ b/shared-module/qrio/QRDecoder.c @@ -98,21 +98,21 @@ STATIC mp_obj_t data_type(int type) { return mp_obj_new_int(type); } -mp_obj_t shared_module_qrio_qrdecoder_decode(qrdecoder_qrdecoder_obj_t *self, const mp_buffer_info_t *bufinfo, qrio_pixel_policy_t policy) { +STATIC void quirc_fill_buffer(qrdecoder_qrdecoder_obj_t *self, void *buf, qrio_pixel_policy_t policy) { int width, height; uint8_t *framebuffer = quirc_begin(self->quirc, &width, &height); - uint8_t *src = bufinfo->buf; + uint8_t *src = buf; switch (policy) { case QRIO_RGB565: { - uint16_t *src16 = bufinfo->buf; + uint16_t *src16 = buf; for (int i = 0; i < width * height; i++) { framebuffer[i] = (src16[i] >> 3) & 0xfc; } break; } case QRIO_RGB565_SWAPPED: { - uint16_t *src16 = bufinfo->buf; + uint16_t *src16 = buf; for (int i = 0; i < width * height; i++) { framebuffer[i] = (__builtin_bswap16(src16[i]) >> 3) & 0xfc; } @@ -133,11 +133,16 @@ mp_obj_t shared_module_qrio_qrdecoder_decode(qrdecoder_qrdecoder_obj_t *self, co break; } quirc_end(self->quirc); +} + +mp_obj_t shared_module_qrio_qrdecoder_decode(qrdecoder_qrdecoder_obj_t *self, const mp_buffer_info_t *bufinfo, qrio_pixel_policy_t policy) { + quirc_fill_buffer(self, bufinfo->buf, policy); int count = quirc_count(self->quirc); mp_obj_t result = mp_obj_new_list(0, NULL); for (int i = 0; i < count; i++) { quirc_extract(self->quirc, i, &self->code); + mp_obj_t code_obj; if (quirc_decode(&self->code, &self->data) != QUIRC_SUCCESS) { continue; } @@ -145,7 +150,32 @@ mp_obj_t shared_module_qrio_qrdecoder_decode(qrdecoder_qrdecoder_obj_t *self, co mp_obj_new_bytes(self->data.payload, self->data.payload_len), data_type(self->data.data_type), }; - mp_obj_t code_obj = namedtuple_make_new((const mp_obj_type_t *)&qrio_qrinfo_type_obj, 2, 0, elems); + code_obj = namedtuple_make_new((const mp_obj_type_t *)&qrio_qrinfo_type_obj, 2, 0, elems); + mp_obj_list_append(result, code_obj); + } + return result; +} + + +mp_obj_t shared_module_qrio_qrdecoder_find(qrdecoder_qrdecoder_obj_t *self, const mp_buffer_info_t *bufinfo, qrio_pixel_policy_t policy) { + quirc_fill_buffer(self, bufinfo->buf, policy); + int count = quirc_count(self->quirc); + mp_obj_t result = mp_obj_new_list(0, NULL); + for (int i = 0; i < count; i++) { + quirc_extract(self->quirc, i, &self->code); + mp_obj_t code_obj; + mp_obj_t elems[9] = { + mp_obj_new_int(self->code.corners[0].x), + mp_obj_new_int(self->code.corners[0].y), + mp_obj_new_int(self->code.corners[1].x), + mp_obj_new_int(self->code.corners[1].y), + mp_obj_new_int(self->code.corners[2].x), + mp_obj_new_int(self->code.corners[2].y), + mp_obj_new_int(self->code.corners[3].x), + mp_obj_new_int(self->code.corners[3].y), + mp_obj_new_int(self->code.size), + }; + code_obj = namedtuple_make_new((const mp_obj_type_t *)&qrio_qrposition_type_obj, 9, 0, elems); mp_obj_list_append(result, code_obj); } return result; diff --git a/shared-module/qrio/QRDecoder.h b/shared-module/qrio/QRDecoder.h index 096dcd2e3f..da000be1f5 100644 --- a/shared-module/qrio/QRDecoder.h +++ b/shared-module/qrio/QRDecoder.h @@ -43,3 +43,4 @@ int shared_module_qrio_qrdecoder_get_width(qrdecoder_qrdecoder_obj_t *); void shared_module_qrio_qrdecoder_set_height(qrdecoder_qrdecoder_obj_t *, int height); void shared_module_qrio_qrdecoder_set_width(qrdecoder_qrdecoder_obj_t *, int width); mp_obj_t shared_module_qrio_qrdecoder_decode(qrdecoder_qrdecoder_obj_t *, const mp_buffer_info_t *bufinfo, qrio_pixel_policy_t policy); +mp_obj_t shared_module_qrio_qrdecoder_find(qrdecoder_qrdecoder_obj_t *, const mp_buffer_info_t *bufinfo, qrio_pixel_policy_t policy);