GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/barcodes.c
Date: 2025-09-22 22:57:09
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 GATE_UNUSED_ARG(flags);
491
492 do
493 {
494 if (NULL == gate_blob_create(&buffer, NULL, required_size))
495 {
496 ret = GATE_RESULT_OUTOFMEMORY;
497 break;
498 }
499 ptr_buffer = gate_blob_databuffer(&buffer);
500
501 status = qrcode_initBytes(&qrcode, (unsigned char*)ptr_buffer, version, ECC_HIGH,
502 (unsigned char const*)input_data, (unsigned int)input_len);
503 if (status < 0)
504 {
505 ret = GATE_RESULT_FAILED;
506 break;
507 }
508
509 if (output)
510 {
511 image_width = border * 2 + qrcode.size;
512 if (NULL == gate_rasterimage_create(output, GATE_IMAGE_PIXELFORMAT_GRAY8, image_width, image_width, NULL))
513 {
514 ret = GATE_RESULT_OUTOFMEMORY;
515 break;
516 }
517 gate_rasterimage_clear(output, &white);
518
519 for (y = 0; y < qrcode.size; ++y)
520 {
521 for (x = 0; x < qrcode.size; ++x)
522 {
523 ptr_col = qrcode_getModule(&qrcode, (gate_uint8_t)x, (gate_uint8_t)y) ? &black : &white;
524 gate_rasterimage_set_pixel(output, border + x, border + y, ptr_col);
525 }
526 }
527 }
528 ret = GATE_RESULT_OK;
529 } while (0);
530
531 gate_blob_release(&buffer);
532
533 return ret;
534 }
535
536 #endif /* GATE_EXTLIB_QRCODE */
537
538
539
540
541
542 #ifndef GATE_TECH_BARCODE_MAX_STRIPES
543 #define GATE_TECH_BARCODE_MAX_STRIPES 1024
544 #endif
545
546
547 /* first number encoding table for left side */
548 static gate_uint8_t const EAN13_CODETABLE_A[] = {
549 0x0d, /* 0b00001101, */ /* 0 */
550 0x19, /* 0b00011001, */ /* 1 */
551 0x13, /* 0b00010011, */ /* 2 */
552 0x3d, /* 0b00111101, */ /* 3 */
553 0x23, /* 0b00100011, */ /* 4 */
554 0x31, /* 0b00110001, */ /* 5 */
555 0x2f, /* 0b00101111, */ /* 6 */
556 0x3b, /* 0b00111011, */ /* 7 */
557 0x37, /* 0b00110111, */ /* 8 */
558 0x0b /* 0b00001011 */ /* 9 */
559 };
560
561 /* second number encoding table for left side */
562 static gate_uint8_t const EAN13_CODETABLE_B[] = {
563 0x27, /* 0b00100111, */ /* 0 */
564 0x33, /* 0b00110011, */ /* 1 */
565 0x1b, /* 0b00011011, */ /* 2 */
566 0x21, /* 0b00100001, */ /* 3 */
567 0x1d, /* 0b00011101, */ /* 4 */
568 0x39, /* 0b00111001, */ /* 5 */
569 0x05, /* 0b00000101, */ /* 6 */
570 0x11, /* 0b00010001, */ /* 7 */
571 0x09, /* 0b00001001, */ /* 8 */
572 0x17 /* 0b00010111 */ /* 9 */
573 };
574
575 /* number encoding table for right side */
576 static gate_uint8_t const EAN13_CODETABLE_C[] = {
577 0x72, /* 0b01110010, */ /* 0 */
578 0x66, /* 0b01100110, */ /* 1 */
579 0x6c, /* 0b01101100, */ /* 2 */
580 0x42, /* 0b01000010, */ /* 3 */
581 0x5c, /* 0b01011100, */ /* 4 */
582 0x4e, /* 0b01001110, */ /* 5 */
583 0x50, /* 0b01010000, */ /* 6 */
584 0x44, /* 0b01000100, */ /* 7 */
585 0x48, /* 0b01001000, */ /* 8 */
586 0x74 /* 0b01110100 */ /* 9 */
587 };
588
589 /* left-table (A or B) usage pattern for very first digit */
590 static gate_uint8_t const EAN13_PATTERNTABLE_LEFT[] =
591 {
592 0x00, /* 0b00000000, */ /* 0 */
593 0x0b, /* 0b00001011, */ /* 1 */
594 0x0d, /* 0b00001101, */ /* 2 */
595 0x0e, /* 0b00001110, */ /* 3 */
596 0x13, /* 0b00010011, */ /* 4 */
597 0x19, /* 0b00011001, */ /* 5 */
598 0x1c, /* 0b00011100, */ /* 6 */
599 0x15, /* 0b00010101, */ /* 7 */
600 0x16, /* 0b00010110, */ /* 8 */
601 0x1a /* 0b00011010, */ /* 9 */
602 };
603
604 #define INVALID_EAN13_NUMBER 255
605
606 16 static gate_uint8_t resolve_ean13_number(gate_uint8_t needle, gate_uint8_t const haystack[10])
607 {
608 gate_uint8_t n;
609
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 3 times.
96 for (n = 0; n != 10; ++n)
610 {
611
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 80 times.
93 if (needle == haystack[n])
612 {
613 13 return n;
614 }
615 }
616 3 return INVALID_EAN13_NUMBER;
617 }
618
619 1 static unsigned gate_tech_barcode_scan_line_stripes(gate_rasterimage_t const* image,
620 unsigned x, unsigned y, unsigned pixel_count,
621 gate_bool_t reverse_scan,
622 gate_uint16_t* stripes, gate_size_t stripes_count)
623 {
624 1 unsigned stripes_used = 0;
625 1 gate_uint16_t counter = 0;
626 1 gate_uint8_t bitcolor = 0;
627 1 gate_uint8_t current_bitcolor_state = 255;
628 gate_color_t pix;
629 unsigned ndx;
630
631
2/2
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 1 times.
112 for (ndx = 0; ndx != pixel_count; ++ndx)
632 {
633
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
111 if (reverse_scan)
634 {
635 /* reverse scan (right to left) */
636 ✗ gate_rasterimage_get_pixel(image, x + pixel_count - ndx - 1, y, &pix);
637 }
638 else
639 {
640 /* regular scan (left to right) */
641 111 gate_rasterimage_get_pixel(image, x + ndx, y, &pix);
642 }
643
644 111 bitcolor = gate_color_to_monochrome(&pix, GATE_COLOR_DEFAULT_MONOCHROME_THRESHOLD);
645
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 60 times.
111 if (current_bitcolor_state == bitcolor)
646 {
647 51 ++counter;
648 }
649 else
650 {
651 /* save length-counter of previous color and start a new counter for current color */
652
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 if (stripes_used < stripes_count)
653 {
654 60 stripes[stripes_used++] = counter;
655 }
656 60 counter = 1;
657 60 current_bitcolor_state = bitcolor;
658 }
659 }
660 1 return stripes_used;
661 }
662
663 1 static gate_size_t gate_tech_barcode_scan_stripe_bits(gate_uint16_t const* stripes, gate_size_t stripes_count,
664 gate_uint16_t min_stripe,
665 gate_uint8_t* bits, gate_size_t bits_capacity)
666 {
667 1 gate_size_t bits_used = 0;
668 gate_real32_t length;
669 1 gate_real32_t min_length = (gate_real32_t)min_stripe;
670 unsigned int bit_length;
671
672
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))
673 {
674 60 length = (((gate_real32_t)*stripes) / min_length) + 0.4f;
675 60 bit_length = (unsigned int)length;
676
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (bit_length > 255)
677 {
678 ✗ bit_length = 255;
679 }
680 60 bits[bits_used] = (gate_uint8_t)bit_length;
681 60 ++bits_used;
682 60 ++stripes;
683 }
684 1 return bits_used;
685 }
686
687
688 1 static gate_bool_t gate_tech_eanbarcode_try_decode(gate_uint8_t const* bits, gate_size_t bits_count, char ean13[13])
689 {
690 gate_size_t index;
691 gate_size_t bitindex;
692 gate_size_t digit_bit_index;
693 1 gate_uint8_t digits[256] = GATE_INIT_EMPTY;
694 gate_uint8_t* ptr_digit;
695 gate_uint8_t decoded_num;
696 gate_bool_t black;
697 1 gate_uint8_t x = 0;
698
699 1 black = false;
700 1 digit_bit_index = 0;
701
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 1 times.
57 for (index = 0; index < bits_count; ++index)
702 {
703
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 56 times.
148 for (bitindex = 0; bitindex < bits[index]; ++bitindex)
704 {
705 92 ptr_digit = &digits[digit_bit_index / 7];
706 92 *ptr_digit <<= 1;
707
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 43 times.
92 if (black)
708 {
709 49 *ptr_digit |= 1;
710 }
711 92 ++digit_bit_index;
712
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 91 times.
92 if (digit_bit_index == 42)
713 {
714 /* 5 bits of separation stripes */
715 1 digit_bit_index += 2;
716 }
717 }
718 56 black = !black;
719 }
720
721
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (index = 0; index < 6; ++index)
722 {
723 6 decoded_num = resolve_ean13_number(digits[index], EAN13_CODETABLE_A);
724
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (decoded_num != INVALID_EAN13_NUMBER)
725 {
726 3 x <<= 1;
727 }
728 else
729 {
730 3 decoded_num = resolve_ean13_number(digits[index], EAN13_CODETABLE_B);
731
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (decoded_num == INVALID_EAN13_NUMBER)
732 {
733 ✗ return false;
734 }
735 3 x <<= 1;
736 3 x |= 1;
737 }
738 6 ean13[index + 1] = '0' + decoded_num;
739 }
740
741 1 decoded_num = resolve_ean13_number(x, EAN13_PATTERNTABLE_LEFT);
742
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (decoded_num == INVALID_EAN13_NUMBER)
743 {
744 ✗ return false;
745 }
746 1 ean13[0] = '0' + decoded_num;
747
748
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (digits[6] != 0x0a /* 0b00001010 */)
749 {
750 /* separation stripes */
751 ✗ return false;
752 }
753
754
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (index = 7; index < 13; ++index)
755 {
756 6 decoded_num = resolve_ean13_number(digits[index], EAN13_CODETABLE_C);
757
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (decoded_num == INVALID_EAN13_NUMBER)
758 {
759 ✗ return false;
760 }
761 6 ean13[index] = '0' + decoded_num;
762 }
763 1 return true;
764 }
765
766 1 static gate_bool_t gate_tech_eanbarcode_analyze_bits(gate_uint8_t const* bits, gate_size_t bits_count, char ean13[13])
767 {
768 gate_size_t index;
769 1 gate_size_t start_index = 0;
770
771
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (bits_count < 20)
772 {
773 ✗ return false;
774 }
775 /* first entry in bits[] array is WHITE */
776 /* EAN13 starts with bits 00000001010 */
777
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 for (index = 1; index < bits_count - 3; index += 2)
778 {
779
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)
780
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 && (bits[index - 1] > 7))
781 {
782 1 start_index = index + 3;
783
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))
784 {
785 1 return true;
786 }
787 }
788 }
789 /* no ean13 barcode detected */
790 ✗ return false;
791 }
792
793 1 static gate_bool_t gate_tech_eanbarcode_scan_line(gate_rasterimage_t const* image, unsigned x, unsigned y, unsigned pixel_count,
794 gate_bool_t reverse_scan, char ean13[13])
795 {
796 1 gate_uint16_t stripes[GATE_TECH_BARCODE_MAX_STRIPES] = GATE_INIT_EMPTY;
797 1 gate_uint8_t bits[GATE_TECH_BARCODE_MAX_STRIPES] = GATE_INIT_EMPTY;
798 1 gate_size_t stripes_used = 0;
799 1 gate_size_t bits_used = 0;
800 1 gate_uint16_t min_bit_length = 0;
801 gate_uint16_t next_min_bit_length;
802 1 gate_bool_t ean_decoded = false;
803 gate_size_t index;
804
805 1 stripes_used = gate_tech_barcode_scan_line_stripes(image, x, y, pixel_count, reverse_scan, stripes, sizeof(stripes) / sizeof(stripes[0]));
806
807
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)
808 {
809 1 next_min_bit_length = 0xffff;
810
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 1 times.
61 for (index = 0; index != stripes_used; ++index)
811 {
812
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))
813 {
814 2 next_min_bit_length = stripes[index];
815 }
816 }
817
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (min_bit_length == next_min_bit_length)
818 {
819 ✗ break;
820 }
821 1 min_bit_length = next_min_bit_length;
822
823 1 bits_used = gate_tech_barcode_scan_stripe_bits(stripes, stripes_used, min_bit_length, bits, sizeof(bits));
824
825 1 ean_decoded = gate_tech_eanbarcode_analyze_bits(bits, bits_used, ean13);
826 }
827 1 return ean_decoded;
828 }
829
830 ✗ static gate_bool_t gate_tech_eanbarcode_scan_image(gate_rasterimage_t const* input, unsigned width, unsigned y, char ean13[13])
831 {
832 ✗ gate_bool_t ean_decoded = false;
833
834 ✗ ean_decoded = gate_tech_eanbarcode_scan_line(input, 0, y, width, false, ean13);
835 ✗ if (!ean_decoded)
836 {
837 ✗ ean_decoded = gate_tech_eanbarcode_scan_line(input, 0, y, width, true, ean13);
838 }
839 ✗ return ean_decoded;
840 }
841
842
843 1 gate_result_t gate_tech_eanbarcode_scan(gate_rasterimage_t const* input, gate_enumint_t flags, gate_string_t* output)
844 {
845 1 gate_result_t ret = GATE_RESULT_FAILED;
846 unsigned width;
847 unsigned height;
848 unsigned y;
849 1 gate_bool_t ean_decoded = false;
850 1 char ean13[64] = GATE_INIT_EMPTY;
851
852 do
853 {
854 1 width = gate_rasterimage_width(input);
855 1 height = gate_rasterimage_height(input);
856
857
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FLAG_ENABLED(flags, GATE_TECH_BARCODE_SCANNER_FLAG_INTENSIVE))
858 {
859 ✗ for (y = 0; y < height; y += 4)
860 {
861 ✗ ean_decoded = gate_tech_eanbarcode_scan_image(input, width, y, ean13);
862 ✗ if (ean_decoded)
863 {
864 ✗ break;
865 }
866 }
867 }
868
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (!GATE_FLAG_ENABLED(flags, GATE_TECH_BARCODE_SCANNER_FLAG_QUICK))
869 {
870 /* quick scan */
871 1 ean_decoded = gate_tech_eanbarcode_scan_line(input, 0, height / 2, width, false, ean13);
872 }
873 else
874 {
875 /* default scan */
876 ✗ for (y = 1; y != 8; ++y)
877 {
878 ✗ ean_decoded = gate_tech_eanbarcode_scan_image(input, width, height * y / 8, ean13);
879 ✗ if (ean_decoded)
880 {
881 ✗ break;
882 }
883 }
884 }
885
886
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!ean_decoded)
887 {
888 ✗ ret = GATE_RESULT_NOMATCH;
889 ✗ break;
890 }
891
892
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_string_create(output, ean13, 13))
893 {
894 ✗ ret = GATE_RESULT_OUTOFMEMORY;
895 ✗ break;
896 }
897 1 ret = GATE_RESULT_OK;
898 } while (0);
899
900 1 return ret;
901 }
902
903 #define EAN13_BITS_COUNT (3 + 6 * 7 + 5 + 6 * 7 + 3)
904
905 #define ADD_BIT(ptr_bits, bits_len, value) \
906 do { \
907 if(bits_len > 0) { \
908 *ptr_bits = value; \
909 ++ptr_bits; \
910 --bits_len; \
911 } \
912 } while(0)
913
914 #define ADD_BITS_OF_PATTERN(ptr_bits, bits_len, pattern_byte) \
915 do { \
916 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x40) ? true : false); \
917 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x20) ? true : false); \
918 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x10) ? true : false); \
919 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x08) ? true : false); \
920 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x04) ? true : false); \
921 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x02) ? true : false); \
922 ADD_BIT(ptr_bits, bits_len, (pattern_byte & 0x01) ? true : false); \
923 } while(0)
924
925
926
927 1 static void generate_ean_bits(gate_uint8_t const digits[13], gate_bool_t* bits, gate_size_t bits_len)
928 {
929 1 const gate_uint8_t left_pattern = EAN13_PATTERNTABLE_LEFT[digits[0]];
930 unsigned ndx;
931 gate_uint8_t const* pattern_table;
932
933 /* intro: */
934
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, true);
935
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, false);
936
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, true);
937
938 /* left-side: */
939
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (ndx = 0; ndx != 6; ++ndx)
940 {
941
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;
942
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]]);
943 }
944
945 /* separators: */
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
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, true);
950
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, false);
951
952 /* right-side: */
953
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (ndx = 0; ndx != 6; ++ndx)
954 {
955
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]]);
956 }
957
958 /* outro: */
959
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, true);
960
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, false);
961
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ADD_BIT(bits, bits_len, true);
962
963 1 }
964
965 1 gate_result_t gate_tech_eanbarcode_print(gate_string_t const* input, gate_enumint_t flags, gate_rasterimage_t* output)
966 {
967 static gate_color_t const white = GATE_COLOR_WHITE;
968 static gate_color_t const black = GATE_COLOR_BLACK;
969 static const unsigned separator_gaps[6] = { 0, 2, 6 * 7 + 2, 2, 6 * 7 + 2, 2 };
970 1 gate_uint8_t payload[13] = GATE_INIT_EMPTY;
971 1 char const* const txt = gate_string_ptr(input, 0);
972 1 gate_size_t len = gate_string_length(input);
973 unsigned ndx;
974 gate_bool_t bits[EAN13_BITS_COUNT];
975 1 unsigned border = 8;
976 1 const unsigned barcode_height = 64;
977 1 const unsigned text_height = 10;
978 1 unsigned height = barcode_height;
979 unsigned x;
980
981 GATE_UNUSED_ARG(flags);
982
983
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (len > 13) len = 13;
984
985
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
14 for (ndx = 0; ndx != len; ++ndx)
986 {
987 13 gate_str_parse_digit(&txt[ndx], &payload[ndx]);
988 }
989
990 1 generate_ean_bits(payload, bits, sizeof(bits) / sizeof(bits[0]));
991
992
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))
993 {
994 ✗ return GATE_RESULT_OUTOFMEMORY;
995 }
996
997 1 gate_rasterimage_clear(output, &white);
998
999
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 1 times.
65 while (height-- > 0)
1000 {
1001
2/2
✓ Branch 0 taken 6080 times.
✓ Branch 1 taken 64 times.
6144 for (ndx = 0; ndx != EAN13_BITS_COUNT; ++ndx)
1002 {
1003
2/2
✓ Branch 0 taken 3264 times.
✓ Branch 1 taken 2816 times.
6080 if (bits[ndx])
1004 {
1005 3264 gate_rasterimage_set_pixel(output, ndx + border, height, &black);
1006 }
1007 }
1008 }
1009
1010 1 gate_font8_print_char(output, '0' + payload[0], 2, barcode_height + 1, black);
1011
1012
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (ndx = 0; ndx != 6; ++ndx)
1013 {
1014 6 gate_font8_print_char(output, '0' + payload[ndx + 1], border + 3 + ndx * 7 + 1, barcode_height + 1, black);
1015 }
1016
1017
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 for (ndx = 0; ndx != 6; ++ndx)
1018 {
1019 6 gate_font8_print_char(output, '0' + payload[ndx + 7], border + 3 + 6 * 7 + 5 + ndx * 7 + 1, barcode_height + 1, black);
1020 }
1021
1022 1 x = border;
1023
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)
1024 {
1025 6 x += separator_gaps[ndx];
1026 6 gate_draw_line(output, x, barcode_height, x, barcode_height + 3, black);
1027 }
1028
1029 1 return GATE_RESULT_OK;
1030 }
1031