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