1 /**
2 * libdmtx - Data Matrix Encoding/Decoding Library
3 * Copyright 2008, 2009 Mike Laughton. All rights reserved.
4 * Copyright 2009 Mackenzie Straight. All rights reserved.
5 * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved.
6 * Copyright 2016 Tim Zaman. All rights reserved.
7 *
8 * See LICENSE file in the main project directory for full
9 * terms of use and distribution.
10 *
11 * Contact:
12 * Vadim A. Misbakh-Soloviov <dmtx@mva.name>
13 * Mike Laughton <mike@dragonflylogic.com>
14 *
15 * \file dmtxdecode.c
16 * \brief Decode regions
17 */
18
19 /**
20 * \brief Initialize decode struct with default values
21 * \param img
22 * \return Initialized DmtxDecode struct
23 */
24
25 #include <stdio.h> // for snprintf
26
27 extern DmtxDecode *
dmtxDecodeCreate(DmtxImage * img,int scale)28 dmtxDecodeCreate(DmtxImage *img, int scale)
29 {
30 DmtxDecode *dec;
31 int width, height;
32
33 dec = (DmtxDecode *)calloc(1, sizeof(DmtxDecode));
34 if(dec == NULL)
35 return NULL;
36
37 width = dmtxImageGetProp(img, DmtxPropWidth) / scale;
38 height = dmtxImageGetProp(img, DmtxPropHeight) / scale;
39
40 dec->fnc1 = DmtxUndefined;
41
42 dec->edgeMin = DmtxUndefined;
43 dec->edgeMax = DmtxUndefined;
44 dec->scanGap = 1;
45 dec->squareDevn = cos(50 * (M_PI/180));
46 dec->sizeIdxExpected = DmtxSymbolShapeAuto;
47 dec->edgeThresh = 10;
48
49 dec->xMin = 0;
50 dec->xMax = width - 1;
51 dec->yMin = 0;
52 dec->yMax = height - 1;
53 dec->scale = scale;
54
55 dec->cache = (unsigned char *)calloc(width * height, sizeof(unsigned char));
56 if(dec->cache == NULL) {
57 free(dec);
58 return NULL;
59 }
60
61 dec->image = img;
62 dec->grid = InitScanGrid(dec);
63
64 return dec;
65 }
66
67 /**
68 * \brief Deinitialize decode struct
69 * \param dec
70 * \return void
71 */
72 extern DmtxPassFail
dmtxDecodeDestroy(DmtxDecode ** dec)73 dmtxDecodeDestroy(DmtxDecode **dec)
74 {
75 if(dec == NULL || *dec == NULL)
76 return DmtxFail;
77
78 if((*dec)->cache != NULL)
79 free((*dec)->cache);
80
81 free(*dec);
82
83 *dec = NULL;
84
85 return DmtxPass;
86 }
87
88 /**
89 * \brief Set decoding behavior property
90 * \param dec
91 * \param prop
92 * \param value
93 * \return DmtxPass | DmtxFail
94 */
95 extern DmtxPassFail
dmtxDecodeSetProp(DmtxDecode * dec,int prop,int value)96 dmtxDecodeSetProp(DmtxDecode *dec, int prop, int value)
97 {
98 switch(prop) {
99 case DmtxPropEdgeMin:
100 dec->edgeMin = value;
101 break;
102 case DmtxPropEdgeMax:
103 dec->edgeMax = value;
104 break;
105 case DmtxPropScanGap:
106 dec->scanGap = value; /* XXX Should this be scaled? */
107 break;
108 case DmtxPropFnc1:
109 dec->fnc1 = value;
110 break;
111 case DmtxPropSquareDevn:
112 dec->squareDevn = cos(value * (M_PI/180.0));
113 break;
114 case DmtxPropSymbolSize:
115 dec->sizeIdxExpected = value;
116 break;
117 case DmtxPropEdgeThresh:
118 dec->edgeThresh = value;
119 break;
120 /* Min and Max values arrive unscaled */
121 case DmtxPropXmin:
122 dec->xMin = value / dec->scale;
123 break;
124 case DmtxPropXmax:
125 dec->xMax = value / dec->scale;
126 break;
127 case DmtxPropYmin:
128 dec->yMin = value / dec->scale;
129 break;
130 case DmtxPropYmax:
131 dec->yMax = value / dec->scale;
132 break;
133 default:
134 break;
135 }
136
137 if(dec->squareDevn <= 0.0 || dec->squareDevn >= 1.0)
138 return DmtxFail;
139
140 if(dec->scanGap < 1)
141 return DmtxFail;
142
143 if(dec->edgeThresh < 1 || dec->edgeThresh > 100)
144 return DmtxFail;
145
146 /* Reinitialize scangrid in case any inputs changed */
147 dec->grid = InitScanGrid(dec);
148
149 return DmtxPass;
150 }
151
152 /**
153 * \brief Get decoding behavior property
154 * \param dec
155 * \param prop
156 * \return value
157 */
158 extern int
dmtxDecodeGetProp(DmtxDecode * dec,int prop)159 dmtxDecodeGetProp(DmtxDecode *dec, int prop)
160 {
161 switch(prop) {
162 case DmtxPropEdgeMin:
163 return dec->edgeMin;
164 case DmtxPropEdgeMax:
165 return dec->edgeMax;
166 case DmtxPropScanGap:
167 return dec->scanGap;
168 case DmtxPropFnc1:
169 return dec->fnc1;
170 case DmtxPropSquareDevn:
171 return (int)(acos(dec->squareDevn) * 180.0/M_PI);
172 case DmtxPropSymbolSize:
173 return dec->sizeIdxExpected;
174 case DmtxPropEdgeThresh:
175 return dec->edgeThresh;
176 case DmtxPropXmin:
177 return dec->xMin;
178 case DmtxPropXmax:
179 return dec->xMax;
180 case DmtxPropYmin:
181 return dec->yMin;
182 case DmtxPropYmax:
183 return dec->yMax;
184 case DmtxPropScale:
185 return dec->scale;
186 case DmtxPropWidth:
187 return dmtxImageGetProp(dec->image, DmtxPropWidth) / dec->scale;
188 case DmtxPropHeight:
189 return dmtxImageGetProp(dec->image, DmtxPropHeight) / dec->scale;
190 default:
191 break;
192 }
193
194 return DmtxUndefined;
195 }
196
197 /**
198 * \brief Returns xxx
199 * \param img
200 * \param Scaled x coordinate
201 * \param Scaled y coordinate
202 * \return Scaled pixel offset
203 */
204 extern unsigned char *
dmtxDecodeGetCache(DmtxDecode * dec,int x,int y)205 dmtxDecodeGetCache(DmtxDecode *dec, int x, int y)
206 {
207 int width, height;
208
209 assert(dec != NULL);
210
211 /* if(dec.cacheComplete == DmtxFalse)
212 CacheImage(); */
213
214 width = dmtxDecodeGetProp(dec, DmtxPropWidth);
215 height = dmtxDecodeGetProp(dec, DmtxPropHeight);
216
217 if(x < 0 || x >= width || y < 0 || y >= height)
218 return NULL;
219
220 return &(dec->cache[y * width + x]);
221 }
222
223 /**
224 *
225 *
226 */
227 extern DmtxPassFail
dmtxDecodeGetPixelValue(DmtxDecode * dec,int x,int y,int channel,int * value)228 dmtxDecodeGetPixelValue(DmtxDecode *dec, int x, int y, int channel, int *value)
229 {
230 int xUnscaled, yUnscaled;
231 DmtxPassFail err;
232
233 xUnscaled = x * dec->scale;
234 yUnscaled = y * dec->scale;
235
236 /* Remove spherical lens distortion */
237 /* int width, height;
238 double radiusPow2, radiusPow4;
239 double factor;
240 DmtxVector2 pointShifted;
241 DmtxVector2 correctedPoint;
242
243 width = dmtxImageGetProp(img, DmtxPropWidth);
244 height = dmtxImageGetProp(img, DmtxPropHeight);
245
246 pointShifted.X = point.X - width/2.0;
247 pointShifted.Y = point.Y - height/2.0;
248
249 radiusPow2 = pointShifted.X * pointShifted.X + pointShifted.Y * pointShifted.Y;
250 radiusPow4 = radiusPow2 * radiusPow2;
251
252 factor = 1 + (k1 * radiusPow2) + (k2 * radiusPow4);
253
254 correctedPoint.X = pointShifted.X * factor + width/2.0;
255 correctedPoint.Y = pointShifted.Y * factor + height/2.0;
256
257 return correctedPoint; */
258
259 err = dmtxImageGetPixelValue(dec->image, xUnscaled, yUnscaled, channel, value);
260
261 return err;
262 }
263
264 /**
265 * \brief Fill the region covered by the quadrilateral given by (p0,p1,p2,p3) in the cache.
266 */
267 static void
CacheFillQuad(DmtxDecode * dec,DmtxPixelLoc p0,DmtxPixelLoc p1,DmtxPixelLoc p2,DmtxPixelLoc p3)268 CacheFillQuad(DmtxDecode *dec, DmtxPixelLoc p0, DmtxPixelLoc p1, DmtxPixelLoc p2, DmtxPixelLoc p3)
269 {
270 DmtxBresLine lines[4];
271 DmtxPixelLoc pEmpty = { 0, 0 };
272 unsigned char *cache;
273 int *scanlineMin, *scanlineMax;
274 int minY, maxY, sizeY, posY, posX;
275 int i, idx;
276
277 lines[0] = BresLineInit(p0, p1, pEmpty);
278 lines[1] = BresLineInit(p1, p2, pEmpty);
279 lines[2] = BresLineInit(p2, p3, pEmpty);
280 lines[3] = BresLineInit(p3, p0, pEmpty);
281
282 minY = dec->yMax;
283 maxY = 0;
284
285 minY = min(minY, p0.Y); maxY = max(maxY, p0.Y);
286 minY = min(minY, p1.Y); maxY = max(maxY, p1.Y);
287 minY = min(minY, p2.Y); maxY = max(maxY, p2.Y);
288 minY = min(minY, p3.Y); maxY = max(maxY, p3.Y);
289
290 sizeY = maxY - minY + 1;
291
292 scanlineMin = (int *)malloc(sizeY * sizeof(int));
293 scanlineMax = (int *)calloc(sizeY, sizeof(int));
294
295 assert(scanlineMin); /* XXX handle this better */
296 assert(scanlineMax); /* XXX handle this better */
297
298 for(i = 0; i < sizeY; i++)
299 scanlineMin[i] = dec->xMax;
300
301 for(i = 0; i < 4; i++) {
302 while(lines[i].loc.X != lines[i].loc1.X || lines[i].loc.Y != lines[i].loc1.Y) {
303 idx = lines[i].loc.Y - minY;
304 scanlineMin[idx] = min(scanlineMin[idx], lines[i].loc.X);
305 scanlineMax[idx] = max(scanlineMax[idx], lines[i].loc.X);
306 BresLineStep(lines + i, 1, 0);
307 }
308 }
309
310 for(posY = minY; posY < maxY && posY < dec->yMax; posY++) {
311 idx = posY - minY;
312 for(posX = scanlineMin[idx]; posX < scanlineMax[idx] && posX < dec->xMax; posX++) {
313 cache = dmtxDecodeGetCache(dec, posX, posY);
314 if(cache != NULL)
315 *cache |= 0x80;
316 }
317 }
318
319 free(scanlineMin);
320 free(scanlineMax);
321 }
322
323 /**
324 * \brief Convert fitted Data Matrix region into a decoded message
325 * \param dec
326 * \param reg
327 * \param fix
328 * \return Decoded message
329 */
330 extern DmtxMessage *
dmtxDecodeMatrixRegion(DmtxDecode * dec,DmtxRegion * reg,int fix)331 dmtxDecodeMatrixRegion(DmtxDecode *dec, DmtxRegion *reg, int fix)
332 {
333 //fprintf(stdout, "libdmtx::dmtxDecodeMatrixRegion()\n");
334 DmtxMessage *msg;
335 DmtxVector2 topLeft, topRight, bottomLeft, bottomRight;
336 DmtxPixelLoc pxTopLeft, pxTopRight, pxBottomLeft, pxBottomRight;
337
338 msg = dmtxMessageCreate(reg->sizeIdx, DmtxFormatMatrix);
339 if(msg == NULL)
340 return NULL;
341
342 if(PopulateArrayFromMatrix(dec, reg, msg) != DmtxPass) {
343 dmtxMessageDestroy(&msg);
344 return NULL;
345 }
346
347 msg->fnc1 = dec->fnc1;
348
349 topLeft.X = bottomLeft.X = topLeft.Y = topRight.Y = -0.1;
350 topRight.X = bottomRight.X = bottomLeft.Y = bottomRight.Y = 1.1;
351
352 dmtxMatrix3VMultiplyBy(&topLeft, reg->fit2raw);
353 dmtxMatrix3VMultiplyBy(&topRight, reg->fit2raw);
354 dmtxMatrix3VMultiplyBy(&bottomLeft, reg->fit2raw);
355 dmtxMatrix3VMultiplyBy(&bottomRight, reg->fit2raw);
356
357 pxTopLeft.X = (int)(0.5 + topLeft.X);
358 pxTopLeft.Y = (int)(0.5 + topLeft.Y);
359 pxBottomLeft.X = (int)(0.5 + bottomLeft.X);
360 pxBottomLeft.Y = (int)(0.5 + bottomLeft.Y);
361 pxTopRight.X = (int)(0.5 + topRight.X);
362 pxTopRight.Y = (int)(0.5 + topRight.Y);
363 pxBottomRight.X = (int)(0.5 + bottomRight.X);
364 pxBottomRight.Y = (int)(0.5 + bottomRight.Y);
365
366 CacheFillQuad(dec, pxTopLeft, pxTopRight, pxBottomRight, pxBottomLeft);
367
368 return dmtxDecodePopulatedArray(reg->sizeIdx, msg, fix);
369 }
370
371 /**
372 * \brief Ripped out a part of dmtxDecodeMatrixRegion function to this one to parse own array
373 * \param sizeIdx
374 * \param msg
375 * \param fix
376 * \return Decoded message (msg pointer) or NULL in case of failure.
377 * \note You should reaffect msg with the result of this call
378 * since a NULL result means msg gets freed and should not be used anymore.
379 * ex: msg = dmtxDecodePopulatedArray(sizeidx, msg, fix);
380 */
381 DmtxMessage *
dmtxDecodePopulatedArray(int sizeIdx,DmtxMessage * msg,int fix)382 dmtxDecodePopulatedArray(int sizeIdx, DmtxMessage *msg, int fix)
383 {
384 /*
385 * Example msg->array indices for a 12x12 datamatrix.
386 * also, the 'L' color (usually black) is defined as 'DmtxModuleOnRGB'
387 *
388 * XX XX XX XX XX XX
389 * XX 0 1 2 3 4 5 6 7 8 9 XX
390 * XX 10 11 12 13 14 15 16 17 18 19
391 * XX 20 21 22 23 24 25 26 27 28 29 XX
392 * XX 30 31 32 33 34 35 36 37 38 39
393 * XX 40 41 42 43 44 45 46 47 48 49 XX
394 * XX 50 51 52 53 54 55 56 57 58 59
395 * XX 60 61 62 63 64 65 66 67 68 69 XX
396 * XX 70 71 72 73 74 75 76 77 78 79
397 * XX 80 81 82 83 84 85 86 87 88 89 XX
398 * XX 90 91 92 93 94 95 96 97 98 99
399 * XX XX XX XX XX XX XX XX XX XX XX XX
400 *
401 */
402
403 ModulePlacementEcc200(msg->array, msg->code, sizeIdx, DmtxModuleOnRed | DmtxModuleOnGreen | DmtxModuleOnBlue);
404
405 if(RsDecode(msg->code, sizeIdx, fix) == DmtxFail){
406 dmtxMessageDestroy(&msg);
407 msg = NULL;
408 return NULL;
409 }
410
411 DecodeDataStream(msg, sizeIdx, NULL);
412
413 return msg;
414 }
415
416 /**
417 * \brief Convert fitted Data Mosaic region into a decoded message
418 * \param dec
419 * \param reg
420 * \param fix
421 * \return Decoded message
422 */
423 extern DmtxMessage *
dmtxDecodeMosaicRegion(DmtxDecode * dec,DmtxRegion * reg,int fix)424 dmtxDecodeMosaicRegion(DmtxDecode *dec, DmtxRegion *reg, int fix)
425 {
426 int offset;
427 int colorPlane;
428 DmtxMessage *oMsg, *rMsg, *gMsg, *bMsg;
429
430 colorPlane = reg->flowBegin.plane;
431
432 /**
433 * Consider performing a color cube fit here to identify exact RGB of
434 * all 6 "cube-like" corners based on pixels located within region. Then
435 * force each sample pixel to the "cube-like" corner based o which one
436 * is nearest "sqrt(dr^2+dg^2+db^2)" (except sqrt is unnecessary).
437 * colorPlane = reg->flowBegin.plane;
438 *
439 * To find RGB values of primary colors, perform something like a
440 * histogram except instead of going from black to color N, go from
441 * (127,127,127) to color. Use color bins along with distance to
442 * identify value. An additional method will be required to get actual
443 * RGB instead of just a plane in 3D. */
444
445 reg->flowBegin.plane = 0; /* kind of a hack */
446 rMsg = dmtxDecodeMatrixRegion(dec, reg, fix);
447
448 reg->flowBegin.plane = 1; /* kind of a hack */
449 gMsg = dmtxDecodeMatrixRegion(dec, reg, fix);
450
451 reg->flowBegin.plane = 2; /* kind of a hack */
452 bMsg = dmtxDecodeMatrixRegion(dec, reg, fix);
453
454 reg->flowBegin.plane = colorPlane;
455
456 oMsg = dmtxMessageCreate(reg->sizeIdx, DmtxFormatMosaic);
457
458 if(oMsg == NULL || rMsg == NULL || gMsg == NULL || bMsg == NULL) {
459 dmtxMessageDestroy(&oMsg);
460 dmtxMessageDestroy(&rMsg);
461 dmtxMessageDestroy(&gMsg);
462 dmtxMessageDestroy(&bMsg);
463 return NULL;
464 }
465
466 offset = 0;
467 memcpy(oMsg->output + offset, rMsg->output, rMsg->outputIdx);
468 offset += rMsg->outputIdx;
469 memcpy(oMsg->output + offset, gMsg->output, gMsg->outputIdx);
470 offset += gMsg->outputIdx;
471 memcpy(oMsg->output + offset, bMsg->output, bMsg->outputIdx);
472 offset += bMsg->outputIdx;
473
474 oMsg->outputIdx = offset;
475
476 dmtxMessageDestroy(&rMsg);
477 dmtxMessageDestroy(&gMsg);
478 dmtxMessageDestroy(&bMsg);
479
480 return oMsg;
481 }
482
483 /**
484 *
485 *
486 */
487 extern unsigned char *
dmtxDecodeCreateDiagnostic(DmtxDecode * dec,int * totalBytes,int * headerBytes,int style)488 dmtxDecodeCreateDiagnostic(DmtxDecode *dec, int *totalBytes, int *headerBytes, int style)
489 {
490 int i, row, col;
491 int width, height;
492 int widthDigits, heightDigits;
493 int count, channelCount;
494 int rgb[3];
495 double shade;
496 unsigned char *pnm, *output, *cache;
497
498 width = dmtxDecodeGetProp(dec, DmtxPropWidth);
499 height = dmtxDecodeGetProp(dec, DmtxPropHeight);
500 channelCount = dmtxImageGetProp(dec->image, DmtxPropChannelCount);
501
502 style = 1; /* this doesn't mean anything yet */
503
504 /* Count width digits */
505 for(widthDigits = 0, i = width; i > 0; i /= 10)
506 widthDigits++;
507
508 /* Count height digits */
509 for(heightDigits = 0, i = height; i > 0; i /= 10)
510 heightDigits++;
511
512 *headerBytes = widthDigits + heightDigits + 9;
513 *totalBytes = *headerBytes + width * height * 3;
514
515 pnm = (unsigned char *)malloc(*totalBytes);
516 if(pnm == NULL)
517 return NULL;
518
519 #ifdef _VISUALC_
520 count = sprintf_s((char *)pnm, *headerBytes + 1, "P6\n%d %d\n255\n", width, height);
521 #else
522 count = snprintf((char *)pnm, *headerBytes + 1, "P6\n%d %d\n255\n", width, height);
523 #endif
524
525 if(count != *headerBytes) {
526 free(pnm);
527 return NULL;
528 }
529
530 output = pnm + (*headerBytes);
531 for(row = height - 1; row >= 0; row--) {
532 for(col = 0; col < width; col++) {
533 cache = dmtxDecodeGetCache(dec, col, row);
534 if(cache == NULL) {
535 rgb[0] = 0;
536 rgb[1] = 0;
537 rgb[2] = 128;
538 }
539 else if(*cache & 0x40) {
540 rgb[0] = 255;
541 rgb[1] = 0;
542 rgb[2] = 0;
543 }
544 else {
545 shade = (*cache & 0x80) ? 0.0 : 0.7;
546 for(i = 0; i < 3; i++) {
547 if(i < channelCount)
548 dmtxDecodeGetPixelValue(dec, col, row, i, &rgb[i]);
549 else
550 dmtxDecodeGetPixelValue(dec, col, row, 0, &rgb[i]);
551
552 rgb[i] += (int)(shade * (double)(255 - rgb[i]) + 0.5);
553 if(rgb[i] > 255)
554 rgb[i] = 255;
555 }
556 }
557 *(output++) = (unsigned char)rgb[0];
558 *(output++) = (unsigned char)rgb[1];
559 *(output++) = (unsigned char)rgb[2];
560 }
561 }
562 assert(output == pnm + *totalBytes);
563
564 return pnm;
565 }
566
567 /**
568 * \brief Increment counters used to determine module values
569 * \param img
570 * \param reg
571 * \param tally
572 * \param xOrigin
573 * \param yOrigin
574 * \param mapWidth
575 * \param mapHeight
576 * \param dir
577 * \return void
578 */
579 static void
TallyModuleJumps(DmtxDecode * dec,DmtxRegion * reg,int tally[][24],int xOrigin,int yOrigin,int mapWidth,int mapHeight,DmtxDirection dir)580 TallyModuleJumps(DmtxDecode *dec, DmtxRegion *reg, int tally[][24], int xOrigin, int yOrigin, int mapWidth, int mapHeight, DmtxDirection dir)
581 {
582 int extent, weight;
583 int travelStep;
584 int symbolRow, symbolCol;
585 int mapRow, mapCol;
586 int lineStart, lineStop;
587 int travelStart, travelStop;
588 int *line, *travel;
589 int jumpThreshold;
590 int darkOnLight;
591 int color;
592 int statusPrev, statusModule;
593 int tPrev, tModule;
594
595 assert(dir == DmtxDirUp || dir == DmtxDirLeft || dir == DmtxDirDown || dir == DmtxDirRight);
596
597 travelStep = (dir == DmtxDirUp || dir == DmtxDirRight) ? 1 : -1;
598
599 /* Abstract row and column progress using pointers to allow grid
600 traversal in all 4 directions using same logic */
601
602 if((dir & DmtxDirHorizontal) != 0x00) {
603 line = &symbolRow;
604 travel = &symbolCol;
605 extent = mapWidth;
606 lineStart = yOrigin;
607 lineStop = yOrigin + mapHeight;
608 travelStart = (travelStep == 1) ? xOrigin - 1 : xOrigin + mapWidth;
609 travelStop = (travelStep == 1) ? xOrigin + mapWidth : xOrigin - 1;
610 }
611 else {
612 assert(dir & DmtxDirVertical);
613 line = &symbolCol;
614 travel = &symbolRow;
615 extent = mapHeight;
616 lineStart = xOrigin;
617 lineStop = xOrigin + mapWidth;
618 travelStart = (travelStep == 1) ? yOrigin - 1: yOrigin + mapHeight;
619 travelStop = (travelStep == 1) ? yOrigin + mapHeight : yOrigin - 1;
620 }
621
622
623 darkOnLight = (int)(reg->offColor > reg->onColor);
624 jumpThreshold = abs((int)(0.4 * (reg->offColor - reg->onColor) + 0.5));
625
626 assert(jumpThreshold >= 0);
627
628 for(*line = lineStart; *line < lineStop; (*line)++) {
629
630 /* Capture tModule for each leading border module as normal but
631 decide status based on predictable barcode border pattern */
632
633
634
635 *travel = travelStart;
636 color = ReadModuleColor(dec, reg, symbolRow, symbolCol, reg->sizeIdx, reg->flowBegin.plane);
637 tModule = (darkOnLight) ? reg->offColor - color : color - reg->offColor;
638
639 statusModule = (travelStep == 1 || (*line & 0x01) == 0) ? DmtxModuleOnRGB : DmtxModuleOff;
640
641 weight = extent;
642
643 while((*travel += travelStep) != travelStop) {
644
645 tPrev = tModule;
646 statusPrev = statusModule;
647
648 /* For normal data-bearing modules capture color and decide
649 module status based on comparison to previous "known" module */
650
651 color = ReadModuleColor(dec, reg, symbolRow, symbolCol, reg->sizeIdx, reg->flowBegin.plane);
652 tModule = (darkOnLight) ? reg->offColor - color : color - reg->offColor;
653
654 if(statusPrev == DmtxModuleOnRGB) {
655 if(tModule < tPrev - jumpThreshold){
656 statusModule = DmtxModuleOff;
657 } else {
658 statusModule = DmtxModuleOnRGB;
659 }
660 }
661 else if(statusPrev == DmtxModuleOff) {
662 if(tModule > tPrev + jumpThreshold) {
663 statusModule = DmtxModuleOnRGB;
664 } else {
665 statusModule = DmtxModuleOff;
666 }
667 }
668
669 mapRow = symbolRow - yOrigin;
670 mapCol = symbolCol - xOrigin;
671 assert(mapRow < 24 && mapCol < 24);
672
673 if(statusModule == DmtxModuleOnRGB){
674 tally[mapRow][mapCol] += (2 * weight);
675 }
676
677 weight--;
678 }
679
680 assert(weight == 0);
681 }
682 }
683
684 /**
685 * \brief Populate array with codeword values based on module colors
686 * \param msg
687 * \param img
688 * \param reg
689 * \return DmtxPass | DmtxFail
690 */
691 static DmtxPassFail
PopulateArrayFromMatrix(DmtxDecode * dec,DmtxRegion * reg,DmtxMessage * msg)692 PopulateArrayFromMatrix(DmtxDecode *dec, DmtxRegion *reg, DmtxMessage *msg)
693 {
694 //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix()\n");
695 int weightFactor;
696 int mapWidth, mapHeight;
697 int xRegionTotal, yRegionTotal;
698 int xRegionCount, yRegionCount;
699 int xOrigin, yOrigin;
700 int mapCol, mapRow;
701 int colTmp, rowTmp, idx;
702 int tally[24][24]; /* Large enough to map largest single region */
703
704 /* memset(msg->array, 0x00, msg->arraySize); */
705
706 /* Capture number of regions present in barcode */
707 xRegionTotal = dmtxGetSymbolAttribute(DmtxSymAttribHorizDataRegions, reg->sizeIdx);
708 yRegionTotal = dmtxGetSymbolAttribute(DmtxSymAttribVertDataRegions, reg->sizeIdx);
709
710 /* Capture region dimensions (not including border modules) */
711 mapWidth = dmtxGetSymbolAttribute(DmtxSymAttribDataRegionCols, reg->sizeIdx);
712 mapHeight = dmtxGetSymbolAttribute(DmtxSymAttribDataRegionRows, reg->sizeIdx);
713
714 weightFactor = 2 * (mapHeight + mapWidth + 2);
715 assert(weightFactor > 0);
716
717 //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::reg->sizeIdx: %d\n", reg->sizeIdx);
718 //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::reg->flowBegin.plane: %d\n", reg->flowBegin.plane);
719 //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::reg->onColor: %d\n", reg->onColor);
720 //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::reg->offColor: %d\n", reg->offColor);
721 //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::xRegionTotal: %d\n", xRegionTotal);
722 //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::yRegionTotal: %d\n", yRegionTotal);
723 //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::mapWidth: %d\n", mapWidth);
724 //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::mapHeight: %d\n", mapHeight);
725 //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::weightFactor: %d\n", weightFactor);
726 //reg->fit2raw[1][0]=0;
727 //reg->fit2raw[0][1]=0;
728 //reg->fit2raw[0][2]=0;
729 //reg->fit2raw[2][2]=1;
730 //reg->fit2raw[1][2]=0;
731 //reg->fit2raw[2][0]=10; //translation
732 //reg->fit2raw[2][1]=10; //translation
733 //reg->fit2raw[0][0]=60; //scale
734 //reg->fit2raw[1][1]=60; //scale
735 //dmtxMatrix3Print(reg->fit2raw);
736
737
738 /* Tally module changes for each region in each direction */
739 for(yRegionCount = 0; yRegionCount < yRegionTotal; yRegionCount++) {
740
741 /* Y location of mapping region origin in symbol coordinates */
742 yOrigin = yRegionCount * (mapHeight + 2) + 1;
743
744 for(xRegionCount = 0; xRegionCount < xRegionTotal; xRegionCount++) {
745
746 /* X location of mapping region origin in symbol coordinates */
747 xOrigin = xRegionCount * (mapWidth + 2) + 1;
748 //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::xOrigin: %d\n", xOrigin);
749
750 memset(tally, 0x00, 24 * 24 * sizeof(int));
751 TallyModuleJumps(dec, reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirUp);
752 TallyModuleJumps(dec, reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirLeft);
753 TallyModuleJumps(dec, reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirDown);
754 TallyModuleJumps(dec, reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirRight);
755
756 /* Decide module status based on final tallies */
757 for(mapRow = 0; mapRow < mapHeight; mapRow++) {
758 //for(mapRow = mapHeight-1; mapRow >= 0; mapRow--) {
759 for(mapCol = 0; mapCol < mapWidth; mapCol++) {
760
761 rowTmp = (yRegionCount * mapHeight) + mapRow;
762 rowTmp = yRegionTotal * mapHeight - rowTmp - 1;
763 colTmp = (xRegionCount * mapWidth) + mapCol;
764 idx = (rowTmp * xRegionTotal * mapWidth) + colTmp;
765 //fprintf(stdout, "libdmtx::PopulateArrayFromMatrix::idx: %d @ %d,%d\n", idx, mapCol, mapRow);
766 //fprintf(stdout, "%c ",tally[mapRow][mapCol]==DmtxModuleOff ? 'X' : ' ');
767 if(tally[mapRow][mapCol]/(double)weightFactor >= 0.5){
768 msg->array[idx] = DmtxModuleOnRGB;
769 //fprintf(stdout, "X ");
770 } else {
771 msg->array[idx] = DmtxModuleOff;
772 //fprintf(stdout, " ");
773 }
774
775 msg->array[idx] |= DmtxModuleAssigned;
776 }
777 //fprintf(stdout, "\n");
778 }
779 }
780 }
781
782 return DmtxPass;
783 }
784