4G_module/third-party/zbar/src/convert.c

1212 lines
38 KiB
C

/*------------------------------------------------------------------------
* Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
*
* This file is part of the ZBar Bar Code Reader.
*
* The ZBar Bar Code Reader is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* The ZBar Bar Code Reader is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser Public License
* along with the ZBar Bar Code Reader; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* http://sourceforge.net/projects/zbar
*------------------------------------------------------------------------*/
#include "image.h"
#include "video.h"
#include "window.h"
#include "cm_mem.h"
#include "cm_sys.h"
/* pack bit size and location offset of a component into one byte
*/
#define RGB_BITS(off, size) ((((8 - (size)) & 0x7) << 5) | ((off) & 0x1f))
void *s_convert_dst_buf = NULL;
typedef void (conversion_handler_t)(zbar_image_t*,
const zbar_format_def_t*,
const zbar_image_t*,
const zbar_format_def_t*);
typedef struct conversion_def_s {
int cost; /* conversion "badness" */
conversion_handler_t *func; /* function that accomplishes it */
} conversion_def_t;
/* NULL terminated list of known formats, in order of preference
* (NB Cr=V Cb=U)
*/
const uint32_t _zbar_formats[] = {
/* planar YUV formats */
fourcc('4', '2', '2', 'P'), /* FIXME also YV16? */
fourcc('I', '4', '2', '0'),
fourcc('Y', 'U', '1', '2'), /* FIXME also IYUV? */
fourcc('Y', 'V', '1', '2'), fourcc('4', '1', '1', 'P'),
/* planar Y + packed UV plane */
fourcc('N', 'V', '1', '2'), fourcc('N', 'V', '2', '1'),
/* packed YUV formats */
fourcc('Y', 'U', 'Y', 'V'), fourcc('U', 'Y', 'V', 'Y'),
fourcc('Y', 'U', 'Y', '2'), /* FIXME add YVYU */
fourcc('Y', 'U', 'V', '4'), /* FIXME where is this from? */
/* packed rgb formats */
fourcc('R', 'G', 'B', '3'), fourcc(3, 0, 0, 0), fourcc('B', 'G', 'R', '3'),
fourcc('R', 'G', 'B', '4'), fourcc('B', 'G', 'R', '4'),
fourcc('R', 'G', 'B', 'P'), fourcc('R', 'G', 'B', 'O'),
fourcc('R', 'G', 'B', 'R'), fourcc('R', 'G', 'B', 'Q'),
fourcc('Y', 'U', 'V', '9'), fourcc('Y', 'V', 'U', '9'),
/* basic grayscale format */
fourcc('G', 'R', 'E', 'Y'), fourcc('Y', '8', '0', '0'),
fourcc('Y', '8', ' ', ' '), fourcc('Y', '8', 0, 0),
/* low quality RGB formats */
fourcc('R', 'G', 'B', '1'), fourcc('R', '4', '4', '4'),
fourcc('B', 'A', '8', '1'),
/* unsupported packed YUV formats */
fourcc('Y', '4', '1', 'P'), fourcc('Y', '4', '4', '4'),
fourcc('Y', 'U', 'V', 'O'), fourcc('H', 'M', '1', '2'),
/* unsupported packed RGB format */
fourcc('H', 'I', '2', '4'),
/* unsupported compressed formats */
fourcc('J', 'P', 'E', 'G'), fourcc('M', 'J', 'P', 'G'),
fourcc('M', 'P', 'E', 'G'),
/* terminator */
0
};
const int _zbar_num_formats = sizeof(_zbar_formats) / sizeof(uint32_t);
/* format definitions */
static const zbar_format_def_t format_defs[] = {
{ fourcc('R', 'G', 'B', '4'),
ZBAR_FMT_RGB_PACKED,
{ { 4, RGB_BITS(8, 8), RGB_BITS(16, 8), RGB_BITS(24, 8) } } },
{ fourcc('B', 'G', 'R', '1'),
ZBAR_FMT_RGB_PACKED,
{ { 1, RGB_BITS(0, 3), RGB_BITS(3, 3), RGB_BITS(6, 2) } } },
{ fourcc('4', '2', '2', 'P'), ZBAR_FMT_YUV_PLANAR, { { 1, 0, 0 /*UV*/ } } },
{
fourcc('Y', '8', '0', '0'),
ZBAR_FMT_GRAY,
},
{ fourcc('Y', 'U', 'Y', '2'),
ZBAR_FMT_YUV_PACKED,
{ { 1, 0, 0, /*YUYV*/ } } },
{
fourcc('J', 'P', 'E', 'G'),
ZBAR_FMT_JPEG,
},
{ fourcc('Y', 'V', 'Y', 'U'),
ZBAR_FMT_YUV_PACKED,
{ { 1, 0, 1, /*YVYU*/ } } },
{
fourcc('Y', '8', 0, 0),
ZBAR_FMT_GRAY,
},
{ fourcc('N', 'V', '2', '1'), ZBAR_FMT_YUV_NV, { { 1, 1, 1 /*VU*/ } } },
{ fourcc('N', 'V', '1', '2'), ZBAR_FMT_YUV_NV, { { 1, 1, 0 /*UV*/ } } },
{ fourcc('B', 'G', 'R', '3'),
ZBAR_FMT_RGB_PACKED,
{ { 3, RGB_BITS(16, 8), RGB_BITS(8, 8), RGB_BITS(0, 8) } } },
{ fourcc('Y', 'V', 'U', '9'), ZBAR_FMT_YUV_PLANAR, { { 2, 2, 1 /*VU*/ } } },
{ fourcc('R', 'G', 'B', 'O'),
ZBAR_FMT_RGB_PACKED,
{ { 2, RGB_BITS(10, 5), RGB_BITS(5, 5), RGB_BITS(0, 5) } } },
{ fourcc('R', 'G', 'B', 'Q'),
ZBAR_FMT_RGB_PACKED,
{ { 2, RGB_BITS(2, 5), RGB_BITS(13, 5), RGB_BITS(8, 5) } } },
{
fourcc('G', 'R', 'E', 'Y'),
ZBAR_FMT_GRAY,
},
{ fourcc(3, 0, 0, 0),
ZBAR_FMT_RGB_PACKED,
{ { 4, RGB_BITS(16, 8), RGB_BITS(8, 8), RGB_BITS(0, 8) } } },
{
fourcc('Y', '8', ' ', ' '),
ZBAR_FMT_GRAY,
},
{ fourcc('I', '4', '2', '0'), ZBAR_FMT_YUV_PLANAR, { { 1, 1, 0 /*UV*/ } } },
{ fourcc('R', 'G', 'B', '1'),
ZBAR_FMT_RGB_PACKED,
{ { 1, RGB_BITS(5, 3), RGB_BITS(2, 3), RGB_BITS(0, 2) } } },
{ fourcc('Y', 'U', '1', '2'), ZBAR_FMT_YUV_PLANAR, { { 1, 1, 0 /*UV*/ } } },
{ fourcc('Y', 'V', '1', '2'), ZBAR_FMT_YUV_PLANAR, { { 1, 1, 1 /*VU*/ } } },
{ fourcc('R', 'G', 'B', '3'),
ZBAR_FMT_RGB_PACKED,
{ { 3, RGB_BITS(0, 8), RGB_BITS(8, 8), RGB_BITS(16, 8) } } },
{ fourcc('R', '4', '4', '4'),
ZBAR_FMT_RGB_PACKED,
{ { 2, RGB_BITS(8, 4), RGB_BITS(4, 4), RGB_BITS(0, 4) } } },
{ fourcc('B', 'G', 'R', '4'),
ZBAR_FMT_RGB_PACKED,
{ { 4, RGB_BITS(16, 8), RGB_BITS(8, 8), RGB_BITS(0, 8) } } },
{ fourcc('Y', 'U', 'V', '9'), ZBAR_FMT_YUV_PLANAR, { { 2, 2, 0 /*UV*/ } } },
{
fourcc('M', 'J', 'P', 'G'),
ZBAR_FMT_JPEG,
},
{ fourcc('4', '1', '1', 'P'), ZBAR_FMT_YUV_PLANAR, { { 2, 0, 0 /*UV*/ } } },
{ fourcc('R', 'G', 'B', 'P'),
ZBAR_FMT_RGB_PACKED,
{ { 2, RGB_BITS(11, 5), RGB_BITS(5, 6), RGB_BITS(0, 5) } } },
{ fourcc('R', 'G', 'B', 'R'),
ZBAR_FMT_RGB_PACKED,
{ { 2, RGB_BITS(3, 5), RGB_BITS(13, 6), RGB_BITS(8, 5) } } },
{ fourcc('Y', 'U', 'Y', 'V'),
ZBAR_FMT_YUV_PACKED,
{ { 1, 0, 0, /*YUYV*/ } } },
{ fourcc('U', 'Y', 'V', 'Y'),
ZBAR_FMT_YUV_PACKED,
{ { 1, 0, 2, /*UYVY*/ } } },
};
static const int num_format_defs =
sizeof(format_defs) / sizeof(zbar_format_def_t);
#if 0
#ifdef DEBUG_CONVERT
static int intsort (const void *a,
const void *b)
{
return(*(uint32_t*)a - *(uint32_t*)b);
}
#endif
/* verify that format list is in required sort order */
static inline int verify_format_sort (void)
{
int i;
for(i = 0; i < num_format_defs; i++) {
int j = i * 2 + 1;
if((j < num_format_defs &&
format_defs[i].format < format_defs[j].format) ||
(j + 1 < num_format_defs &&
format_defs[j + 1].format < format_defs[i].format))
break;
}
if(i == num_format_defs)
return(0);
/* spew correct order for fix */
fprintf(stderr, "ERROR: image format list is not sorted!?\n");
#ifdef DEBUG_CONVERT
assert(num_format_defs);
uint32_t sorted[num_format_defs];
uint32_t ordered[num_format_defs];
for(i = 0; i < num_format_defs; i++)
sorted[i] = format_defs[i].format;
qsort(sorted, num_format_defs, sizeof(uint32_t), intsort);
for(i = 0; i < num_format_defs; i = i << 1 | 1);
i = (i - 1) / 2;
ordered[i] = sorted[0];
int j, k;
for(j = 1; j < num_format_defs; j++) {
k = i * 2 + 2;
if(k < num_format_defs) {
i = k;
for(k = k * 2 + 1; k < num_format_defs; k = k * 2 + 1)
i = k;
}
else {
for(k = (i - 1) / 2; i != k * 2 + 1; k = (i - 1) / 2) {
assert(i);
i = k;
}
i = k;
}
ordered[i] = sorted[j];
}
fprintf(stderr, "correct sort order is:");
for(i = 0; i < num_format_defs; i++)
fprintf(stderr, " %4.4s", (char*)&ordered[i]);
fprintf(stderr, "\n");
#endif
return(-1);
}
static inline void uv_round (zbar_image_t *img,
const zbar_format_def_t *fmt)
{
img->width >>= fmt->p.yuv.xsub2;
img->width <<= fmt->p.yuv.xsub2;
img->height >>= fmt->p.yuv.ysub2;
img->height <<= fmt->p.yuv.ysub2;
}
#endif
static inline void uv_roundup(zbar_image_t *img, const zbar_format_def_t *fmt)
{
unsigned xmask, ymask;
if (fmt->group == ZBAR_FMT_GRAY)
return;
xmask = (1 << fmt->p.yuv.xsub2) - 1;
if (img->width & xmask)
img->width = (img->width + xmask) & ~xmask;
ymask = (1 << fmt->p.yuv.ysub2) - 1;
if (img->height & ymask)
img->height = (img->height + ymask) & ~ymask;
}
static inline unsigned long uvp_size(const zbar_image_t *img,
const zbar_format_def_t *fmt)
{
if (fmt->group == ZBAR_FMT_GRAY)
return (0);
return ((img->width >> fmt->p.yuv.xsub2) *
(img->height >> fmt->p.yuv.ysub2));
}
#if 0
static inline uint32_t convert_read_rgb (const uint8_t *srcp,
int bpp)
{
uint32_t p;
if(bpp == 3) {
p = *srcp;
p |= *(srcp + 1) << 8;
p |= *(srcp + 2) << 16;
}
else if(bpp == 4)
p = *((uint32_t*)(srcp));
else if(bpp == 2)
p = *((uint16_t*)(srcp));
else
p = *srcp;
return(p);
}
static inline void convert_write_rgb (uint8_t *dstp,
uint32_t p,
int bpp)
{
if(bpp == 3) {
*dstp = p & 0xff;
*(dstp + 1) = (p >> 8) & 0xff;
*(dstp + 2) = (p >> 16) & 0xff;
}
else if(bpp == 4)
*((uint32_t*)dstp) = p;
else if(bpp == 2)
*((uint16_t*)dstp) = p;
else
*dstp = p;
}
#endif
/* cleanup linked image by unrefing */
static void cleanup_ref(zbar_image_t *img)
{
if (img->next)
_zbar_image_refcnt(img->next, -1);
}
/* resize y plane, drop extra columns/rows from the right/bottom,
* or duplicate last column/row to pad missing data
*/
static inline void convert_y_resize(zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt, size_t n)
{
uint8_t *psrc, *pdst;
unsigned width, height, xpad, y;
if (dst->width == src->width && dst->height == src->height) {
memcpy((void *)dst->data, src->data, n);
return;
}
psrc = (void *)src->data;
pdst = (void *)dst->data;
width = (dst->width > src->width) ? src->width : dst->width;
xpad = (dst->width > src->width) ? dst->width - src->width : 0;
height = (dst->height > src->height) ? src->height : dst->height;
for (y = 0; y < height; y++) {
memcpy(pdst, psrc, width);
pdst += width;
psrc += src->width;
if (xpad) {
memset(pdst, *(psrc - 1), xpad);
pdst += xpad;
}
}
psrc -= src->width;
for (; y < dst->height; y++) {
memcpy(pdst, psrc, width);
pdst += width;
if (xpad) {
memset(pdst, *(psrc - 1), xpad);
pdst += xpad;
}
}
}
/* make new image w/reference to the same image data */
static void convert_copy(zbar_image_t *dst, const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt)
{
if (src->width == dst->width && src->height == dst->height) {
zbar_image_t *s = (zbar_image_t *)src;
dst->data = src->data;
dst->datalen = src->datalen;
dst->cleanup = cleanup_ref;
dst->next = s;
_zbar_image_refcnt(s, 1);
} else
/* NB only for GRAY/YUV_PLANAR formats */
convert_y_resize(dst, dstfmt, src, srcfmt, dst->width * dst->height);
}
#if 0
/* append neutral UV plane to grayscale image */
static void convert_uvp_append (zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt)
{
uv_roundup(dst, dstfmt);
dst->datalen = uvp_size(dst, dstfmt) * 2;
unsigned long n = dst->width * dst->height;
dst->datalen += n;
assert(src->datalen >= src->width * src->height);
zprintf(24, "dst=%dx%d (%lx) %lx src=%dx%d %lx\n",
dst->width, dst->height, n, dst->datalen,
src->width, src->height, src->datalen);
dst->data = cm_malloc(dst->datalen);
if(!dst->data) return;
convert_y_resize(dst, dstfmt, src, srcfmt, n);
memset((void *)((uint32_t)dst->data + n), 0x80, dst->datalen - n);
}
/* interleave YUV planes into packed YUV */
static void convert_yuv_pack (zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt)
{
uv_roundup(dst, dstfmt);
dst->datalen = dst->width * dst->height + uvp_size(dst, dstfmt) * 2;
dst->data = cm_malloc(dst->datalen);
if(!dst->data) return;
uint8_t *dstp = (void*)dst->data;
unsigned long srcm = uvp_size(src, srcfmt);
unsigned long srcn = src->width * src->height;
assert(src->datalen >= srcn + 2 * srcn);
uint8_t flags = dstfmt->p.yuv.packorder ^ srcfmt->p.yuv.packorder;
uint8_t *srcy = (void*)src->data;
const uint8_t *srcu, *srcv;
if(flags & 1) {
srcv = (uint8_t *)src->data + srcn;
srcu = srcv + srcm;
} else {
srcu = (uint8_t *)src->data + srcn;
srcv = srcu + srcm;
}
flags = dstfmt->p.yuv.packorder & 2;
unsigned srcl = src->width >> srcfmt->p.yuv.xsub2;
unsigned xmask = (1 << srcfmt->p.yuv.xsub2) - 1;
unsigned ymask = (1 << srcfmt->p.yuv.ysub2) - 1;
unsigned x, y;
uint8_t y0 = 0, y1 = 0, u = 0x80, v = 0x80;
for(y = 0; y < dst->height; y++) {
if(y >= src->height) {
srcy -= src->width;
srcu -= srcl; srcv -= srcl;
}
else if(y & ymask) {
srcu -= srcl; srcv -= srcl;
}
for(x = 0; x < dst->width; x += 2) {
if(x < src->width) {
y0 = *(srcy++); y1 = *(srcy++);
if(!(x & xmask)) {
u = *(srcu++); v = *(srcv++);
}
}
if(flags) {
*(dstp++) = u; *(dstp++) = y0;
*(dstp++) = v; *(dstp++) = y1;
} else {
*(dstp++) = y0; *(dstp++) = u;
*(dstp++) = y1; *(dstp++) = v;
}
}
for(; x < src->width; x += 2) {
srcy += 2;
if(!(x & xmask)) {
srcu++; srcv++;
}
}
}
}
void *s_convert_dst_buf = NULL;
int cm_zbar_convert_buf_init(int width, int height)
{
if(!s_convert_dst_buf)
{
// s_convert_dst_buf = cm_malloc(width * height + sizeof(size_t));
s_convert_dst_buf = cm_calloc(width * height + sizeof(size_t), sizeof(uint8_t));
if(!s_convert_dst_buf)
{
return -1;
}
}
return 0;
}
void cm_zbar_convert_buf_destroy(void)
{
if(s_convert_dst_buf)
{
cm_free(s_convert_dst_buf);
s_convert_dst_buf = NULL;
}
}
/* split packed YUV samples and join into YUV planes
* FIXME currently ignores color and grayscales the image
*/
#endif
static void convert_yuv_unpack(zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt)
{
unsigned long dstn, dstm2;
uint8_t *dsty, flags;
const uint8_t *srcp;
unsigned srcl, x, y;
uint8_t y0 = 0, y1 = 0;
uv_roundup(dst, dstfmt);
dstn = dst->width * dst->height;
dstm2 = uvp_size(dst, dstfmt) * 2;
dst->datalen = dstn + dstm2;
//dst->data = cm_malloc(dst->datalen);
dst->data = s_convert_dst_buf;
if (!dst->data)
return;
if (dstm2)
memset((uint8_t *)dst->data + dstn, 0x80, dstm2);
dsty = (uint8_t *)dst->data;
flags = srcfmt->p.yuv.packorder ^ dstfmt->p.yuv.packorder;
flags &= 2;
srcp = src->data;
if (flags)
srcp++;
srcl = src->width + (src->width >> srcfmt->p.yuv.xsub2);
for (y = 0; y < dst->height; y++) {
if (y >= src->height)
srcp -= srcl;
for (x = 0; x < dst->width; x += 2) {
if (x < src->width) {
y0 = *(srcp++);
srcp++;
y1 = *(srcp++);
srcp++;
}
*(dsty++) = y0;
*(dsty++) = y1;
}
if (x < src->width)
srcp += (src->width - x) * 2;
}
}
#if 0
/* resample and resize UV plane(s)
* FIXME currently ignores color and grayscales the image
*/
static void convert_uvp_resample (zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt)
{
uv_roundup(dst, dstfmt);
unsigned long dstn = dst->width * dst->height;
unsigned long dstm2 = uvp_size(dst, dstfmt) * 2;
dst->datalen = dstn + dstm2;
dst->data = cm_malloc(dst->datalen);
if(!dst->data) return;
convert_y_resize(dst, dstfmt, src, srcfmt, dstn);
if(dstm2)
memset((void*)((uint32_t)dst->data + dstn), 0x80, dstm2);
}
/* rearrange interleaved UV componets */
static void convert_uv_resample (zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt)
{
uv_roundup(dst, dstfmt);
unsigned long dstn = dst->width * dst->height;
dst->datalen = dstn + uvp_size(dst, dstfmt) * 2;
dst->data = cm_malloc(dst->datalen);
if(!dst->data) return;
uint8_t *dstp = (void*)dst->data;
uint8_t flags = (srcfmt->p.yuv.packorder ^ dstfmt->p.yuv.packorder) & 1;
const uint8_t *srcp = src->data;
unsigned srcl = src->width + (src->width >> srcfmt->p.yuv.xsub2);
unsigned x, y;
uint8_t y0 = 0, y1 = 0, u = 0x80, v = 0x80;
for(y = 0; y < dst->height; y++) {
if(y >= src->height)
srcp -= srcl;
for(x = 0; x < dst->width; x += 2) {
if(x < src->width) {
if(!(srcfmt->p.yuv.packorder & 2)) {
y0 = *(srcp++); u = *(srcp++);
y1 = *(srcp++); v = *(srcp++);
}
else {
u = *(srcp++); y0 = *(srcp++);
v = *(srcp++); y1 = *(srcp++);
}
if(flags) {
uint8_t tmp = u; u = v; v = tmp;
}
}
if(!(dstfmt->p.yuv.packorder & 2)) {
*(dstp++) = y0; *(dstp++) = u;
*(dstp++) = y1; *(dstp++) = v;
}
else {
*(dstp++) = u; *(dstp++) = y0;
*(dstp++) = v; *(dstp++) = y1;
}
}
if(x < src->width)
srcp += (src->width - x) * 2;
}
}
/* YUV planes to packed RGB
* FIXME currently ignores color and grayscales the image
*/
static void convert_yuvp_to_rgb (zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt)
{
dst->datalen = dst->width * dst->height * dstfmt->p.rgb.bpp;
dst->data = cm_malloc(dst->datalen);
if(!dst->data) return;
uint8_t *dstp = (void*)dst->data;
int drbits = RGB_SIZE(dstfmt->p.rgb.red);
int drbit0 = RGB_OFFSET(dstfmt->p.rgb.red);
int dgbits = RGB_SIZE(dstfmt->p.rgb.green);
int dgbit0 = RGB_OFFSET(dstfmt->p.rgb.green);
int dbbits = RGB_SIZE(dstfmt->p.rgb.blue);
int dbbit0 = RGB_OFFSET(dstfmt->p.rgb.blue);
unsigned long srcm = uvp_size(src, srcfmt);
unsigned long srcn = src->width * src->height;
assert(src->datalen >= srcn + 2 * srcm);
uint8_t *srcy = (void*)src->data;
unsigned x, y;
uint32_t p = 0;
for(y = 0; y < dst->height; y++) {
if(y >= src->height)
srcy -= src->width;
for(x = 0; x < dst->width; x++) {
if(x < src->width) {
/* FIXME color space? */
unsigned y0 = *(srcy++);
p = (((y0 >> drbits) << drbit0) |
((y0 >> dgbits) << dgbit0) |
((y0 >> dbbits) << dbbit0));
}
convert_write_rgb(dstp, p, dstfmt->p.rgb.bpp);
dstp += dstfmt->p.rgb.bpp;
}
if(x < src->width)
srcy += (src->width - x);
}
}
/* packed RGB to YUV planes
* FIXME currently ignores color and grayscales the image
*/
static void convert_rgb_to_yuvp (zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt)
{
uv_roundup(dst, dstfmt);
unsigned long dstn = dst->width * dst->height;
unsigned long dstm2 = uvp_size(dst, dstfmt) * 2;
dst->datalen = dstn + dstm2;
dst->data = cm_malloc(dst->datalen);
if(!dst->data) return;
if(dstm2)
memset((void*)((uint32_t)dst->data + dstn), 0x80, dstm2);
uint8_t *dsty = (void*)dst->data;
assert(src->datalen >= (src->width * src->height * srcfmt->p.rgb.bpp));
const uint8_t *srcp = src->data;
int rbits = RGB_SIZE(srcfmt->p.rgb.red);
int rbit0 = RGB_OFFSET(srcfmt->p.rgb.red);
int gbits = RGB_SIZE(srcfmt->p.rgb.green);
int gbit0 = RGB_OFFSET(srcfmt->p.rgb.green);
int bbits = RGB_SIZE(srcfmt->p.rgb.blue);
int bbit0 = RGB_OFFSET(srcfmt->p.rgb.blue);
unsigned srcl = src->width * srcfmt->p.rgb.bpp;
unsigned x, y;
uint16_t y0 = 0;
for(y = 0; y < dst->height; y++) {
if(y >= src->height)
srcp -= srcl;
for(x = 0; x < dst->width; x++) {
if(x < src->width) {
uint8_t r, g, b;
uint32_t p = convert_read_rgb(srcp, srcfmt->p.rgb.bpp);
srcp += srcfmt->p.rgb.bpp;
/* FIXME endianness? */
r = ((p >> rbit0) << rbits) & 0xff;
g = ((p >> gbit0) << gbits) & 0xff;
b = ((p >> bbit0) << bbits) & 0xff;
/* FIXME color space? */
y0 = ((77 * r + 150 * g + 29 * b) + 0x80) >> 8;
}
*(dsty++) = y0;
}
if(x < src->width)
srcp += (src->width - x) * srcfmt->p.rgb.bpp;
}
}
/* packed YUV to packed RGB */
static void convert_yuv_to_rgb (zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt)
{
unsigned long dstn = dst->width * dst->height;
dst->datalen = dstn * dstfmt->p.rgb.bpp;
dst->data = cm_malloc(dst->datalen);
if(!dst->data) return;
uint8_t *dstp = (void*)dst->data;
int drbits = RGB_SIZE(dstfmt->p.rgb.red);
int drbit0 = RGB_OFFSET(dstfmt->p.rgb.red);
int dgbits = RGB_SIZE(dstfmt->p.rgb.green);
int dgbit0 = RGB_OFFSET(dstfmt->p.rgb.green);
int dbbits = RGB_SIZE(dstfmt->p.rgb.blue);
int dbbit0 = RGB_OFFSET(dstfmt->p.rgb.blue);
assert(src->datalen >= (src->width * src->height +
uvp_size(src, srcfmt) * 2));
const uint8_t *srcp = src->data;
if(srcfmt->p.yuv.packorder & 2)
srcp++;
assert(srcfmt->p.yuv.xsub2 == 1);
unsigned srcl = src->width + (src->width >> 1);
unsigned x, y;
uint32_t p = 0;
for(y = 0; y < dst->height; y++) {
if(y >= src->height)
srcp -= srcl;
for(x = 0; x < dst->width; x++) {
if(x < src->width) {
uint8_t y0 = *(srcp++);
srcp++;
if(y0 <= 16)
y0 = 0;
else if(y0 >= 235)
y0 = 255;
else
y0 = (uint16_t)(y0 - 16) * 255 / 219;
p = (((y0 >> drbits) << drbit0) |
((y0 >> dgbits) << dgbit0) |
((y0 >> dbbits) << dbbit0));
}
convert_write_rgb(dstp, p, dstfmt->p.rgb.bpp);
dstp += dstfmt->p.rgb.bpp;
}
if(x < src->width)
srcp += (src->width - x) * 2;
}
}
/* packed RGB to packed YUV
* FIXME currently ignores color and grayscales the image
*/
static void convert_rgb_to_yuv (zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt)
{
uv_roundup(dst, dstfmt);
dst->datalen = dst->width * dst->height + uvp_size(dst, dstfmt) * 2;
dst->data = cm_malloc(dst->datalen);
if(!dst->data) return;
uint8_t *dstp = (void*)dst->data;
uint8_t flags = dstfmt->p.yuv.packorder & 2;
assert(src->datalen >= (src->width * src->height * srcfmt->p.rgb.bpp));
const uint8_t *srcp = src->data;
int rbits = RGB_SIZE(srcfmt->p.rgb.red);
int rbit0 = RGB_OFFSET(srcfmt->p.rgb.red);
int gbits = RGB_SIZE(srcfmt->p.rgb.green);
int gbit0 = RGB_OFFSET(srcfmt->p.rgb.green);
int bbits = RGB_SIZE(srcfmt->p.rgb.blue);
int bbit0 = RGB_OFFSET(srcfmt->p.rgb.blue);
unsigned srcl = src->width * srcfmt->p.rgb.bpp;
unsigned x, y;
uint16_t y0 = 0;
for(y = 0; y < dst->height; y++) {
if(y >= src->height)
srcp -= srcl;
for(x = 0; x < dst->width; x++) {
if(x < src->width) {
uint8_t r, g, b;
uint32_t p = convert_read_rgb(srcp, srcfmt->p.rgb.bpp);
srcp += srcfmt->p.rgb.bpp;
/* FIXME endianness? */
r = ((p >> rbit0) << rbits) & 0xff;
g = ((p >> gbit0) << gbits) & 0xff;
b = ((p >> bbit0) << bbits) & 0xff;
/* FIXME color space? */
y0 = ((77 * r + 150 * g + 29 * b) + 0x80) >> 8;
}
if(flags) {
*(dstp++) = 0x80; *(dstp++) = y0;
}
else {
*(dstp++) = y0; *(dstp++) = 0x80;
}
}
if(x < src->width)
srcp += (src->width - x) * srcfmt->p.rgb.bpp;
}
}
/* resample and resize packed RGB components */
static void convert_rgb_resample (zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt)
{
unsigned long dstn = dst->width * dst->height;
dst->datalen = dstn * dstfmt->p.rgb.bpp;
dst->data = cm_malloc(dst->datalen);
if(!dst->data) return;
uint8_t *dstp = (void*)dst->data;
int drbits = RGB_SIZE(dstfmt->p.rgb.red);
int drbit0 = RGB_OFFSET(dstfmt->p.rgb.red);
int dgbits = RGB_SIZE(dstfmt->p.rgb.green);
int dgbit0 = RGB_OFFSET(dstfmt->p.rgb.green);
int dbbits = RGB_SIZE(dstfmt->p.rgb.blue);
int dbbit0 = RGB_OFFSET(dstfmt->p.rgb.blue);
assert(src->datalen >= (src->width * src->height * srcfmt->p.rgb.bpp));
const uint8_t *srcp = src->data;
int srbits = RGB_SIZE(srcfmt->p.rgb.red);
int srbit0 = RGB_OFFSET(srcfmt->p.rgb.red);
int sgbits = RGB_SIZE(srcfmt->p.rgb.green);
int sgbit0 = RGB_OFFSET(srcfmt->p.rgb.green);
int sbbits = RGB_SIZE(srcfmt->p.rgb.blue);
int sbbit0 = RGB_OFFSET(srcfmt->p.rgb.blue);
unsigned srcl = src->width * srcfmt->p.rgb.bpp;
unsigned x, y;
uint32_t p = 0;
for(y = 0; y < dst->height; y++) {
if(y >= src->height)
y -= srcl;
for(x = 0; x < dst->width; x++) {
if(x < src->width) {
uint8_t r, g, b;
p = convert_read_rgb(srcp, srcfmt->p.rgb.bpp);
srcp += srcfmt->p.rgb.bpp;
/* FIXME endianness? */
r = (p >> srbit0) << srbits;
g = (p >> sgbit0) << sgbits;
b = (p >> sbbit0) << sbbits;
p = (((r >> drbits) << drbit0) |
((g >> dgbits) << dgbit0) |
((b >> dbbits) << dbbit0));
}
convert_write_rgb(dstp, p, dstfmt->p.rgb.bpp);
dstp += dstfmt->p.rgb.bpp;
}
if(x < src->width)
srcp += (src->width - x) * srcfmt->p.rgb.bpp;
}
}
#endif
#ifdef HAVE_LIBJPEG
void _zbar_convert_jpeg_to_y(zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt);
static void convert_jpeg(zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt);
#endif
#if 0
/* group conversion matrix */
static conversion_def_t conversions[][ZBAR_FMT_NUM] = {
{ /* *from* GRAY */
{ 0, convert_copy }, /* to GRAY */
{ 8, convert_uvp_append }, /* to YUV_PLANAR */
{ 24, convert_yuv_pack }, /* to YUV_PACKED */
{ 32, convert_yuvp_to_rgb }, /* to RGB_PACKED */
{ 8, convert_uvp_append }, /* to YUV_NV */
{ -1, NULL }, /* to JPEG */
},
{ /* from YUV_PLANAR */
{ 1, convert_copy }, /* to GRAY */
{ 48, convert_uvp_resample }, /* to YUV_PLANAR */
{ 64, convert_yuv_pack }, /* to YUV_PACKED */
{ 128, convert_yuvp_to_rgb }, /* to RGB_PACKED */
{ 40, convert_uvp_append }, /* to YUV_NV */
{ -1, NULL }, /* to JPEG */
},
{ /* from YUV_PACKED */
{ 24, convert_yuv_unpack }, /* to GRAY */
{ 52, convert_yuv_unpack }, /* to YUV_PLANAR */
{ 20, convert_uv_resample }, /* to YUV_PACKED */
{ 144, convert_yuv_to_rgb }, /* to RGB_PACKED */
{ 18, convert_yuv_unpack }, /* to YUV_NV */
{ -1, NULL }, /* to JPEG */
},
{ /* from RGB_PACKED */
{ 112, convert_rgb_to_yuvp }, /* to GRAY */
{ 160, convert_rgb_to_yuvp }, /* to YUV_PLANAR */
{ 144, convert_rgb_to_yuv }, /* to YUV_PACKED */
{ 120, convert_rgb_resample }, /* to RGB_PACKED */
{ 152, convert_rgb_to_yuvp }, /* to YUV_NV */
{ -1, NULL }, /* to JPEG */
},
{ /* from YUV_NV (FIXME treated as GRAY) */
{ 1, convert_copy }, /* to GRAY */
{ 8, convert_uvp_append }, /* to YUV_PLANAR */
{ 24, convert_yuv_pack }, /* to YUV_PACKED */
{ 32, convert_yuvp_to_rgb }, /* to RGB_PACKED */
{ 8, convert_uvp_append }, /* to YUV_NV */
{ -1, NULL }, /* to JPEG */
},
#ifdef HAVE_LIBJPEG
{ /* from JPEG */
{ 96, _zbar_convert_jpeg_to_y }, /* to GRAY */
{ 104, convert_jpeg }, /* to YUV_PLANAR */
{ 116, convert_jpeg }, /* to YUV_PACKED */
{ 256, convert_jpeg }, /* to RGB_PACKED */
{ 104, convert_jpeg }, /* to YUV_NV */
{ -1, NULL }, /* to JPEG */
},
#else
{ /* from JPEG */
{ -1, NULL }, /* to GRAY */
{ -1, NULL }, /* to YUV_PLANAR */
{ -1, NULL }, /* to YUV_PACKED */
{ -1, NULL }, /* to RGB_PACKED */
{ -1, NULL }, /* to YUV_NV */
{ -1, NULL }, /* to JPEG */
},
#endif
};
#endif
const zbar_format_def_t *_zbar_format_lookup(uint32_t fmt)
{
const zbar_format_def_t *def = NULL;
int i = 0;
while (i < num_format_defs) {
def = &format_defs[i];
if (fmt == def->format)
return (def);
i = i * 2 + 1;
if (fmt > def->format)
i++;
}
return (NULL);
}
#ifdef HAVE_LIBJPEG
/* convert JPEG data via an intermediate format supported by libjpeg */
static void convert_jpeg (zbar_image_t *dst,
const zbar_format_def_t *dstfmt,
const zbar_image_t *src,
const zbar_format_def_t *srcfmt)
{
/* define intermediate image in a format supported by libjpeg
* (currently only grayscale)
*/
zbar_image_t *tmp;
if(!src->src) {
tmp = zbar_image_create();
tmp->format = fourcc('Y','8','0','0');
tmp->width = dst->width;
tmp->height = dst->height;
}
else {
tmp = src->src->jpeg_img;
assert(tmp);
dst->width = tmp->width;
dst->height = tmp->height;
}
const zbar_format_def_t *tmpfmt = _zbar_format_lookup(tmp->format);
assert(tmpfmt);
/* convert to intermediate format */
_zbar_convert_jpeg_to_y(tmp, tmpfmt, src, srcfmt);
/* now convert to dst */
dst->width = tmp->width;
dst->height = tmp->height;
conversion_handler_t *func =
conversions[tmpfmt->group][dstfmt->group].func;
func(dst, dstfmt, tmp, tmpfmt);
if(!src->src)
zbar_image_destroy(tmp);
}
#endif
int cm_zbar_convert_buf_init(int width, int height)
{
if(!s_convert_dst_buf)
{
// s_convert_dst_buf = cm_malloc(width * height + sizeof(size_t));
s_convert_dst_buf = cm_calloc(width * height + sizeof(size_t), sizeof(uint8_t));
if(!s_convert_dst_buf)
{
return -1;
}
}
return 0;
}
void cm_zbar_convert_buf_destroy(void)
{
if(s_convert_dst_buf)
{
cm_free(s_convert_dst_buf);
s_convert_dst_buf = NULL;
}
}
zbar_image_t *zbar_image_convert_resize(const zbar_image_t *src,
unsigned long fmt, unsigned width,
unsigned height)
{
const zbar_format_def_t *srcfmt, *dstfmt;
conversion_handler_t *func;
zbar_image_t *dst = zbar_image_create();
dst->format = fmt;
dst->width = width;
dst->height = height;
zbar_image_set_crop(dst, src->crop_x, src->crop_y, src->crop_w,
src->crop_h);
if (src->format == fmt && src->width == width && src->height == height) {
convert_copy(dst, NULL, src, NULL);
return (dst);
}
srcfmt = _zbar_format_lookup(src->format);
dstfmt = _zbar_format_lookup(dst->format);
if (!srcfmt || !dstfmt)
/* FIXME free dst */
return (NULL);
if (srcfmt->group == dstfmt->group && srcfmt->p.cmp == dstfmt->p.cmp &&
src->width == width && src->height == height) {
convert_copy(dst, NULL, src, NULL);
return (dst);
}
//func = conversions[srcfmt->group][dstfmt->group].func;
//dst->cleanup = zbar_image_free_data;
//func(dst, dstfmt, src, srcfmt);
convert_yuv_unpack(dst, dstfmt, src, srcfmt);
if (!dst->data) {
/* conversion failed */
zbar_image_destroy(dst);
return (NULL);
}
return (dst);
}
zbar_image_t *zbar_image_convert(const zbar_image_t *src, unsigned long fmt)
{
return (zbar_image_convert_resize(src, fmt, src->width, src->height));
}
#if 0
static inline int has_format (uint32_t fmt,
const uint32_t *fmts)
{
for(; *fmts; fmts++)
if(*fmts == fmt)
return(1);
return(0);
}
/* select least cost conversion from src format to available dsts */
int _zbar_best_format (uint32_t src,
uint32_t *dst,
const uint32_t *dsts)
{
if(dst)
*dst = 0;
if(!dsts)
return(-1);
if(has_format(src, dsts)) {
zprintf(8, "shared format: %4.4s\n", (char*)&src);
if(dst)
*dst = src;
return(0);
}
const zbar_format_def_t *srcfmt = _zbar_format_lookup(src);
if(!srcfmt)
return(-1);
zprintf(8, "from %.4s(%08" PRIx32 ") to", (char*)&src, src);
unsigned min_cost = -1;
for(; *dsts; dsts++) {
const zbar_format_def_t *dstfmt = _zbar_format_lookup(*dsts);
if(!dstfmt)
continue;
int cost;
if(srcfmt->group == dstfmt->group &&
srcfmt->p.cmp == dstfmt->p.cmp)
cost = 0;
else
cost = conversions[srcfmt->group][dstfmt->group].cost;
if(_zbar_verbosity >= 8)
fprintf(stderr, " %.4s(%08" PRIx32 ")=%d",
(char*)dsts, *dsts, cost);
if(cost >= 0 && min_cost > cost) {
min_cost = cost;
if(dst)
*dst = *dsts;
}
}
if(_zbar_verbosity >= 8)
fprintf(stderr, "\n");
return(min_cost);
}
int zbar_negotiate_format (zbar_video_t *vdo,
zbar_window_t *win)
{
if(!vdo && !win)
return(0);
if(win)
(void)window_lock(win);
errinfo_t *errdst = (vdo) ? &vdo->err : &win->err;
if(verify_format_sort()) {
if(win)
(void)window_unlock(win);
return(err_capture(errdst, SEV_FATAL, ZBAR_ERR_INTERNAL, __func__,
"image format list is not sorted!?"));
}
if((vdo && !vdo->formats) || (win && !win->formats)) {
if(win)
(void)window_unlock(win);
return(err_capture(errdst, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__,
"no input or output formats available"));
}
static const uint32_t y800[2] = { fourcc('Y','8','0','0'), 0 };
const uint32_t *srcs = (vdo) ? vdo->formats : y800;
const uint32_t *dsts = (win) ? win->formats : y800;
unsigned min_cost = -1;
uint32_t min_fmt = 0;
const uint32_t *fmt;
for(fmt = _zbar_formats; *fmt; fmt++) {
/* only consider formats supported by video device */
if(!has_format(*fmt, srcs))
continue;
uint32_t win_fmt = 0;
int cost = _zbar_best_format(*fmt, &win_fmt, dsts);
if(cost < 0) {
zprintf(4, "%.4s(%08" PRIx32 ") -> ? (unsupported)\n",
(char*)fmt, *fmt);
continue;
}
zprintf(4, "%.4s(%08" PRIx32 ") -> %.4s(%08" PRIx32 ") (%d)\n",
(char*)fmt, *fmt, (char*)&win_fmt, win_fmt, cost);
if(min_cost > cost) {
min_cost = cost;
min_fmt = *fmt;
if(!cost)
break;
}
}
if(win)
(void)window_unlock(win);
if(!min_fmt)
return(err_capture(errdst, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__,
"no supported image formats available"));
if(!vdo)
return(0);
zprintf(2, "setting best format %.4s(%08" PRIx32 ") (%d)\n",
(char*)&min_fmt, min_fmt, min_cost);
return(zbar_video_init(vdo, min_fmt));
}
#endif