diff --git a/demo/index.html b/demo/index.html index c1a8409..fedf55d 100644 --- a/demo/index.html +++ b/demo/index.html @@ -1,170 +1,112 @@ - - + - - HTML5 Camera QR UI - - - - - + + QR Scanner Demo - - - -
- -
- Test - - - - + - + \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 86c93d1..6683e13 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -8,7 +8,7 @@ gulp.task('default', ['build-entry', 'build-library', 'build-worker']) gulp.task('build-entry', () => gulp.src([ - './src/qr-scanner.js', + './src/qr-scanner.js', './src/qr-scanner-lib.js']) .pipe(concat('qr-scanner.min.js')) .pipe(gulp.dest('.')) diff --git a/qr-scanner-lib.min.js b/qr-scanner-lib.min.js index dc9a4ea..6f1165f 100644 --- a/qr-scanner-lib.min.js +++ b/qr-scanner-lib.min.js @@ -19,13 +19,6 @@ class QrScannerLib { _updateSourceRect() { const smallestDimension = Math.min(this.$video.videoWidth, this.$video.videoHeight); this._sourceRectSize = Math.round(2 / 3 * smallestDimension); - - const scannerWidth = this.$video.parentElement.offsetWidth; - const scannerHeight = this.$video.parentElement.offsetHeight; - const widthRatio = this.$video.videoWidth / scannerWidth; - const heightRatio = this.$video.videoHeight / scannerHeight; - const scaleFactor = 1 / (Math.min(heightRatio, widthRatio) || 1); - const scaledOverlaySize = this._sourceRectSize * scaleFactor; } _scanFrame() { @@ -93,4 +86,87 @@ class QrScannerLib { data: { red, green, blue } }); } + + static scanImage(imageOrFileOrUrl) { + return new Promise((resolve, reject) => { + const timeout = setTimeout(reject, 3000); + const worker = new Worker('/qr-scanner/qr-scanner-worker.min.js'); + worker.onerror = reject; + worker.onmessage = event => { + if (event.data.type !== 'qrResult') { + return; + } + clearTimeout(timeout); + if (event.data.data !== null) { + resolve(event.data.data); + } else { + reject(); + } + }; + QrScannerLib._loadImage(imageOrFileOrUrl).then(image => { + const imageData = QrScannerLib._getImageData(image); + worker.postMessage({ + type: 'decode', + data: imageData + }, [imageData.data.buffer]); + }).catch(reject); + }); + } + + /* async */ + static _getImageData(image) { + const canvas = document.createElement('canvas'); + canvas.width = image.width; + canvas.height = image.height; + const context = canvas.getContext('2d'); + context.drawImage(image, 0, 0); + return context.getImageData(0, 0, image.width, image.height); + } + + /* async */ + static _loadImage(imageOrFileOrUrl) { + if (imageOrFileOrUrl instanceof HTMLCanvasElement + || typeof('ImageBitmap')!=='undefined' && imageOrFileOrUrl instanceof ImageBitmap) { + return Promise.resolve(imageOrFileOrUrl); + } else if (imageOrFileOrUrl instanceof Image) { + return QrScannerLib._awaitImageLoad(imageOrFileOrUrl).then(() => imageOrFileOrUrl); + } else if (imageOrFileOrUrl instanceof File || imageOrFileOrUrl instanceof URL + || typeof(imageOrFileOrUrl)==='string') { + const image = new Image(); + if (imageOrFileOrUrl instanceof File) { + image.src = URL.createObjectURL(imageOrFileOrUrl); + } else { + image.src = imageOrFileOrUrl; + } + return QrScannerLib._awaitImageLoad(image).then(() => { + if (imageOrFileOrUrl instanceof File) { + URL.revokeObjectURL(image.src); + } + return image; + }); + } else { + return Promise.reject('Unsupported image type.'); + } + } + + /* async */ + static _awaitImageLoad(image) { + return new Promise((resolve, reject) => { + if (image.complete && image.naturalWidth!==0) { + // already loaded + resolve(); + } else { + image.onload = () => { + image.onload = null; + image.onerror = null; + resolve(); + }; + image.onerror = () => { + image.onload = null; + image.onerror = null; + reject(); + }; + } + }); + } } \ No newline at end of file diff --git a/qr-scanner-worker.min.js b/qr-scanner-worker.min.js index 5625eee..67ad8cf 100644 --- a/qr-scanner-worker.min.js +++ b/qr-scanner-worker.min.js @@ -1,64 +1,64 @@ (function(){ -'use strict';var aa="function"==typeof Object.defineProperties?Object.defineProperty:function(c,d,a){c!=Array.prototype&&c!=Object.prototype&&(c[d]=a.value)},e="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this;function ba(){ba=function(){};e.Symbol||(e.Symbol=ca)}var ca=function(){var c=0;return function(d){return"jscomp_symbol_"+(d||"")+c++}}(); -function ra(){ba();var c=e.Symbol.iterator;c||(c=e.Symbol.iterator=e.Symbol("iterator"));"function"!=typeof Array.prototype[c]&&aa(Array.prototype,c,{configurable:!0,writable:!0,value:function(){return sa(this)}});ra=function(){}}function sa(c){var d=0;return ta(function(){return d=c}}); -Pa("Array.prototype.find",function(c){return c?c:function(d,a){a:{var b=this;b instanceof String&&(b=String(b));for(var g=b.length,c=0;c>8}var da=l,F= -q,C=f,R=n;da=void 0===da?h:da;F=void 0===F?null:F;var S=ua(Sa(C,R)),A=S.next().value,E=S.next().value,X=S.next().value;if(F){if(!(F instanceof Uint8ClampedArray)||F.byteLength!==E*X)throw Error("QR Error: Illegal Buffer.");var Y=F}else Y=new Uint8ClampedArray(E*X);for(var U=0;UZ&&(Z=ea)}Wa+=C}if(Z-N>Ua){var Ya=(N+Z)/2;var ya=Math.min(255,Ya+ -(N+Z)/4,1.1*Ya)}else if(0===V||0===U)ya=N-1;else{var za=U*E+V,Za=(Y[za-1]+Y[za-E]+E[za-E-1])/3;ya=Za>N?Za:N-1}Y[U*E+V]=ya}for(var fa=0;fa=Aa;++Aa)for(var Ba=-2;2>=Ba;++Ba)$a+=Y[Math.max(0,Math.min(X-1,fa+Ba))*E+Math.max(0,Math.min(E-1,ha+Aa))];var ab=h,Ca=C,Lb=$a/25,ia=da;ia=void 0===ia?ab:ia;for(var bb=Math.min(fa*A,R-A)*Ca+Math.min(ha*A,Ca-A),cb=0;cbc||40=d?Eb(a):null};function Hb(c,d,a,b,g,k,h,l,m){this.o=c;this.s=b;this.u=h;this.v=d;this.w=g;this.A=l;this.B=a;this.C=k;this.D=m;this.sb=function(b){for(var a=b.length,g=this.o,d=this.s,c=this.u,k=this.v,h=this.w,l=this.A,m=this.B,p=this.C,F=this.D,C=0;CMath.abs(b-d);if(c){var h=d;d=a;a=h;h=b;b=g;g=h}var l=Math.abs(b-d),m=Math.abs(g-a),p=-l>>1,w=ab?(h=d/(d-b),b=0):b>=f&&(h= -(f-1-d)/(b-d),b=f-1);g=Math.floor(a-(g-a)*h);h=1;0>g?(h=a/(a-g),g=0):g>=n&&(h=(n-1-a)/(g-a),g=n-1);b=Math.floor(d+(b-d)*h);c+=this.Ca(d,a,b,g);return c-1};this.qa=function(d,a){var b=this.Da(Math.floor(d.g()),Math.floor(d.h()),Math.floor(a.g()),Math.floor(a.h()));d=this.Da(Math.floor(a.g()),Math.floor(a.h()),Math.floor(d.g()),Math.floor(d.h()));return isNaN(b)?d/7:isNaN(d)?b/7:(b+d)/14};this.Ja=function(d,a,b){return(this.qa(d,a)+this.qa(d,b))/2};this.sa=function(d,a){var b=d.g()-a.g();d=d.h()-a.h(); -return Math.sqrt(b*b+d*d)};this.Ka=function(d,a,b,g){d=(Math.round(this.sa(d,a)/g)+Math.round(this.sa(d,b)/g)>>1)+7;switch(d&3){case 0:d++;break;case 2:d--;break;case 3:throw Error("QR Error: in detector");}return d};this.Qa=function(d,a,b,g){g=Math.floor(g*d);var c=Math.max(0,a-g);a=Math.min(f-1,a+g);if(a-c<3*d)throw Error("QR Error: in detector");var h=Math.max(0,b-g);return(new Kb(this.H,c,h,a-c,Math.min(n-1,b+g)-h,d,this.U)).find()};this.La=function(d,a,b,g,c){c-=3.5;var k;if(null!=g){var l=g.g(); -g=g.h();var m=k=c-3}else l=a.g()-d.g()+b.g(),g=a.h()-d.h()+b.h(),m=k=c;return Ib(d.g(),d.h(),a.g(),a.h(),l,g,b.g(),b.h()).pb(Ib(3.5,3.5,c,3.5,m,k,3.5,c).Ha())};this.lb=function(d,a,b){for(var g=new Bb(b),c=Array(b<<1),h=0;h>1)+.5,c[p+1]=m;a.sb(c);m=c;for(var w=f,q=n,u=!0,t=0;tx||x>w||-1>v||v>q)throw Error("QR Error: Error.checkAndNudgePoints");u=!1;-1==x?(m[t]=0,u=!0):x==w&& -(m[t]=w-1,u=!0);-1==v?(m[t+1]=0,u=!0):v==q&&(m[t+1]=q-1,u=!0)}u=!0;for(t=m.length-2;0<=t&&u;t-=2){x=Math.floor(m[t]);v=Math.floor(m[t+1]);if(-1>x||x>w||-1>v||v>q)throw Error("QR Error: Error.checkAndNudgePoints");u=!1;-1==x?(m[t]=0,u=!0):x==w&&(m[t]=w-1,u=!0);-1==v?(m[t+1]=0,u=!0):v==q&&(m[t+1]=q-1,u=!0)}try{for(p=0;p>1,h)}catch(va){throw Error("QR Error: Error.checkAndNudgePoints");}}return g};this.ib=function(d){var a=d.qb,b=d.rb;d=d.Ga;var g= -this.Ja(a,b,d);if(1>g)throw Error("QR Error: in detector");var c=this.Ka(a,b,d,g);if(1!=c%4)throw Error("QR Error: Error getProvisionalVersionForDimension");try{var h=Eb(c-17>>2)}catch(w){throw Error("QR Error: Error getVersionForNumber");}var l=h.fa()-7,m=null;if(0=p;p<<=1)try{m=this.Qa(g,h,l,p);break}catch(w){}}a=this.lb(this.H,this.La(a,b,d,m,c),c);return new Jb(a)}; -this.Oa=function(){var d=(new Mb).Ta(this.H);return this.ib(d)}};var Nb=[[21522,0],[20773,1],[24188,2],[23371,3],[17913,4],[16590,5],[20375,6],[19104,7],[30660,8],[29427,9],[32170,10],[30877,11],[26159,12],[25368,13],[27713,14],[26998,15],[5769,16],[5054,17],[7399,18],[6608,19],[1890,20],[597,21],[3340,22],[2107,23],[13663,24],[12392,25],[16177,26],[14854,27],[9396,28],[8579,29],[11994,30],[11245,31]],M=[0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4];function Pb(c){var d=c>>3&3;if(0>d||d>=Tb.length)throw Error("QR Error: ArgumentException");this.Pa=Tb[d];this.Na=c&7} -function Gb(c,d){c^=d;return M[c&15]+M[P(c,4)&15]+M[P(c,8)&15]+M[P(c,12)&15]+M[P(c,16)&15]+M[P(c,20)&15]+M[P(c,24)&15]+M[P(c,28)&15]}function Ub(c){var d=Vb(c);return null!=d?d:Vb(c^21522)}function Vb(c){for(var d=4294967295,a=0,b=0;b=d?new Pb(a):null};function Wb(c,d,a){this.gb=c;this.i=d;this.name=a;this.getName=function(){return this.name}}var Tb=[new Wb(1,0,"M"),new Wb(0,1,"L"),new Wb(3,2,"H"),new Wb(2,3,"Q")];function Bb(c){var d;d||(d=c);if(1>c||1>d)throw Error("QR Error: Both dimensions must be greater than 0");this.width=c;this.height=d;var a=c>>5;0!=(c&31)&&a++;this.X=a;this.i=Array(a*d);for(c=0;c>5)],b&31)&1)};this.nb=function(b,a){this.i[a*this.X+(b>>5)]|=1<<(b&31)};this.M=function(b, -a){this.i[a*this.X+(b>>5)]^=1<<(b&31)};this.clear=function(){for(var b=this.i.length,a=0;aa||0>b)throw Error("QR Error: Left and top must be nonnegative");if(1>c||1>d)throw Error("QR Error: Height and width must be at least 1");d=b+d;c=a+c;if(c>this.height||d>this.width)throw Error("QR Error: The region must fit inside the matrix");for(;a>5)]|=1<<(k&31)}};function vb(c,d){this.ya=c;this.T=d};function ub(c){var d=c.ea();if(21>d||1!=(d&3))throw Error("QR Error: Error BitMatrixParser");this.V=c;this.O=this.J=null;this.K=function(a,b,d){return this.V.ga(a,b)?d<<1|1:d<<1};this.Aa=function(){if(null!=this.O)return this.O;for(var a=0,b=0;6>b;b++)a=this.K(b,8,a);a=this.K(7,8,a);a=this.K(8,8,a);a=this.K(8,7,a);for(b=5;0<=b;b--)a=this.K(8,b,a);this.O=Ub(a);if(null!=this.O)return this.O;var d=this.V.ea();a=0;var c=d-8;for(b=d-1;b>=c;b--)a=this.K(b,8,a);for(b=d-7;b>2;if(6>=b)return Eb(b);b=0;for(var d=a-11,c=5;0<=c;c--)for(var h=a-9;h>=d;h--)b=this.K(h,c,b);this.J=Fb(b);if(null!=this.J&&this.J.fa()==a)return this.J;b=0;for(h=5;0<=h;h--)for(c=a-9;c>=d;c--)b=this.K(h,c,b);this.J=Fb(b);if(null!=this.J&&this.J.fa()==a)return this.J;throw Error("QR Error: Error readVersion");};this.jb=function(){var a=this.Aa(), -b=this.Ba();a=a.Na;if(0>a||7t;t++)d.ga(w-t,u)||(p++,m<<=1,this.V.ga(w-t,u)&&(m|=1),8==p&&(h[l++]=m,m=p=0));c^=1}if(l!=b.na)throw Error("QR Error: Error readCodewords");return h}};var Xb=[new function(){this.R=function(c,d){for(var a=0;ab.length){var d=a;a=b;b=d}d=Array(b.length);for(var g=b.length-a.length,m=0;mb)throw Error("QR Error: System.ArgumentException");if(0==a)return this.a.l();var d=this.f.length; -b=Array(d+b);for(var c=0;ca;a++)this.Y[a]=d,d<<=1,256<=d&&(d^=c);for(a=0;255>a;a++)this.aa[this.Y[a]]=a;c=Array(1);c[0]=0;this.Ea=new Q(this,Array(c));c=Array(1);c[0]=1;this.za=new Q(this,Array(c));this.l=function(){return this.Ea};this.pa=function(b,a){if(0>b)throw Error("QR Error: System.ArgumentException");if(0==a)return this.Ea;b=Array(b+1);for(var d=0;dc)throw Error("QR Error: ReedSolomonException Bad error location");d[c]^=b[k]}};this.kb=function(d,a,b){if(d.I()=Math.floor(b/2);){var m=d,p=c,w=h;d=a;c=k;h=l;if(d.l())throw Error("QR Error: r_{i-1} was zero");a=m;l=this.a.l();for(k=this.a.inverse(d.Z(d.I()));a.I()>=d.I()&&!a.l();){m=a.I()-d.I();var q=this.a.multiply(a.Z(a.I()),k);l=l.ba(this.a.pa(m,q));a=a.ba(d.eb(m,q))}k=l.wa(c).ba(p);l=l.wa(h).ba(w)}b=l.Z(0);if(0==b)throw Error("QR Error: ReedSolomonException sigmaTilde(0) was zero");b=this.a.inverse(b);d=l.xa(b);b=a.xa(b);return[d,b]};this.Ra=function(d){var a= -d.I();if(1==a)return Array(d.Z(1));for(var b=Array(a),c=0,k=1;256>k&&c>d:(c>>d)+(2<<~d)};var bc=3,cc=57,dc=2;function ec(c){function d(b,a){var c=b.g()-a.g();b=b.h()-a.h();return Math.sqrt(c*c+b*b)}var a=d(c[0],c[1]),b=d(c[1],c[2]),g=d(c[0],c[2]);b>=a&&b>=g?(b=c[0],a=c[1],g=c[2]):g>=b&&g>=a?(b=c[1],a=c[0],g=c[2]):(b=c[2],a=c[0],g=c[1]);if(0>function(b,a,c){var d=a.x;a=a.y;return(c.x-d)*(b.y-a)-(c.y-a)*(b.x-d)}(a,b,g)){var k=a;a=g;g=k}c[0]=a;c[1]=b;c[2]=g} -function fc(c,d,a){this.x=c;this.y=d;this.count=1;this.G=a;this.g=function(){return this.x};this.h=function(){return this.y};this.va=function(){this.count++};this.ha=function(b,a,c){return Math.abs(a-this.y)<=b&&Math.abs(c-this.x)<=b?(b=Math.abs(b-this.G),1>=b||1>=b/this.G):!1}}function gc(c){this.Ga=c[0];this.qb=c[1];this.rb=c[2]} -function Mb(){this.H=null;this.c=[];this.ma=!1;this.L=[0,0,0,0,0];this.U=null;this.ta=function(){this.L[0]=0;this.L[1]=0;this.L[2]=0;this.L[3]=0;this.L[4]=0;return this.L};this.N=function(c){for(var d=0,a=0;5>a;a++){var b=c[a];if(0==b)return!1;d+=b}if(7>d)return!1;d=Math.floor(d/7);a=Math.floor(.7*d);return Math.abs(d-c[0])l)return NaN;for(;0<=l&&!g[d+l*f]&&h[1]<=a;)h[1]++,l--;if(0>l||h[1]>a)return NaN;for(;0<=l&&g[d+l*f]&&h[0]<=a;)h[0]++,l--;if(h[0]>a)return NaN;for(l=c+1;l=a)return NaN;for(;l=a||5*Math.abs(h[0]+h[1]+h[2]+h[3]+h[4]-b)>=2*b?NaN:this.N(h)?this.W(h,l):NaN};this.Ma=function(c,d,a,b){for(var g=this.H,k=f,h=this.ta(), -l=c;0<=l&&g[l+d*f];)h[2]++,l--;if(0>l)return NaN;for(;0<=l&&!g[l+d*f]&&h[1]<=a;)h[1]++,l--;if(0>l||h[1]>a)return NaN;for(;0<=l&&g[l+d*f]&&h[0]<=a;)h[0]++,l--;if(h[0]>a)return NaN;for(l=c+1;l=a)return NaN;for(;l=a||5*Math.abs(h[0]+h[1]+h[2]+h[3]+h[4]-b)>=b?NaN:this.N(h)?this.W(h,l):NaN};this.$=function(c,d,a){var b=c[0]+c[1]+c[2]+c[3]+c[4];a=this.W(c,a);d=this.ia(d, -Math.floor(a),c[2],b);if(!isNaN(d)&&(a=this.Ma(Math.floor(a),Math.floor(d),c[2],b),!isNaN(a))){c=b/7;b=!1;for(var g=this.c.length,k=0;kc)throw Error("QR Error: Couldn't find enough finder patterns (found "+c+")");if(3c&&this.c.splice(b,1)}3a.count?-1:b.count=c)return 0;for(var d=null,a=0;a=dc)if(null==d)d=b;else return this.ma=!0,Math.floor((Math.abs(d.g()-b.g())-Math.abs(d.h()- -b.h()))/2)}return 0};this.ua=function(){for(var c=0,d=0,a=this.c.length,b=0;b=dc&&(c++,d+=g.G)}if(3>c)return!1;c=d/a;var k=0;for(b=0;bk[2]&&(h+=l-k[2]-b,m=a-1));else{do m++;while(m=b||1>=b/this.G):!1}} -function Kb(c,d,a,b,g,k,h){this.H=c;this.c=[];this.ob=d;this.width=b;this.height=g;this.cb=k;this.L=[0,0,0];this.U=h;this.W=function(b,a){return a-b[2]-b[1]/2};this.N=function(b){for(var a=this.cb,c=a/2,d=0;3>d;d++)if(Math.abs(a-b[d])>=c)return!1;return!0};this.ia=function(b,a,c,d){var g=this.H,k=n,h=this.L;h[0]=0;h[1]=0;h[2]=0;for(var l=b;0<=l&&g[a+l*f]&&h[1]<=c;)h[1]++,l--;if(0>l||h[1]>c)return NaN;for(;0<=l&&!g[a+l*f]&&h[0]<=c;)h[0]++,l--;if(h[0]>c)return NaN;for(l=b+1;lc)return NaN;for(;lc||5*Math.abs(h[0]+h[1]+h[2]-d)>=2*d?NaN:this.N(h)?this.W(h,l):NaN};this.$=function(b,a,c){c=this.W(b,c);a=this.ia(a,Math.floor(c),2*b[1],b[0]+b[1]+b[2]);if(!isNaN(a)){b=(b[0]+b[1]+b[2])/3;for(var d=this.c.length,g=0;g>1),q=[0,0, -0],u=0;u>1:-(u+1>>1));q[0]=0;q[1]=0;q[2]=0;for(var x=d;x=d?this.ja=0:10<=d&&26>=d?this.ja=1:27<=d&&40>=d&&(this.ja=2);this.m=function(b){var a;if(b>this.b-b+1;this.b-=b;return a}if(b>8-(b-(this.b+1));this.b=this.b-b%8;0>this.b&&(this.b=8+this.b);return a}if(b>8-(b-(this.b+1+8)));this.b=this.b-(b-8)%8;0>this.b&&(this.b=8+this.b);return a}return 0};this.Fa=function(){return this.F>this.S.length-this.fb-2?0:this.m(4)};this.Ya=function(b){for(var a=0;1!=b>>a;)a++;return this.m(ac[this.ja][a])};this.bb=function(b){var a="",c="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".split(""); +'use strict';var aa="function"==typeof Object.defineProperties?Object.defineProperty:function(d,c,a){d!=Array.prototype&&d!=Object.prototype&&(d[c]=a.value)},e="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this;function ba(){ba=function(){};e.Symbol||(e.Symbol=da)}var da=function(){var d=0;return function(c){return"jscomp_symbol_"+(c||"")+d++}}(); +function sa(){ba();var d=e.Symbol.iterator;d||(d=e.Symbol.iterator=e.Symbol("iterator"));"function"!=typeof Array.prototype[d]&&aa(Array.prototype,d,{configurable:!0,writable:!0,value:function(){return ta(this)}});sa=function(){}}function ta(d){var c=0;return ua(function(){return c=d}}); +Oa("Array.prototype.find",function(d){return d?d:function(c,a){a:{var b=this;b instanceof String&&(b=String(b));for(var g=b.length,d=0;d>8}var ea=h,Q= +w,R=f,E=n;ea=void 0===ea?k:ea;Q=void 0===Q?null:Q;var S=va(Ra(R,E)),z=S.next().value,C=S.next().value,X=S.next().value;if(Q){if(!(Q instanceof Uint8ClampedArray)||Q.byteLength!==C*X)throw Error("QR Error: Illegal Buffer.");var Y=Q}else Y=new Uint8ClampedArray(C*X);for(var U=0;UZ&&(Z=fa)}Va+=R}if(Z-M>Ta){var Xa=(M+Z)/2;var xa=Math.min(255,Xa+ +(M+Z)/4,1.1*Xa)}else if(0===V||0===U)xa=M-1;else{var ya=U*C+V,Ya=(Y[ya-1]+Y[ya-C]+C[ya-C-1])/3;xa=Ya>M?Ya:M-1}Y[U*C+V]=xa}for(var ha=0;ha=za;++za)for(var Aa=-2;2>=Aa;++Aa)Za+=Y[Math.max(0,Math.min(X-1,ha+Aa))*C+Math.max(0,Math.min(C-1,ia+za))];var $a=k,Ba=R,Kb=Za/25,ja=ea;ja=void 0===ja?$a:ja;for(var ab=Math.min(ha*z,E-z)*Ba+Math.min(ia*z,Ba-z),bb=0;bbd||40=c?Db(a):null};function Gb(d,c,a,b,g,k,h,l,m){this.o=d;this.s=b;this.u=h;this.v=c;this.w=g;this.A=l;this.B=a;this.C=k;this.D=m;this.sb=function(b){for(var a=b.length,c=this.o,g=this.s,d=this.u,k=this.v,h=this.w,l=this.A,m=this.B,p=this.C,R=this.D,E=0;EMath.abs(b-c);if(d){var h=c;c=a;a=h;h=b;b=g;g=h}var l=Math.abs(b-c),m=Math.abs(g-a),p=-l>>1,w=ab?(h=c/(c-b),b=0):b>=f&&(h= +(f-1-c)/(b-c),b=f-1);g=Math.floor(a-(g-a)*h);h=1;0>g?(h=a/(a-g),g=0):g>=n&&(h=(n-1-a)/(g-a),g=n-1);b=Math.floor(c+(b-c)*h);d+=this.Ca(c,a,b,g);return d-1};this.qa=function(c,a){var b=this.Da(Math.floor(c.g()),Math.floor(c.h()),Math.floor(a.g()),Math.floor(a.h()));c=this.Da(Math.floor(a.g()),Math.floor(a.h()),Math.floor(c.g()),Math.floor(c.h()));return isNaN(b)?c/7:isNaN(c)?b/7:(b+c)/14};this.Ja=function(c,a,b){return(this.qa(c,a)+this.qa(c,b))/2};this.sa=function(c,a){var b=c.g()-a.g();c=c.h()-a.h(); +return Math.sqrt(b*b+c*c)};this.Ka=function(c,a,b,g){c=(Math.round(this.sa(c,a)/g)+Math.round(this.sa(c,b)/g)>>1)+7;switch(c&3){case 0:c++;break;case 2:c--;break;case 3:throw Error("QR Error: in detector");}return c};this.Qa=function(c,a,b,g){g=Math.floor(g*c);var d=Math.max(0,a-g);a=Math.min(f-1,a+g);if(a-d<3*c)throw Error("QR Error: in detector");var h=Math.max(0,b-g);return(new Jb(this.H,d,h,a-d,Math.min(n-1,b+g)-h,c,this.U)).find()};this.La=function(c,a,b,g,d){d-=3.5;var k;if(null!=g){var l=g.g(); +g=g.h();var m=k=d-3}else l=a.g()-c.g()+b.g(),g=a.h()-c.h()+b.h(),m=k=d;return Hb(c.g(),c.h(),a.g(),a.h(),l,g,b.g(),b.h()).pb(Hb(3.5,3.5,d,3.5,m,k,3.5,d).Ha())};this.lb=function(c,a,b){for(var g=new Ab(b),d=Array(b<<1),h=0;h>1)+.5,d[p+1]=m;a.sb(d);m=d;for(var w=f,q=n,u=!0,t=0;tx||x>w||-1>v||v>q)throw Error("QR Error: Error.checkAndNudgePoints");u=!1;-1==x?(m[t]=0,u=!0):x==w&& +(m[t]=w-1,u=!0);-1==v?(m[t+1]=0,u=!0):v==q&&(m[t+1]=q-1,u=!0)}u=!0;for(t=m.length-2;0<=t&&u;t-=2){x=Math.floor(m[t]);v=Math.floor(m[t+1]);if(-1>x||x>w||-1>v||v>q)throw Error("QR Error: Error.checkAndNudgePoints");u=!1;-1==x?(m[t]=0,u=!0):x==w&&(m[t]=w-1,u=!0);-1==v?(m[t+1]=0,u=!0):v==q&&(m[t+1]=q-1,u=!0)}try{for(p=0;p>1,h)}catch(ca){throw Error("QR Error: Error.checkAndNudgePoints");}}return g};this.ib=function(c){var a=c.qb,b=c.rb;c=c.Ga;var g= +this.Ja(a,b,c);if(1>g)throw Error("QR Error: in detector");var d=this.Ka(a,b,c,g);if(1!=d%4)throw Error("QR Error: Error getProvisionalVersionForDimension");try{var h=Db(d-17>>2)}catch(w){throw Error("QR Error: Error getVersionForNumber");}var l=h.fa()-7,m=null;if(0=p;p<<=1)try{m=this.Qa(g,h,l,p);break}catch(w){}}a=this.lb(this.H,this.La(a,b,c,m,d),d);return new Ib(a)}; +this.Oa=function(){var c=(new Lb).Ta(this.H);return this.ib(c)}};var Mb=[[21522,0],[20773,1],[24188,2],[23371,3],[17913,4],[16590,5],[20375,6],[19104,7],[30660,8],[29427,9],[32170,10],[30877,11],[26159,12],[25368,13],[27713,14],[26998,15],[5769,16],[5054,17],[7399,18],[6608,19],[1890,20],[597,21],[3340,22],[2107,23],[13663,24],[12392,25],[16177,26],[14854,27],[9396,28],[8579,29],[11994,30],[11245,31]],L=[0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4];function Ob(d){var c=d>>3&3;if(0>c||c>=Sb.length)throw Error("QR Error: ArgumentException");this.Pa=Sb[c];this.Na=d&7} +function Fb(d,c){d^=c;return L[d&15]+L[O(d,4)&15]+L[O(d,8)&15]+L[O(d,12)&15]+L[O(d,16)&15]+L[O(d,20)&15]+L[O(d,24)&15]+L[O(d,28)&15]}function Tb(d){var c=Ub(d);return null!=c?c:Ub(d^21522)}function Ub(d){for(var c=4294967295,a=0,b=0;b=c?new Ob(a):null};function Vb(d,c,a){this.gb=d;this.i=c;this.name=a;this.getName=function(){return this.name}}var Sb=[new Vb(1,0,"M"),new Vb(0,1,"L"),new Vb(3,2,"H"),new Vb(2,3,"Q")];function Ab(d){var c;c||(c=d);if(1>d||1>c)throw Error("QR Error: Both dimensions must be greater than 0");this.width=d;this.height=c;var a=d>>5;0!=(d&31)&&a++;this.X=a;this.i=Array(a*c);for(d=0;d>5)],b&31)&1)};this.nb=function(b,a){this.i[a*this.X+(b>>5)]|=1<<(b&31)};this.M=function(b, +a){this.i[a*this.X+(b>>5)]^=1<<(b&31)};this.clear=function(){for(var b=this.i.length,a=0;aa||0>b)throw Error("QR Error: Left and top must be nonnegative");if(1>d||1>c)throw Error("QR Error: Height and width must be at least 1");c=b+c;d=a+d;if(d>this.height||c>this.width)throw Error("QR Error: The region must fit inside the matrix");for(;a>5)]|=1<<(k&31)}};function ub(d,c){this.ya=d;this.T=c};function tb(d){var c=d.ea();if(21>c||1!=(c&3))throw Error("QR Error: Error BitMatrixParser");this.V=d;this.O=this.J=null;this.K=function(a,b,c){return this.V.ga(a,b)?c<<1|1:c<<1};this.Aa=function(){if(null!=this.O)return this.O;for(var a=0,b=0;6>b;b++)a=this.K(b,8,a);a=this.K(7,8,a);a=this.K(8,8,a);a=this.K(8,7,a);for(b=5;0<=b;b--)a=this.K(8,b,a);this.O=Tb(a);if(null!=this.O)return this.O;var c=this.V.ea();a=0;var d=c-8;for(b=c-1;b>=d;b--)a=this.K(b,8,a);for(b=c-7;b>2;if(6>=b)return Db(b);b=0;for(var c=a-11,d=5;0<=d;d--)for(var h=a-9;h>=c;h--)b=this.K(h,d,b);this.J=Eb(b);if(null!=this.J&&this.J.fa()==a)return this.J;b=0;for(h=5;0<=h;h--)for(d=a-9;d>=c;d--)b=this.K(h,d,b);this.J=Eb(b);if(null!=this.J&&this.J.fa()==a)return this.J;throw Error("QR Error: Error readVersion");};this.jb=function(){var a=this.Aa(), +b=this.Ba();a=a.Na;if(0>a||7t;t++)c.ga(w-t,u)||(p++,m<<=1,this.V.ga(w-t,u)&&(m|=1),8==p&&(h[l++]=m,m=p=0));d^=1}if(l!=b.na)throw Error("QR Error: Error readCodewords");return h}};var Wb=[new function(){this.R=function(d,c){for(var a=0;ab.length){var c=a;a=b;b=c}c=Array(b.length);for(var g=b.length-a.length,m=0;mb)throw Error("QR Error: System.ArgumentException");if(0==a)return this.a.l();var c=this.f.length; +b=Array(c+b);for(var d=0;da;a++)this.Y[a]=c,c<<=1,256<=c&&(c^=d);for(a=0;255>a;a++)this.aa[this.Y[a]]=a;d=Array(1);d[0]=0;this.Ea=new P(this,Array(d));d=Array(1);d[0]=1;this.za=new P(this,Array(d));this.l=function(){return this.Ea};this.pa=function(b,a){if(0>b)throw Error("QR Error: System.ArgumentException");if(0==a)return this.Ea;b=Array(b+1);for(var c=0;cd)throw Error("QR Error: ReedSolomonException Bad error location");c[d]^=b[k]}};this.kb=function(c,a,b){if(c.I()=Math.floor(b/2);){var m=c,p=d,w=h;c=a;d=k;h=l;if(c.l())throw Error("QR Error: r_{i-1} was zero");a=m;l=this.a.l();for(k=this.a.inverse(c.Z(c.I()));a.I()>=c.I()&&!a.l();){m=a.I()-c.I();var q=this.a.multiply(a.Z(a.I()),k);l=l.ba(this.a.pa(m,q));a=a.ba(c.eb(m,q))}k=l.wa(d).ba(p);l=l.wa(h).ba(w)}b=l.Z(0);if(0==b)throw Error("QR Error: ReedSolomonException sigmaTilde(0) was zero");b=this.a.inverse(b);c=l.xa(b);b=a.xa(b);return[c,b]};this.Ra=function(c){var a= +c.I();if(1==a)return Array(c.Z(1));for(var b=Array(a),d=0,k=1;256>k&&d>c:(d>>c)+(2<<~c)};var ac=3,bc=57,cc=2;function dc(d){function c(b,a){var c=b.g()-a.g();b=b.h()-a.h();return Math.sqrt(c*c+b*b)}var a=c(d[0],d[1]),b=c(d[1],d[2]),g=c(d[0],d[2]);b>=a&&b>=g?(b=d[0],a=d[1],g=d[2]):g>=b&&g>=a?(b=d[1],a=d[0],g=d[2]):(b=d[2],a=d[0],g=d[1]);if(0>function(b,a,c){var d=a.x;a=a.y;return(c.x-d)*(b.y-a)-(c.y-a)*(b.x-d)}(a,b,g)){var k=a;a=g;g=k}d[0]=a;d[1]=b;d[2]=g} +function ec(d,c,a){this.x=d;this.y=c;this.count=1;this.G=a;this.g=function(){return this.x};this.h=function(){return this.y};this.va=function(){this.count++};this.ha=function(b,a,c){return Math.abs(a-this.y)<=b&&Math.abs(c-this.x)<=b?(b=Math.abs(b-this.G),1>=b||1>=b/this.G):!1}}function fc(d){this.Ga=d[0];this.qb=d[1];this.rb=d[2]} +function Lb(){this.H=null;this.c=[];this.ma=!1;this.L=[0,0,0,0,0];this.U=null;this.ta=function(){this.L[0]=0;this.L[1]=0;this.L[2]=0;this.L[3]=0;this.L[4]=0;return this.L};this.N=function(d){for(var c=0,a=0;5>a;a++){var b=d[a];if(0==b)return!1;c+=b}if(7>c)return!1;c=Math.floor(c/7);a=Math.floor(.7*c);return Math.abs(c-d[0])l)return NaN;for(;0<=l&&!g[c+l*f]&&h[1]<=a;)h[1]++,l--;if(0>l||h[1]>a)return NaN;for(;0<=l&&g[c+l*f]&&h[0]<=a;)h[0]++,l--;if(h[0]>a)return NaN;for(l=d+1;l=a)return NaN;for(;l=a||5*Math.abs(h[0]+h[1]+h[2]+h[3]+h[4]-b)>=2*b?NaN:this.N(h)?this.W(h,l):NaN};this.Ma=function(d,c,a,b){for(var g=this.H,k=f,h=this.ta(), +l=d;0<=l&&g[l+c*f];)h[2]++,l--;if(0>l)return NaN;for(;0<=l&&!g[l+c*f]&&h[1]<=a;)h[1]++,l--;if(0>l||h[1]>a)return NaN;for(;0<=l&&g[l+c*f]&&h[0]<=a;)h[0]++,l--;if(h[0]>a)return NaN;for(l=d+1;l=a)return NaN;for(;l=a||5*Math.abs(h[0]+h[1]+h[2]+h[3]+h[4]-b)>=b?NaN:this.N(h)?this.W(h,l):NaN};this.$=function(d,c,a){var b=d[0]+d[1]+d[2]+d[3]+d[4];a=this.W(d,a);c=this.ia(c, +Math.floor(a),d[2],b);if(!isNaN(c)&&(a=this.Ma(Math.floor(a),Math.floor(c),d[2],b),!isNaN(a))){d=b/7;b=!1;for(var g=this.c.length,k=0;kd)throw Error("QR Error: Couldn't find enough finder patterns (found "+d+")");if(3d&&this.c.splice(b,1)}3a.count?-1:b.count=d)return 0;for(var c=null,a=0;a=cc)if(null==c)c=b;else return this.ma=!0,Math.floor((Math.abs(c.g()-b.g())-Math.abs(c.h()- +b.h()))/2)}return 0};this.ua=function(){for(var d=0,c=0,a=this.c.length,b=0;b=cc&&(d++,c+=g.G)}if(3>d)return!1;d=c/a;var k=0;for(b=0;bk[2]&&(h+=l-k[2]-b,m=a-1));else{do m++;while(m=b||1>=b/this.G):!1}} +function Jb(d,c,a,b,g,k,h){this.H=d;this.c=[];this.ob=c;this.width=b;this.height=g;this.cb=k;this.L=[0,0,0];this.U=h;this.W=function(b,a){return a-b[2]-b[1]/2};this.N=function(b){for(var a=this.cb,c=a/2,d=0;3>d;d++)if(Math.abs(a-b[d])>=c)return!1;return!0};this.ia=function(b,a,c,d){var g=this.H,k=n,h=this.L;h[0]=0;h[1]=0;h[2]=0;for(var l=b;0<=l&&g[a+l*f]&&h[1]<=c;)h[1]++,l--;if(0>l||h[1]>c)return NaN;for(;0<=l&&!g[a+l*f]&&h[0]<=c;)h[0]++,l--;if(h[0]>c)return NaN;for(l=b+1;lc)return NaN;for(;lc||5*Math.abs(h[0]+h[1]+h[2]-d)>=2*d?NaN:this.N(h)?this.W(h,l):NaN};this.$=function(b,a,c){c=this.W(b,c);a=this.ia(a,Math.floor(c),2*b[1],b[0]+b[1]+b[2]);if(!isNaN(a)){b=(b[0]+b[1]+b[2])/3;for(var d=this.c.length,g=0;g>1),q=[0,0, +0],u=0;u>1:-(u+1>>1));q[0]=0;q[1]=0;q[2]=0;for(var x=c;x=c?this.ja=0:10<=c&&26>=c?this.ja=1:27<=c&&40>=c&&(this.ja=2);this.m=function(b){var a;if(b>this.b-b+1;this.b-=b;return a}if(b>8-(b-(this.b+1));this.b=this.b-b%8;0>this.b&&(this.b=8+this.b);return a}if(b>8-(b-(this.b+1+8)));this.b=this.b-(b-8)%8;0>this.b&&(this.b=8+this.b);return a}return 0};this.Fa=function(){return this.F>this.S.length-this.fb-2?0:this.m(4)};this.Ya=function(b){for(var a=0;1!=b>>a;)a++;return this.m($b[this.ja][a])};this.bb=function(b){var a="",c="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".split(""); do if(1a&&(c+="0"),10>a&&(c+="0"),b-=3):2==b?(a=this.m(7),10>a&&(c+="0"),b-=2):1==b&&(a=this.m(4),--b),c+=a;while(0=c+33088? c+33088:c+49472);b--}while(0c)throw Error("QR Error: Invalid data length: "+c);switch(a){case 1:a=this.$a(c);c=Array(a.length); for(var d=0;d { + const timeout = setTimeout(reject, 3000); + const worker = new Worker('/qr-scanner/qr-scanner-worker.min.js'); + worker.onerror = reject; + worker.onmessage = event => { + if (event.data.type !== 'qrResult') { + return; + } + clearTimeout(timeout); + if (event.data.data !== null) { + resolve(event.data.data); + } else { + reject(); + } + }; + QrScannerLib._loadImage(imageOrFileOrUrl).then(image => { + const imageData = QrScannerLib._getImageData(image); + worker.postMessage({ + type: 'decode', + data: imageData + }, [imageData.data.buffer]); + }).catch(reject); + }); + } + + /* async */ + static _getImageData(image) { + const canvas = document.createElement('canvas'); + canvas.width = image.width; + canvas.height = image.height; + const context = canvas.getContext('2d'); + context.drawImage(image, 0, 0); + return context.getImageData(0, 0, image.width, image.height); + } + + /* async */ + static _loadImage(imageOrFileOrUrl) { + if (imageOrFileOrUrl instanceof HTMLCanvasElement + || typeof('ImageBitmap')!=='undefined' && imageOrFileOrUrl instanceof ImageBitmap) { + return Promise.resolve(imageOrFileOrUrl); + } else if (imageOrFileOrUrl instanceof Image) { + return QrScannerLib._awaitImageLoad(imageOrFileOrUrl).then(() => imageOrFileOrUrl); + } else if (imageOrFileOrUrl instanceof File || imageOrFileOrUrl instanceof URL + || typeof(imageOrFileOrUrl)==='string') { + const image = new Image(); + if (imageOrFileOrUrl instanceof File) { + image.src = URL.createObjectURL(imageOrFileOrUrl); + } else { + image.src = imageOrFileOrUrl; + } + return QrScannerLib._awaitImageLoad(image).then(() => { + if (imageOrFileOrUrl instanceof File) { + URL.revokeObjectURL(image.src); + } + return image; + }); + } else { + return Promise.reject('Unsupported image type.'); + } + } + + /* async */ + static _awaitImageLoad(image) { + return new Promise((resolve, reject) => { + if (image.complete && image.naturalWidth!==0) { + // already loaded + resolve(); + } else { + image.onload = () => { + image.onload = null; + image.onerror = null; + resolve(); + }; + image.onerror = () => { + image.onload = null; + image.onerror = null; + reject(); + }; + } + }); + } } \ No newline at end of file diff --git a/src/qr-scanner-lib.js b/src/qr-scanner-lib.js index bb32f1c..6f1165f 100644 --- a/src/qr-scanner-lib.js +++ b/src/qr-scanner-lib.js @@ -19,13 +19,6 @@ class QrScannerLib { _updateSourceRect() { const smallestDimension = Math.min(this.$video.videoWidth, this.$video.videoHeight); this._sourceRectSize = Math.round(2 / 3 * smallestDimension); - - const scannerWidth = this.$video.parentElement.offsetWidth; - const scannerHeight = this.$video.parentElement.offsetHeight; - const widthRatio = this.$video.videoWidth / scannerWidth; - const heightRatio = this.$video.videoHeight / scannerHeight; - const scaleFactor = 1 / (Math.min(heightRatio, widthRatio) || 1); - const scaledOverlaySize = this._sourceRectSize * scaleFactor; } _scanFrame() { @@ -94,7 +87,86 @@ class QrScannerLib { }); } - scanImage(imageFile){ - return Promise.resolve('<< detected >>') + static scanImage(imageOrFileOrUrl) { + return new Promise((resolve, reject) => { + const timeout = setTimeout(reject, 3000); + const worker = new Worker('/qr-scanner/qr-scanner-worker.min.js'); + worker.onerror = reject; + worker.onmessage = event => { + if (event.data.type !== 'qrResult') { + return; + } + clearTimeout(timeout); + if (event.data.data !== null) { + resolve(event.data.data); + } else { + reject(); + } + }; + QrScannerLib._loadImage(imageOrFileOrUrl).then(image => { + const imageData = QrScannerLib._getImageData(image); + worker.postMessage({ + type: 'decode', + data: imageData + }, [imageData.data.buffer]); + }).catch(reject); + }); + } + + /* async */ + static _getImageData(image) { + const canvas = document.createElement('canvas'); + canvas.width = image.width; + canvas.height = image.height; + const context = canvas.getContext('2d'); + context.drawImage(image, 0, 0); + return context.getImageData(0, 0, image.width, image.height); + } + + /* async */ + static _loadImage(imageOrFileOrUrl) { + if (imageOrFileOrUrl instanceof HTMLCanvasElement + || typeof('ImageBitmap')!=='undefined' && imageOrFileOrUrl instanceof ImageBitmap) { + return Promise.resolve(imageOrFileOrUrl); + } else if (imageOrFileOrUrl instanceof Image) { + return QrScannerLib._awaitImageLoad(imageOrFileOrUrl).then(() => imageOrFileOrUrl); + } else if (imageOrFileOrUrl instanceof File || imageOrFileOrUrl instanceof URL + || typeof(imageOrFileOrUrl)==='string') { + const image = new Image(); + if (imageOrFileOrUrl instanceof File) { + image.src = URL.createObjectURL(imageOrFileOrUrl); + } else { + image.src = imageOrFileOrUrl; + } + return QrScannerLib._awaitImageLoad(image).then(() => { + if (imageOrFileOrUrl instanceof File) { + URL.revokeObjectURL(image.src); + } + return image; + }); + } else { + return Promise.reject('Unsupported image type.'); + } + } + + /* async */ + static _awaitImageLoad(image) { + return new Promise((resolve, reject) => { + if (image.complete && image.naturalWidth!==0) { + // already loaded + resolve(); + } else { + image.onload = () => { + image.onload = null; + image.onerror = null; + resolve(); + }; + image.onerror = () => { + image.onload = null; + image.onerror = null; + reject(); + }; + } + }); } } \ No newline at end of file diff --git a/src/worker.js b/src/worker.js index 5923dc5..def62f2 100644 --- a/src/worker.js +++ b/src/worker.js @@ -30,7 +30,7 @@ function decode(data) { } } -function setGreyscaleWeights(data) { +function setGrayscaleWeights(data) { if (data.red + data.green + data.blue !== 256) throw new Error('Weights have to sum up to 256'); qrcode.grayscaleWeights = data;