Line data Source code
1 : /*
2 : * liblierre - reader.c
3 : *
4 : * This file is part of liblierre.
5 : *
6 : * Author: Go Kudo <zeriyoshi@gmail.com>
7 : * SPDX-License-Identifier: MIT
8 : */
9 :
10 : #include <stdlib.h>
11 : #include <string.h>
12 :
13 : #include <lierre.h>
14 : #include <lierre/portable.h>
15 : #include <lierre/reader.h>
16 :
17 : #include "../internal/decoder.h"
18 : #include "../internal/image.h"
19 : #include "../internal/memory.h"
20 :
21 : #if LIERRE_USE_SIMD
22 : #include "../internal/simd.h"
23 : #endif
24 :
25 : #include "../internal/structs.h"
26 :
27 : #define LIERRE_IMAGE_MINIMIZE_MAX_SCALE 16
28 :
29 : #define LIERRE_GRAY_WEIGHT_R 77
30 : #define LIERRE_GRAY_WEIGHT_G 150
31 : #define LIERRE_GRAY_WEIGHT_B 29
32 : #define LIERRE_GRAY_SHIFT 8
33 : #define LIERRE_MIN_QR_SIZE 21
34 : #define LIERRE_PIXEL_VALUE_DEFAULT 255
35 :
36 103 : static inline void lierre_rgb_to_gray(const uint8_t *src, uint8_t *dst, size_t pixel_count)
37 : {
38 : #if LIERRE_USE_SIMD && defined(LIERRE_SIMD_AVX2)
39 : __m256i r_weight, g_weight, b_weight;
40 : __m128i r0, g0, b0, r1, g1, gray8;
41 : __m256i r16, g16, b16, sum;
42 : size_t i, simd_count;
43 : const uint8_t *p;
44 :
45 103 : r_weight = _mm256_set1_epi16(LIERRE_GRAY_WEIGHT_R);
46 103 : g_weight = _mm256_set1_epi16(LIERRE_GRAY_WEIGHT_G);
47 103 : b_weight = _mm256_set1_epi16(LIERRE_GRAY_WEIGHT_B);
48 :
49 103 : simd_count = pixel_count & ~15ULL;
50 :
51 7556667 : for (i = 0; i < simd_count; i += 16) {
52 7556564 : p = src + i * 3;
53 :
54 15113128 : r0 = _mm_setr_epi8(p[0], p[3], p[6], p[9], p[12], p[15], p[18], p[21], p[24], p[27], p[30], p[33], p[36], p[39],
55 7556564 : p[42], p[45]);
56 15113128 : g0 = _mm_setr_epi8(p[1], p[4], p[7], p[10], p[13], p[16], p[19], p[22], p[25], p[28], p[31], p[34], p[37],
57 7556564 : p[40], p[43], p[46]);
58 7556564 : b0 = _mm_setr_epi8(p[2], p[5], p[8], p[11], p[14], p[17], p[20], p[23], p[26], p[29], p[32], p[35], p[38],
59 7556564 : p[41], p[44], p[47]);
60 :
61 7556564 : r16 = _mm256_cvtepu8_epi16(r0);
62 7556564 : g16 = _mm256_cvtepu8_epi16(g0);
63 7556564 : b16 = _mm256_cvtepu8_epi16(b0);
64 :
65 37782820 : sum = _mm256_add_epi16(_mm256_mullo_epi16(r16, r_weight),
66 : _mm256_add_epi16(_mm256_mullo_epi16(g16, g_weight), _mm256_mullo_epi16(b16, b_weight)));
67 7556564 : sum = _mm256_srli_epi16(sum, LIERRE_GRAY_SHIFT);
68 :
69 7556564 : r1 = _mm256_castsi256_si128(sum);
70 7556564 : g1 = _mm256_extracti128_si256(sum, 1);
71 7556564 : gray8 = _mm_packus_epi16(r1, g1);
72 :
73 7556564 : _mm_storeu_si128((__m128i *)(dst + i), gray8);
74 : }
75 :
76 410 : for (i = simd_count; i < pixel_count; i++) {
77 307 : p = src + i * 3;
78 307 : dst[i] = (uint8_t)((p[0] * LIERRE_GRAY_WEIGHT_R + p[1] * LIERRE_GRAY_WEIGHT_G + p[2] * LIERRE_GRAY_WEIGHT_B) >>
79 : LIERRE_GRAY_SHIFT);
80 : }
81 : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_NEON)
82 : uint8x16x3_t rgb;
83 : uint16x8_t sum_lo, sum_hi;
84 : uint8x8_t gray_lo, gray_hi;
85 : uint8x16_t gray;
86 : size_t i, simd_count;
87 :
88 : simd_count = pixel_count & ~15ULL;
89 :
90 : for (i = 0; i < simd_count; i += 16) {
91 : rgb = vld3q_u8(src + i * 3);
92 :
93 : sum_lo = vmull_u8(vget_low_u8(rgb.val[0]), vdup_n_u8(LIERRE_GRAY_WEIGHT_R));
94 : sum_lo = vmlal_u8(sum_lo, vget_low_u8(rgb.val[1]), vdup_n_u8(LIERRE_GRAY_WEIGHT_G));
95 : sum_lo = vmlal_u8(sum_lo, vget_low_u8(rgb.val[2]), vdup_n_u8(LIERRE_GRAY_WEIGHT_B));
96 :
97 : sum_hi = vmull_u8(vget_high_u8(rgb.val[0]), vdup_n_u8(LIERRE_GRAY_WEIGHT_R));
98 : sum_hi = vmlal_u8(sum_hi, vget_high_u8(rgb.val[1]), vdup_n_u8(LIERRE_GRAY_WEIGHT_G));
99 : sum_hi = vmlal_u8(sum_hi, vget_high_u8(rgb.val[2]), vdup_n_u8(LIERRE_GRAY_WEIGHT_B));
100 :
101 : gray_lo = vshrn_n_u16(sum_lo, LIERRE_GRAY_SHIFT);
102 : gray_hi = vshrn_n_u16(sum_hi, LIERRE_GRAY_SHIFT);
103 : gray = vcombine_u8(gray_lo, gray_hi);
104 :
105 : vst1q_u8(dst + i, gray);
106 : }
107 :
108 : for (i = simd_count; i < pixel_count; i++) {
109 : const uint8_t *p = src + i * 3;
110 : dst[i] = (uint8_t)((p[0] * LIERRE_GRAY_WEIGHT_R + p[1] * LIERRE_GRAY_WEIGHT_G + p[2] * LIERRE_GRAY_WEIGHT_B) >>
111 : LIERRE_GRAY_SHIFT);
112 : }
113 : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_WASM)
114 : v128_t r_weight, g_weight, b_weight;
115 : v128_t r0, g0, b0, r_lo, r_hi, g_lo, g_hi, b_lo, b_hi;
116 : v128_t sum_lo, sum_hi, gray;
117 : size_t i, simd_count;
118 : const uint8_t *p;
119 : uint8_t r_buf[16], g_buf[16], b_buf[16];
120 : size_t j;
121 :
122 : r_weight = wasm_i16x8_splat(LIERRE_GRAY_WEIGHT_R);
123 : g_weight = wasm_i16x8_splat(LIERRE_GRAY_WEIGHT_G);
124 : b_weight = wasm_i16x8_splat(LIERRE_GRAY_WEIGHT_B);
125 :
126 : simd_count = pixel_count & ~15ULL;
127 :
128 : for (i = 0; i < simd_count; i += 16) {
129 : p = src + i * 3;
130 :
131 : for (j = 0; j < 16; j++) {
132 : r_buf[j] = p[j * 3];
133 : g_buf[j] = p[j * 3 + 1];
134 : b_buf[j] = p[j * 3 + 2];
135 : }
136 : r0 = wasm_v128_load(r_buf);
137 : g0 = wasm_v128_load(g_buf);
138 : b0 = wasm_v128_load(b_buf);
139 :
140 : r_lo = wasm_u16x8_extend_low_u8x16(r0);
141 : r_hi = wasm_u16x8_extend_high_u8x16(r0);
142 : g_lo = wasm_u16x8_extend_low_u8x16(g0);
143 : g_hi = wasm_u16x8_extend_high_u8x16(g0);
144 : b_lo = wasm_u16x8_extend_low_u8x16(b0);
145 : b_hi = wasm_u16x8_extend_high_u8x16(b0);
146 :
147 : sum_lo = wasm_i16x8_add(wasm_i16x8_mul(r_lo, r_weight),
148 : wasm_i16x8_add(wasm_i16x8_mul(g_lo, g_weight), wasm_i16x8_mul(b_lo, b_weight)));
149 : sum_hi = wasm_i16x8_add(wasm_i16x8_mul(r_hi, r_weight),
150 : wasm_i16x8_add(wasm_i16x8_mul(g_hi, g_weight), wasm_i16x8_mul(b_hi, b_weight)));
151 :
152 : sum_lo = wasm_u16x8_shr(sum_lo, LIERRE_GRAY_SHIFT);
153 : sum_hi = wasm_u16x8_shr(sum_hi, LIERRE_GRAY_SHIFT);
154 :
155 : gray = wasm_u8x16_narrow_i16x8(sum_lo, sum_hi);
156 :
157 : wasm_v128_store(dst + i, gray);
158 : }
159 :
160 : for (i = simd_count; i < pixel_count; i++) {
161 : p = src + i * 3;
162 : dst[i] = (uint8_t)((p[0] * LIERRE_GRAY_WEIGHT_R + p[1] * LIERRE_GRAY_WEIGHT_G + p[2] * LIERRE_GRAY_WEIGHT_B) >>
163 : LIERRE_GRAY_SHIFT);
164 : }
165 : #else
166 : size_t i;
167 : const uint8_t *p;
168 :
169 : for (i = 0; i < pixel_count; i++) {
170 : p = src + i * 3;
171 : dst[i] = (uint8_t)((p[0] * LIERRE_GRAY_WEIGHT_R + p[1] * LIERRE_GRAY_WEIGHT_G + p[2] * LIERRE_GRAY_WEIGHT_B) >>
172 : LIERRE_GRAY_SHIFT);
173 : }
174 : #endif
175 103 : }
176 :
177 132 : extern lierre_error_t lierre_reader_param_init(lierre_reader_param_t *param)
178 : {
179 132 : if (!param) {
180 1 : return LIERRE_ERROR_INVALID_PARAMS;
181 : }
182 :
183 131 : param->strategy_flags = LIERRE_READER_STRATEGY_NONE;
184 131 : param->rect = NULL;
185 :
186 131 : return LIERRE_ERROR_SUCCESS;
187 : }
188 :
189 82 : extern void lierre_reader_param_set_flag(lierre_reader_param_t *param, lierre_reader_strategy_flag_t flag)
190 : {
191 82 : if (!param) {
192 1 : return;
193 : }
194 :
195 81 : param->strategy_flags |= flag;
196 : }
197 :
198 10 : extern void lierre_reader_param_set_rect(lierre_reader_param_t *param, const lierre_rect_t *rect)
199 : {
200 10 : if (!param) {
201 1 : return;
202 : }
203 :
204 9 : param->rect = rect;
205 : }
206 :
207 126 : extern lierre_reader_t *lierre_reader_create(const lierre_reader_param_t *param)
208 : {
209 : lierre_reader_t *reader;
210 :
211 126 : if (!param) {
212 1 : return NULL;
213 : }
214 :
215 125 : reader = lmalloc(sizeof(lierre_reader_t));
216 125 : if (!reader) {
217 0 : return NULL;
218 : }
219 :
220 125 : reader->data = NULL;
221 125 : reader->param = lmalloc(sizeof(lierre_reader_param_t));
222 125 : if (!reader->param) {
223 0 : lfree(reader);
224 0 : return NULL;
225 : }
226 :
227 125 : lmemcpy(reader->param, param, sizeof(lierre_reader_param_t));
228 :
229 125 : return reader;
230 : }
231 :
232 126 : extern void lierre_reader_destroy(lierre_reader_t *reader)
233 : {
234 126 : if (!reader) {
235 1 : return;
236 : }
237 :
238 125 : if (reader->param) {
239 125 : lfree(reader->param);
240 : }
241 :
242 125 : lfree(reader);
243 : }
244 :
245 110 : extern void lierre_reader_set_data(lierre_reader_t *reader, lierre_rgb_data_t *data)
246 : {
247 110 : if (!reader) {
248 1 : return;
249 : }
250 :
251 109 : reader->data = data;
252 : }
253 :
254 112 : extern lierre_error_t lierre_reader_read(lierre_reader_t *reader, lierre_reader_result_t **result)
255 : {
256 : const uint8_t *pixel;
257 : lierre_reader_result_t *res;
258 : decoder_t *decoder;
259 : decoder_result_t *dec_result;
260 : lierre_error_t err;
261 : uint32_t num_threads, scale, sum, dy, dx, scale_shift, temp;
262 : uint8_t *gray_data, *scaled_gray, r, g, b, gray;
263 : int32_t rect_w, rect_h;
264 : size_t i, j, start_x, start_y, width, height, src_x, src_y, src_idx, sw, sh, sy, sx, gx, gy;
265 : bool use_mt, use_quirc_grayscale;
266 :
267 112 : if (!reader || !result || !reader->data || !reader->data->data) {
268 3 : return LIERRE_ERROR_INVALID_PARAMS;
269 : }
270 :
271 109 : dec_result = lmalloc(sizeof(decoder_result_t));
272 109 : if (!dec_result) {
273 0 : return LIERRE_ERROR_DATA_OVERFLOW;
274 : }
275 :
276 109 : use_mt = (reader->param->strategy_flags & LIERRE_READER_STRATEGY_MT) != 0;
277 109 : num_threads = use_mt ? lierre_get_cpu_count() : 1;
278 :
279 109 : start_x = 0;
280 109 : start_y = 0;
281 109 : width = reader->data->width;
282 109 : height = reader->data->height;
283 :
284 109 : if ((reader->param->strategy_flags & LIERRE_READER_STRATEGY_USE_RECT) && reader->param->rect) {
285 6 : start_x = reader->param->rect->origin.x;
286 6 : start_y = reader->param->rect->origin.y;
287 6 : width = reader->param->rect->size.width;
288 6 : height = reader->param->rect->size.height;
289 6 : if (width == 0 || height == 0 || width > SIZE_MAX / height) {
290 0 : lfree(dec_result);
291 0 : return LIERRE_ERROR_INVALID_PARAMS;
292 : }
293 : }
294 :
295 109 : gray_data = lmalloc(width * height);
296 109 : if (!gray_data) {
297 0 : lfree(dec_result);
298 0 : return LIERRE_ERROR_DATA_OVERFLOW;
299 : }
300 :
301 109 : if (start_x == 0 && start_y == 0 && width == reader->data->width && height == reader->data->height) {
302 103 : lierre_rgb_to_gray(reader->data->data, gray_data, width * height);
303 : } else {
304 1106 : for (i = 0; i < height; i++) {
305 531100 : for (j = 0; j < width; j++) {
306 530000 : src_x = start_x + j;
307 530000 : src_y = start_y + i;
308 530000 : if (src_x >= reader->data->width || src_y >= reader->data->height) {
309 0 : gray_data[i * width + j] = LIERRE_PIXEL_VALUE_DEFAULT;
310 0 : continue;
311 : }
312 530000 : src_idx = (src_y * reader->data->width + src_x) * 3;
313 530000 : r = reader->data->data[src_idx];
314 530000 : g = reader->data->data[src_idx + 1];
315 530000 : b = reader->data->data[src_idx + 2];
316 530000 : gray = (uint8_t)((r * LIERRE_GRAY_WEIGHT_R + g * LIERRE_GRAY_WEIGHT_G + b * LIERRE_GRAY_WEIGHT_B) >>
317 : LIERRE_GRAY_SHIFT);
318 530000 : gray_data[i * width + j] = gray;
319 : }
320 : }
321 : }
322 :
323 109 : if (reader->param->strategy_flags & LIERRE_READER_STRATEGY_DENOISE) {
324 4 : if (use_mt) {
325 2 : image_denoise_mt(gray_data, width, height, num_threads);
326 : } else {
327 2 : image_denoise(gray_data, width, height);
328 : }
329 : }
330 :
331 109 : if (reader->param->strategy_flags & LIERRE_READER_STRATEGY_BRIGHTNESS_NORMALIZE) {
332 1 : image_brightness_normalize(gray_data, width, height);
333 : }
334 :
335 109 : if (reader->param->strategy_flags & LIERRE_READER_STRATEGY_CONTRAST_NORMALIZE) {
336 1 : image_contrast_normalize(gray_data, width, height);
337 : }
338 :
339 109 : if (reader->param->strategy_flags & LIERRE_READER_STRATEGY_SHARPENING) {
340 3 : if (use_mt) {
341 2 : image_sharpen_mt(gray_data, width, height, num_threads);
342 : } else {
343 1 : image_sharpen(gray_data, width, height);
344 : }
345 : }
346 :
347 109 : decoder = lierre_decoder_create();
348 109 : if (!decoder) {
349 0 : lfree(gray_data);
350 0 : lfree(dec_result);
351 :
352 0 : return LIERRE_ERROR_DATA_OVERFLOW;
353 : }
354 :
355 109 : dec_result->count = 0;
356 :
357 109 : if (reader->param->strategy_flags & LIERRE_READER_STRATEGY_MINIMIZE) {
358 5 : use_quirc_grayscale = (reader->param->strategy_flags & LIERRE_READER_STRATEGY_GRAYSCALE) != 0;
359 :
360 19 : for (scale = 1; scale <= LIERRE_IMAGE_MINIMIZE_MAX_SCALE; scale *= 2) {
361 19 : sw = width / scale;
362 19 : sh = height / scale;
363 :
364 19 : if (sw < LIERRE_MIN_QR_SIZE || sh < LIERRE_MIN_QR_SIZE) {
365 : break;
366 : }
367 :
368 17 : scale_shift = 0;
369 17 : temp = scale;
370 38 : while (temp > 1) {
371 21 : temp >>= 1;
372 21 : scale_shift++;
373 : }
374 17 : scale_shift *= 2;
375 :
376 17 : scaled_gray = lmalloc(sw * sh);
377 17 : if (!scaled_gray) {
378 0 : break;
379 : }
380 :
381 17 : if (use_quirc_grayscale) {
382 0 : for (sy = 0; sy < sh; sy++) {
383 0 : for (sx = 0; sx < sw; sx++) {
384 0 : sum = 0;
385 0 : for (dy = 0; dy < scale; dy++) {
386 0 : for (dx = 0; dx < scale; dx++) {
387 0 : src_x = start_x + sx * scale + dx;
388 0 : src_y = start_y + sy * scale + dy;
389 0 : if (src_x >= reader->data->width || src_y >= reader->data->height) {
390 0 : sum += LIERRE_PIXEL_VALUE_DEFAULT;
391 0 : continue;
392 : }
393 0 : src_idx = (src_y * reader->data->width + src_x) * 3;
394 0 : pixel = reader->data->data + src_idx;
395 0 : sum += (pixel[0] * LIERRE_GRAY_WEIGHT_R + pixel[1] * LIERRE_GRAY_WEIGHT_G +
396 0 : pixel[2] * LIERRE_GRAY_WEIGHT_B) >>
397 : LIERRE_GRAY_SHIFT;
398 : }
399 : }
400 0 : scaled_gray[sy * sw + sx] = (uint8_t)(sum >> scale_shift);
401 : }
402 : }
403 : } else {
404 6437 : for (sy = 0; sy < sh; sy++) {
405 8277470 : for (sx = 0; sx < sw; sx++) {
406 8271050 : sum = 0;
407 19307450 : for (dy = 0; dy < scale; dy++) {
408 30018800 : for (dx = 0; dx < scale; dx++) {
409 18982400 : gx = sx * scale + dx;
410 18982400 : gy = sy * scale + dy;
411 18982400 : if (gx < width && gy < height) {
412 18982400 : sum += gray_data[gy * width + gx];
413 : } else {
414 0 : sum += LIERRE_PIXEL_VALUE_DEFAULT;
415 : }
416 : }
417 : }
418 8271050 : scaled_gray[sy * sw + sx] = (uint8_t)(sum >> scale_shift);
419 : }
420 : }
421 : }
422 :
423 17 : if (use_mt) {
424 : err =
425 7 : lierre_decoder_process_mt(decoder, scaled_gray, (int32_t)sw, (int32_t)sh, dec_result, num_threads);
426 : } else {
427 10 : err = lierre_decoder_process(decoder, scaled_gray, (int32_t)sw, (int32_t)sh, dec_result);
428 : }
429 :
430 17 : lfree(scaled_gray);
431 :
432 17 : if (err == LIERRE_ERROR_SUCCESS && dec_result->count > 0) {
433 3 : break;
434 : }
435 : }
436 :
437 5 : lfree(gray_data);
438 : } else {
439 104 : if (use_mt) {
440 : err =
441 41 : lierre_decoder_process_mt(decoder, gray_data, (int32_t)width, (int32_t)height, dec_result, num_threads);
442 : } else {
443 63 : err = lierre_decoder_process(decoder, gray_data, (int32_t)width, (int32_t)height, dec_result);
444 : }
445 104 : lfree(gray_data);
446 :
447 104 : if (err != LIERRE_ERROR_SUCCESS) {
448 0 : lfree(dec_result);
449 0 : lierre_decoder_destroy(decoder);
450 :
451 0 : return err;
452 : }
453 : }
454 :
455 109 : res = lmalloc(sizeof(lierre_reader_result_t));
456 109 : if (!res) {
457 0 : lfree(dec_result);
458 0 : lierre_decoder_destroy(decoder);
459 :
460 0 : return LIERRE_ERROR_DATA_OVERFLOW;
461 : }
462 :
463 109 : res->num_qr_codes = dec_result->count;
464 109 : res->qr_code_rects = NULL;
465 109 : res->qr_code_datas = NULL;
466 109 : res->qr_code_data_sizes = NULL;
467 :
468 109 : if (dec_result->count > 0) {
469 101 : res->qr_code_rects = lcalloc(dec_result->count, sizeof(lierre_rect_t));
470 101 : res->qr_code_datas = lcalloc(dec_result->count, sizeof(uint8_t *));
471 101 : res->qr_code_data_sizes = lcalloc(dec_result->count, sizeof(size_t));
472 :
473 101 : if (!res->qr_code_rects || !res->qr_code_datas || !res->qr_code_data_sizes) {
474 0 : lfree(res->qr_code_rects);
475 0 : lfree(res->qr_code_datas);
476 0 : lfree(res->qr_code_data_sizes);
477 0 : lfree(res);
478 0 : lfree(dec_result);
479 0 : lierre_decoder_destroy(decoder);
480 :
481 0 : return LIERRE_ERROR_DATA_OVERFLOW;
482 : }
483 :
484 205 : for (i = 0; i < dec_result->count; i++) {
485 104 : res->qr_code_rects[i].origin.x = start_x + dec_result->codes[i].corners[0].x;
486 104 : res->qr_code_rects[i].origin.y = start_y + dec_result->codes[i].corners[0].y;
487 :
488 104 : rect_w = dec_result->codes[i].corners[2].x - dec_result->codes[i].corners[0].x;
489 104 : rect_h = dec_result->codes[i].corners[2].y - dec_result->codes[i].corners[0].y;
490 104 : res->qr_code_rects[i].size.width = (rect_w > 0) ? (size_t)rect_w : 0;
491 104 : res->qr_code_rects[i].size.height = (rect_h > 0) ? (size_t)rect_h : 0;
492 :
493 104 : res->qr_code_data_sizes[i] = (size_t)dec_result->codes[i].payload_len;
494 104 : res->qr_code_datas[i] = lmalloc(res->qr_code_data_sizes[i] + 1);
495 104 : if (res->qr_code_datas[i]) {
496 104 : lmemcpy(res->qr_code_datas[i], dec_result->codes[i].payload, res->qr_code_data_sizes[i]);
497 104 : res->qr_code_datas[i][res->qr_code_data_sizes[i]] = '\0';
498 : }
499 : }
500 : }
501 :
502 109 : lfree(dec_result);
503 109 : lierre_decoder_destroy(decoder);
504 109 : *result = res;
505 :
506 109 : return LIERRE_ERROR_SUCCESS;
507 : }
508 :
509 110 : extern void lierre_reader_result_destroy(lierre_reader_result_t *result)
510 : {
511 : uint32_t i;
512 :
513 110 : if (!result) {
514 1 : return;
515 : }
516 :
517 109 : if (result->qr_code_datas) {
518 205 : for (i = 0; i < result->num_qr_codes; i++) {
519 104 : if (result->qr_code_datas[i]) {
520 104 : lfree(result->qr_code_datas[i]);
521 : }
522 : }
523 101 : lfree(result->qr_code_datas);
524 : }
525 :
526 109 : if (result->qr_code_rects) {
527 101 : lfree(result->qr_code_rects);
528 : }
529 :
530 109 : if (result->qr_code_data_sizes) {
531 101 : lfree(result->qr_code_data_sizes);
532 : }
533 :
534 109 : lfree(result);
535 : }
536 :
537 105 : extern uint32_t lierre_reader_result_get_num_qr_codes(const lierre_reader_result_t *result)
538 : {
539 105 : if (!result) {
540 1 : return 0;
541 : }
542 :
543 104 : return result->num_qr_codes;
544 : }
545 :
546 1 : extern const lierre_rect_t *lierre_reader_result_get_qr_code_rect(const lierre_reader_result_t *result, uint32_t index)
547 : {
548 1 : if (!result || index >= result->num_qr_codes || !result->qr_code_rects) {
549 1 : return NULL;
550 : }
551 :
552 0 : return &result->qr_code_rects[index];
553 : }
554 :
555 102 : extern const uint8_t *lierre_reader_result_get_qr_code_data(const lierre_reader_result_t *result, uint32_t index)
556 : {
557 102 : if (!result || index >= result->num_qr_codes || !result->qr_code_datas) {
558 1 : return NULL;
559 : }
560 :
561 101 : return result->qr_code_datas[index];
562 : }
563 :
564 102 : extern size_t lierre_reader_result_get_qr_code_data_size(const lierre_reader_result_t *result, uint32_t index)
565 : {
566 102 : if (!result || index >= result->num_qr_codes || !result->qr_code_data_sizes) {
567 1 : return 0;
568 : }
569 :
570 101 : return result->qr_code_data_sizes[index];
571 : }
|