1 /**
2  * libdmtx - Data Matrix Encoding/Decoding Library
3  * Copyright 2008, 2009 Mike Laughton. All rights reserved.
4  * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved.
5  *
6  * See LICENSE file in the main project directory for full
7  * terms of use and distribution.
8  *
9  * Contact:
10  * Vadim A. Misbakh-Soloviov <dmtx@mva.name>
11  * Mike Laughton <mike@dragonflylogic.com>
12  *
13  * \file dmtxencode.c
14  * \brief Base encoding logic
15  */
16 
17 #undef ISDIGIT
18 #define ISDIGIT(n) (n > 47 && n < 58)
19 
20 /**
21  * \brief  Initialize encode struct with default values
22  * \return Initialized DmtxEncode struct
23  */
24 extern DmtxEncode *
dmtxEncodeCreate(void)25 dmtxEncodeCreate(void)
26 {
27    DmtxEncode *enc;
28 
29    enc = (DmtxEncode *)calloc(1, sizeof(DmtxEncode));
30    if(enc == NULL)
31       return NULL;
32 
33    enc->scheme = DmtxSchemeAscii;
34    enc->sizeIdxRequest = DmtxSymbolSquareAuto;
35    enc->marginSize = 10;
36    enc->moduleSize = 5;
37    enc->pixelPacking = DmtxPack24bppRGB;
38    enc->imageFlip = DmtxFlipNone;
39    enc->rowPadBytes = 0;
40 
41    enc->fnc1 = DmtxUndefined;
42 
43    /* Initialize background color to white */
44 /* enc.region.gradient.ray.p.R = 255.0;
45    enc.region.gradient.ray.p.G = 255.0;
46    enc.region.gradient.ray.p.B = 255.0; */
47 
48    /* Initialize foreground color to black */
49 /* enc.region.gradient.tMin = 0.0;
50    enc.region.gradient.tMax = xyz; */
51 
52    dmtxMatrix3Identity(enc->xfrm);
53 
54    return enc;
55 }
56 
57 /**
58  * \brief  Deinitialize encode struct
59  * \param  enc
60  * \return void
61  */
62 extern DmtxPassFail
dmtxEncodeDestroy(DmtxEncode ** enc)63 dmtxEncodeDestroy(DmtxEncode **enc)
64 {
65    if(enc == NULL || *enc == NULL)
66       return DmtxFail;
67 
68    /* Free pixel array allocated in dmtxEncodeDataMatrix() */
69    if((*enc)->image != NULL && (*enc)->image->pxl != NULL) {
70       free((*enc)->image->pxl);
71       (*enc)->image->pxl = NULL;
72    }
73 
74    dmtxImageDestroy(&((*enc)->image));
75    dmtxMessageDestroy(&((*enc)->message));
76 
77    free(*enc);
78 
79    *enc = NULL;
80 
81    return DmtxPass;
82 }
83 
84 /**
85  * \brief  Set encoding behavior property
86  * \param  enc
87  * \param  prop
88  * \param  value
89  * \return DmtxPass | DmtxFail
90  */
91 extern DmtxPassFail
dmtxEncodeSetProp(DmtxEncode * enc,int prop,int value)92 dmtxEncodeSetProp(DmtxEncode *enc, int prop, int value)
93 {
94    switch(prop) {
95 
96       /* Encoding details */
97       case DmtxPropScheme:
98          enc->scheme = value;
99          break;
100       case DmtxPropSizeRequest:
101          if(value == DmtxSymbolShapeAuto)
102             return DmtxFail;
103          enc->sizeIdxRequest = value;
104          break;
105       case DmtxPropFnc1:
106          enc->fnc1 = value;
107          break;
108 
109       /* Presentation details */
110       case DmtxPropMarginSize:
111          enc->marginSize = value;
112          break;
113       case DmtxPropModuleSize:
114          enc->moduleSize = value;
115          break;
116 
117       /* Image properties */
118       case DmtxPropPixelPacking:
119          enc->pixelPacking = value;
120          break;
121       case DmtxPropImageFlip:
122          enc->imageFlip = value;
123          break;
124       case DmtxPropRowPadBytes:
125          enc->rowPadBytes = value;
126       default:
127          break;
128    }
129 
130    return DmtxPass;
131 }
132 
133 /**
134  * \brief  Get encoding behavior property
135  * \param  enc
136  * \param  prop
137  * \return value
138  */
139 extern int
dmtxEncodeGetProp(DmtxEncode * enc,int prop)140 dmtxEncodeGetProp(DmtxEncode *enc, int prop)
141 {
142    switch(prop) {
143       case DmtxPropMarginSize:
144          return enc->marginSize;
145       case DmtxPropModuleSize:
146          return enc->moduleSize;
147       case DmtxPropScheme:
148          return enc->scheme;
149       case DmtxPropFnc1:
150          return enc->fnc1;
151       default:
152          break;
153    }
154 
155    return DmtxUndefined;
156 }
157 
158 /**
159  * \brief  Convert message into Data Matrix image
160  * \param  enc
161  * \param  inputSize
162  * \param  inputString
163  * \param  sizeIdxRequest
164  * \return DmtxPass | DmtxFail
165  */
166 extern DmtxPassFail
dmtxEncodeDataMatrix(DmtxEncode * enc,int inputSize,unsigned char * inputString)167 dmtxEncodeDataMatrix(DmtxEncode *enc, int inputSize, unsigned char *inputString)
168 {
169    int sizeIdx;
170    int width, height, bitsPerPixel;
171    unsigned char *pxl;
172    DmtxByte outputStorage[4096];
173    DmtxByteList output = dmtxByteListBuild(outputStorage, sizeof(outputStorage));
174    DmtxByteList input = dmtxByteListBuild(inputString, inputSize);
175 
176    input.length = inputSize;
177 
178    /* Future: stream = StreamInit() ... */
179    /* Future: EncodeDataCodewords(&stream) ... */
180 
181    /* Encode input string into data codewords */
182    sizeIdx = EncodeDataCodewords(&input, &output, enc->sizeIdxRequest, enc->scheme, enc->fnc1);
183    if(sizeIdx == DmtxUndefined || output.length <= 0)
184       return DmtxFail;
185 
186    /* EncodeDataCodewords() should have updated any auto sizeIdx to a real one */
187    assert(sizeIdx != DmtxSymbolSquareAuto && sizeIdx != DmtxSymbolRectAuto);
188 
189    /* XXX we can remove a lot of this redundant data */
190    enc->region.sizeIdx = sizeIdx;
191    enc->region.symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, sizeIdx);
192    enc->region.symbolCols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, sizeIdx);
193    enc->region.mappingRows = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixRows, sizeIdx);
194    enc->region.mappingCols = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixCols, sizeIdx);
195 
196    /* Allocate memory for message and array */
197    enc->message = dmtxMessageCreate(sizeIdx, DmtxFormatMatrix);
198    enc->message->padCount = 0; /* XXX this needs to be added back */
199    memcpy(enc->message->code, output.b, output.length);
200 
201    /* Generate error correction codewords */
202    RsEncode(enc->message, enc->region.sizeIdx);
203 
204    /* Module placement in region */
205    ModulePlacementEcc200(enc->message->array, enc->message->code,
206          enc->region.sizeIdx, DmtxModuleOnRGB);
207 
208    width = 2 * enc->marginSize + (enc->region.symbolCols * enc->moduleSize);
209    height = 2 * enc->marginSize + (enc->region.symbolRows * enc->moduleSize);
210    bitsPerPixel = GetBitsPerPixel(enc->pixelPacking);
211    if(bitsPerPixel == DmtxUndefined)
212       return DmtxFail;
213    assert(bitsPerPixel % 8 == 0);
214 
215    /* Allocate memory for the image to be generated */
216    pxl = (unsigned char *)malloc(width * height * (bitsPerPixel/8) + enc->rowPadBytes);
217    if(pxl == NULL) {
218       perror("pixel malloc error");
219       return DmtxFail;
220    }
221 
222    enc->image = dmtxImageCreate(pxl, width, height, enc->pixelPacking);
223    if(enc->image == NULL) {
224       perror("image malloc error");
225       return DmtxFail;
226    }
227 
228    dmtxImageSetProp(enc->image, DmtxPropImageFlip, enc->imageFlip);
229    dmtxImageSetProp(enc->image, DmtxPropRowPadBytes, enc->rowPadBytes);
230 
231    /* Insert finder and aligment pattern modules */
232    PrintPattern(enc);
233 
234    return DmtxPass;
235 }
236 
237 /**
238  * \brief  Convert message into Data Mosaic image
239  *
240  *  1) count how many codewords it would take to encode the whole thing
241  *  2) take ceiling N of codeword count divided by 3
242  *  3) using minimum symbol size that can accomodate N codewords:
243  *  4) create several barcodes over iterations of increasing numbers of
244  *     input codewords until you go one too far
245  *  5) if codewords remain after filling R, G, and B barcodes then go back
246  *     to 3 and try with next larger size
247  *  6) take the 3 different images you created and write out a new barcode
248  *
249  * \param  enc
250  * \param  inputSize
251  * \param  inputString
252  * \param  sizeIdxRequest
253  * \return DmtxPass | DmtxFail
254  */
255 extern DmtxPassFail
dmtxEncodeDataMosaic(DmtxEncode * enc,int inputSize,unsigned char * inputString)256 dmtxEncodeDataMosaic(DmtxEncode *enc, int inputSize, unsigned char *inputString)
257 {
258    unsigned char *inputStringR, *inputStringG, *inputStringB;
259    int tmpInputSize;
260    int inputSizeR, inputSizeG, inputSizeB;
261    int sizeIdxAttempt, sizeIdxFirst, sizeIdxLast;
262    int row, col, mappingRows, mappingCols;
263    DmtxEncode *encR, *encG, *encB;
264 
265    /* Use 1/3 (ceiling) of inputSize establish input size target */
266    tmpInputSize = (inputSize + 2) / 3;
267    inputSizeR = tmpInputSize;
268    inputSizeG = tmpInputSize;
269    inputSizeB = inputSize - (inputSizeR + inputSizeG);
270 
271    inputStringR = inputString;
272    inputStringG = inputStringR + inputSizeR;
273    inputStringB = inputStringG + inputSizeG;
274 
275    /* Use 1/3 (floor) of dataWordCount establish first symbol size attempt */
276    sizeIdxFirst = FindSymbolSize(tmpInputSize, enc->sizeIdxRequest);
277    if(sizeIdxFirst == DmtxUndefined)
278       return DmtxFail;
279 
280    /* Set the last possible symbol size for this symbol shape or specific size request */
281    if(enc->sizeIdxRequest == DmtxSymbolSquareAuto)
282       sizeIdxLast = DmtxSymbolSquareCount - 1;
283    else if(enc->sizeIdxRequest == DmtxSymbolRectAuto)
284       sizeIdxLast = DmtxSymbolSquareCount + DmtxSymbolRectCount - 1;
285    else
286       sizeIdxLast = sizeIdxFirst;
287 
288    encR = encG = encB = NULL;
289 
290    /* Try increasing symbol sizes until 3 of them can hold all input values */
291    for(sizeIdxAttempt = sizeIdxFirst; sizeIdxAttempt <= sizeIdxLast; sizeIdxAttempt++)
292    {
293       dmtxEncodeDestroy(&encR);
294       dmtxEncodeDestroy(&encG);
295       dmtxEncodeDestroy(&encB);
296 
297       encR = dmtxEncodeCreate();
298       encG = dmtxEncodeCreate();
299       encB = dmtxEncodeCreate();
300 
301       /* Copy all settings from master DmtxEncode, including pointer to image
302          and message, which is initially null */
303       *encR = *encG = *encB = *enc;
304 
305       dmtxEncodeSetProp(encR, DmtxPropSizeRequest, sizeIdxAttempt);
306       dmtxEncodeSetProp(encG, DmtxPropSizeRequest, sizeIdxAttempt);
307       dmtxEncodeSetProp(encB, DmtxPropSizeRequest, sizeIdxAttempt);
308 
309       /* RED LAYER - Holds temporary copy */
310       dmtxEncodeDataMatrix(encR, inputSizeR, inputStringR);
311       if(encR->region.sizeIdx != sizeIdxAttempt)
312          continue;
313 
314       /* GREEN LAYER - Holds temporary copy */
315       dmtxEncodeDataMatrix(encG, inputSizeG, inputStringG);
316       if(encG->region.sizeIdx != sizeIdxAttempt)
317          continue;
318 
319       /* BLUE LAYER - Holds temporary copy */
320       dmtxEncodeDataMatrix(encB, inputSizeB, inputStringB);
321       if(encB->region.sizeIdx != sizeIdxAttempt)
322          continue;
323 
324       /* If we get this far we found a fit */
325       break;
326    }
327 
328    if(encR == NULL || encG == NULL || encB == NULL)
329    {
330       dmtxEncodeDestroy(&encR);
331       dmtxEncodeDestroy(&encG);
332       dmtxEncodeDestroy(&encB);
333       return DmtxFail;
334    }
335 
336    /* Now we have the correct sizeIdxAttempt, and they all fit into the desired size */
337 
338    /* Perform the red portion of the final encode to set internals correctly */
339    dmtxEncodeSetProp(enc, DmtxPropSizeRequest, sizeIdxAttempt);
340    dmtxEncodeDataMatrix(enc, inputSizeR, inputStringR);
341 
342    /* Zero out the array and overwrite the bits in 3 passes */
343    mappingRows = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixRows, sizeIdxAttempt);
344    mappingCols = dmtxGetSymbolAttribute(DmtxSymAttribMappingMatrixCols, sizeIdxAttempt);
345    memset(enc->message->array, 0x00, sizeof(unsigned char) *
346          enc->region.mappingRows * enc->region.mappingCols);
347 
348    ModulePlacementEcc200(enc->message->array, encR->message->code, sizeIdxAttempt, DmtxModuleOnRed);
349 
350    /* Reset DmtxModuleAssigned and DMX_MODULE_VISITED bits */
351    for(row = 0; row < mappingRows; row++) {
352       for(col = 0; col < mappingCols; col++) {
353          enc->message->array[row*mappingCols+col] &= (0xff ^ (DmtxModuleAssigned | DmtxModuleVisited));
354       }
355    }
356 
357    ModulePlacementEcc200(enc->message->array, encG->message->code, sizeIdxAttempt, DmtxModuleOnGreen);
358 
359    /* Reset DmtxModuleAssigned and DMX_MODULE_VISITED bits */
360    for(row = 0; row < mappingRows; row++) {
361       for(col = 0; col < mappingCols; col++) {
362          enc->message->array[row*mappingCols+col] &= (0xff ^ (DmtxModuleAssigned | DmtxModuleVisited));
363       }
364    }
365 
366    ModulePlacementEcc200(enc->message->array, encB->message->code, sizeIdxAttempt, DmtxModuleOnBlue);
367 
368    /* Destroy encR, encG, and encB */
369    dmtxEncodeDestroy(&encR);
370    dmtxEncodeDestroy(&encG);
371    dmtxEncodeDestroy(&encB);
372 
373    PrintPattern(enc);
374 
375    return DmtxPass;
376 }
377 
378 /**
379  * \brief  Convert input into message using specific encodation scheme
380  * \param  buf
381  * \param  inputString
382  * \param  inputSize
383  * \param  scheme
384  * \param  sizeIdx
385  * \return Count of encoded data words
386  *
387  * Future: pass DmtxEncode to this function with an error reason field, which
388  *         goes to EncodeSingle... too
389  */
390 static int
EncodeDataCodewords(DmtxByteList * input,DmtxByteList * output,int sizeIdxRequest,DmtxScheme scheme,int fnc1)391 EncodeDataCodewords(DmtxByteList *input, DmtxByteList *output, int sizeIdxRequest, DmtxScheme scheme, int fnc1)
392 {
393    int sizeIdx;
394 
395    /* Encode input string into data codewords */
396    switch(scheme)
397    {
398       case DmtxSchemeAutoBest:
399          sizeIdx = EncodeOptimizeBest(input, output, sizeIdxRequest, fnc1);
400          break;
401       case DmtxSchemeAutoFast:
402          sizeIdx = DmtxUndefined; /* EncodeAutoFast(input, output, sizeIdxRequest, passFail); */
403          break;
404       default:
405          sizeIdx = EncodeSingleScheme(input, output, sizeIdxRequest, scheme, fnc1);
406          break;
407    }
408 
409    return sizeIdx;
410 }
411 
412 /**
413  * \brief  Write encoded message to image
414  * \param  enc
415  * \return void
416  */
417 static void
PrintPattern(DmtxEncode * enc)418 PrintPattern(DmtxEncode *enc)
419 {
420    int i, j;
421    int symbolRow, symbolCol;
422    int pixelRow, pixelCol;
423    int moduleStatus;
424    size_t rowSize, height;
425    int rgb[3];
426    double sxy, txy;
427    DmtxMatrix3 m1, m2;
428    DmtxVector2 vIn, vOut;
429 
430    txy = enc->marginSize;
431    sxy = 1.0/enc->moduleSize;
432 
433    dmtxMatrix3Translate(m1, -txy, -txy);
434    dmtxMatrix3Scale(m2, sxy, -sxy);
435    dmtxMatrix3Multiply(enc->xfrm, m1, m2);
436 
437    dmtxMatrix3Translate(m1, txy, txy);
438    dmtxMatrix3Scale(m2, enc->moduleSize, enc->moduleSize);
439    dmtxMatrix3Multiply(enc->rxfrm, m2, m1);
440 
441    rowSize = dmtxImageGetProp(enc->image, DmtxPropRowSizeBytes);
442    height = dmtxImageGetProp(enc->image, DmtxPropHeight);
443 
444    memset(enc->image->pxl, 0xff, rowSize * height);
445 
446    for(symbolRow = 0; symbolRow < enc->region.symbolRows; symbolRow++) {
447       for(symbolCol = 0; symbolCol < enc->region.symbolCols; symbolCol++) {
448 
449          vIn.X = symbolCol;
450          vIn.Y = symbolRow;
451 
452          dmtxMatrix3VMultiply(&vOut, &vIn, enc->rxfrm);
453 
454          pixelCol = (int)(vOut.X);
455          pixelRow = (int)(vOut.Y);
456 
457          moduleStatus = dmtxSymbolModuleStatus(enc->message,
458                enc->region.sizeIdx, symbolRow, symbolCol);
459 
460 		 if (enc->image->bytesPerPixel == 1)
461 		 {
462 			 for(i = pixelRow; i < pixelRow + enc->moduleSize; i++) {
463 				for(j = pixelCol; j < pixelCol + enc->moduleSize; j++) {
464 				   rgb[0] = ((moduleStatus & DmtxModuleOnRed) != 0x00) ? 0 : 255;
465 				   dmtxImageSetPixelValue(enc->image, j, i, 0, rgb[0]);
466 				}
467 			 }
468 		 }
469 		 else
470 		 {
471 			 for(i = pixelRow; i < pixelRow + enc->moduleSize; i++) {
472 				 for(j = pixelCol; j < pixelCol + enc->moduleSize; j++) {
473 					 rgb[0] = ((moduleStatus & DmtxModuleOnRed) != 0x00) ? 0 : 255;
474 					 rgb[1] = ((moduleStatus & DmtxModuleOnGreen) != 0x00) ? 0 : 255;
475 					 rgb[2] = ((moduleStatus & DmtxModuleOnBlue) != 0x00) ? 0 : 255;
476 					 /*             dmtxImageSetRgb(enc->image, j, i, rgb); */
477 					 dmtxImageSetPixelValue(enc->image, j, i, 0, rgb[0]);
478 					 dmtxImageSetPixelValue(enc->image, j, i, 1, rgb[1]);
479 					 dmtxImageSetPixelValue(enc->image, j, i, 2, rgb[2]);
480 				 }
481 			 }
482 		 }
483       }
484    }
485 }
486