vegancheck.me/js/BarcodeScanner.js
2021-08-26 11:17:01 +02:00

346 lines
13 KiB
JavaScript

/**
* CallBacks:
* __________________________________________________________________________________
* All the callback function should have one parameter:
* function(result){};
* And the result parameter will contain an array of objects that look like BarcodeScanner.
* result = [{Format: the barcode type, Value: the value of the barcode}];
* __________________________________________________________________________________
*
* You can use either the set functions or just access the properties directly to set callback or
* other properties. Just always remember to call Init() before starting to decode something never mess
* around with the SupportedFormats property.
*
*/
BarcodeScanner = {
Config : {
// Set to false if the decoder should look for one barcode and then stop. Increases performance.
Multiple : true,
// The formats that the decoder will look for.
DecodeFormats : ["Code128","Code93","Code39","EAN-13", "2Of5", "Inter2Of5", "Codabar"],
// ForceUnique just must makes sure that the callback function isn't repeatedly called
// with the same barcode. Especially in the case of a video stream.
ForceUnique: true,
// Set to true if information about the localization should be recieved from the worker.
LocalizationFeedback: false,
// Set to true if checking orientation of the image should be skipped.
// Checking orientation takes a bit of time for larger images, so if
// you are sure that the image orientation is 1 you should skip it.
SkipOrientation : false
},
SupportedFormats : ["Code128","Code93","Code39","EAN-13", "2Of5", "Inter2Of5", "Codabar"],// Don't touch.
ScanCanvas : null, // Don't touch the canvas either.
ScanContext : null,
SquashCanvas : document.createElement("canvas"),
ImageCallback : null, // Callback for the decoding of an image.
streamCallback : null, // Callback for the decoding of a video.
LocalizationCallback : null, // Callback for localization.
Stream : null, // The actual video.
DecodeStreamActive : false, // Will be set to false when StopStreamDecode() is called.
Decoded : [], // Used to enfore the ForceUnique property.
DecoderWorker : new Worker("js/DecoderWorker.js"),
OrientationCallback : null,
// Always call the Init().
init : function() {
BarcodeScanner.ScanCanvas = BarcodeScanner.FixCanvas(document.createElement("canvas"));
BarcodeScanner.ScanCanvas.width = 640;
BarcodeScanner.ScanCanvas.height = 480;
BarcodeScanner.ScanContext = BarcodeScanner.ScanCanvas.getContext("2d");
var script = document.createElement('script');
script.src = "exif.js";
script.type = 'text/javascript';
document.getElementsByTagName('head').item(0).appendChild(script);
},
// Value should be true or false.
SetRotationSkip : function(value) {
BarcodeScanner.Config.SkipOrientation = value;
},
// Sets the callback function for the image decoding.
SetImageCallback : function(callBack) {
BarcodeScanner.ImageCallback = callBack;
},
// Sets the callback function for the video decoding.
setStreamCallback : function(callBack) {
BarcodeScanner.streamCallback = callBack;
},
// Sets callback for localization, the callback function should take one argument.
// This will be an array with objects with format.
// {x, y, width, height}
// This represents a localization rectangle.
// The rectangle comes from a 320, 240 area i.e the search canvas.
SetLocalizationCallback : function(callBack) {
BarcodeScanner.LocalizationCallback = callBack;
BarcodeScanner.Config.LocalizationFeedback = true;
},
// Set to true if LocalizationCallback is set and you would like to
// receive the feedback or false if
SwitchLocalizationFeedback : function(bool) {
BarcodeScanner.Config.LocalizationFeedback = bool;
},
// Switches for changing the Multiple property.
DecodeSingleBarcode : function() {
BarcodeScanner.Config.Multiple = false;
},
DecodeMultiple : function() {
BarcodeScanner.Config.Multiple = true;
},
// Sets the formats to decode, formats should be an array of a subset of the supported formats.
SetDecodeFormats : function(formats) {
BarcodeScanner.Config.DecodeFormats = [];
for(var i = 0; i < formats.length; i++) {
if(BarcodeScanner.SupportedFormats.indexOf(formats[i]) != -1) {
BarcodeScanner.Config.DecodeFormats.push(formats[i]);
}
}
if(BarcodeScanner.Config.DecodeFormats.length == 0) {
BarcodeScanner.Config.DecodeFormats = BarcodeScanner.SupportedFormats.slice();
}
},
// Removes a list of formats from the formats to decode.
SkipFormats : function(formats) {
for(var i = 0; i < formats.length; i++) {
var index = BarcodeScanner.Config.DecodeFormats.indexOf(formats[i]);
if(index >= 0) {
BarcodeScanner.Config.DecodeFormats.splice(index,1);
}
}
},
// Adds a list of formats to the formats to decode.
AddFormats : function(formats) {
for(var i = 0; i < formats.length; i++) {
if(BarcodeScanner.SupportedFormats.indexOf(formats[i]) != -1) {
if(BarcodeScanner.Config.DecodeFormats.indexOf(formats[i]) == -1) {
BarcodeScanner.Config.DecodeFormats.push(formats[i]);
}
}
}
},
// The callback function for image decoding used internally by BarcodeScanner.
BarcodeScannerImageCallback : function(e) {
if(e.data.success == "localization") {
if(BarcodeScanner.Config.LocalizationFeedback) {
BarcodeScanner.LocalizationCallback(e.data.result);
}
return;
}
if(e.data.success == "orientationData") {
BarcodeScanner.OrientationCallback(e.data.result);
return;
}
var filteredData = [];
for(var i = 0; i < e.data.result.length; i++) {
if(BarcodeScanner.Decoded.indexOf(e.data.result[i].Value) == -1 || BarcodeScanner.Config.ForceUnique == false) {
filteredData.push(e.data.result[i]);
if(BarcodeScanner.Config.ForceUnique) BarcodeScanner.Decoded.push(e.data.result[i].Value);
}
}
BarcodeScanner.ImageCallback(filteredData);
BarcodeScanner.Decoded = [];
},
// The callback function for stream decoding used internally by BarcodeScanner.
BarcodeScannerStreamCallback : function(e) {
if(e.data.success == "localization") {
if(BarcodeScanner.Config.LocalizationFeedback) {
BarcodeScanner.LocalizationCallback(e.data.result);
}
return;
}
if(e.data.success && BarcodeScanner.DecodeStreamActive) {
var filteredData = [];
for(var i = 0; i < e.data.result.length; i++) {
if(BarcodeScanner.Decoded.indexOf(e.data.result[i].Value) == -1 || BarcodeScanner.ForceUnique == false) {
filteredData.push(e.data.result[i]);
if(BarcodeScanner.ForceUnique) BarcodeScanner.Decoded.push(e.data.result[i].Value);
}
}
if(filteredData.length > 0) {
BarcodeScanner.streamCallback(filteredData);
}
}
if(BarcodeScanner.DecodeStreamActive) {
BarcodeScanner.ScanContext.drawImage(BarcodeScanner.Stream,0,0,BarcodeScanner.ScanCanvas.width,BarcodeScanner.ScanCanvas.height);
BarcodeScanner.DecoderWorker.postMessage({
scan : BarcodeScanner.ScanContext.getImageData(0,0,BarcodeScanner.ScanCanvas.width,BarcodeScanner.ScanCanvas.height).data,
scanWidth : BarcodeScanner.ScanCanvas.width,
scanHeight : BarcodeScanner.ScanCanvas.height,
multiple : BarcodeScanner.Config.Multiple,
decodeFormats : BarcodeScanner.Config.DecodeFormats,
cmd : "normal",
rotation : 1,
});
}
if(!BarcodeScanner.DecodeStreamActive) {
BarcodeScanner.Decoded = [];
}
},
// The image decoding function, image is a data source for an image or an image element.
DecodeImage : function(image) {
if(image instanceof Image || image instanceof HTMLImageElement)
{
image.exifdata = false;
if(image.complete) {
if(BarcodeScanner.Config.SkipOrientation) {
BarcodeScanner.BarcodeScannerDecodeImage(image,1,"");
} else {
EXIF.getData(image, function(exifImage) {
var orientation = EXIF.getTag(exifImage,"Orientation");
var sceneType = EXIF.getTag(exifImage,"SceneCaptureType");
if(typeof orientation != 'number') orientation = 1;
BarcodeScanner.BarcodeScannerDecodeImage(exifImage,orientation,sceneType);
});
}
} else {
var img = new Image();
img.onload = function() {
if(BarcodeScanner.Config.SkipOrientation) {
BarcodeScanner.BarcodeScannerDecodeImage(img,1,"");
} else {
EXIF.getData(this, function(exifImage) {
var orientation = EXIF.getTag(exifImage,"Orientation");
var sceneType = EXIF.getTag(exifImage,"SceneCaptureType");
if(typeof orientation != 'number') orientation = 1;
BarcodeScanner.BarcodeScannerDecodeImage(exifImage,orientation,sceneType);
});
}
};
img.src = image.src;
}
} else {
var img = new Image();
img.onload = function() {
if(BarcodeScanner.Config.SkipOrientation) {
BarcodeScanner.BarcodeScannerDecodeImage(img,1,"");
} else {
EXIF.getData(this, function(exifImage) {
var orientation = EXIF.getTag(exifImage,"Orientation");
var sceneType = EXIF.getTag(exifImage,"SceneCaptureType");
if(typeof orientation != 'number') orientation = 1;
BarcodeScanner.BarcodeScannerDecodeImage(exifImage,orientation,sceneType);
});
}
};
img.src = image;
}
},
// Starts the decoding of a stream, the stream is a video not a blob i.e it's an element.
DecodeStream : function(stream) {
BarcodeScanner.Stream = stream;
BarcodeScanner.DecodeStreamActive = true;
BarcodeScanner.DecoderWorker.onmessage = BarcodeScanner.BarcodeScannerStreamCallback;
BarcodeScanner.ScanContext.drawImage(stream,0,0,BarcodeScanner.ScanCanvas.width,BarcodeScanner.ScanCanvas.height);
BarcodeScanner.DecoderWorker.postMessage({
scan : BarcodeScanner.ScanContext.getImageData(0,0,BarcodeScanner.ScanCanvas.width,BarcodeScanner.ScanCanvas.height).data,
scanWidth : BarcodeScanner.ScanCanvas.width,
scanHeight : BarcodeScanner.ScanCanvas.height,
multiple : BarcodeScanner.Config.Multiple,
decodeFormats : BarcodeScanner.Config.DecodeFormats,
cmd : "normal",
rotation : 1,
});
},
// Stops the decoding of a stream.
StopStreamDecode : function() {
BarcodeScanner.DecodeStreamActive = false;
BarcodeScanner.Decoded = [];
},
BarcodeScannerDecodeImage : function (image,orientation,sceneCaptureType) {
if(orientation == 8 || orientation == 6) {
if(sceneCaptureType == "Landscape" && image.width > image.height) {
orientation = 1;
BarcodeScanner.ScanCanvas.width = 640;
BarcodeScanner.ScanCanvas.height = 480;
} else {
BarcodeScanner.ScanCanvas.width = 480;
BarcodeScanner.ScanCanvas.height = 640;
}
} else {
BarcodeScanner.ScanCanvas.width = 640;
BarcodeScanner.ScanCanvas.height = 480;
}
BarcodeScanner.DecoderWorker.onmessage = BarcodeScanner.BarcodeScannerImageCallback;
BarcodeScanner.ScanContext.drawImage(image,0,0,BarcodeScanner.ScanCanvas.width,BarcodeScanner.ScanCanvas.height);
BarcodeScanner.Orientation = orientation;
BarcodeScanner.DecoderWorker.postMessage({
scan : BarcodeScanner.ScanContext.getImageData(0,0,BarcodeScanner.ScanCanvas.width,BarcodeScanner.ScanCanvas.height).data,
scanWidth : BarcodeScanner.ScanCanvas.width,
scanHeight : BarcodeScanner.ScanCanvas.height,
multiple : BarcodeScanner.Config.Multiple,
decodeFormats : BarcodeScanner.Config.DecodeFormats,
cmd : "normal",
rotation : orientation,
postOrientation : BarcodeScanner.PostOrientation
});
},
DetectVerticalSquash : function (img) {
var ih = img.naturalHeight;
var canvas = BarcodeScanner.SquashCanvas;
canvas.width = 1;
canvas.height = ih;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
try {
var data = ctx.getImageData(0, 0, 1, ih).data;
} catch (err) {
console.log("Cannot check verticalSquash: CORS?");
return 1;
}
var sy = 0;
var ey = ih;
var py = ih;
while (py > sy) {
var alpha = data[(py - 1) * 4 + 3];
if (alpha === 0) {
ey = py;
} else {
sy = py;
}
py = (ey + sy) >> 1;
}
var ratio = (py / ih);
return (ratio===0)?1:ratio;
},
FixCanvas : function (canvas)
{
var ctx = canvas.getContext('2d');
var drawImage = ctx.drawImage;
ctx.drawImage = function(img, sx, sy, sw, sh, dx, dy, dw, dh)
{
var vertSquashRatio = 1;
if (!!img && img.nodeName == 'IMG')
{
vertSquashRatio = BarcodeScanner.DetectVerticalSquash(img);
sw || (sw = img.naturalWidth);
sh || (sh = img.naturalHeight);
}
if (arguments.length == 9)
drawImage.call(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio);
else if (typeof sw != 'undefined')
drawImage.call(ctx, img, sx, sy, sw, sh / vertSquashRatio);
else
drawImage.call(ctx, img, sx, sy);
};
return canvas;
}
};