use web worker

This commit is contained in:
danimoh
2017-12-03 17:38:42 -06:00
parent 5d4700a3a4
commit 62fe3c6e3d
10 changed files with 126 additions and 233 deletions
+2 -1
View File
@@ -11,6 +11,7 @@ gulp.task('build', function() {
.pipe(sourcemaps.init()),
gulp.src([
'./src/worker.js',
'./src/binarizer.js',
'./src/grid.js',
'./src/version.js',
@@ -39,7 +40,7 @@ gulp.task('build', function() {
gulp.src('./src/suffix.js')
.pipe(sourcemaps.init())
)
.pipe(concat('qr-scanner-lib.min.js'))
.pipe(concat('qr-scanner-worker.min.js'))
.pipe(uglify())
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('.'));
+20 -27
View File
@@ -67,6 +67,13 @@
display: none;
}
#debug-canvas {
display: none;
position: absolute;
top: 0;
left: 0;
}
.label {
position: absolute;
max-width: 320px;
@@ -117,27 +124,6 @@
</x-qr-scanner>
<a class="label">Test</a>
</body>
<script src="qr-scanner-lib.min.js"></script>
<!--
<script type="text/javascript" src="src/binarizer.js"></script>
<script type="text/javascript" src="src/grid.js"></script>
<script type="text/javascript" src="src/version.js"></script>
<script type="text/javascript" src="src/detector.js"></script>
<script type="text/javascript" src="src/formatinf.js"></script>
<script type="text/javascript" src="src/errorlevel.js"></script>
<script type="text/javascript" src="src/bitmat.js"></script>
<script type="text/javascript" src="src/datablock.js"></script>
<script type="text/javascript" src="src/bmparser.js"></script>
<script type="text/javascript" src="src/datamask.js"></script>
<script type="text/javascript" src="src/rsdecoder.js"></script>
<script type="text/javascript" src="src/gf256poly.js"></script>
<script type="text/javascript" src="src/gf256.js"></script>
<script type="text/javascript" src="src/decoder.js"></script>
<script type="text/javascript" src="src/qrcode.js"></script>
<script type="text/javascript" src="src/findpat.js"></script>
<script type="text/javascript" src="src/alignpat.js"></script>
<script type="text/javascript" src="src/databr.js"></script>
-->
<script src="/x-element/src/x-element.js"></script>
<script src="qr-scanner.js"></script>
<script>
@@ -148,12 +134,18 @@ scanner.active = true;
scanner.$el.addEventListener('x-decoded', (e) => {
const decoded = e.detail;
console.log(decoded);
setLabel(decoded);
resetClass(label, 'label-active', decoded);
resetClass(decoded);
});
function setLabel(decoded) {
if (qrscanner.isUrl(decoded)) {
var isUrl = false;
try {
new Url(decoded);
isUrl = true;
} catch(e) {}
if (isUrl) {
label.href = decoded;
} else {
label.removeAttribute('href');
@@ -164,13 +156,14 @@ function setLabel(decoded) {
var lastDecoded;
var lastDecodedTimer;
function resetClass(el, className, decoded) {
if (decoded == lastDecoded) return;
function resetClass(decoded) {
if (decoded === lastDecoded) return;
clearTimeout(lastDecodedTimer);
lastDecodedTimer = setTimeout(() => lastDecoded = false, 3000);
lastDecoded = decoded;
label.classList.remove(className);
requestAnimationFrame(_ => label.classList.add(className))
label.classList.remove('label-active');
label.offsetWidth; // force render
label.classList.add('label-active');
}
</script>
-2
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+37 -14
View File
@@ -1,10 +1,15 @@
class QrScanner extends XElement {
onCreate() {
this._qrWorker = new Worker('qr-scanner-worker.min.js');
this._qrWorker.onmessage = event => this._handleWorkerMessage(event);
this.$video = this.$('video');
this.$canvas = this.$('canvas');
this.$debugCanvas = null;
this.$debugContext = null;
this.$context = this.$canvas.getContext('2d');
this.$overlay = this.$('#qr-overlay');
this._sourceRectSize = 400;
this._canvasSize = this.$canvas.width;
this._sourceRectSize = this._canvasSize;
window.addEventListener('resize', () => this._updateSourceRect());
this.$video.addEventListener('canplay', () => this._updateSourceRect());
this.$video.addEventListener('play', () => this._scanFrame(), false);
@@ -32,22 +37,24 @@ class QrScanner extends XElement {
if (this.$video.paused || this.$video.ended) return false;
this.$context.drawImage(this.$video, (this.$video.videoWidth - this._sourceRectSize) / 2,
(this.$video.videoHeight - this._sourceRectSize) / 2, this._sourceRectSize, this._sourceRectSize,
0, 0, 400, 400);
this._decode();
requestAnimationFrame(() => this._scanFrame());
0, 0, this._canvasSize, this._canvasSize);
var imageData = this.$context.getImageData(0, 0, this._canvasSize, this._canvasSize);
this._qrWorker.postMessage({
type: 'decode',
data: imageData
}, [imageData.data.buffer]);
}
_decode() {
try {
var decoded = qrscanner.decode();
this.fire('x-decoded', decoded);
} catch (e) {
if (e.message.startsWith('QR Error')) {
// no valid QR code found.
console.log(e);
} else {
throw e; // some different error
_handleWorkerMessage(event) {
var type = event.data.type;
var data = event.data.data;
if (type === 'qrResult') {
if (data !== null) {
this.fire('x-decoded', data);
}
requestAnimationFrame(() => this._scanFrame());
} else if (type === 'debugImage') {
this.$debugContext.putImageData(data, 0, 0);
}
}
@@ -91,6 +98,22 @@ class QrScanner extends XElement {
this.$video.pause();
setTimeout(() => this.$video.srcObject.getTracks()[0].stop(), 3000);
}
set debug(isDebug) {
this._qrWorker.postMessage({
type: 'setDebug',
data: isDebug
});
if (!this.$debugCanvas) {
this.$debugCanvas = document.createElement('canvas');
this.$debugCanvas.setAttribute('id', 'debug-canvas');
this.$debugCanvas.width = this._canvasSize;
this.$debugCanvas.height = this._canvasSize;
this.$debugContext = this.$debugCanvas.getContext('2d');
document.body.appendChild(this.$debugCanvas);
}
this.$debugCanvas.style.display = isDebug? 'block' : 'none';
}
}
+1 -1
View File
@@ -136,4 +136,4 @@ class Binarizer {
}
Binarizer.TARGET_BLOCK_COUNT_ALONG_SHORTER_SIDE = 40;
Binarizer.MIN_BLOCK_SIZE = 16;
Binarizer.MIN_DYNAMIC_RANGE = 24; // if the dynamic range in a block is below this value it's assumed to be single color
Binarizer.MIN_DYNAMIC_RANGE = 12; // if the dynamic range in a block is below this value it's assumed to be single color
+34 -187
View File
@@ -27,93 +27,24 @@ qrcode.sizeOfDataLengthInfo = [ [ 10, 9, 8, 8 ], [ 12, 11, 16, 10 ], [ 14, 1
qrcode.callback = null;
qrcode.decode = function(src){
if(arguments.length==0)
{
if(qrcode.canvas_qr2)
{
var canvas_qr = qrcode.canvas_qr2;
var context = qrcode.qrcontext2;
}
else
{
var canvas_qr = document.getElementById("qr-canvas");
var context = canvas_qr.getContext('2d');
}
qrcode.width = canvas_qr.width;
qrcode.height = canvas_qr.height;
qrcode.imagedata = context.getImageData(0, 0, qrcode.width, qrcode.height);
qrcode.result = qrcode.process(context);
if(qrcode.callback!=null)
qrcode.callback(qrcode.result);
return qrcode.result;
}
else
{
var image = new Image();
image.crossOrigin = "Anonymous";
image.onload=function(){
//var canvas_qr = document.getElementById("qr-canvas");
var canvas_out = document.getElementById("out-canvas");
if(canvas_out!=null)
{
var outctx = canvas_out.getContext('2d');
outctx.clearRect(0, 0, 320, 240);
outctx.drawImage(image, 0, 0, 320, 240);
}
var canvas_qr = document.createElement('canvas');
var context = canvas_qr.getContext('2d');
var nheight = image.height;
var nwidth = image.width;
if(image.width*image.height>qrcode.maxImgSize)
{
var ir = image.width / image.height;
nheight = Math.sqrt(qrcode.maxImgSize/ir);
nwidth=ir*nheight;
}
canvas_qr.width = nwidth;
canvas_qr.height = nheight;
context.drawImage(image, 0, 0, canvas_qr.width, canvas_qr.height );
qrcode.width = canvas_qr.width;
qrcode.height = canvas_qr.height;
try{
qrcode.imagedata = context.getImageData(0, 0, canvas_qr.width, canvas_qr.height);
}catch(e){
qrcode.result = "Cross domain image reading not supported in your browser! Save it to your computer then drag and drop the file!";
if(qrcode.callback!=null)
qrcode.callback(qrcode.result);
return;
}
try
{
qrcode.result = qrcode.process(context);
}
catch(e)
{
console.log(e);
qrcode.result = "error decoding QR Code";
}
if(qrcode.callback!=null)
qrcode.callback(qrcode.result);
}
image.onerror = function ()
{
if(qrcode.callback!=null)
qrcode.callback("Failed to load the image");
}
image.src = src;
}
qrcode.decode = function(imageData) {
qrcode.imagedata = imageData;
qrcode.width = imageData.width;
qrcode.height = imageData.height;
qrcode.result = qrcode.process();
if(qrcode.callback!=null)
qrcode.callback(qrcode.result);
return qrcode.result;
}
qrcode.isUrl = function(s)
{
var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
return regexp.test(s);
try {
new URL(s);
return true;
} catch(e) {
return false;
}
}
qrcode.decode_url = function (s)
@@ -147,7 +78,7 @@ qrcode.decode_utf8 = function ( s )
return s;
}
qrcode.process = function(ctx){
qrcode.process = function(){
var inputRgba = qrcode.imagedata.data;
// asign the grayscale and binary image within the rgba buffer as the rgba image will not be needed anymore
var offset = 0;
@@ -177,30 +108,30 @@ qrcode.process = function(ctx){
debugImage.data[point+3] = 255; // alpha
}
}
ctx.putImageData(debugImage, 0, 0);
}
//var finderPatternInfo = new FinderPatternFinder().findFinderPattern(image);
var detector = new Detector(binaryImage);
var qRCodeMatrix = detector.detect(); // throws if no qr code was found
try {
var detector = new Detector(binaryImage);
if(qrcode.debug)
{
for (var y = 0; y < qRCodeMatrix.bits.Height; y++)
{
for (var x = 0; x < qRCodeMatrix.bits.Width; x++)
{
var point = (x * 4*2) + (y*2 * qrcode.width * 4);
var isSet = qRCodeMatrix.bits.get_Renamed(x,y)
debugImage.data[point] = isSet? 0 : 255;
debugImage.data[point+1] = isSet? 0 : 255;
debugImage.data[point+2] = 255;
var qRCodeMatrix = detector.detect(); // throws if no qr code was found
if (qrcode.debug) {
for (var y = 0; y < qRCodeMatrix.bits.Height; y++) {
for (var x = 0; x < qRCodeMatrix.bits.Width; x++) {
var point = (x * 4 * 2) + (y * 2 * qrcode.width * 4);
var isSet = qRCodeMatrix.bits.get_Renamed(x, y)
debugImage.data[point] = isSet ? 0 : 255;
debugImage.data[point + 1] = isSet ? 0 : 255;
debugImage.data[point + 2] = 255;
}
}
}
ctx.putImageData(debugImage, 0, 0);
} finally {
if (qrcode.debug) {
sendDebugImage(debugImage);
}
}
var reader = Decoder.decode(qRCodeMatrix.bits);
@@ -213,89 +144,8 @@ qrcode.process = function(ctx){
}
return qrcode.decode_utf8(str);
//alert("Time:" + time + " Code: "+str);
}
qrcode.getMiddleBrightnessPerArea=function(image)
{
var numSqrtArea = 4;
//obtain middle brightness((min + max) / 2) per area
var areaWidth = Math.floor(qrcode.width / numSqrtArea);
var areaHeight = Math.floor(qrcode.height / numSqrtArea);
var minmax = new Array(numSqrtArea);
for (var i = 0; i < numSqrtArea; i++)
{
minmax[i] = new Array(numSqrtArea);
for (var i2 = 0; i2 < numSqrtArea; i2++)
{
minmax[i][i2] = new Array(0,0);
}
}
for (var ay = 0; ay < numSqrtArea; ay++)
{
for (var ax = 0; ax < numSqrtArea; ax++)
{
minmax[ax][ay][0] = 0xFF;
for (var dy = 0; dy < areaHeight; dy++)
{
for (var dx = 0; dx < areaWidth; dx++)
{
var target = image[areaWidth * ax + dx+(areaHeight * ay + dy)*qrcode.width];
if (target < minmax[ax][ay][0])
minmax[ax][ay][0] = target;
if (target > minmax[ax][ay][1])
minmax[ax][ay][1] = target;
}
}
//minmax[ax][ay][0] = (minmax[ax][ay][0] + minmax[ax][ay][1]) / 2;
}
}
var middle = new Array(numSqrtArea);
for (var i3 = 0; i3 < numSqrtArea; i3++)
{
middle[i3] = new Array(numSqrtArea);
}
for (var ay = 0; ay < numSqrtArea; ay++)
{
for (var ax = 0; ax < numSqrtArea; ax++)
{
middle[ax][ay] = Math.floor((minmax[ax][ay][0] + minmax[ax][ay][1]) / 2);
//Console.out.print(middle[ax][ay] + ",");
}
//Console.out.println("");
}
//Console.out.println("");
return middle;
}
qrcode.grayScaleToBitmap=function(grayScale)
{
var middle = qrcode.getMiddleBrightnessPerArea(grayScale);
var sqrtNumArea = middle.length;
var areaWidth = Math.floor(qrcode.width / sqrtNumArea);
var areaHeight = Math.floor(qrcode.height / sqrtNumArea);
var buff = new ArrayBuffer(qrcode.width*qrcode.height);
var bitmap = new Uint8Array(buff);
//var bitmap = new Array(qrcode.height*qrcode.width);
for (var ay = 0; ay < sqrtNumArea; ay++)
{
for (var ax = 0; ax < sqrtNumArea; ax++)
{
for (var dy = 0; dy < areaHeight; dy++)
{
for (var dx = 0; dx < areaWidth; dx++)
{
bitmap[areaWidth * ax + dx+ (areaHeight * ay + dy)*qrcode.width] = (grayScale[areaWidth * ax + dx+ (areaHeight * ay + dy)*qrcode.width] < middle[ax][ay])?true:false;
}
}
}
}
return bitmap;
}
qrcode.grayscale = function(inputRgba, width, height, out_grayscale)
{
@@ -321,7 +171,4 @@ function URShift( number, bits)
return number >> bits;
else
return (number >> bits) + (2 << ~bits);
}
window.qrscanner = qrcode;
}
+29
View File
@@ -0,0 +1,29 @@
self.onmessage = event => {
const type = event.data.type;
const data = event.data.data;
if (type === 'setDebug') {
qrcode.debug = data;
} else if (type === 'decode') {
let result = null;
try {
result = qrcode.decode(data);
} catch(e) {
if (!e.message.startsWith('QR Error')) {
throw e; // some unexpected error
}
} finally {
self.postMessage({
type: 'qrResult',
data: result
});
}
}
};
function sendDebugImage(debugImage) {
self.postMessage({
type: 'debugImage',
data: debugImage
}, [debugImage.data.buffer]);
}