GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/barcodes.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 214 357 59.9%
Functions: 12 15 80.0%
Branches: 120 234 51.3%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/tech/barcodes.h"
30 #include "gate/results.h"
31 #include "gate/debugging.h"
32 #include "gate/blobs.h"
33 #include "gate/graphics/fonts.h"
34 #include "gate/graphics/drawing.h"
35
36
37 #if !defined(GATE_EXTLIB_DMTX)
38
39 gate_result_t gate_tech_dmtx_scan(gate_rasterimage_t const* input, gate_enumint_t flags, gate_string_t* output)
40 {
41 (void)input;
42 (void)flags;
43 (void)output;
44 return GATE_RESULT_NOTSUPPORTED;
45 }
46
47 gate_result_t gate_tech_dmtx_print(gate_string_t const* input, gate_enumint_t flags, gate_rasterimage_t* output)
48 {
49 (void)input;
50 (void)flags;
51 (void)output;
52 return GATE_RESULT_NOTSUPPORTED;
53 }
54
55
56 #else /* GATE_EXTLIB_DMTX */
57
58 #include <dmtx.h>
59
60 1 gate_result_t gate_tech_dmtx_scan(gate_rasterimage_t const* input, gate_enumint_t flags, gate_string_t* output)
61 {
62 1 gate_result_t ret = GATE_RESULT_FAILED;
63 1 DmtxImage* dmtx_image = NULL;
64 1 DmtxDecode* decoder = NULL;
65 1 DmtxRegion* region = NULL;
66 1 DmtxMessage* message = NULL;
67 int width;
68 int height;
69 int y;
70 1 gate_blob_t blob = GATE_INIT_EMPTY;
71 1 gate_uint8_t* ptr_blob = NULL;
72
73 GATE_UNUSED_ARG(flags);
74
75 do
76 {
77 1 width = (int)gate_rasterimage_width(input);
78 1 height = (int)gate_rasterimage_height(input);
79
1/3
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 switch (gate_rasterimage_format(input))
80 {
81 case GATE_IMAGE_PIXELFORMAT_RGBA:
82 {
83 dmtx_image = dmtxImageCreate((unsigned char*)gate_rasterimage_get_line_ptr(input, 0), width, height, DmtxPack32bppRGBX);
84 dmtxImageSetProp(dmtx_image, DmtxPropRowPadBytes, 0);
85 dmtxImageSetProp(dmtx_image, DmtxPropImageFlip, DmtxFlipNone);
86 break;
87 }
88 1 case GATE_IMAGE_PIXELFORMAT_GRAY8:
89 {
90
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (input->line_padding == 0)
91 {
92 1 dmtx_image = dmtxImageCreate((unsigned char*)gate_rasterimage_get_line_ptr(input, 0), width, height, DmtxPack8bppK);
93 }
94 else
95 {
96 if (NULL == gate_blob_create(&blob, NULL, width * height))
97 {
98 break;
99 }
100 ptr_blob = gate_blob_databuffer(&blob);
101
102 for (y = 0; y < height; ++y)
103 {
104 gate_mem_copy(ptr_blob + y * width, gate_rasterimage_get_line_ptr(input, y), (gate_size_t)width);
105 }
106 dmtx_image = dmtxImageCreate(ptr_blob, width, height, DmtxPack8bppK);
107 }
108 1 dmtxImageSetProp(dmtx_image, DmtxPropRowPadBytes, 0);
109 1 dmtxImageSetProp(dmtx_image, DmtxPropImageFlip, DmtxFlipNone);
110 1 break;
111 }
112 }
113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == dmtx_image)
114 {
115 ret = GATE_RESULT_FAILED;
116 break;
117 }
118
119 1 decoder = dmtxDecodeCreate(dmtx_image, 1);
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == decoder)
121 {
122 ret = GATE_RESULT_FAILED;
123 break;
124 }
125
126
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 while (NULL != (region = dmtxRegionFindNext(decoder, NULL)))
127 {
128 1 message = dmtxDecodeMatrixRegion(decoder, region, DmtxUndefined);
129
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (NULL != message)
130 {
131 /* region could be decoded -> something was found -> success */
132 1 break;
133 }
134 /* could not decode region, destroy it and try to find another one*/
135 dmtxRegionDestroy(&region);
136 }
137
138
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == message)
139 {
140 ret = GATE_RESULT_NOMATCH;
141 break;
142 }
143
144
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_string_create(output, (char const*)message->output, (gate_size_t)message->outputIdx))
145 {
146 ret = GATE_RESULT_OUTOFMEMORY;
147 break;
148 }
149
150 1 ret = GATE_RESULT_OK;
151
152 } while (0);
153
154 1 dmtxMessageDestroy(&message);
155 1 dmtxRegionDestroy(&region);
156 1 dmtxDecodeDestroy(&decoder);
157 1 dmtxImageDestroy(&dmtx_image);
158 1 gate_blob_release(&blob);
159
160 1 return ret;
161 }
162
163 258 static void draw_dmtx_dot(gate_rasterimage_t* image, unsigned x, unsigned y, unsigned strength, unsigned border)
164 {
165 static const gate_color_t black = GATE_COLOR_BLACK;
166 unsigned a, b;
167
2/2
✓ Branch 0 taken 1032 times.
✓ Branch 1 taken 258 times.
1290 for (a = 0; a != strength; ++a)
168 {
169
2/2
✓ Branch 0 taken 4128 times.
✓ Branch 1 taken 1032 times.
5160 for (b = 0; b != strength; ++b)
170 {
171 4128 gate_rasterimage_set_pixel(image, border + x * strength + b, border + y * strength + a, &black);
172 }
173 }
174 258 }
175
176 1 gate_result_t gate_tech_dmtx_print(gate_string_t const* input, gate_enumint_t flags, gate_rasterimage_t* output)
177 {
178 static const gate_color_t white = GATE_COLOR_WHITE;
179 1 gate_result_t ret = GATE_RESULT_FAILED;
180 1 DmtxEncode* encoder = NULL;
181 unsigned int result;
182 int width, height;
183 gate_uint8_t* ptr_gray;
184 int row, col, rowinverse;
185 int module_status;
186 1 const unsigned pixel_strength = 4;
187 1 const unsigned border = pixel_strength * 4;
188
189 GATE_UNUSED_ARG(flags);
190
191 do
192 {
193 1 encoder = dmtxEncodeCreate();
194
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == encoder)
195 {
196 ret = GATE_RESULT_FAILED;
197 break;
198 }
199
200 //DmtxPass | DmtxFail
201 1 result = dmtxEncodeDataMatrix(encoder, (int)gate_string_length(input), (unsigned char*)gate_string_ptr(input, 0));
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (DmtxFail == result)
203 {
204 ret = GATE_RESULT_EXECUTIONFAILED;
205 break;
206 }
207
208 1 width = encoder->region.symbolCols * (int)pixel_strength + (int)border * 2;
209 1 height = encoder->region.symbolRows * (int)pixel_strength + (int)border * 2;
210
211
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_rasterimage_create(output, GATE_IMAGE_PIXELFORMAT_GRAY8, width, height, NULL))
212 {
213 ret = GATE_RESULT_OUTOFMEMORY;
214 break;
215 }
216 1 gate_rasterimage_clear(output, &white);
217
218
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 1 times.
23 for (row = 0; row < encoder->region.symbolRows; ++row)
219 {
220 22 ptr_gray = (gate_uint8_t*)gate_rasterimage_get_line_ptr(output, (unsigned)row + border);
221 22 ptr_gray += border;
222 22 rowinverse = encoder->region.symbolRows - row - 1;
223
2/2
✓ Branch 0 taken 484 times.
✓ Branch 1 taken 22 times.
506 for (col = 0; col < encoder->region.symbolCols; ++col)
224 {
225 484 module_status = dmtxSymbolModuleStatus(encoder->message, encoder->region.sizeIdx, rowinverse, col);
226
2/2
✓ Branch 0 taken 258 times.
✓ Branch 1 taken 226 times.
484 if (module_status & DmtxModuleOn)
227 {
228 258 draw_dmtx_dot(output, col, row, pixel_strength, border);
229 }
230 }
231 }
232 1 ret = GATE_RESULT_OK;
233 } while (0);
234
235 1 dmtxEncodeDestroy(&encoder);
236 1 return ret;
237 }
238
239 #endif /* GATE_EXTLIB_DMTX */
240
241
242
243 #if !defined(GATE_EXTLIB_QUIRC)
244
245 gate_result_t gate_tech_qrcode_scan(gate_rasterimage_t const* input, gate_enumint_t flags, gate_string_t* output)
246 {
247 return GATE_RESULT_NOTSUPPORTED;
248 }
249
250 #else /* GATE_EXTLIB_QUIRC */
251
252 #include <quirc.h>
253
254 gate_result_t gate_tech_qrcode_scan(gate_rasterimage_t const* input, gate_enumint_t flags, gate_string_t* output)
255 {
256 gate_result_t ret = GATE_RESULT_FAILED;
257 struct quirc* qr = NULL;
258 int result;
259 int width, height;
260 int x, y;
261 uint8_t* gray_image;
262 uint8_t* dst_pixel;
263 gate_color_t const* src_pixel;
264 gate_uint8_t const* src_byte;
265 gate_color_t pixel;
266 int num_codes, code_index;
267 struct quirc_code code;
268 struct quirc_data data;
269 quirc_decode_error_t err;
270 gate_strbuilder_t builder = GATE_INIT_EMPTY;
271 gate_size_t success_count, error_count;
272 gate_enumint_t format;
273
274 GATE_UNUSED_ARG(flags);
275
276 do
277 {
278 gate_strbuilder_create(&builder, 192);
279
280 qr = quirc_new();
281 if (!qr)
282 {
283 ret = GATE_RESULT_OUTOFMEMORY;
284 break;
285 }
286
287 width = gate_rasterimage_width(input);
288 height = gate_rasterimage_height(input);
289 format = gate_rasterimage_format(input);
290 result = quirc_resize(qr, width, height);
291 if (result < 0)
292 {
293 ret = GATE_RESULT_OUTOFMEMORY;
294 break;
295 }
296
297 gray_image = quirc_begin(qr, &width, &height);
298 dst_pixel = gray_image;
299
300 switch (format)
301 {
302 case GATE_IMAGE_PIXELFORMAT_GRAY8:
303 {
304 for (y = 0; y != height; ++y)
305 {
306 src_byte = (gate_uint8_t const*)gate_rasterimage_get_line_ptr(input, y);
307 for (x = 0; x != width; ++x)
308 {
309 *dst_pixel = *src_byte;
310 ++src_byte;
311 ++dst_pixel;
312 }
313 }
314 break;
315 }
316 case GATE_IMAGE_PIXELFORMAT_RGBA:
317 {
318 for (y = 0; y != height; ++y)
319 {
320 src_pixel = (gate_color_t const*)gate_rasterimage_get_line_ptr(input, y);
321 for (x = 0; x != width; ++x)
322 {
323 *dst_pixel = (gate_uint8_t)((0
324 + ((int)src_pixel->r * 54)
325 + ((int)src_pixel->g * 182)
326 + ((int)src_pixel->b * 19))
327 / 255);
328 ++src_pixel;
329 ++dst_pixel;
330 }
331 }
332 break;
333 }
334 default:
335 {
336 for (y = 0; y != height; ++y)
337 {
338 for (x = 0; x != width; ++x)
339 {
340 gate_rasterimage_get_pixel(input, x, y, &pixel);
341 *dst_pixel = (gate_uint8_t)((0
342 + ((int)pixel.r * 54)
343 + ((int)pixel.g * 182)
344 + ((int)pixel.b * 19))
345 / 255);
346 ++dst_pixel;
347 }
348 }
349 break;
350 }
351 }
352
353 quirc_end(qr);
354
355 num_codes = quirc_count(qr);
356 ret = GATE_RESULT_OK;
357 success_count = 0;
358 error_count = 0;
359
360 for (code_index = 0; code_index < num_codes; code_index++)
361 {
362 quirc_extract(qr, code_index, &code);
363
364 err = quirc_decode(&code, &data);
365 if (QUIRC_ERROR_DATA_ECC == err)
366 {
367 quirc_flip(&code);
368 err = quirc_decode(&code, &data);
369 }
370
371 if (err != QUIRC_SUCCESS)
372 {
373 GATE_DEBUG_TRACE_MSG_VALUE("Failed to decode QR Code", code_index);
374 GATE_DEBUG_TRACE(quirc_strerror(err));
375 ++error_count;
376 continue;
377 /*
378 ret = GATE_RESULT_NOMATCH;
379 */
380 break;
381 }
382
383 switch (data.data_type)
384 {
385 case QUIRC_DATA_TYPE_NUMERIC:
386 case QUIRC_DATA_TYPE_ALPHA:
387 case QUIRC_DATA_TYPE_BYTE:
388 case QUIRC_DATA_TYPE_KANJI:
389 break;
390 }
391
392 if (success_count != 0)
393 {
394 gate_strbuilder_append_text(&builder, GATE_STR_NEWLINE, GATE_STR_NEWLINE_LENGTH);
395 }
396
397 if (data.payload_len != gate_strbuilder_append_text(&builder, (char const*)data.payload, data.payload_len))
398 {
399 ret = GATE_RESULT_OUTOFMEMORY;
400 break;
401 }
402 ++success_count;
403 }
404
405 if (success_count > 0)
406 {
407 if (NULL == gate_strbuilder_to_string(&builder, output))
408 {
409 ret = GATE_RESULT_OUTOFMEMORY;
410 break;
411 }
412 ret = GATE_RESULT_OK;
413 }
414 else if (error_count == 0)
415 {
416 ret = GATE_RESULT_NOMATCH;
417 }
418 else
419 {
420 ret = GATE_RESULT_FAILED;
421 }
422
423 } while (0);
424
425
426 if (qr)
427 {
428 quirc_destroy(qr);
429 }
430
431 gate_strbuilder_release(&builder);
432
433 return ret;
434 }
435
436 #endif /* GATE_EXTLIB_QUIRC */
437
438
439 #if !defined(GATE_EXTLIB_QRCODE)
440
441 gate_result_t gate_tech_qrcode_print(gate_string_t const* input, gate_enumint_t flags, gate_rasterimage_t* output)
442 {
443 return GATE_RESULT_NOTSUPPORTED;
444 }
445
446 #else
447
448 #include <qrcode.h>
449
450 static gate_uint16_t qrcode_version_max_bin_length[] = {
451 0, 7, 14, 24, 34, 44, 58, 64, 84, 98, 119,
452 137, 155, 177, 194, 220, 250, 280, 310, 338, 382,
453 403, 439, 461, 511, 535, 593, 625, 658, 698, 742,
454 790, 842, 898, 958, 983, 1051, 1093, 1139, 1219, 1273
455 };
456 static gate_size_t qrcode_version_max_bin_length_count = sizeof(qrcode_version_max_bin_length) / sizeof(qrcode_version_max_bin_length[0]);
457
458 static gate_uint8_t get_version_by_length(gate_size_t len)
459 {
460 gate_uint8_t v;
461 for (v = 1; v < qrcode_version_max_bin_length_count; ++v)
462 {
463 const gate_uint16_t max_len_of_version = qrcode_version_max_bin_length[v];
464 if (len <= max_len_of_version)
465 {
466 return v;
467 }
468 }
469 return (gate_uint8_t)qrcode_version_max_bin_length_count - 1;
470 }
471
472 gate_result_t gate_tech_qrcode_print(gate_string_t const* input, gate_enumint_t flags, gate_rasterimage_t* output)
473 {
474 gate_result_t ret = GATE_RESULT_FAILED;
475 char const* input_data = gate_string_ptr(input, 0);
476 gate_size_t input_len = gate_string_length(input);
477 gate_uint8_t version = get_version_by_length(input_len);
478 QRCode qrcode;
479 gate_blob_t buffer = GATE_INIT_EMPTY;
480 gate_size_t required_size = qrcode_getBufferSize(version);
481 void* ptr_buffer = NULL;
482 int status;
483 static const gate_color_t black = GATE_COLOR_BLACK;
484 static const gate_color_t white = GATE_COLOR_WHITE;
485 unsigned x, y;
486 unsigned border = 4;
487 unsigned image_width;
488 gate_color_t const* ptr_col;
489
490 do
491 {
492 if (NULL == gate_blob_create(&buffer, NULL, required_size))
493 {
494 ret = GATE_RESULT_OUTOFMEMORY;
495 break;
496 }
497 ptr_buffer = gate_blob_databuffer(&buffer);
498
499 status = qrcode_initBytes(&qrcode, (unsigned char*)ptr_buffer, version, ECC_HIGH,
500 (unsigned char const*)input_data, (unsigned int)input_len);
501 if (status < 0)
502 {
503 ret = GATE_RESULT_FAILED;
504 break;
505 }
506
507 if (output)
508 {
509 image_width = border * 2 + qrcode.size;
510 if (NULL == gate_rasterimage_create(output, GATE_IMAGE_PIXELFORMAT_GRAY8, image_width, image_width, NULL))
511 {
512 ret = GATE_RESULT_OUTOFMEMORY;
513 break;
514 }
515 gate_rasterimage_clear(output, &white);
516
517 for (y = 0; y < qrcode.size; ++y)
518 {
519 for (x = 0; x < qrcode.size; ++x)
520 {
521 ptr_col = qrcode_getModule(&qrcode, (gate_uint8_t)x, (gate_uint8_t)y) ? &black : &white;
522 gate_rasterimage_set_pixel(output, border + x, border + y, ptr_col);
523 }
524 }
525 }
526 ret = GATE_RESULT_OK;
527 } while (0);
528
529 gate_blob_release(&buffer);
530
531 return ret;
532 }
533
534 #endif /* GATE_EXTLIB_QRCODE */
535
536
537
538
539
540 #ifndef GATE_TECH_BARCODE_MAX_STRIPES
541 #define GATE_TECH_BARCODE_MAX_STRIPES 1024
542 #endif
543
544
545 /* first number encoding table for left side */
546 static gate_uint8_t const EAN13_CODETABLE_A[] = {
547 0x0d, /* 0b00001101, */ /* 0 */
548 0x19, /* 0b00011001, */ /* 1 */
549 0x13, /* 0b00010011, */ /* 2 */
550 0x3d, /* 0b00111101, */ /* 3 */
551 0x23, /* 0b00100011, */ /* 4 */
552 0x31, /* 0b00110001, */ /* 5 */
553 0x2f, /* 0b00101111, */ /* 6 */
554 0x3b, /* 0b00111011, */ /* 7 */
555 0x37, /* 0b00110111, */ /* 8 */
556 0x0b /* 0b00001011 */ /* 9 */
557 };
558
559 /* second number encoding table for left side */
560 static gate_uint8_t const EAN13_CODETABLE_B[] = {
561 0x27, /* 0b00100111, */ /* 0 */
562 0x33, /* 0b00110011, */ /* 1 */
563 0x1b, /* 0b00011011, */ /* 2 */
564 0x21, /* 0b00100001, */ /* 3 */
565 0x1d, /* 0b00011101, */ /* 4 */
566 0x39, /* 0b00111001, */ /* 5 */
567 0x05, /* 0b00000101, */ /* 6 */
568 0x11, /* 0b00010001, */ /* 7 */
569 0x09, /* 0b00001001, */ /* 8 */
570 0x17 /* 0b00010111 */ /* 9 */
571 };
572
573 /* number encoding table for right side */
574 static gate_uint8_t const EAN13_CODETABLE_C[] = {
575 0x72, /* 0b01110010, */ /* 0 */
576 0x66, /* 0b01100110, */ /* 1 */
577 0x6c, /* 0b01101100, */ /* 2 */
578 0x42, /* 0b01000010, */ /* 3 */
579 0x5c, /* 0b01011100, */ /* 4 */
580 0x4e, /* 0b01001110, */ /* 5 */
581 0x50, /* 0b01010000, */ /* 6 */
582 0x44, /* 0b01000100, */ /* 7 */
583 0x48, /* 0b01001000, */ /* 8 */
584 0x74 /* 0b01110100 */ /* 9 */
585 };
586
587 /* left-table (A or B) usage pattern for very first digit */
588 static gate_uint8_t const EAN13_PATTERNTABLE_LEFT[] =
589 {
590 0x00, /* 0b00000000, */ /* 0 */
591 0x0b, /* 0b00001011, */ /* 1 */
592 0x0d, /* 0b00001101, */ /* 2 */
593 0x0e, /* 0b00001110, */ /* 3 */
594 0x13, /* 0b00010011, */ /* 4 */
595 0x19, /* 0b00011001, */ /* 5 */
596 0x1c, /* 0b00011100, */ /* 6 */
597 0x15, /* 0b00010101, */ /* 7 */
598 0x16, /* 0b00010110, */ /* 8 */
599 0x1a, /* 0b00011010, */ /* 9 */
600 };
601
602 #define INVALID_EAN13_NUMBER 255
603
604 16 static gate_uint8_t resolve_ean13_number(gate_uint8_t needle, gate_uint8_t const haystack[10])
605 {
606 gate_uint8_t n;
607
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 3 times.
96 for (n = 0; n != 10; ++n)
608 {
609
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 80 times.
93 if (needle == haystack[n])
610 {
611 13 return n;
612 }
613 }
614 3 return INVALID_EAN13_NUMBER;
615 }
616
617 1 static unsigned gate_tech_barcode_scan_line_stripes(gate_rasterimage_t const* image,
618 unsigned x, unsigned y, unsigned pixel_count,
619 gate_bool_t reverse_scan,
620 gate_uint16_t* stripes, gate_size_t stripes_count)
621 {
622 1 unsigned stripes_used = 0;
623 1 gate_uint16_t counter = 0;
624 1 gate_uint8_t bitcolor = 0;
625 1 gate_uint8_t current_bitcolor_state = 255;
626 gate_color_t pix;
627 unsigned ndx;
628
629
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 1 times.
112 for (ndx = 0; ndx != pixel_count; ++ndx)
630 {
631
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
111 if (reverse_scan)
632 {
633 /* reverse scan (right to left) */
634 gate_rasterimage_get_pixel(image, x + pixel_count - ndx - 1, y, &pix);
635 }
636 else
637 {
638 /* regular scan (left to right) */
639 111 gate_rasterimage_get_pixel(image, x + ndx, y, &pix);
640 }
641
642 111 bitcolor = gate_color_to_monochrome(&pix, GATE_COLOR_DEFAULT_MONOCHROME_THRESHOLD);
643
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 60 times.
111 if (current_bitcolor_state == bitcolor)
644 {
645 51 ++counter;
646 }
647 else
648 {
649 /* save length-counter of previous color and start a new counter for current color */
650
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 if (stripes_used < stripes_count)
651 {
652 60 stripes[stripes_used++] = counter;
653 }
654 60 counter = 1;
655 60 current_bitcolor_state = bitcolor;
656 }
657 }
658 1 return stripes_used;
659 }
660
661 1 static gate_size_t gate_tech_barcode_scan_stripe_bits(gate_uint16_t const* stripes, gate_size_t stripes_count,
662 gate_uint16_t min_stripe,
663 gate_uint8_t* bits, gate_size_t bits_capacity)
664 {
665 1 gate_size_t bits_used = 0;
666 gate_real32_t length;
667 1 gate_real32_t min_length = (gate_real32_t)min_stripe;
668 unsigned int bit_length;
669
670
3/4
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
61 while ((stripes_count-- != 0) && (bits_used < bits_capacity))
671 {
672 60 length = (((gate_real32_t)*stripes) / min_length) + 0.4f;
673 60 bit_length = (unsigned int)length;
674
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (bit_length > 255)
675 {
676 bit_length = 255;
677 }
678 60 bits[bits_used] = (gate_uint8_t)bit_length;
679 60 ++bits_used;
680 60 ++stripes;
681 }
682 1 return bits_used;
683 }
684
685
686 1 static gate_bool_t gate_tech_eanbarcode_try_decode(gate_uint8_t const* bits, gate_size_t bits_count, char ean13[13])
687 {
688 gate_size_t index;
689 gate_size_t bitindex;
690 gate_size_t digit_bit_index;
691 1 gate_uint8_t digits[256] = GATE_INIT_EMPTY;
692 gate_uint8_t* ptr_digit;
693 gate_uint8_t decoded_num;
694 gate_bool_t black;
695 1 gate_uint8_t x = 0;
696
697 1 black = false;
698 1 digit_bit_index = 0;
699
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 1 times.
57 for (index = 0; index < bits_count; ++index)
700 {
701
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 56 times.
148 for (bitindex = 0; bitindex < bits[index]; ++bitindex)
702 {
703 92 ptr_digit = &digits[digit_bit_index / 7];
704 92 *ptr_digit <<= 1;
705
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 43 times.
92 if (black)
706 {
707 49 *ptr_digit |= 1;
708 }
709 92 ++digit_bit_index;
710
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 91 times.
92 if (digit_bit_index == 42)
711 {
712 /* 5 bits of separation stripes */
713 1 digit_bit_index += 2;
714 }
715 }
716 56 black = !black;
717 }
718
719
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (index = 0; index < 6; ++index)
720 {
721 6 decoded_num = resolve_ean13_number(digits[index], EAN13_CODETABLE_A);
722
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (decoded_num != INVALID_EAN13_NUMBER)
723 {
724 3 x <<= 1;
725 }
726 else
727 {
728 3 decoded_num = resolve_ean13_number(digits[index], EAN13_CODETABLE_B);
729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (decoded_num == INVALID_EAN13_NUMBER)
730 {
731 return false;
732 }
733 3 x <<= 1;
734 3 x |= 1;
735 }
736 6 ean13[index + 1] = '0' + decoded_num;
737 }
738
739 1 decoded_num = resolve_ean13_number(x, EAN13_PATTERNTABLE_LEFT);
740
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (decoded_num == INVALID_EAN13_NUMBER)
741 {
742 return false;
743 }
744 1 ean13[0] = '0' + decoded_num;
745
746
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (digits[6] != 0x0a /* 0b00001010 */)
747 {
748 /* separation stripes */
749 return false;
750 }
751
752
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (index = 7; index < 13; ++index)
753 {
754 6 decoded_num = resolve_ean13_number(digits[index], EAN13_CODETABLE_C);
755
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (decoded_num == INVALID_EAN13_NUMBER)
756 {
757 return false;
758 }
759 6 ean13[index] = '0' + decoded_num;
760 }
761 1 return true;
762 }
763
764 1 static gate_bool_t gate_tech_eanbarcode_analyze_bits(gate_uint8_t const* bits, gate_size_t bits_count, char ean13[13])
765 {
766 gate_size_t index;
767 1 gate_size_t start_index = 0;
768
769
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (bits_count < 20)
770 {
771 return false;
772 }
773 /* first entry in bits[] array is WHITE */
774 /* EAN13 starts with bits 00000001010 */
775
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 for (index = 1; index < bits_count - 3; index += 2)
776 {
777
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 if ((bits[index] == 1) && (bits[index + 1] == 1) && (bits[index + 2] == 1)
778
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 && (bits[index - 1] > 7))
779 {
780 1 start_index = index + 3;
781
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (gate_tech_eanbarcode_try_decode(&bits[start_index], bits_count - start_index, ean13))
782 {
783 1 return true;
784 }
785 }
786 }
787 /* no ean13 barcode detected */
788 return false;
789 }
790
791 1 static gate_bool_t gate_tech_eanbarcode_scan_line(gate_rasterimage_t const* image, unsigned x, unsigned y, unsigned pixel_count,
792 gate_bool_t reverse_scan, char ean13[13])
793 {
794 1 gate_uint16_t stripes[GATE_TECH_BARCODE_MAX_STRIPES] = GATE_INIT_EMPTY;
795 1 gate_uint8_t bits[GATE_TECH_BARCODE_MAX_STRIPES] = GATE_INIT_EMPTY;
796 1 gate_size_t stripes_used = 0;
797 1 gate_size_t bits_used = 0;
798 1 gate_uint16_t min_bit_length = 0;
799 gate_uint16_t next_min_bit_length;
800 1 gate_bool_t ean_decoded = false;
801 gate_size_t index;
802
803 1 stripes_used = gate_tech_barcode_scan_line_stripes(image, x, y, pixel_count, reverse_scan, stripes, sizeof(stripes) / sizeof(stripes[0]));
804
805
3/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 while ((stripes_used > 20) && !ean_decoded)
806 {
807 1 next_min_bit_length = 0xffff;
808
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 1 times.
61 for (index = 0; index != stripes_used; ++index)
809 {
810
3/4
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 58 times.
60 if ((stripes[index] > min_bit_length) && (stripes[index] < next_min_bit_length))
811 {
812 2 next_min_bit_length = stripes[index];
813 }
814 }
815
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (min_bit_length == next_min_bit_length)
816 {
817 break;
818 }
819 1 min_bit_length = next_min_bit_length;
820
821 1 bits_used = gate_tech_barcode_scan_stripe_bits(stripes, stripes_used, min_bit_length, bits, sizeof(bits));
822
823 1 ean_decoded = gate_tech_eanbarcode_analyze_bits(bits, bits_used, ean13);
824 }
825 1 return ean_decoded;
826 }
827
828 static gate_bool_t gate_tech_eanbarcode_scan_image(gate_rasterimage_t const* input, unsigned width, unsigned y, char ean13[13])
829 {
830 gate_bool_t ean_decoded = false;
831
832 ean_decoded = gate_tech_eanbarcode_scan_line(input, 0, y, width, false, ean13);
833 if (!ean_decoded)
834 {
835 ean_decoded = gate_tech_eanbarcode_scan_line(input, 0, y, width, true, ean13);
836 }
837 return ean_decoded;
838 }
839
840
841 1 gate_result_t gate_tech_eanbarcode_scan(gate_rasterimage_t const* input, gate_enumint_t flags, gate_string_t* output)
842 {
843 1 gate_result_t ret = GATE_RESULT_FAILED;
844 unsigned width;
845 unsigned height;
846 unsigned y;
847 1 gate_bool_t ean_decoded = false;
848 1 char ean13[64] = GATE_INIT_EMPTY;
849
850 do
851 {
852 1 width = gate_rasterimage_width(input);
853 1 height = gate_rasterimage_height(input);
854
855
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FLAG_ENABLED(flags, GATE_TECH_BARCODE_SCANNER_FLAG_INTENSIVE))
856 {
857 for (y = 0; y < height; y += 4)
858 {
859 ean_decoded = gate_tech_eanbarcode_scan_image(input, width, y, ean13);
860 if (ean_decoded)
861 {
862 break;
863 }
864 }
865 }
866
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (!GATE_FLAG_ENABLED(flags, GATE_TECH_BARCODE_SCANNER_FLAG_QUICK))
867 {
868 /* quick scan */
869 1 ean_decoded = gate_tech_eanbarcode_scan_line(input, 0, height / 2, width, false, ean13);
870 }
871 else
872 {
873 /* default scan */
874 for (y = 1; y != 8; ++y)
875 {
876 ean_decoded = gate_tech_eanbarcode_scan_image(input, width, height * y / 8, ean13);
877 if (ean_decoded)
878 {
879 break;
880 }
881 }
882 }
883
884
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!ean_decoded)
885 {
886 ret = GATE_RESULT_NOMATCH;
887 break;
888 }
889
890
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_string_create(output, ean13, 13))
891 {
892 ret = GATE_RESULT_OUTOFMEMORY;
893 break;
894 }
895 1 ret = GATE_RESULT_OK;
896 } while (0);
897
898 1 return ret;
899 }
900
901 #define EAN13_BITS_COUNT (3 + 6 * 7 + 5 + 6 * 7 + 3)
902
903 #define ADD_BIT(ptr_bits, bits_len, value) \
904 do { \
905 if(bits_len > 0) { \
906 *ptr_bits = value; \
907 ++ptr_bits; \
908 --bits_len; \
909 } \
910 } while(0)
911
912 #define ADD_BITS_OF_PATTERN(ptr_bits, bits_len, pattern_byte) \
913 do { \
914 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x40) ? true : false); \
915 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x20) ? true : false); \
916 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x10) ? true : false); \
917 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x08) ? true : false); \
918 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x04) ? true : false); \
919 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x02) ? true : false); \
920 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x01) ? true : false); \
921 } while(0)
922
923
924
925 1 static void generate_ean_bits(gate_uint8_t const digits[13], gate_bool_t* bits, gate_size_t bits_len)
926 {
927 1 const gate_uint8_t left_pattern = EAN13_PATTERNTABLE_LEFT[digits[0]];
928 unsigned ndx;
929 gate_uint8_t const* pattern_table;
930
931 /* intro: */
932
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, true);
933
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, false);
934
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, true);
935
936 /* left-side: */
937
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (ndx = 0; ndx != 6; ++ndx)
938 {
939
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 pattern_table = (left_pattern & (1 << (5 - ndx))) == 0 ? EAN13_CODETABLE_A : EAN13_CODETABLE_B;
940
7/14
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 6 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 6 times.
✗ Branch 13 not taken.
6 ADD_BITS_OF_PATTERN(bits, bits_len, pattern_table[digits[ndx + 1]]);
941 }
942
943 /* separators: */
944
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, false);
945
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, true);
946
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, false);
947
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, true);
948
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, false);
949
950 /* right-side: */
951
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (ndx = 0; ndx != 6; ++ndx)
952 {
953
7/14
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 6 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 6 times.
✗ Branch 13 not taken.
6 ADD_BITS_OF_PATTERN(bits, bits_len, EAN13_CODETABLE_C[digits[ndx + 7]]);
954 }
955
956 /* outro: */
957
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, true);
958
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, false);
959
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, true);
960
961 1 }
962
963 1 gate_result_t gate_tech_eanbarcode_print(gate_string_t const* input, gate_enumint_t flags, gate_rasterimage_t* output)
964 {
965 static gate_color_t const white = GATE_COLOR_WHITE;
966 static gate_color_t const black = GATE_COLOR_BLACK;
967 static const unsigned separator_gaps[6] = { 0, 2, 6 * 7 + 2, 2, 6 * 7 + 2, 2 };
968 1 gate_uint8_t payload[13] = GATE_INIT_EMPTY;
969 1 char const* const txt = gate_string_ptr(input, 0);
970 1 gate_size_t len = gate_string_length(input);
971 unsigned ndx;
972 gate_bool_t bits[EAN13_BITS_COUNT];
973 1 unsigned border = 8;
974 1 const unsigned barcode_height = 64;
975 1 const unsigned text_height = 10;
976 1 unsigned height = barcode_height;
977 unsigned x;
978
979
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (len > 13) len = 13;
980
981
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
14 for (ndx = 0; ndx != len; ++ndx)
982 {
983 13 gate_str_parse_digit(&txt[ndx], &payload[ndx]);
984 }
985
986 1 generate_ean_bits(payload, bits, sizeof(bits) / sizeof(bits[0]));
987
988
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_rasterimage_create(output, GATE_IMAGE_PIXELFORMAT_GRAY8, EAN13_BITS_COUNT + border * 2, height + text_height, NULL))
989 {
990 return GATE_RESULT_OUTOFMEMORY;
991 }
992
993 1 gate_rasterimage_clear(output, &white);
994
995
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 1 times.
65 while (height-- > 0)
996 {
997
2/2
✓ Branch 0 taken 6080 times.
✓ Branch 1 taken 64 times.
6144 for (ndx = 0; ndx != EAN13_BITS_COUNT; ++ndx)
998 {
999
2/2
✓ Branch 0 taken 3264 times.
✓ Branch 1 taken 2816 times.
6080 if (bits[ndx])
1000 {
1001 3264 gate_rasterimage_set_pixel(output, ndx + border, height, &black);
1002 }
1003 }
1004 }
1005
1006 1 gate_font8_print_char(output, '0' + payload[0], 2, barcode_height + 1, black);
1007
1008
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (ndx = 0; ndx != 6; ++ndx)
1009 {
1010 6 gate_font8_print_char(output, '0' + payload[ndx + 1], border + 3 + ndx * 7 + 1, barcode_height + 1, black);
1011 }
1012
1013
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (ndx = 0; ndx != 6; ++ndx)
1014 {
1015 6 gate_font8_print_char(output, '0' + payload[ndx + 7], border + 3 + 6 * 7 + 5 + ndx * 7 + 1, barcode_height + 1, black);
1016 }
1017
1018 1 x = border;
1019
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (ndx = 0; ndx != sizeof(separator_gaps) / sizeof(separator_gaps[0]); ++ndx)
1020 {
1021 6 x += separator_gaps[ndx];
1022 6 gate_draw_line(output, x, barcode_height, x, barcode_height + 3, black);
1023 }
1024
1025 1 return GATE_RESULT_OK;
1026 }
1027