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(®ion); | |
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(®ion); | |
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 |