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