-
Notifications
You must be signed in to change notification settings - Fork 70
Expand file tree
/
Copy pathquirc_decode.c
More file actions
231 lines (195 loc) · 8.7 KB
/
quirc_decode.c
File metadata and controls
231 lines (195 loc) · 8.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "py/mperrno.h"
#include "py/nlr.h" // Include for nlr_buf_t
#ifdef __xtensa__
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#else
size_t uxTaskGetStackHighWaterMark(void * unused) {
return 99999999;
}
#endif
#include "../quirc/lib/quirc.h"
#include "../quirc/lib/quirc_internal.h" // Exposes full struct quirc
#define QRDECODE_DEBUG_PRINT(...) mp_printf(&mp_plat_print, __VA_ARGS__)
static mp_obj_t qrdecode(mp_uint_t n_args, const mp_obj_t *args) {
//QRDECODE_DEBUG_PRINT("qrdecode: Starting\n");
//QRDECODE_DEBUG_PRINT("qrdecode: Stack high-water mark: %u bytes\n", uxTaskGetStackHighWaterMark(NULL));
if (n_args != 3) {
mp_raise_ValueError(MP_ERROR_TEXT("quirc_decode expects 3 arguments: buffer, width, height"));
}
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
mp_int_t width = mp_obj_get_int(args[1]);
mp_int_t height = mp_obj_get_int(args[2]);
//QRDECODE_DEBUG_PRINT("qrdecode: Width=%u, Height=%u\n", width, height);
if (width <= 0 || height <= 0) {
mp_raise_ValueError(MP_ERROR_TEXT("width and height must be positive"));
}
if (bufinfo.len != (size_t)(width * height)) {
QRDECODE_DEBUG_PRINT("qrdecode wrong bufsize: %u bytes\n", bufinfo.len);
mp_raise_ValueError(MP_ERROR_TEXT("buffer size must match width * height"));
}
struct quirc *qr = quirc_new();
if (!qr) {
mp_raise_OSError(MP_ENOMEM);
}
//QRDECODE_DEBUG_PRINT("qrdecode: Allocated quirc object\n");
if (quirc_resize(qr, width, height) < 0) {
quirc_destroy(qr);
mp_raise_OSError(MP_ENOMEM);
}
//QRDECODE_DEBUG_PRINT("qrdecode: Resized quirc object\n");
uint8_t *image = quirc_begin(qr, NULL, NULL);
memcpy(image, bufinfo.buf, width * height);
// would be nice to be able to use the existing buffer (bufinfo.buf) here, avoiding memcpy,
// but that buffer is also being filled by image capture and displayed by lvgl
// and that becomes unstable... it shows black artifacts and crashes sometimes...
//uint8_t *temp_image = image;
//image = bufinfo.buf;
//qr->image = bufinfo.buf; // if this works then we can also eliminate quirc's ps_alloc()
quirc_end(qr);
//qr->image = temp_image; // restore, because quirc will try to free it
/*
// Pointer swap - NO memcpy, NO internal.h needed
uint8_t *quirc_buffer = quirc_begin(qr, NULL, NULL);
uint8_t *saved_bufinfo = bufinfo.buf;
bufinfo.buf = quirc_buffer; // quirc now uses your buffer
quirc_end(qr); // QR detection works!
// Restore your buffer pointer
//bufinfo.buf = saved_bufinfo;
*/
// now num_grids is set, as well as others, probably
int count = quirc_count(qr);
if (count == 0) {
// Restore your buffer pointer
quirc_destroy(qr);
//QRDECODE_DEBUG_PRINT("qrdecode: No QR code found, freed quirc object\n");
mp_raise_ValueError(MP_ERROR_TEXT("no QR code found"));
}
struct quirc_code *code = (struct quirc_code *)malloc(sizeof(struct quirc_code));
if (!code) {
quirc_destroy(qr);
mp_raise_OSError(MP_ENOMEM);
}
//QRDECODE_DEBUG_PRINT("qrdecode: Allocated quirc_code\n");
quirc_extract(qr, 0, code);
// the code struct now contains the corners of the QR code, as well as the bitmap of the values
// this could be used to display debug info to the user - they might even be able to see which modules are being misidentified!
struct quirc_data *data = (struct quirc_data *)malloc(sizeof(struct quirc_data));
if (!data) {
free(code);
quirc_destroy(qr);
mp_raise_OSError(MP_ENOMEM);
}
//QRDECODE_DEBUG_PRINT("qrdecode: Allocated quirc_data\n");
int err = quirc_decode(code, data);
if (err != QUIRC_SUCCESS) {
free(data);
free(code);
quirc_destroy(qr);
//QRDECODE_DEBUG_PRINT("qrdecode: Decode failed, freed data, code, and quirc object\n");
mp_raise_TypeError(MP_ERROR_TEXT("failed to decode QR code"));
}
mp_obj_t result = mp_obj_new_bytes((const uint8_t *)data->payload, data->payload_len);
free(data);
free(code);
quirc_destroy(qr);
//QRDECODE_DEBUG_PRINT("qrdecode: Freed data, code, and quirc object, returning result\n");
return result;
}
static mp_obj_t qrdecode_rgb565(mp_uint_t n_args, const mp_obj_t *args) {
//QRDECODE_DEBUG_PRINT("qrdecode_rgb565: Starting\n");
if (n_args != 3) {
mp_raise_ValueError(MP_ERROR_TEXT("qrdecode_rgb565 expects 3 arguments: buffer, width, height"));
}
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
mp_int_t width = mp_obj_get_int(args[1]);
mp_int_t height = mp_obj_get_int(args[2]);
//QRDECODE_DEBUG_PRINT("qrdecode_rgb565: Width=%u, Height=%u\n", width, height);
if (width <= 0 || height <= 0) {
mp_raise_ValueError(MP_ERROR_TEXT("width and height must be positive"));
}
if (bufinfo.len != (size_t)(width * height * 2)) {
QRDECODE_DEBUG_PRINT("qrdecode_rgb565 wrong bufsize: %u bytes\n", bufinfo.len);
mp_raise_ValueError(MP_ERROR_TEXT("buffer size must match width * height * 2 for RGB565"));
}
uint8_t *gray_buffer = (uint8_t *)malloc(width * height * sizeof(uint8_t));
if (!gray_buffer) {
mp_raise_OSError(MP_ENOMEM);
}
//QRDECODE_DEBUG_PRINT("qrdecode_rgb565: Allocated gray_buffer (%u bytes)\n", width * height * sizeof(uint8_t));
uint16_t *rgb565 = (uint16_t *)bufinfo.buf;
for (size_t i = 0; i < (size_t)(width * height); i++) {
uint16_t pixel = rgb565[i];
uint8_t r = ((pixel >> 11) & 0x1F) << 3;
uint8_t g = ((pixel >> 5) & 0x3F) << 2;
uint8_t b = (pixel & 0x1F) << 3;
gray_buffer[i] = (uint8_t)((0.299 * r + 0.587 * g + 0.114 * b) + 0.5);
}
mp_obj_t gray_args[3] = {
mp_obj_new_bytes(gray_buffer, width * height),
mp_obj_new_int(width),
mp_obj_new_int(height)
};
mp_obj_t result = mp_const_none;
nlr_buf_t exception_handler;
if (nlr_push(&exception_handler) == 0) {
result = qrdecode(3, gray_args);
nlr_pop();
//QRDECODE_DEBUG_PRINT("qrdecode_rgb565: qrdecode succeeded, freeing gray_buffer\n");
free(gray_buffer);
} else {
//QRDECODE_DEBUG_PRINT("qrdecode_rgb565: Exception caught, freeing gray_buffer\n");
// Cleanup
if (gray_buffer) {
free(gray_buffer);
gray_buffer = NULL;
}
//mp_raise_TypeError(MP_ERROR_TEXT("qrdecode_rgb565: failed to decode QR code"));
// Re-raising the exception results in an Unhandled exception in thread started by <function qrdecode_live at 0x7f6f55af0680>
// which isn't caught, even when catching Exception, so this looks like a bug in MicroPython...
nlr_pop();
nlr_raise(exception_handler.ret_val);
// Re-raise the original exception with optional additional message
/*
mp_raise_msg_and_obj(
mp_obj_exception_get_type(exception_handler.ret_val),
MP_OBJ_NEW_QSTR(qstr_from_str("qrdecode_rgb565: failed during processing")),
exception_handler.ret_val
);
*/
// Re-raise as new exception of same type, with message + original as arg
// (embeds original for traceback chaining)
// crashes:
//const mp_obj_type_t *exc_type = mp_obj_get_type(exception_handler.ret_val);
//mp_raise_msg_varg(exc_type, MP_ERROR_TEXT("qrdecode_rgb565: failed during processing: %q"), exception_handler.ret_val);
}
//nlr_pop(); maybe it needs to be done after instead of before the re-raise?
return result;
}
static mp_obj_t qrdecode_wrapper(size_t n_args, const mp_obj_t *args) {
return qrdecode(n_args, args);
}
static mp_obj_t qrdecode_rgb565_wrapper(size_t n_args, const mp_obj_t *args) {
return qrdecode_rgb565(n_args, args);
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qrdecode_obj, 3, 3, qrdecode_wrapper);
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qrdecode_rgb565_obj, 3, 3, qrdecode_rgb565_wrapper);
static const mp_rom_map_elem_t qrdecode_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_qrdecode) },
{ MP_ROM_QSTR(MP_QSTR_qrdecode), MP_ROM_PTR(&qrdecode_obj) },
{ MP_ROM_QSTR(MP_QSTR_qrdecode_rgb565), MP_ROM_PTR(&qrdecode_rgb565_obj) },
};
static MP_DEFINE_CONST_DICT(qrdecode_module_globals, qrdecode_module_globals_table);
const mp_obj_module_t qrdecode_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&qrdecode_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_qrdecode, qrdecode_module);