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