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 dmtximage.c
14  * \brief Image handling
15  */
16 
17 /**
18  * libdmtx stores image data as a large one-dimensional array of packed pixels,
19  * reading from the array when scanning barcodes and writing to it when creating
20  * a barcode. Beyond this interaction the calling program is responsible for
21  * populating and dispatching pixels between the image array and the outside
22  * world, whether that means loading an image from a file, acquiring camera
23  * input, displaying output to a screen, saving to disk, etc...
24  *
25  * By default, libdmtx treats the first pixel of an image array as the top-left
26  * corner of the physical image, with the final pixel landing at the bottom-
27  * right. However, if mapping a pixel buffer this way produces an inverted
28  * image the calling program can specify DmtxFlipY at image creation time to
29  * remove the inversion. This has a negligible effect on performance since it
30  * only modifies the pixel mapping math, and does not alter any pixel data.
31  *
32  * Regardless of how an image is stored internally, all libdmtx functions
33  * consider coordinate (0,0) to mathematically represent the bottom-left pixel
34  * location of an image using a right-handed coordinate system.
35  *
36  *                (0,HEIGHT-1)        (WIDTH-1,HEIGHT-1)
37  *
38  *          array pos = 0,1,2,3,...-----------+
39  *                      |                     |
40  *                      |                     |
41  *                      |       libdmtx       |
42  *                      |        image        |
43  *                      |     coordinates     |
44  *                      |                     |
45  *                      |                     |
46  *                      +---------...,N-2,N-1,N = array pos
47  *
48  *                    (0,0)              (WIDTH-1,0)
49  *
50  * Notes:
51  *   - OpenGL pixel arrays obtained with glReadPixels() are stored
52  *     bottom-to-top; use DmtxFlipY
53  *   - Many popular image formats (e.g., PNG, GIF) store rows
54  *     top-to-bottom; use DmtxFlipNone
55  */
56 
57 /**
58  * \brief  XXX
59  * \param  XXX
60  * \return XXX
61  */
62 extern DmtxImage *
dmtxImageCreate(unsigned char * pxl,int width,int height,int pack)63 dmtxImageCreate(unsigned char *pxl, int width, int height, int pack)
64 {
65 //   DmtxPassFail err;
66    DmtxImage *img;
67 
68    if(pxl == NULL || width < 1 || height < 1)
69       return NULL;
70 
71    img = (DmtxImage *)calloc(1, sizeof(DmtxImage));
72    if(img == NULL)
73       return NULL;
74 
75    img->pxl = pxl;
76    img->width = width;
77    img->height = height;
78    img->pixelPacking = pack;
79    img->bitsPerPixel = GetBitsPerPixel(pack);
80    img->bytesPerPixel = img->bitsPerPixel/8;
81    img->rowPadBytes = 0;
82    img->rowSizeBytes = img->width * img->bytesPerPixel + img->rowPadBytes;
83    img->imageFlip = DmtxFlipNone;
84 
85    /* Leave channelStart[] and bitsPerChannel[] with zeros from calloc */
86    img->channelCount = 0;
87 
88    switch(pack) {
89       case DmtxPackCustom:
90          break;
91       case DmtxPack1bppK:
92          dmtxImageSetChannel(img, 0, 1);
93          return NULL; /* unsupported packing order */
94 /*       break; */
95       case DmtxPack8bppK:
96          dmtxImageSetChannel(img, 0, 8);
97          break;
98       case DmtxPack16bppRGB:
99       case DmtxPack16bppBGR:
100       case DmtxPack16bppYCbCr:
101          dmtxImageSetChannel(img,  0, 5);
102          dmtxImageSetChannel(img,  5, 5);
103          dmtxImageSetChannel(img, 10, 5);
104          break;
105       case DmtxPack24bppRGB:
106       case DmtxPack24bppBGR:
107       case DmtxPack24bppYCbCr:
108       case DmtxPack32bppRGBX:
109       case DmtxPack32bppBGRX:
110          dmtxImageSetChannel(img,  0, 8);
111          dmtxImageSetChannel(img,  8, 8);
112          dmtxImageSetChannel(img, 16, 8);
113          break;
114       case DmtxPack16bppRGBX:
115       case DmtxPack16bppBGRX:
116          dmtxImageSetChannel(img,  0, 5);
117          dmtxImageSetChannel(img,  5, 5);
118          dmtxImageSetChannel(img, 10, 5);
119          break;
120       case DmtxPack16bppXRGB:
121       case DmtxPack16bppXBGR:
122          dmtxImageSetChannel(img,  1, 5);
123          dmtxImageSetChannel(img,  6, 5);
124          dmtxImageSetChannel(img, 11, 5);
125          break;
126       case DmtxPack32bppXRGB:
127       case DmtxPack32bppXBGR:
128          dmtxImageSetChannel(img,  8, 8);
129          dmtxImageSetChannel(img, 16, 8);
130          dmtxImageSetChannel(img, 24, 8);
131          break;
132       case DmtxPack32bppCMYK:
133          dmtxImageSetChannel(img,  0, 8);
134          dmtxImageSetChannel(img,  8, 8);
135          dmtxImageSetChannel(img, 16, 8);
136          dmtxImageSetChannel(img, 24, 8);
137          break;
138       default:
139          return NULL;
140    }
141 
142    return img;
143 }
144 
145 /**
146  * \brief  Free libdmtx image memory
147  * \param  img pointer to img location
148  * \return DmtxFail | DmtxPass
149  */
150 extern DmtxPassFail
dmtxImageDestroy(DmtxImage ** img)151 dmtxImageDestroy(DmtxImage **img)
152 {
153    if(img == NULL || *img == NULL)
154       return DmtxFail;
155 
156    free(*img);
157 
158    *img = NULL;
159 
160    return DmtxPass;
161 }
162 
163 /**
164  *
165  *
166  */
167 extern DmtxPassFail
dmtxImageSetChannel(DmtxImage * img,int channelStart,int bitsPerChannel)168 dmtxImageSetChannel(DmtxImage *img, int channelStart, int bitsPerChannel)
169 {
170    if(img->channelCount >= 4) /* IMAGE_MAX_CHANNEL */
171       return DmtxFail;
172 
173    /* New channel extends beyond pixel data */
174 /* if(channelStart + bitsPerChannel > img->bitsPerPixel)
175       return DmtxFail; */
176 
177    img->bitsPerChannel[img->channelCount] = bitsPerChannel;
178    img->channelStart[img->channelCount] = channelStart;
179    (img->channelCount)++;
180 
181    return DmtxPass;
182 }
183 
184 /**
185  * \brief  Set image property
186  * \param  img pointer to image
187  * \return image width
188  */
189 extern DmtxPassFail
dmtxImageSetProp(DmtxImage * img,int prop,int value)190 dmtxImageSetProp(DmtxImage *img, int prop, int value)
191 {
192    if(img == NULL)
193       return DmtxFail;
194 
195    switch(prop) {
196       case DmtxPropRowPadBytes:
197          img->rowPadBytes = value;
198          img->rowSizeBytes = img->width * (img->bitsPerPixel/8) + img->rowPadBytes;
199          break;
200       case DmtxPropImageFlip:
201          img->imageFlip = value;
202          break;
203       default:
204          break;
205    }
206 
207    return DmtxPass;
208 }
209 
210 /**
211  * \brief  Get image width
212  * \param  img pointer to image
213  * \return image width
214  */
215 extern int
dmtxImageGetProp(DmtxImage * img,int prop)216 dmtxImageGetProp(DmtxImage *img, int prop)
217 {
218    if(img == NULL)
219       return DmtxUndefined;
220 
221    switch(prop) {
222       case DmtxPropWidth:
223          return img->width;
224       case DmtxPropHeight:
225          return img->height;
226       case DmtxPropPixelPacking:
227          return img->pixelPacking;
228       case DmtxPropBitsPerPixel:
229          return img->bitsPerPixel;
230       case DmtxPropBytesPerPixel:
231          return img->bytesPerPixel;
232       case DmtxPropRowPadBytes:
233          return img->rowPadBytes;
234       case DmtxPropRowSizeBytes:
235          return img->rowSizeBytes;
236       case DmtxPropImageFlip:
237          return img->imageFlip;
238       case DmtxPropChannelCount:
239          return img->channelCount;
240       default:
241          break;
242    }
243 
244    return DmtxUndefined;
245 }
246 
247 /**
248  * \brief  Returns pixel offset for image
249  * \param  img
250  * \param  x coordinate
251  * \param  y coordinate
252  * \return pixel byte offset
253  */
254 extern int
dmtxImageGetByteOffset(DmtxImage * img,int x,int y)255 dmtxImageGetByteOffset(DmtxImage *img, int x, int y)
256 {
257    assert(img != NULL);
258    assert(!(img->imageFlip & DmtxFlipX)); /* DmtxFlipX is not an option */
259 
260    if(dmtxImageContainsInt(img, 0, x, y) == DmtxFalse)
261       return DmtxUndefined;
262 
263    if(img->imageFlip & DmtxFlipY)
264       return (y * img->rowSizeBytes + x * img->bytesPerPixel);
265 
266    return ((img->height - y - 1) * img->rowSizeBytes + x * img->bytesPerPixel);
267 }
268 
269 /**
270  *
271  *
272  */
273 extern DmtxPassFail
dmtxImageGetPixelValue(DmtxImage * img,int x,int y,int channel,int * value)274 dmtxImageGetPixelValue(DmtxImage *img, int x, int y, int channel, int *value)
275 {
276    int offset;
277 /* unsigned char *pixelPtr;
278    int pixelValue;
279    int mask;
280    int bitShift; */
281 
282    assert(img != NULL);
283    assert(channel < img->channelCount);
284 
285    offset = dmtxImageGetByteOffset(img, x, y);
286    if(offset == DmtxUndefined)
287       return DmtxFail;
288 
289    switch(img->bitsPerChannel[channel]) {
290       case 1:
291 /*       assert(img->bitsPerPixel == 1);
292          mask = 0x01 << (7 - offset%8);
293          *value = (img->pxl[offset/8] & mask) ? 255 : 0; */
294          break;
295       case 5:
296          /* XXX might be expensive if we want to scale perfect 0-255 range */
297 /*       assert(img->bitsPerPixel == 16);
298          pixelPtr = img->pxl + (offset * (img->bitsPerPixel/8));
299          pixelValue = (*pixelPtr << 8) | (*(pixelPtr+1));
300          bitShift = img->bitsPerPixel - 5 - img->channelStart[channel];
301          mask = 0x1f << bitShift;
302          *value = (((pixelValue & mask) >> bitShift) << 3); */
303          break;
304       case 8:
305          assert(img->channelStart[channel] % 8 == 0);
306          assert(img->bitsPerPixel % 8 == 0);
307          *value = img->pxl[offset + channel];
308          break;
309    }
310 
311    return DmtxPass;
312 }
313 
314 /**
315  *
316  *
317  */
318 extern DmtxPassFail
dmtxImageSetPixelValue(DmtxImage * img,int x,int y,int channel,int value)319 dmtxImageSetPixelValue(DmtxImage *img, int x, int y, int channel, int value)
320 {
321    int offset;
322 /* unsigned char *pixelPtr; */
323 /* int pixelValue; */
324 /* int mask; */
325 /* int bitShift; */
326 
327    assert(img != NULL);
328    assert(channel < img->channelCount);
329 
330    offset = dmtxImageGetByteOffset(img, x, y);
331    if(offset == DmtxUndefined)
332       return DmtxFail;
333 
334    switch(img->bitsPerChannel[channel]) {
335       case 1:
336 /*       assert(img->bitsPerPixel == 1);
337          mask = 0x01 << (7 - offset%8);
338          *value = (img->pxl[offset/8] & mask) ? 255 : 0; */
339          break;
340       case 5:
341          /* XXX might be expensive if we want to scale perfect 0-255 range */
342 /*       assert(img->bitsPerPixel == 16);
343          pixelPtr = img->pxl + (offset * (img->bitsPerPixel/8));
344          pixelValue = (*pixelPtr << 8) | (*(pixelPtr+1));
345          bitShift = img->bitsPerPixel - 5 - img->channelStart[channel];
346          mask = 0x1f << bitShift;
347          *value = (((pixelValue & mask) >> bitShift) << 3); */
348          break;
349       case 8:
350          assert(img->channelStart[channel] % 8 == 0);
351          assert(img->bitsPerPixel % 8 == 0);
352          img->pxl[offset + channel] = value;
353          break;
354    }
355 
356    return DmtxPass;
357 }
358 
359 /**
360  * \brief  Test whether image contains a coordinate expressed in integers
361  * \param  img
362  * \param  margin width
363  * \param  x coordinate
364  * \param  y coordinate
365  * \return DmtxTrue | DmtxFalse
366  */
367 extern DmtxBoolean
dmtxImageContainsInt(DmtxImage * img,int margin,int x,int y)368 dmtxImageContainsInt(DmtxImage *img, int margin, int x, int y)
369 {
370    assert(img != NULL);
371 
372    if(x - margin >= 0 && x + margin < img->width &&
373          y - margin >= 0 && y + margin < img->height)
374       return DmtxTrue;
375 
376    return DmtxFalse;
377 }
378 
379 /**
380  * \brief  Test whether image contains a coordinate expressed in floating points
381  * \param  img
382  * \param  x coordinate
383  * \param  y coordinate
384  * \return DmtxTrue | DmtxFalse
385  */
386 extern DmtxBoolean
dmtxImageContainsFloat(DmtxImage * img,double x,double y)387 dmtxImageContainsFloat(DmtxImage *img, double x, double y)
388 {
389    assert(img != NULL);
390 
391    if(x >= 0.0 && x < (double)img->width && y >= 0.0 && y < (double)img->height)
392       return DmtxTrue;
393 
394    return DmtxFalse;
395 }
396 
397 /**
398  *
399  *
400  */
401 static int
GetBitsPerPixel(int pack)402 GetBitsPerPixel(int pack)
403 {
404    switch(pack) {
405       case DmtxPack1bppK:
406          return 1;
407       case DmtxPack8bppK:
408          return 8;
409       case DmtxPack16bppRGB:
410       case DmtxPack16bppRGBX:
411       case DmtxPack16bppXRGB:
412       case DmtxPack16bppBGR:
413       case DmtxPack16bppBGRX:
414       case DmtxPack16bppXBGR:
415       case DmtxPack16bppYCbCr:
416          return 16;
417       case DmtxPack24bppRGB:
418       case DmtxPack24bppBGR:
419       case DmtxPack24bppYCbCr:
420          return  24;
421       case DmtxPack32bppRGBX:
422       case DmtxPack32bppXRGB:
423       case DmtxPack32bppBGRX:
424       case DmtxPack32bppXBGR:
425       case DmtxPack32bppCMYK:
426          return  32;
427       default:
428          break;
429    }
430 
431    return DmtxUndefined;
432 }
433