GCC Code Coverage Report


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