/*
* tclzbar.c --
*
* This file contains the implementation of the "zbar" Tcl
* command which uses the ZBar library to scan barcodes.
*
* Copyright (c) 2015-2023 Christian Werner <chw@ch-werner.de>
*
* See the file "LICENSE" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
#ifdef ZBAR_NO_TK
/* Partial photo image block */
typedef struct {
int width;
int height;
unsigned char *pixelPtr;
} Tk_PhotoImageBlock;
#else
#include <tk.h>
#endif
#include <tcl.h>
#include <zbar.h>
#include <string.h>
/*
* Symbology mapping.
*/
static const struct {
const char *string;
zbar_symbol_type_t type;
} ZbarSyms[] = {
{ "NONE", ZBAR_NONE },
{ "PARTIAL", ZBAR_PARTIAL },
{ "EAN8", ZBAR_EAN8 },
{ "UPCE", ZBAR_UPCE },
{ "ISBN10", ZBAR_ISBN10 },
{ "UPCA", ZBAR_UPCA },
{ "EAN13", ZBAR_EAN13 },
{ "ISBN13", ZBAR_ISBN13 },
{ "DATABAR", ZBAR_DATABAR },
{ "DATABAR_EXP", ZBAR_DATABAR_EXP},
{ "I25", ZBAR_I25 },
{ "CODABAR", ZBAR_CODABAR },
{ "CODE39", ZBAR_CODE39 },
#ifdef ZBAR_PDF417
{ "PDF417", ZBAR_PDF417 },
#endif
{ "QRCODE", ZBAR_QRCODE },
{ "CODE93", ZBAR_CODE93 },
{ "CODE128", ZBAR_CODE128 }
};
#if !defined(__arm__)
/*
* Color weight tables
* R=0.299, G=0.587, B=0.114
*/
static const int weightR[256] = {
0, 19518, 39036, 58554, 78072, 97590, 117108, 136626,
156144, 175662, 195180, 214698, 234216, 253734, 273252, 292770,
312288, 331806, 351324, 370842, 390360, 409878, 429396, 448914,
468432, 487950, 507468, 526986, 546504, 566022, 585540, 605058,
624576, 644094, 663612, 683130, 702648, 722166, 741684, 761202,
780720, 800238, 819756, 839274, 858792, 878310, 897828, 917346,
936864, 956382, 975900, 995418, 1014936, 1034454, 1053972, 1073490,
1093008, 1112526, 1132044, 1151562, 1171080, 1190598, 1210116, 1229634,
1249152, 1268670, 1288188, 1307706, 1327224, 1346742, 1366260, 1385778,
1405296, 1424814, 1444332, 1463850, 1483368, 1502886, 1522404, 1541922,
1561440, 1580958, 1600476, 1619994, 1639512, 1659030, 1678548, 1698066,
1717584, 1737102, 1756620, 1776138, 1795656, 1815174, 1834692, 1854210,
1873728, 1893246, 1912764, 1932282, 1951800, 1971318, 1990836, 2010354,
2029872, 2049390, 2068908, 2088426, 2107944, 2127462, 2146980, 2166498,
2186016, 2205534, 2225052, 2244570, 2264088, 2283606, 2303124, 2322642,
2342160, 2361678, 2381196, 2400714, 2420232, 2439750, 2459268, 2478786,
2498304, 2517822, 2537340, 2556858, 2576376, 2595894, 2615412, 2634930,
2654448, 2673966, 2693484, 2713002, 2732520, 2752038, 2771556, 2791074,
2810592, 2830110, 2849628, 2869146, 2888664, 2908182, 2927700, 2947218,
2966736, 2986254, 3005772, 3025290, 3044808, 3064326, 3083844, 3103362,
3122880, 3142398, 3161916, 3181434, 3200952, 3220470, 3239988, 3259506,
3279024, 3298542, 3318060, 3337578, 3357096, 3376614, 3396132, 3415650,
3435168, 3454686, 3474204, 3493722, 3513240, 3532758, 3552276, 3571794,
3591312, 3610830, 3630348, 3649866, 3669384, 3688902, 3708420, 3727938,
3747456, 3766974, 3786492, 3806010, 3825528, 3845046, 3864564, 3884082,
3903600, 3923118, 3942636, 3962154, 3981672, 4001190, 4020708, 4040226,
4059744, 4079262, 4098780, 4118298, 4137816, 4157334, 4176852, 4196370,
4215888, 4235406, 4254924, 4274442, 4293960, 4313478, 4332996, 4352514,
4372032, 4391550, 4411068, 4430586, 4450104, 4469622, 4489140, 4508658,
4528176, 4547694, 4567212, 4586730, 4606248, 4625766, 4645284, 4664802,
4684320, 4703838, 4723356, 4742874, 4762392, 4781910, 4801428, 4820946,
4840464, 4859982, 4879500, 4899018, 4918536, 4938054, 4957572, 4977090
};
static const int weightG[256] = {
0, 38319, 76638, 114957, 153276, 191595, 229914, 268233,
306552, 344871, 383190, 421509, 459828, 498147, 536466, 574785,
613104, 651423, 689742, 728061, 766380, 804699, 843018, 881337,
919656, 957975, 996294, 1034613, 1072932, 1111251, 1149570, 1187889,
1226208, 1264527, 1302846, 1341165, 1379484, 1417803, 1456122, 1494441,
1532760, 1571079, 1609398, 1647717, 1686036, 1724355, 1762674, 1800993,
1839312, 1877631, 1915950, 1954269, 1992588, 2030907, 2069226, 2107545,
2145864, 2184183, 2222502, 2260821, 2299140, 2337459, 2375778, 2414097,
2452416, 2490735, 2529054, 2567373, 2605692, 2644011, 2682330, 2720649,
2758968, 2797287, 2835606, 2873925, 2912244, 2950563, 2988882, 3027201,
3065520, 3103839, 3142158, 3180477, 3218796, 3257115, 3295434, 3333753,
3372072, 3410391, 3448710, 3487029, 3525348, 3563667, 3601986, 3640305,
3678624, 3716943, 3755262, 3793581, 3831900, 3870219, 3908538, 3946857,
3985176, 4023495, 4061814, 4100133, 4138452, 4176771, 4215090, 4253409,
4291728, 4330047, 4368366, 4406685, 4445004, 4483323, 4521642, 4559961,
4598280, 4636599, 4674918, 4713237, 4751556, 4789875, 4828194, 4866513,
4904832, 4943151, 4981470, 5019789, 5058108, 5096427, 5134746, 5173065,
5211384, 5249703, 5288022, 5326341, 5364660, 5402979, 5441298, 5479617,
5517936, 5556255, 5594574, 5632893, 5671212, 5709531, 5747850, 5786169,
5824488, 5862807, 5901126, 5939445, 5977764, 6016083, 6054402, 6092721,
6131040, 6169359, 6207678, 6245997, 6284316, 6322635, 6360954, 6399273,
6437592, 6475911, 6514230, 6552549, 6590868, 6629187, 6667506, 6705825,
6744144, 6782463, 6820782, 6859101, 6897420, 6935739, 6974058, 7012377,
7050696, 7089015, 7127334, 7165653, 7203972, 7242291, 7280610, 7318929,
7357248, 7395567, 7433886, 7472205, 7510524, 7548843, 7587162, 7625481,
7663800, 7702119, 7740438, 7778757, 7817076, 7855395, 7893714, 7932033,
7970352, 8008671, 8046990, 8085309, 8123628, 8161947, 8200266, 8238585,
8276904, 8315223, 8353542, 8391861, 8430180, 8468499, 8506818, 8545137,
8583456, 8621775, 8660094, 8698413, 8736732, 8775051, 8813370, 8851689,
8890008, 8928327, 8966646, 9004965, 9043284, 9081603, 9119922, 9158241,
9196560, 9234879, 9273198, 9311517, 9349836, 9388155, 9426474, 9464793,
9503112, 9541431, 9579750, 9618069, 9656388, 9694707, 9733026, 9771345
};
static const int weightB[256] = {
0, 7442, 14884, 22326, 29768, 37210, 44652, 52094,
59536, 66978, 74420, 81862, 89304, 96746, 104188, 111630,
119072, 126514, 133956, 141398, 148840, 156282, 163724, 171166,
178608, 186050, 193492, 200934, 208376, 215818, 223260, 230702,
238144, 245586, 253028, 260470, 267912, 275354, 282796, 290238,
297680, 305122, 312564, 320006, 327448, 334890, 342332, 349774,
357216, 364658, 372100, 379542, 386984, 394426, 401868, 409310,
416752, 424194, 431636, 439078, 446520, 453962, 461404, 468846,
476288, 483730, 491172, 498614, 506056, 513498, 520940, 528382,
535824, 543266, 550708, 558150, 565592, 573034, 580476, 587918,
595360, 602802, 610244, 617686, 625128, 632570, 640012, 647454,
654896, 662338, 669780, 677222, 684664, 692106, 699548, 706990,
714432, 721874, 729316, 736758, 744200, 751642, 759084, 766526,
773968, 781410, 788852, 796294, 803736, 811178, 818620, 826062,
833504, 840946, 848388, 855830, 863272, 870714, 878156, 885598,
893040, 900482, 907924, 915366, 922808, 930250, 937692, 945134,
952576, 960018, 967460, 974902, 982344, 989786, 997228, 1004670,
1012112, 1019554, 1026996, 1034438, 1041880, 1049322, 1056764, 1064206,
1071648, 1079090, 1086532, 1093974, 1101416, 1108858, 1116300, 1123742,
1131184, 1138626, 1146068, 1153510, 1160952, 1168394, 1175836, 1183278,
1190720, 1198162, 1205604, 1213046, 1220488, 1227930, 1235372, 1242814,
1250256, 1257698, 1265140, 1272582, 1280024, 1287466, 1294908, 1302350,
1309792, 1317234, 1324676, 1332118, 1339560, 1347002, 1354444, 1361886,
1369328, 1376770, 1384212, 1391654, 1399096, 1406538, 1413980, 1421422,
1428864, 1436306, 1443748, 1451190, 1458632, 1466074, 1473516, 1480958,
1488400, 1495842, 1503284, 1510726, 1518168, 1525610, 1533052, 1540494,
1547936, 1555378, 1562820, 1570262, 1577704, 1585146, 1592588, 1600030,
1607472, 1614914, 1622356, 1629798, 1637240, 1644682, 1652124, 1659566,
1667008, 1674450, 1681892, 1689334, 1696776, 1704218, 1711660, 1719102,
1726544, 1733986, 1741428, 1748870, 1756312, 1763754, 1771196, 1778638,
1786080, 1793522, 1800964, 1808406, 1815848, 1823290, 1830732, 1838174,
1845616, 1853058, 1860500, 1867942, 1875384, 1882826, 1890268, 1897710
};
#endif /* !defined(__arm__) */
/*
*-------------------------------------------------------------------------
*
* ZbarGetSymbolObj --
*
* Return string object given symbol type or list of
* all known symbol types.
*
*-------------------------------------------------------------------------
*/
static Tcl_Obj *
ZbarGetSymbolObj(zbar_symbol_type_t t, int all)
{
Tcl_Obj *result;
int i;
if (all) {
result = Tcl_NewListObj(0, NULL);
for (i = 0; i < sizeof(ZbarSyms) / sizeof(ZbarSyms[0]); i++) {
Tcl_ListObjAppendElement(NULL, result,
Tcl_NewStringObj(ZbarSyms[i].string, -1));
}
} else {
result = NULL;
for (i = 0; i < sizeof(ZbarSyms) / sizeof(ZbarSyms[0]); i++) {
if (t == ZbarSyms[i].type) {
result = Tcl_NewStringObj(ZbarSyms[i].string, -1);
break;
}
}
if (result == NULL) {
result = Tcl_NewObj();
}
}
return result;
}
/*
*-------------------------------------------------------------------------
*
* ZbarGetSymbolType --
*
* Return zbar_symbol_type_t given string object.
*
*-------------------------------------------------------------------------
*/
static zbar_symbol_type_t
ZbarGetSymbolType(Tcl_Obj *obj, int *idxPtr)
{
char *string = Tcl_GetString(obj);
int i;
for (i = 0; i < sizeof(ZbarSyms) / sizeof(ZbarSyms[0]); i++) {
if (strcmp(string, ZbarSyms[i].string) == 0) {
if (idxPtr != NULL) {
*idxPtr = i;
}
return ZbarSyms[i].type;
}
}
return ZBAR_SYMBOL; /* no match */
}
#ifdef TCL_THREADS
/*
* NB: as of Tcl 8.6.5 asynchronous event handlers (Tcl_AsyncCreate())
* are unreliable since they can break the Tcl catch command.
* If USE_ASYNC_HANDLER is undefined, Tcl_ThreadQueueEvent() is used
* instead and runtime behaviour should be almost identical.
*/
#undef USE_ASYNC_HANDLER
/*
* Structure used for asynchronous decoding carried out by
* a dedicated thread. At most, one decoding job can be
* outstanding at any one time.
*/
typedef struct {
int tip609; /* When true, TIP#609 is available. */
int run; /* Controls thread loop */
Tcl_Mutex mutex; /* Lock for this struct */
Tcl_Condition cond; /* For waking up thread */
Tcl_ThreadId tid; /* Thread identifier */
Tcl_Interp *interp; /* Interpreter using Zbar decoder */
#ifdef USE_ASYNC_HANDLER
Tcl_AsyncHandler async; /* For signalling result */
#else
Tcl_ThreadId interpTid; /* Thread identifier of interp */
#endif
unsigned char *pixPtr; /* Thread input: pixel array or NULL */
zbar_image_t *imgPtr; /* Thread input: ZBar image struct */
int nCmdObjs; /* Callback, number Tcl_Objs */
Tcl_Obj **cmdObjs; /* Callback, nCmdObjs plus 3 for result */
/* Thread input: enabled symbologies */
unsigned char enabled[sizeof(ZbarSyms) / sizeof(ZbarSyms[0])];
/* Thread output: ms, symbol type, data */
int ms;
zbar_symbol_type_t sym;
unsigned char *dataPtr;
int dataLength;
} AsyncDecode;
#ifndef USE_ASYNC_HANDLER
/*
* Event type for the event queue.
*/
typedef struct {
Tcl_Event header;
AsyncDecode *aPtr;
} AsyncEvent;
static int ZbarDecodeHandleEvent(Tcl_Event *evPtr, int flags);
#endif
/*
*-------------------------------------------------------------------------
*
* ZBarThread --
*
* Decoder thread, waits per condition for a decode request.
* Reports the result back by an asynchronous event which
* triggers a do-when-idle handler in the requesting thread.
*
*-------------------------------------------------------------------------
*/
static Tcl_ThreadCreateType
ZbarThread(ClientData clientData)
{
AsyncDecode *aPtr = (AsyncDecode *) clientData;
zbar_image_scanner_t *scanPtr;
unsigned char enabled[sizeof(ZbarSyms) / sizeof(ZbarSyms[0])];
Tcl_Time now;
int i, ms;
Tcl_WideInt tw[2];
#ifndef USE_ASYNC_HANDLER
AsyncEvent *event;
#endif
Tcl_MutexLock(&aPtr->mutex);
for (;;) {
while (aPtr->run && (aPtr->imgPtr == NULL)) {
Tcl_ConditionWait(&aPtr->cond, &aPtr->mutex, NULL);
}
if (!aPtr->run) {
break;
}
if (aPtr->imgPtr == NULL) {
continue;
}
memcpy(enabled, aPtr->enabled, sizeof(enabled));
Tcl_MutexUnlock(&aPtr->mutex);
Tcl_GetTime(&now);
tw[0] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000;
scanPtr = zbar_image_scanner_create();
if (scanPtr == NULL) {
Tcl_GetTime(&now);
tw[1] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000;
ms = tw[1] - tw[0];
if (ms < 0) {
ms = -1;
}
Tcl_MutexLock(&aPtr->mutex);
zbar_image_destroy(aPtr->imgPtr);
aPtr->imgPtr = NULL;
ckfree((char *) aPtr->pixPtr);
aPtr->pixPtr = NULL;
if (aPtr->cmdObjs != NULL) {
aPtr->ms = ms;
aPtr->dataPtr = NULL;
aPtr->dataLength = 0;
#ifdef USE_ASYNC_HANDLER
Tcl_AsyncMark(aPtr->async);
#else
event = (AsyncEvent *) ckalloc(sizeof(AsyncEvent));
event->header.proc = ZbarDecodeHandleEvent;
event->header.nextPtr = NULL;
event->aPtr = aPtr;
if (aPtr->tip609) {
/* TCL_QUEUE_TAIL_ALERT_IF_EMPTY */
Tcl_ThreadQueueEvent(aPtr->interpTid, &event->header,
TCL_QUEUE_TAIL | 4);
} else {
Tcl_ThreadQueueEvent(aPtr->interpTid, &event->header,
TCL_QUEUE_TAIL);
Tcl_ThreadAlert(aPtr->interpTid);
}
#endif
}
continue;
}
zbar_image_scanner_set_config(scanPtr, ZBAR_NONE, ZBAR_CFG_ENABLE, 0);
for (i = 0; i < sizeof(enabled); i++) {
if (enabled[i]) {
zbar_symbol_type_t t = ZbarSyms[i].type;
zbar_image_scanner_set_config(scanPtr, t, ZBAR_CFG_ENABLE, 1);
}
}
zbar_scan_image(scanPtr, aPtr->imgPtr);
Tcl_GetTime(&now);
tw[1] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000;
ms = tw[1] - tw[0];
if (ms < 0) {
ms = -1;
}
Tcl_MutexLock(&aPtr->mutex);
if (aPtr->cmdObjs != NULL) {
const zbar_symbol_t *symPtr;
aPtr->ms = ms;
aPtr->dataPtr = NULL;
aPtr->dataLength = 0;
symPtr = zbar_image_first_symbol(aPtr->imgPtr);
if (symPtr != NULL) {
zbar_symbol_type_t t = zbar_symbol_get_type(symPtr);
const unsigned char *dp = (const unsigned char *)
zbar_symbol_get_data(symPtr);
aPtr->sym = t;
aPtr->dataLength = zbar_symbol_get_data_length(symPtr);
aPtr->dataPtr =
(unsigned char *) attemptckalloc(aPtr->dataLength);
if (aPtr->dataPtr != NULL) {
memcpy(aPtr->dataPtr, dp, aPtr->dataLength);
}
}
#ifdef USE_ASYNC_HANDLER
Tcl_AsyncMark(aPtr->async);
#else
event = (AsyncEvent *) ckalloc(sizeof(AsyncEvent));
event->header.proc = ZbarDecodeHandleEvent;
event->header.nextPtr = NULL;
event->aPtr = aPtr;
if (aPtr->tip609) {
/* TCL_QUEUE_TAIL_ALERT_IF_EMPTY */
Tcl_ThreadQueueEvent(aPtr->interpTid, &event->header,
TCL_QUEUE_TAIL | 4);
} else {
Tcl_ThreadQueueEvent(aPtr->interpTid, &event->header,
TCL_QUEUE_TAIL);
Tcl_ThreadAlert(aPtr->interpTid);
}
#endif
}
zbar_image_scanner_destroy(scanPtr);
zbar_image_destroy(aPtr->imgPtr);
aPtr->imgPtr = NULL;
ckfree((char *) aPtr->pixPtr);
aPtr->pixPtr = NULL;
}
if (aPtr->imgPtr) {
zbar_image_destroy(aPtr->imgPtr);
aPtr->imgPtr = NULL;
}
if (aPtr->pixPtr != NULL) {
ckfree((char *) aPtr->pixPtr);
aPtr->pixPtr = NULL;
}
Tcl_MutexUnlock(&aPtr->mutex);
Tcl_ExitThread(0);
TCL_THREAD_CREATE_RETURN;
}
/*
*-------------------------------------------------------------------------
*
* ZbarDecodeDone --
*
* Process asynchronous result callback.
* Function executes as a do-when-idle handler.
*
*-------------------------------------------------------------------------
*/
#ifdef USE_ASYNC_HANDLER
static void
ZbarDecodeDone(ClientData clientData)
#else
static int
ZbarDecodeHandleEvent(Tcl_Event *evPtr, int flags)
#endif
{
#ifdef USE_ASYNC_HANDLER
AsyncDecode *aPtr = (AsyncDecode *) clientData;
#else
AsyncEvent *aevPtr = (AsyncEvent *) evPtr;
AsyncDecode *aPtr = aevPtr->aPtr;
#endif
int ret, i, ms, dataLength, nCmdObjs = 0;
unsigned char *dataPtr;
zbar_symbol_type_t sym;
Tcl_Obj **cmdObjs;
#ifndef USE_ASYNC_HANDLER
if (aPtr->interpTid == NULL) {
return 1;
}
#endif
Tcl_Preserve(aPtr);
Tcl_Preserve(aPtr->interp);
Tcl_MutexLock(&aPtr->mutex);
cmdObjs = aPtr->cmdObjs;
if (cmdObjs != NULL) {
nCmdObjs = aPtr->nCmdObjs;
}
aPtr->nCmdObjs = 0;
aPtr->cmdObjs = NULL;
ms = aPtr->ms;
sym = aPtr->sym;
dataPtr = aPtr->dataPtr;
dataLength = aPtr->dataLength;
aPtr->dataPtr = NULL;
aPtr->dataLength = 0;
Tcl_MutexUnlock(&aPtr->mutex);
if (cmdObjs != NULL) {
cmdObjs[nCmdObjs] = Tcl_NewIntObj(ms);
if (dataPtr != NULL) {
cmdObjs[nCmdObjs + 1] = ZbarGetSymbolObj(sym, 0);
cmdObjs[nCmdObjs + 2] = Tcl_NewByteArrayObj(dataPtr, dataLength);
ckfree((char *) dataPtr);
} else {
cmdObjs[nCmdObjs + 1] = Tcl_NewObj();
cmdObjs[nCmdObjs + 2] = Tcl_NewObj();
}
Tcl_IncrRefCount(cmdObjs[nCmdObjs]);
Tcl_IncrRefCount(cmdObjs[nCmdObjs + 1]);
Tcl_IncrRefCount(cmdObjs[nCmdObjs + 2]);
ret = Tcl_EvalObjv(aPtr->interp, nCmdObjs + 3, cmdObjs,
TCL_GLOBAL_ONLY);
for (i = 0; i < nCmdObjs + 3; i++) {
if (cmdObjs[i] != NULL) {
Tcl_DecrRefCount(cmdObjs[i]);
cmdObjs[i] = NULL;
}
}
ckfree((char *) cmdObjs);
if (ret == TCL_ERROR) {
Tcl_AddErrorInfo(aPtr->interp, "\n (zbar event handler)");
Tcl_BackgroundException(aPtr->interp, ret);
}
} else if (dataPtr != NULL) {
ckfree((char *) dataPtr);
}
Tcl_Release(aPtr->interp);
Tcl_Release(aPtr);
#ifndef USE_ASYNC_HANDLER
return 1; /* event handled */
#endif
}
#ifdef USE_ASYNC_HANDLER
/*
*-------------------------------------------------------------------------
*
* ZbarDecodeHandler --
*
* Function triggered by asynchronous event from decoder thread
* dispatching do-when-idle handler to invoke Tcl callback.
*
*-------------------------------------------------------------------------
*/
static int
ZbarDecodeHandler(ClientData clientData, Tcl_Interp *interp, int code)
{
Tcl_DoWhenIdle(ZbarDecodeDone, clientData);
return code;
}
#endif
/*
*-------------------------------------------------------------------------
*
* ZbarAsyncStop --
*
* Stop the decoder thread, if any.
*
*-------------------------------------------------------------------------
*/
static int
ZbarAsyncStop(Tcl_Interp *interp, AsyncDecode *aPtr)
{
int i;
#ifdef USE_ASYNC_HANDLER
Tcl_CancelIdleCall(ZbarDecodeDone, (ClientData) aPtr);
#endif
Tcl_MutexLock(&aPtr->mutex);
if (aPtr->run) {
int dummy;
aPtr->run = 0;
Tcl_ConditionNotify(&aPtr->cond);
Tcl_MutexUnlock(&aPtr->mutex);
Tcl_JoinThread(aPtr->tid, &dummy);
aPtr->tid = NULL;
Tcl_MutexLock(&aPtr->mutex);
}
#ifdef USE_ASYNC_HANDLER
if (aPtr->async != NULL) {
Tcl_AsyncDelete(aPtr->async);
aPtr->async = NULL;
}
#else
aPtr->interpTid = NULL;
#endif
Tcl_MutexUnlock(&aPtr->mutex);
if (aPtr->cmdObjs != NULL) {
for (i = 0; i < aPtr->nCmdObjs + 3; i++) {
if (aPtr->cmdObjs[i] != NULL) {
Tcl_DecrRefCount(aPtr->cmdObjs[i]);
aPtr->cmdObjs[i] = NULL;
}
}
ckfree((char *) aPtr->cmdObjs);
}
aPtr->nCmdObjs = 0;
aPtr->cmdObjs = NULL;
if (aPtr->dataPtr != NULL) {
ckfree((char *) aPtr->dataPtr);
aPtr->dataPtr = NULL;
aPtr->dataLength = 0;
}
return TCL_OK;
}
/*
*-------------------------------------------------------------------------
*
* ZbarAsyncState --
*
* Return status of decoder thread, if any.
*
*-------------------------------------------------------------------------
*/
static int
ZbarAsyncStatus(Tcl_Interp *interp, AsyncDecode *aPtr)
{
int state;
char *stateString;
Tcl_MutexLock(&aPtr->mutex);
if (!aPtr->run) {
state = 0;
} else if ((aPtr->imgPtr != NULL) ||
(aPtr->pixPtr != NULL) ||
(aPtr->cmdObjs != NULL)) {
state = 2;
} else {
state = 1;
}
Tcl_MutexUnlock(&aPtr->mutex);
switch (state) {
case 1:
stateString = "ready";
break;
case 2:
stateString = "running";
break;
default:
stateString = "stopped";
break;
}
Tcl_SetResult(interp, stateString, TCL_STATIC);
return TCL_OK;
}
/*
*-------------------------------------------------------------------------
*
* ZbarAsyncStart --
*
* Check/start the decoder thread. Error cases:
* - thread creation failed, could not be started
* - thread is already started but still processing a request
*
*-------------------------------------------------------------------------
*/
static int
ZbarAsyncStart(Tcl_Interp *interp, AsyncDecode *aPtr)
{
int success = 0;
Tcl_MutexLock(&aPtr->mutex);
if (!aPtr->run) {
if (Tcl_CreateThread(&aPtr->tid, ZbarThread, (ClientData) aPtr,
TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE)
== TCL_OK) {
aPtr->interp = interp;
#ifdef USE_ASYNC_HANDLER
aPtr->async = Tcl_AsyncCreate(ZbarDecodeHandler, (ClientData) aPtr);
#else
aPtr->interpTid = Tcl_GetCurrentThread();
#endif
aPtr->run = success = 1;
}
} else if ((aPtr->imgPtr != NULL) ||
(aPtr->pixPtr != NULL) ||
(aPtr->cmdObjs != NULL)) {
success = -1;
} else {
success = 1;
}
Tcl_MutexUnlock(&aPtr->mutex);
if (success < 0) {
Tcl_SetResult(interp, "decode process still running", TCL_STATIC);
return TCL_ERROR;
}
if (success == 0) {
Tcl_SetResult(interp, "decode process not started", TCL_STATIC);
return TCL_ERROR;
}
return TCL_OK;
}
/*
*-------------------------------------------------------------------------
*
* ZbarAsyncFree --
*
* Free zbar::async_decode command client data structure.
*
*-------------------------------------------------------------------------
*/
static void
ZbarAsyncFree(char *clientData)
{
AsyncDecode *aPtr = (AsyncDecode *) clientData;
ZbarAsyncStop(aPtr->interp, aPtr);
Tcl_ConditionFinalize(&aPtr->cond);
Tcl_MutexFinalize(&aPtr->mutex);
ckfree((char *) aPtr);
}
/*
*-------------------------------------------------------------------------
*
* ZbarAsyncCmdDeleted --
*
* Callback for deletion of zbar::async_decode command.
*
*-------------------------------------------------------------------------
*/
static void
ZbarAsyncCmdDeleted(ClientData clientData)
{
Tcl_EventuallyFree(clientData, ZbarAsyncFree);
}
/*
*-------------------------------------------------------------------------
*
* ZbarAsyncDeocdeObjCmd --
*
* zbar::async_decode Tcl command, asynchronous decoding.
* Command formats/arguments are
*
* Stop (finish) decoder thread, releasing resources
*
* zbar::async_decode stop
*
* Return status of asynchronous decoding process
*
* zbar::async_decode status
*
* Start decoding an image
*
* zbar::async_decode photoEtc callback ?syms?
*
* photoEtc photo image name or list of
* {width height bpp bytes}
* callback procedure to invoke at end of
* decoding process
* syms list of enabled symbologies
*
* Arguments appended to callback
*
* time decode/processing time in milliseconds
* type decoded symbol type for first found symbol or empty
* data decoded symbol data for first found symbol or empty
*
*-------------------------------------------------------------------------
*/
static int
ZbarAsyncDecodeObjCmd(ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
AsyncDecode *aPtr = (AsyncDecode *) clientData;
Tk_PhotoImageBlock block;
int bpp, x, y, nElems, nCmdObjs;
unsigned char *pixPtr;
zbar_image_t *imgPtr;
Tcl_Obj **elems, **cmdObjs;
unsigned char enabled[sizeof(ZbarSyms) / sizeof(ZbarSyms[0])];
if ((objc < 2) || (objc > 4)) {
Tcl_WrongNumArgs(interp, 1, objv, "stop|photoEtc ?callback? ?syms?");
return TCL_ERROR;
}
if (objc == 2) {
const char *cmd = Tcl_GetString(objv[1]);
if (strcmp(cmd, "status") == 0) {
return ZbarAsyncStatus(interp, aPtr);
}
if (strcmp(cmd, "stop") == 0) {
return ZbarAsyncStop(interp, aPtr);
}
Tcl_WrongNumArgs(interp, 1, objv, "status|stop");
return TCL_ERROR;
}
if (Tcl_ListObjGetElements(interp, objv[1], &nElems, &elems) != TCL_OK) {
return TCL_ERROR;
}
if (nElems < 1) {
Tcl_SetResult(interp, "need photo image or list", TCL_STATIC);
return TCL_ERROR;
} else if (nElems >= 4) {
int size, length;
if ((Tcl_GetIntFromObj(interp, elems[0], &block.width) != TCL_OK) ||
(Tcl_GetIntFromObj(interp, elems[1], &block.height) != TCL_OK) ||
(Tcl_GetIntFromObj(interp, elems[2], &bpp) != TCL_OK)) {
return TCL_ERROR;
}
if ((bpp != 1) || (bpp != 3)) {
Tcl_SetResult(interp, "unsupported image depth", TCL_STATIC);
return TCL_ERROR;
}
size = block.width * block.height;
if (size <= 0) {
Tcl_SetResult(interp, "invalid image size", TCL_STATIC);
return TCL_ERROR;
}
block.pixelPtr = Tcl_GetByteArrayFromObj(elems[3], &length);
if ((block.pixelPtr == NULL) || (length < size)) {
Tcl_SetResult(interp, "malformed image", TCL_STATIC);
return TCL_ERROR;
}
pixPtr = (unsigned char *) attemptckalloc(block.width * block.height);
if (pixPtr == NULL) {
Tcl_SetResult(interp, "out of memory", TCL_STATIC);
return TCL_ERROR;
}
if (bpp == 3) {
for (y = 0; y < block.height; y++) {
unsigned char *srcPtr, *dstPtr;
int tmp;
srcPtr = block.pixelPtr + y * block.width * 3;
dstPtr = pixPtr + y * block.width;
for (x = 0; x < block.width; x++) {
#if !defined(__arm__)
tmp = weightR[*srcPtr++];
tmp += weightG[*srcPtr++];
tmp += weightB[*srcPtr++];
#else
tmp = 19518 * *srcPtr++;
tmp += 38319 * *srcPtr++;
tmp += 7442 * *srcPtr++;
#endif
*dstPtr++ = tmp >> 16;
}
}
} else {
memcpy(pixPtr, block.pixelPtr, block.width * block.height);
}
} else {
#ifdef ZBAR_NO_TK
Tcl_SetResult(interp, "need list of width, height, bpp, bytes",
TCL_STATIC);
return TCL_ERROR;
#else
Tk_PhotoHandle handle;
handle = Tk_FindPhoto(interp, Tcl_GetString(objv[1]));
if (handle == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("photo \"%s\" not found",
Tcl_GetString(objv[1])));
return TCL_ERROR;
}
if (Tk_PhotoGetImage(handle, &block) != 1) {
Tcl_SetResult(interp, "error retrieving photo image", TCL_STATIC);
return TCL_ERROR;
}
if ((block.offset[0] == block.offset[1]) &&
(block.offset[0] == block.offset[2])) {
bpp = 1;
} else {
bpp = 3;
}
pixPtr = (unsigned char *) attemptckalloc(block.width * block.height);
if (pixPtr == NULL) {
Tcl_SetResult(interp, "out of memory", TCL_STATIC);
return TCL_ERROR;
}
for (y = 0; y < block.height; y++) {
unsigned char *srcPtr, *dstPtr;
int tmp;
srcPtr = block.pixelPtr + y * block.pitch;
dstPtr = pixPtr + y * block.width;
for (x = 0; x < block.width; x++) {
if (bpp == 1) {
dstPtr[0] = srcPtr[block.offset[0]];
} else {
#if !defined(__arm__)
tmp = weightR[srcPtr[block.offset[0]]];
tmp += weightG[srcPtr[block.offset[1]]];
tmp += weightB[srcPtr[block.offset[2]]];
#else
tmp = 19518 * srcPtr[block.offset[0]];
tmp += 38319 * srcPtr[block.offset[1]];
tmp += 7442 * srcPtr[block.offset[2]];
#endif
dstPtr[0] = tmp >> 16;
}
dstPtr += 1;
srcPtr += block.pixelSize;
}
}
#endif
}
if (ZbarAsyncStart(interp, aPtr) != TCL_OK) {
ckfree((char *) pixPtr);
return TCL_ERROR;
}
imgPtr = zbar_image_create();
if (imgPtr == NULL) {
ckfree((char *) pixPtr);
Tcl_SetResult(interp, "error creating image", TCL_STATIC);
return TCL_ERROR;
}
zbar_image_set_format(imgPtr, zbar_fourcc('Y', '8', '0', '0'));
zbar_image_set_size(imgPtr, block.width, block.height);
zbar_image_set_data(imgPtr, pixPtr, block.width * block.height, NULL);
if (Tcl_ListObjGetElements(interp, objv[2], &nCmdObjs, &cmdObjs)
!= TCL_OK) {
zbar_image_destroy(imgPtr);
ckfree((char *) pixPtr);
return TCL_ERROR;
}
if (nCmdObjs <= 0) {
Tcl_SetResult(interp, "empty callback", TCL_STATIC);
zbar_image_destroy(imgPtr);
ckfree((char *) pixPtr);
return TCL_ERROR;
}
memset(enabled, 1, sizeof(enabled));
if (objc > 3) {
if (Tcl_ListObjGetElements(interp, objv[3], &nElems, &elems)
!= TCL_OK) {
zbar_image_destroy(imgPtr);
ckfree((char *) pixPtr);
return TCL_ERROR;
}
if (nElems > 0) {
memset(enabled, 0, sizeof(enabled));
for (x = 0; x < nElems; x++) {
zbar_symbol_type_t t = ZbarGetSymbolType(elems[x], &y);
if (t != ZBAR_SYMBOL) {
enabled[y] = 1;
}
}
}
}
Tcl_MutexLock(&aPtr->mutex);
aPtr->pixPtr = pixPtr;
aPtr->imgPtr = imgPtr;
aPtr->nCmdObjs = nCmdObjs;
aPtr->cmdObjs =
(Tcl_Obj **) ckalloc((nCmdObjs + 3) * sizeof(Tcl_Obj *));
for (x = 0; x < nCmdObjs; x++) {
aPtr->cmdObjs[x] = cmdObjs[x];
Tcl_IncrRefCount(aPtr->cmdObjs[x]);
}
aPtr->cmdObjs[x++] = NULL;
aPtr->cmdObjs[x++] = NULL;
aPtr->cmdObjs[x++] = NULL;
memcpy(aPtr->enabled, enabled, sizeof(aPtr->enabled));
Tcl_ConditionNotify(&aPtr->cond);
Tcl_MutexUnlock(&aPtr->mutex);
return TCL_OK;
}
#endif /* TCL_THREADS */
/*
*-------------------------------------------------------------------------
*
* ZbarDecodeObjCmd --
*
* zbar::decode Tcl command, synchronous decoding.
* Command arguments are
*
* zbar::decode photoEtc ?syms?
*
* photoEtc photo image name or list of
* {width height bpp bytes}
* syms list of enabled symbologies
*
* Result is a list with three element
*
* time decode/processing time in milliseconds
* type decoded symbol type for first found symbol or empty
* data decoded symbol data for first found symbol or empty
*
*-------------------------------------------------------------------------
*/
static int
ZbarDecodeObjCmd(ClientData unused, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
Tk_PhotoImageBlock block;
int bpp, x, y, nElems;
unsigned char *pixPtr = NULL;
zbar_image_scanner_t *scanPtr;
zbar_image_t *imgPtr;
const zbar_symbol_t *symPtr;
Tcl_Time now;
Tcl_WideInt tw[2];
Tcl_Obj **elems, *list[3];
if ((objc < 2) || (objc > 3)) {
Tcl_WrongNumArgs(interp, 1, objv, "photoEtc ?syms?");
return TCL_ERROR;
}
Tcl_GetTime(&now);
tw[0] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000;
if (Tcl_ListObjGetElements(interp, objv[1], &nElems, &elems) != TCL_OK) {
return TCL_ERROR;
}
if (nElems < 1) {
Tcl_SetResult(interp, "need photo image or list", TCL_STATIC);
return TCL_ERROR;
} else if (nElems >= 4) {
int size, length;
if ((Tcl_GetIntFromObj(interp, elems[0], &block.width) != TCL_OK) ||
(Tcl_GetIntFromObj(interp, elems[1], &block.height) != TCL_OK) ||
(Tcl_GetIntFromObj(interp, elems[2], &bpp) != TCL_OK)) {
return TCL_ERROR;
}
if ((bpp != 1) || (bpp != 3)) {
Tcl_SetResult(interp, "unsupported image depth", TCL_STATIC);
return TCL_ERROR;
}
size = block.width * block.height;
if (size <= 0) {
Tcl_SetResult(interp, "invalid image size", TCL_STATIC);
return TCL_ERROR;
}
block.pixelPtr = Tcl_GetByteArrayFromObj(elems[3], &length);
if ((block.pixelPtr == NULL) || (length < size)) {
Tcl_SetResult(interp, "malformed image", TCL_STATIC);
return TCL_ERROR;
}
if (bpp == 3) {
pixPtr = (unsigned char *)
attemptckalloc(block.width * block.height);
if (pixPtr == NULL) {
Tcl_SetResult(interp, "out of memory", TCL_STATIC);
return TCL_ERROR;
}
for (y = 0; y < block.height; y++) {
unsigned char *srcPtr, *dstPtr;
int tmp;
srcPtr = block.pixelPtr + y * block.width * 3;
dstPtr = pixPtr + y * block.width;
for (x = 0; x < block.width; x++) {
#if !defined(__arm__)
tmp = weightR[*srcPtr++];
tmp += weightG[*srcPtr++];
tmp += weightB[*srcPtr++];
#else
tmp = 19518 * *srcPtr++;
tmp += 38319 * *srcPtr++;
tmp += 7442 * *srcPtr++;
#endif
*dstPtr++ = tmp >> 16;
}
}
}
} else {
#ifdef ZBAR_NO_TK
Tcl_SetResult(interp, "need list of width, height, bpp, bytes",
TCL_STATIC);
return TCL_ERROR;
#else
Tk_PhotoHandle handle;
handle = Tk_FindPhoto(interp, Tcl_GetString(objv[1]));
if (handle == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("photo \"%s\" not found",
Tcl_GetString(objv[1])));
return TCL_ERROR;
}
if (Tk_PhotoGetImage(handle, &block) != 1) {
Tcl_SetResult(interp, "error retrieving photo image", TCL_STATIC);
return TCL_ERROR;
}
if ((block.offset[0] == block.offset[1]) &&
(block.offset[0] == block.offset[2])) {
bpp = 1;
} else {
bpp = 3;
}
pixPtr = (unsigned char *) attemptckalloc(block.width * block.height);
if (pixPtr == NULL) {
Tcl_SetResult(interp, "out of memory", TCL_STATIC);
return TCL_ERROR;
}
for (y = 0; y < block.height; y++) {
unsigned char *srcPtr, *dstPtr;
int tmp;
srcPtr = block.pixelPtr + y * block.pitch;
dstPtr = pixPtr + y * block.width;
for (x = 0; x < block.width; x++) {
if (bpp == 1) {
dstPtr[0] = srcPtr[block.offset[0]];
} else {
#if !defined(__arm__)
tmp = weightR[srcPtr[block.offset[0]]];
tmp += weightG[srcPtr[block.offset[1]]];
tmp += weightB[srcPtr[block.offset[2]]];
#else
tmp = 19518 * srcPtr[block.offset[0]];
tmp += 38319 * srcPtr[block.offset[1]];
tmp += 7442 * srcPtr[block.offset[2]];
#endif
dstPtr[0] = tmp >> 16;
}
dstPtr += 1;
srcPtr += block.pixelSize;
}
}
#endif
}
scanPtr = zbar_image_scanner_create();
if (scanPtr == NULL) {
if (pixPtr != NULL) {
ckfree((char *) pixPtr);
}
Tcl_SetResult(interp, "error creating image scanner", TCL_STATIC);
return TCL_ERROR;
}
if (objc > 2) {
if (Tcl_ListObjGetElements(interp, objv[2], &nElems, &elems)
!= TCL_OK) {
zbar_image_scanner_destroy(scanPtr);
if (pixPtr != NULL) {
ckfree((char *) pixPtr);
}
return TCL_ERROR;
}
zbar_image_scanner_set_config(scanPtr, ZBAR_NONE, ZBAR_CFG_ENABLE, 0);
for (x = 0; x < nElems; x++) {
zbar_symbol_type_t t = ZbarGetSymbolType(elems[x], NULL);
if (t != ZBAR_SYMBOL) {
zbar_image_scanner_set_config(scanPtr, t, ZBAR_CFG_ENABLE, 1);
}
}
}
imgPtr = zbar_image_create();
if (imgPtr == NULL) {
zbar_image_scanner_destroy(scanPtr);
if (pixPtr != NULL) {
ckfree((char *) pixPtr);
}
Tcl_SetResult(interp, "error creating image", TCL_STATIC);
return TCL_ERROR;
}
zbar_image_set_format(imgPtr, zbar_fourcc('Y', '8', '0', '0'));
zbar_image_set_size(imgPtr, block.width, block.height);
zbar_image_set_data(imgPtr, (pixPtr == NULL) ? block.pixelPtr : pixPtr,
block.width * block.height, NULL);
zbar_scan_image(scanPtr, imgPtr);
Tcl_GetTime(&now);
tw[1] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000;
x = tw[1] - tw[0];
if (x < 0) {
x = -1;
}
list[0] = Tcl_NewIntObj(x);
/* report only first symbol as result */
symPtr = zbar_image_first_symbol(imgPtr);
if (symPtr != NULL) {
zbar_symbol_type_t t = zbar_symbol_get_type(symPtr);
const unsigned char *dp = (const unsigned char *)
zbar_symbol_get_data(symPtr);
list[1] = ZbarGetSymbolObj(t, 0);
list[2] = Tcl_NewByteArrayObj(dp,
zbar_symbol_get_data_length(symPtr));
} else {
list[1] = Tcl_NewObj();
list[2] = Tcl_NewObj();
}
zbar_image_destroy(imgPtr);
zbar_image_scanner_destroy(scanPtr);
if (pixPtr != NULL) {
ckfree((char *) pixPtr);
}
Tcl_SetObjResult(interp, Tcl_NewListObj(3, list));
return TCL_OK;
}
/*
*-------------------------------------------------------------------------
*
* ZbarSymbolTypesObjCmd --
*
* zbar::symbol_types Tcl command, returns list of symbol types.
* Command format
*
* zbar::symbol_types
*
* Result is a list with supported symbologies
*
*-------------------------------------------------------------------------
*/
static int
ZbarSymbolTypesObjCmd(ClientData unused, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
if (objc != 1) {
Tcl_WrongNumArgs(interp, 1, objv, "");
return TCL_ERROR;
}
Tcl_SetObjResult(interp, ZbarGetSymbolObj(ZBAR_NONE, 1));
return TCL_OK;
}
#ifndef TCL_THREADS
/*
*-------------------------------------------------------------------------
*
* ZbarAsyncDecodeObjCmd --
*
* zbar::async_decode Tcl command, asynchronous decoding.
* Dummy for non-threaded builds
*
*-------------------------------------------------------------------------
*/
static int
ZbarAsyncDecodeObjCmd_NoThreads(ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *CONST objv[])
{
Tcl_SetResult(interp, "unsupported in non-threaded builds",
TCL_STATIC);
return TCL_OK;
}
#endif
/*
*-------------------------------------------------------------------------
*
* ZbarInit --
*
* Module initializer
*
*-------------------------------------------------------------------------
*/
int
Zbar_Init(Tcl_Interp *interp)
{
#ifdef TCL_THREADS
AsyncDecode *aPtr;
int major = 0, minor = 0;
const char *val;
#endif
#ifdef USE_TCL_STUBS
if (Tcl_InitStubs(interp, "8.4", 0) == NULL)
#else
if (Tcl_PkgRequire(interp, "Tcl", "8.4", 0) == NULL)
#endif
{
return TCL_ERROR;
}
#ifndef ZBAR_NO_TK
#ifdef USE_TK_STUBS
if (Tk_InitStubs(interp, "8.4", 0) == NULL)
#else
if (Tcl_PkgRequire(interp, "Tk", "8.4", 0) == NULL)
#endif
{
return TCL_ERROR;
}
#endif
#ifdef TCL_THREADS
aPtr = (AsyncDecode *) ckalloc(sizeof(AsyncDecode));
memset(aPtr, 0, sizeof(AsyncDecode));
Tcl_CreateObjCommand(interp, "zbar::async_decode",
ZbarAsyncDecodeObjCmd, (ClientData) aPtr,
ZbarAsyncCmdDeleted);
Tcl_GetVersion(&major, &minor, NULL, NULL);
if ((major > 8) || ((major == 8) && (minor > 6))) {
aPtr->tip609 = 1;
} else {
val = Tcl_GetVar2(interp, "tcl_platform", "tip609", TCL_GLOBAL_ONLY);
if ((val != NULL) && *val && (*val != '0')) {
aPtr->tip609 = 1;
}
}
#else
Tcl_CreateObjCommand(interp, "zbar::async_decode",
ZbarAsyncDecodeObjCmd_NoThreads, NULL, NULL);
#endif
Tcl_CreateObjCommand(interp, "zbar::decode", ZbarDecodeObjCmd,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "zbar::symbol_types", ZbarSymbolTypesObjCmd,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION);
return TCL_OK;
}
/*
* Local Variables:
* mode: c
* c-basic-offset: 4
* fill-column: 78
* tab-width: 8
* End:
*/