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