Merge lp:~dpm/qreator/touch-scanner into lp:qreator/touch

Proposed by David Planella
Status: Merged
Merged at revision: 16
Proposed branch: lp:~dpm/qreator/touch-scanner
Merge into: lp:qreator/touch
Diff against target: 4388 lines (+4189/-50)
12 files modified
.bzrignore (+2/-0)
js/jsqrcode-combined.js (+3748/-0)
qreator-touch.qml (+49/-16)
qreator/Creator.qml (+0/-17)
qreator/Decoder.qml (+68/-0)
qreator/DeviceOrientation.qml (+37/-0)
qreator/History.qml (+2/-2)
qreator/QrCodeCanvas.qml (+4/-4)
qreator/QrCodeDialog.qml (+44/-0)
qreator/QrCodeToolbar.qml (+13/-2)
qreator/Scanner.qml (+185/-9)
qreator/UploadImageDialog.qml (+37/-0)
To merge this branch: bzr merge lp:~dpm/qreator/touch-scanner
Reviewer Review Type Date Requested Status
Schwarzburg Approve
Review via email: mp+169683@code.launchpad.net

Description of the change

This branch implements the initial scanning support for Qreator

# Choice of decoding backend

I've tried hard to use the JavaScript backend, but due to either the camera, JavaScript limitations, or my own limitations in JavaScript knowledge, I've not managed to get it working in a way that it offers acceptable performance on a device.

In any case, I've abstracted the decoding backend in a Decoder component, and I'll leave the JavaScript backend in the source tree for a few revisions for testing purposes and in case we need to review the decision. Switching backends is as easy as setting the useJsbackend boolean property of the Decoder component.

The main issue is that for the decoding to be fast (as in less than 30 seconds) on a device, the picture taken needs to be downscaled to about 640px width. The C++ backend does that without trouble, but I've not found a way to do it with JavaScript. Downscaling the resolution at the camera level before passing the image to the decoding backend was something else I tried, but first the camera needs to be queried for supported resolutions (otherwise if I just set its resolution to 640x480 it might or might not work), and there seems to be no way to do that in QML.

Therefore, given the fact that it's working already, I've chosen to use the C++ plugin. This means that for either developing or running Qreator the qtdeclarative5-qzxing-plugin package [1] will need to be installed.

I want to consider the decoding backend as a library (i.e. a black box with interfaces) and it not be the focus of development. It just needs a bit of polish right now, but I hope to have it done in a couple of days.

# Caveats

• Decoding is not yet automated (e.g. with a timer), in the sense that the user has to take the picture himself/herself by revealing the toolbar on the scan page and press the "Decode" action. A further iteration of the scanner will implement automatic decoding.
• The camera is always active when Qreator is running. In a further iteration we'll need to deactivate it when a) the app loses focus and b) when we switch away from the Scanner tab to another one
• The "Open" action on the dialog shown upon successful decoding does nothing yet
• There is an issue whereby the root page of the PageStack in the Create tab allows a toolbar to be revealed, when in fact there should not be any toolbar there. It seems to happen only the first time the root page is shown. Perhaps an SDK bug?

# Camera caveats

For all its awesomeness and plug-and-play feel, the QML camera component has also been a source of headaches:

• When using camera.imageCapture.captureToLocation(captureLocation); pictures don't get overwritten on the device as expected. This works on the desktop, but inexplicably, it crashes the camera on a device whenever you take a second picture that should overwrite the picture at captureLocation
• There is no way to tell the camera not to store the pictures on the filesystem, and e.g. simply pass the in-memory preview to the decoder. It's nice to be able to store the pictures taken, but we cannot do it when scanning for codes automatically, as we'd be filling the filesystem with pictures taken every half a second or so. An option might be
just to delete the files from the filesystem via the C++ plugin.
• Cannot query device resolutions from the camera, thus there is no guarantee a given resolution will be set. I.e. we cannot use a low resolution to scan images, they need to be downscaled afterwards

[1] https://launchpad.net/~qreator-hackers/+archive/qreator-experimental

To post a comment you must log in.
Revision history for this message
Schwarzburg (stefan-schwarzburg) wrote :

Hi David,

First of all: Really great work!

I played with it on the desktop and it worked quite well (except for the camera..., now I have also pictures that are half bright, half dark :-) but thats another topic...).

I'm not surprised about the increase of performance with C++, and I think we should keep that in mind as advice for others and ourselves in the future: QML/JS for the interface, C++ for intensive tasks. On mobile devices, snappy applications are important!

I do have a few minor questions:

- you wrote "qt5declarative-qzxing-plugin", but you meant "qtdeclarative5-qzxing-plugin", right?

- Did you remove the history tab since it is still empty, or did you change your mind about including it as a tab, or do you want it removed in general?

- I guess that the debug tab is just there temporary? Do you want to remove it for every release but keep it there in-between releases?

As I said, great work!

review: Approve
Revision history for this message
David Planella (dpm) wrote :

On Sun, Jun 16, 2013 at 4:18 PM, Schwarzburg <
<email address hidden>> wrote:

> Review: Approve
>
> Hi David,
>
> First of all: Really great work!
>
> I played with it on the desktop and it worked quite well

Cool, thanks!

> (except for the camera..., now I have also pictures that are half bright,
> half dark :-) but thats another topic...).
>
>
Yeah, the Camera QML module (and perhaps also the desktop GStreamer
backend) is proving to be a bit of a pain :/

> I'm not surprised about the increase of performance with C++, and I think
> we should keep that in mind as advice for others and ourselves in the
> future: QML/JS for the interface, C++ for intensive tasks. On mobile
> devices, snappy applications are important!
>
>
Indeed. Still, I haven't quite given up on it, as JS does offer advantages
for development (if performance would be on par), as it avoids the need to
do cross-compilation (or at least compile for ARM on the device itself):
I've tried to downscale the image before sending it to the JS decoder, but
so far I've been unsuccessful:

https://code.launchpad.net/~dpm/qreator/touch-decoder-improvements

In any case, I've now created a project for the C++ plugin and I'm going to
get the daily package builds from there:

https://launchpad.net/qzxing

>
> I do have a few minor questions:
>
> - you wrote "qt5declarative-qzxing-plugin", but you meant
> "qtdeclarative5-qzxing-plugin", right?
>
>
You're right, fixed it on the description.

> - Did you remove the history tab since it is still empty, or did you
> change your mind about including it as a tab, or do you want it removed in
> general?
>
>
I still want history as a tab. If I removed any content there was in it, it
was unintentional, but I didn't recall it containing anything yet.

> - I guess that the debug tab is just there temporary? Do you want to
> remove it for every release but keep it there in-between releases?
>
>
Yeah, the debug tab is temporary. You might have seen the comment where I
tried to make it "hideable" via a property, but it turns out it is not
possible at the moment (
https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1190972).

Yes, until Qt Creator gains support for debugging code on devices it might
be worth keeping for in between releases, and comment it out for releases.

>
> As I said, great work!
>

Thanks for the review!

> --
> https://code.launchpad.net/~dpm/qreator/touch-scanner/+merge/169683
> You are the owner of lp:~dpm/qreator/touch-scanner.
>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2013-02-16 14:27:12 +0000
3+++ .bzrignore 2013-06-16 10:03:26 +0000
4@@ -1,1 +1,3 @@
5 qreator.qmlproject.user
6+qreator-touch.qmlproject.user
7+jsqrcode
8
9=== removed file 'img/about.png'
10Binary files img/about.png 2013-02-09 16:41:45 +0000 and img/about.png 1970-01-01 00:00:00 +0000 differ
11=== added file 'img/add@18.png'
12Binary files img/add@18.png 1970-01-01 00:00:00 +0000 and img/add@18.png 2013-06-16 10:03:26 +0000 differ
13=== added file 'img/camera@18.png'
14Binary files img/camera@18.png 1970-01-01 00:00:00 +0000 and img/camera@18.png 2013-06-16 10:03:26 +0000 differ
15=== added file 'img/edit@18.png'
16Binary files img/edit@18.png 1970-01-01 00:00:00 +0000 and img/edit@18.png 2013-06-16 10:03:26 +0000 differ
17=== removed file 'img/history.png'
18Binary files img/history.png 2013-02-09 16:41:45 +0000 and img/history.png 1970-01-01 00:00:00 +0000 differ
19=== added file 'img/save@18.png'
20Binary files img/save@18.png 1970-01-01 00:00:00 +0000 and img/save@18.png 2013-06-16 10:03:26 +0000 differ
21=== removed file 'img/settings.png'
22Binary files img/settings.png 2013-02-09 16:41:45 +0000 and img/settings.png 1970-01-01 00:00:00 +0000 differ
23=== added file 'img/share@18.png'
24Binary files img/share@18.png 1970-01-01 00:00:00 +0000 and img/share@18.png 2013-06-16 10:03:26 +0000 differ
25=== added file 'js/jsqrcode-combined.js'
26--- js/jsqrcode-combined.js 1970-01-01 00:00:00 +0000
27+++ js/jsqrcode-combined.js 2013-06-16 10:03:26 +0000
28@@ -0,0 +1,3748 @@
29+/*
30+ Ported to JavaScript by Lazar Laszlo 2011
31+
32+ lazarsoft@gmail.com, www.lazarsoft.info
33+
34+*/
35+
36+/*
37+*
38+* Copyright 2007 ZXing authors
39+*
40+* Licensed under the Apache License, Version 2.0 (the "License");
41+* you may not use this file except in compliance with the License.
42+* You may obtain a copy of the License at
43+*
44+* http://www.apache.org/licenses/LICENSE-2.0
45+*
46+* Unless required by applicable law or agreed to in writing, software
47+* distributed under the License is distributed on an "AS IS" BASIS,
48+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
49+* See the License for the specific language governing permissions and
50+* limitations under the License.
51+*/
52+
53+
54+var GridSampler = {};
55+
56+GridSampler.checkAndNudgePoints=function( image, points)
57+ {
58+ var width = qrcode.width;
59+ var height = qrcode.height;
60+ // Check and nudge points from start until we see some that are OK:
61+ var nudged = true;
62+ for (var offset = 0; offset < points.Length && nudged; offset += 2)
63+ {
64+ var x = Math.floor (points[offset]);
65+ var y = Math.floor( points[offset + 1]);
66+ if (x < - 1 || x > width || y < - 1 || y > height)
67+ {
68+ throw "Error.checkAndNudgePoints ";
69+ }
70+ nudged = false;
71+ if (x == - 1)
72+ {
73+ points[offset] = 0.0;
74+ nudged = true;
75+ }
76+ else if (x == width)
77+ {
78+ points[offset] = width - 1;
79+ nudged = true;
80+ }
81+ if (y == - 1)
82+ {
83+ points[offset + 1] = 0.0;
84+ nudged = true;
85+ }
86+ else if (y == height)
87+ {
88+ points[offset + 1] = height - 1;
89+ nudged = true;
90+ }
91+ }
92+ // Check and nudge points from end:
93+ nudged = true;
94+ for (var offset = points.Length - 2; offset >= 0 && nudged; offset -= 2)
95+ {
96+ var x = Math.floor( points[offset]);
97+ var y = Math.floor( points[offset + 1]);
98+ if (x < - 1 || x > width || y < - 1 || y > height)
99+ {
100+ throw "Error.checkAndNudgePoints ";
101+ }
102+ nudged = false;
103+ if (x == - 1)
104+ {
105+ points[offset] = 0.0;
106+ nudged = true;
107+ }
108+ else if (x == width)
109+ {
110+ points[offset] = width - 1;
111+ nudged = true;
112+ }
113+ if (y == - 1)
114+ {
115+ points[offset + 1] = 0.0;
116+ nudged = true;
117+ }
118+ else if (y == height)
119+ {
120+ points[offset + 1] = height - 1;
121+ nudged = true;
122+ }
123+ }
124+ }
125+
126+
127+
128+GridSampler.sampleGrid3=function( image, dimension, transform)
129+ {
130+ var bits = new BitMatrix(dimension);
131+ var points = new Array(dimension << 1);
132+ for (var y = 0; y < dimension; y++)
133+ {
134+ var max = points.length;
135+ var iValue = y + 0.5;
136+ for (var x = 0; x < max; x += 2)
137+ {
138+ points[x] = (x >> 1) + 0.5;
139+ points[x + 1] = iValue;
140+ }
141+ transform.transformPoints1(points);
142+ // Quick check to see if points transformed to something inside the image;
143+ // sufficient to check the endpoints
144+ GridSampler.checkAndNudgePoints(image, points);
145+ try
146+ {
147+ for (var x = 0; x < max; x += 2)
148+ {
149+ var xpoint = (Math.floor( points[x]) * 4) + (Math.floor( points[x + 1]) * qrcode.width * 4);
150+ var bit = image[Math.floor( points[x])+ qrcode.width* Math.floor( points[x + 1])];
151+ qrcode.imagedata.data[xpoint] = bit?255:0;
152+ qrcode.imagedata.data[xpoint+1] = bit?255:0;
153+ qrcode.imagedata.data[xpoint+2] = 0;
154+ qrcode.imagedata.data[xpoint+3] = 255;
155+ //bits[x >> 1][ y]=bit;
156+ if(bit)
157+ bits.set_Renamed(x >> 1, y);
158+ }
159+ }
160+ catch ( aioobe)
161+ {
162+ // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
163+ // transform gets "twisted" such that it maps a straight line of points to a set of points
164+ // whose endpoints are in bounds, but others are not. There is probably some mathematical
165+ // way to detect this about the transformation that I don't know yet.
166+ // This results in an ugly runtime exception despite our clever checks above -- can't have
167+ // that. We could check each point's coordinates but that feels duplicative. We settle for
168+ // catching and wrapping ArrayIndexOutOfBoundsException.
169+ throw "Error.checkAndNudgePoints";
170+ }
171+ }
172+ return bits;
173+ }
174+
175+GridSampler.sampleGridx=function( image, dimension, p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY)
176+{
177+ var transform = PerspectiveTransform.quadrilateralToQuadrilateral(p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
178+
179+ return GridSampler.sampleGrid3(image, dimension, transform);
180+}/*
181+ Ported to JavaScript by Lazar Laszlo 2011
182+
183+ lazarsoft@gmail.com, www.lazarsoft.info
184+
185+*/
186+
187+/*
188+*
189+* Copyright 2007 ZXing authors
190+*
191+* Licensed under the Apache License, Version 2.0 (the "License");
192+* you may not use this file except in compliance with the License.
193+* You may obtain a copy of the License at
194+*
195+* http://www.apache.org/licenses/LICENSE-2.0
196+*
197+* Unless required by applicable law or agreed to in writing, software
198+* distributed under the License is distributed on an "AS IS" BASIS,
199+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200+* See the License for the specific language governing permissions and
201+* limitations under the License.
202+*/
203+
204+
205+
206+function ECB(count, dataCodewords)
207+{
208+ this.count = count;
209+ this.dataCodewords = dataCodewords;
210+
211+ this.__defineGetter__("Count", function()
212+ {
213+ return this.count;
214+ });
215+ this.__defineGetter__("DataCodewords", function()
216+ {
217+ return this.dataCodewords;
218+ });
219+}
220+
221+function ECBlocks( ecCodewordsPerBlock, ecBlocks1, ecBlocks2)
222+{
223+ this.ecCodewordsPerBlock = ecCodewordsPerBlock;
224+ if(ecBlocks2)
225+ this.ecBlocks = new Array(ecBlocks1, ecBlocks2);
226+ else
227+ this.ecBlocks = new Array(ecBlocks1);
228+
229+ this.__defineGetter__("ECCodewordsPerBlock", function()
230+ {
231+ return this.ecCodewordsPerBlock;
232+ });
233+
234+ this.__defineGetter__("TotalECCodewords", function()
235+ {
236+ return this.ecCodewordsPerBlock * this.NumBlocks;
237+ });
238+
239+ this.__defineGetter__("NumBlocks", function()
240+ {
241+ var total = 0;
242+ for (var i = 0; i < this.ecBlocks.length; i++)
243+ {
244+ total += this.ecBlocks[i].length;
245+ }
246+ return total;
247+ });
248+
249+ this.getECBlocks=function()
250+ {
251+ return this.ecBlocks;
252+ }
253+}
254+
255+function Version( versionNumber, alignmentPatternCenters, ecBlocks1, ecBlocks2, ecBlocks3, ecBlocks4)
256+{
257+ this.versionNumber = versionNumber;
258+ this.alignmentPatternCenters = alignmentPatternCenters;
259+ this.ecBlocks = new Array(ecBlocks1, ecBlocks2, ecBlocks3, ecBlocks4);
260+
261+ var total = 0;
262+ var ecCodewords = ecBlocks1.ECCodewordsPerBlock;
263+ var ecbArray = ecBlocks1.getECBlocks();
264+ for (var i = 0; i < ecbArray.length; i++)
265+ {
266+ var ecBlock = ecbArray[i];
267+ total += ecBlock.Count * (ecBlock.DataCodewords + ecCodewords);
268+ }
269+ this.totalCodewords = total;
270+
271+ this.__defineGetter__("VersionNumber", function()
272+ {
273+ return this.versionNumber;
274+ });
275+
276+ this.__defineGetter__("AlignmentPatternCenters", function()
277+ {
278+ return this.alignmentPatternCenters;
279+ });
280+ this.__defineGetter__("TotalCodewords", function()
281+ {
282+ return this.totalCodewords;
283+ });
284+ this.__defineGetter__("DimensionForVersion", function()
285+ {
286+ return 17 + 4 * this.versionNumber;
287+ });
288+
289+ this.buildFunctionPattern=function()
290+ {
291+ var dimension = this.DimensionForVersion;
292+ var bitMatrix = new BitMatrix(dimension);
293+
294+ // Top left finder pattern + separator + format
295+ bitMatrix.setRegion(0, 0, 9, 9);
296+ // Top right finder pattern + separator + format
297+ bitMatrix.setRegion(dimension - 8, 0, 8, 9);
298+ // Bottom left finder pattern + separator + format
299+ bitMatrix.setRegion(0, dimension - 8, 9, 8);
300+
301+ // Alignment patterns
302+ var max = this.alignmentPatternCenters.length;
303+ for (var x = 0; x < max; x++)
304+ {
305+ var i = this.alignmentPatternCenters[x] - 2;
306+ for (var y = 0; y < max; y++)
307+ {
308+ if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0))
309+ {
310+ // No alignment patterns near the three finder paterns
311+ continue;
312+ }
313+ bitMatrix.setRegion(this.alignmentPatternCenters[y] - 2, i, 5, 5);
314+ }
315+ }
316+
317+ // Vertical timing pattern
318+ bitMatrix.setRegion(6, 9, 1, dimension - 17);
319+ // Horizontal timing pattern
320+ bitMatrix.setRegion(9, 6, dimension - 17, 1);
321+
322+ if (this.versionNumber > 6)
323+ {
324+ // Version info, top right
325+ bitMatrix.setRegion(dimension - 11, 0, 3, 6);
326+ // Version info, bottom left
327+ bitMatrix.setRegion(0, dimension - 11, 6, 3);
328+ }
329+
330+ return bitMatrix;
331+ }
332+ this.getECBlocksForLevel=function( ecLevel)
333+ {
334+ return this.ecBlocks[ecLevel.ordinal()];
335+ }
336+}
337+
338+Version.VERSION_DECODE_INFO = new Array(0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78, 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, 0x2542E, 0x26A64, 0x27541, 0x28C69);
339+
340+Version.VERSIONS = buildVersions();
341+
342+Version.getVersionForNumber=function( versionNumber)
343+{
344+ if (versionNumber < 1 || versionNumber > 40)
345+ {
346+ throw "ArgumentException";
347+ }
348+ return Version.VERSIONS[versionNumber - 1];
349+}
350+
351+Version.getProvisionalVersionForDimension=function(dimension)
352+{
353+ if (dimension % 4 != 1)
354+ {
355+ throw "Error getProvisionalVersionForDimension";
356+ }
357+ try
358+ {
359+ return Version.getVersionForNumber((dimension - 17) >> 2);
360+ }
361+ catch ( iae)
362+ {
363+ throw "Error getVersionForNumber";
364+ }
365+}
366+
367+Version.decodeVersionInformation=function( versionBits)
368+{
369+ var bestDifference = 0xffffffff;
370+ var bestVersion = 0;
371+ for (var i = 0; i < Version.VERSION_DECODE_INFO.length; i++)
372+ {
373+ var targetVersion = Version.VERSION_DECODE_INFO[i];
374+ // Do the version info bits match exactly? done.
375+ if (targetVersion == versionBits)
376+ {
377+ return this.getVersionForNumber(i + 7);
378+ }
379+ // Otherwise see if this is the closest to a real version info bit string
380+ // we have seen so far
381+ var bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion);
382+ if (bitsDifference < bestDifference)
383+ {
384+ bestVersion = i + 7;
385+ bestDifference = bitsDifference;
386+ }
387+ }
388+ // We can tolerate up to 3 bits of error since no two version info codewords will
389+ // differ in less than 4 bits.
390+ if (bestDifference <= 3)
391+ {
392+ return this.getVersionForNumber(bestVersion);
393+ }
394+ // If we didn't find a close enough match, fail
395+ return null;
396+}
397+
398+function buildVersions()
399+{
400+ return new Array(new Version(1, new Array(), new ECBlocks(7, new ECB(1, 19)), new ECBlocks(10, new ECB(1, 16)), new ECBlocks(13, new ECB(1, 13)), new ECBlocks(17, new ECB(1, 9))),
401+ new Version(2, new Array(6, 18), new ECBlocks(10, new ECB(1, 34)), new ECBlocks(16, new ECB(1, 28)), new ECBlocks(22, new ECB(1, 22)), new ECBlocks(28, new ECB(1, 16))),
402+ new Version(3, new Array(6, 22), new ECBlocks(15, new ECB(1, 55)), new ECBlocks(26, new ECB(1, 44)), new ECBlocks(18, new ECB(2, 17)), new ECBlocks(22, new ECB(2, 13))),
403+ new Version(4, new Array(6, 26), new ECBlocks(20, new ECB(1, 80)), new ECBlocks(18, new ECB(2, 32)), new ECBlocks(26, new ECB(2, 24)), new ECBlocks(16, new ECB(4, 9))),
404+ new Version(5, new Array(6, 30), new ECBlocks(26, new ECB(1, 108)), new ECBlocks(24, new ECB(2, 43)), new ECBlocks(18, new ECB(2, 15), new ECB(2, 16)), new ECBlocks(22, new ECB(2, 11), new ECB(2, 12))),
405+ new Version(6, new Array(6, 34), new ECBlocks(18, new ECB(2, 68)), new ECBlocks(16, new ECB(4, 27)), new ECBlocks(24, new ECB(4, 19)), new ECBlocks(28, new ECB(4, 15))),
406+ new Version(7, new Array(6, 22, 38), new ECBlocks(20, new ECB(2, 78)), new ECBlocks(18, new ECB(4, 31)), new ECBlocks(18, new ECB(2, 14), new ECB(4, 15)), new ECBlocks(26, new ECB(4, 13), new ECB(1, 14))),
407+ new Version(8, new Array(6, 24, 42), new ECBlocks(24, new ECB(2, 97)), new ECBlocks(22, new ECB(2, 38), new ECB(2, 39)), new ECBlocks(22, new ECB(4, 18), new ECB(2, 19)), new ECBlocks(26, new ECB(4, 14), new ECB(2, 15))),
408+ new Version(9, new Array(6, 26, 46), new ECBlocks(30, new ECB(2, 116)), new ECBlocks(22, new ECB(3, 36), new ECB(2, 37)), new ECBlocks(20, new ECB(4, 16), new ECB(4, 17)), new ECBlocks(24, new ECB(4, 12), new ECB(4, 13))),
409+ new Version(10, new Array(6, 28, 50), new ECBlocks(18, new ECB(2, 68), new ECB(2, 69)), new ECBlocks(26, new ECB(4, 43), new ECB(1, 44)), new ECBlocks(24, new ECB(6, 19), new ECB(2, 20)), new ECBlocks(28, new ECB(6, 15), new ECB(2, 16))),
410+ new Version(11, new Array(6, 30, 54), new ECBlocks(20, new ECB(4, 81)), new ECBlocks(30, new ECB(1, 50), new ECB(4, 51)), new ECBlocks(28, new ECB(4, 22), new ECB(4, 23)), new ECBlocks(24, new ECB(3, 12), new ECB(8, 13))),
411+ new Version(12, new Array(6, 32, 58), new ECBlocks(24, new ECB(2, 92), new ECB(2, 93)), new ECBlocks(22, new ECB(6, 36), new ECB(2, 37)), new ECBlocks(26, new ECB(4, 20), new ECB(6, 21)), new ECBlocks(28, new ECB(7, 14), new ECB(4, 15))),
412+ new Version(13, new Array(6, 34, 62), new ECBlocks(26, new ECB(4, 107)), new ECBlocks(22, new ECB(8, 37), new ECB(1, 38)), new ECBlocks(24, new ECB(8, 20), new ECB(4, 21)), new ECBlocks(22, new ECB(12, 11), new ECB(4, 12))),
413+ new Version(14, new Array(6, 26, 46, 66), new ECBlocks(30, new ECB(3, 115), new ECB(1, 116)), new ECBlocks(24, new ECB(4, 40), new ECB(5, 41)), new ECBlocks(20, new ECB(11, 16), new ECB(5, 17)), new ECBlocks(24, new ECB(11, 12), new ECB(5, 13))),
414+ new Version(15, new Array(6, 26, 48, 70), new ECBlocks(22, new ECB(5, 87), new ECB(1, 88)), new ECBlocks(24, new ECB(5, 41), new ECB(5, 42)), new ECBlocks(30, new ECB(5, 24), new ECB(7, 25)), new ECBlocks(24, new ECB(11, 12), new ECB(7, 13))),
415+ new Version(16, new Array(6, 26, 50, 74), new ECBlocks(24, new ECB(5, 98), new ECB(1, 99)), new ECBlocks(28, new ECB(7, 45), new ECB(3, 46)), new ECBlocks(24, new ECB(15, 19), new ECB(2, 20)), new ECBlocks(30, new ECB(3, 15), new ECB(13, 16))),
416+ new Version(17, new Array(6, 30, 54, 78), new ECBlocks(28, new ECB(1, 107), new ECB(5, 108)), new ECBlocks(28, new ECB(10, 46), new ECB(1, 47)), new ECBlocks(28, new ECB(1, 22), new ECB(15, 23)), new ECBlocks(28, new ECB(2, 14), new ECB(17, 15))),
417+ new Version(18, new Array(6, 30, 56, 82), new ECBlocks(30, new ECB(5, 120), new ECB(1, 121)), new ECBlocks(26, new ECB(9, 43), new ECB(4, 44)), new ECBlocks(28, new ECB(17, 22), new ECB(1, 23)), new ECBlocks(28, new ECB(2, 14), new ECB(19, 15))),
418+ new Version(19, new Array(6, 30, 58, 86), new ECBlocks(28, new ECB(3, 113), new ECB(4, 114)), new ECBlocks(26, new ECB(3, 44), new ECB(11, 45)), new ECBlocks(26, new ECB(17, 21), new ECB(4, 22)), new ECBlocks(26, new ECB(9, 13), new ECB(16, 14))),
419+ new Version(20, new Array(6, 34, 62, 90), new ECBlocks(28, new ECB(3, 107), new ECB(5, 108)), new ECBlocks(26, new ECB(3, 41), new ECB(13, 42)), new ECBlocks(30, new ECB(15, 24), new ECB(5, 25)), new ECBlocks(28, new ECB(15, 15), new ECB(10, 16))),
420+ new Version(21, new Array(6, 28, 50, 72, 94), new ECBlocks(28, new ECB(4, 116), new ECB(4, 117)), new ECBlocks(26, new ECB(17, 42)), new ECBlocks(28, new ECB(17, 22), new ECB(6, 23)), new ECBlocks(30, new ECB(19, 16), new ECB(6, 17))),
421+ new Version(22, new Array(6, 26, 50, 74, 98), new ECBlocks(28, new ECB(2, 111), new ECB(7, 112)), new ECBlocks(28, new ECB(17, 46)), new ECBlocks(30, new ECB(7, 24), new ECB(16, 25)), new ECBlocks(24, new ECB(34, 13))),
422+ new Version(23, new Array(6, 30, 54, 74, 102), new ECBlocks(30, new ECB(4, 121), new ECB(5, 122)), new ECBlocks(28, new ECB(4, 47), new ECB(14, 48)), new ECBlocks(30, new ECB(11, 24), new ECB(14, 25)), new ECBlocks(30, new ECB(16, 15), new ECB(14, 16))),
423+ new Version(24, new Array(6, 28, 54, 80, 106), new ECBlocks(30, new ECB(6, 117), new ECB(4, 118)), new ECBlocks(28, new ECB(6, 45), new ECB(14, 46)), new ECBlocks(30, new ECB(11, 24), new ECB(16, 25)), new ECBlocks(30, new ECB(30, 16), new ECB(2, 17))),
424+ new Version(25, new Array(6, 32, 58, 84, 110), new ECBlocks(26, new ECB(8, 106), new ECB(4, 107)), new ECBlocks(28, new ECB(8, 47), new ECB(13, 48)), new ECBlocks(30, new ECB(7, 24), new ECB(22, 25)), new ECBlocks(30, new ECB(22, 15), new ECB(13, 16))),
425+ new Version(26, new Array(6, 30, 58, 86, 114), new ECBlocks(28, new ECB(10, 114), new ECB(2, 115)), new ECBlocks(28, new ECB(19, 46), new ECB(4, 47)), new ECBlocks(28, new ECB(28, 22), new ECB(6, 23)), new ECBlocks(30, new ECB(33, 16), new ECB(4, 17))),
426+ new Version(27, new Array(6, 34, 62, 90, 118), new ECBlocks(30, new ECB(8, 122), new ECB(4, 123)), new ECBlocks(28, new ECB(22, 45), new ECB(3, 46)), new ECBlocks(30, new ECB(8, 23), new ECB(26, 24)), new ECBlocks(30, new ECB(12, 15), new ECB(28, 16))),
427+ new Version(28, new Array(6, 26, 50, 74, 98, 122), new ECBlocks(30, new ECB(3, 117), new ECB(10, 118)), new ECBlocks(28, new ECB(3, 45), new ECB(23, 46)), new ECBlocks(30, new ECB(4, 24), new ECB(31, 25)), new ECBlocks(30, new ECB(11, 15), new ECB(31, 16))),
428+ new Version(29, new Array(6, 30, 54, 78, 102, 126), new ECBlocks(30, new ECB(7, 116), new ECB(7, 117)), new ECBlocks(28, new ECB(21, 45), new ECB(7, 46)), new ECBlocks(30, new ECB(1, 23), new ECB(37, 24)), new ECBlocks(30, new ECB(19, 15), new ECB(26, 16))),
429+ new Version(30, new Array(6, 26, 52, 78, 104, 130), new ECBlocks(30, new ECB(5, 115), new ECB(10, 116)), new ECBlocks(28, new ECB(19, 47), new ECB(10, 48)), new ECBlocks(30, new ECB(15, 24), new ECB(25, 25)), new ECBlocks(30, new ECB(23, 15), new ECB(25, 16))),
430+ new Version(31, new Array(6, 30, 56, 82, 108, 134), new ECBlocks(30, new ECB(13, 115), new ECB(3, 116)), new ECBlocks(28, new ECB(2, 46), new ECB(29, 47)), new ECBlocks(30, new ECB(42, 24), new ECB(1, 25)), new ECBlocks(30, new ECB(23, 15), new ECB(28, 16))),
431+ new Version(32, new Array(6, 34, 60, 86, 112, 138), new ECBlocks(30, new ECB(17, 115)), new ECBlocks(28, new ECB(10, 46), new ECB(23, 47)), new ECBlocks(30, new ECB(10, 24), new ECB(35, 25)), new ECBlocks(30, new ECB(19, 15), new ECB(35, 16))),
432+ new Version(33, new Array(6, 30, 58, 86, 114, 142), new ECBlocks(30, new ECB(17, 115), new ECB(1, 116)), new ECBlocks(28, new ECB(14, 46), new ECB(21, 47)), new ECBlocks(30, new ECB(29, 24), new ECB(19, 25)), new ECBlocks(30, new ECB(11, 15), new ECB(46, 16))),
433+ new Version(34, new Array(6, 34, 62, 90, 118, 146), new ECBlocks(30, new ECB(13, 115), new ECB(6, 116)), new ECBlocks(28, new ECB(14, 46), new ECB(23, 47)), new ECBlocks(30, new ECB(44, 24), new ECB(7, 25)), new ECBlocks(30, new ECB(59, 16), new ECB(1, 17))),
434+ new Version(35, new Array(6, 30, 54, 78, 102, 126, 150), new ECBlocks(30, new ECB(12, 121), new ECB(7, 122)), new ECBlocks(28, new ECB(12, 47), new ECB(26, 48)), new ECBlocks(30, new ECB(39, 24), new ECB(14, 25)),new ECBlocks(30, new ECB(22, 15), new ECB(41, 16))),
435+ new Version(36, new Array(6, 24, 50, 76, 102, 128, 154), new ECBlocks(30, new ECB(6, 121), new ECB(14, 122)), new ECBlocks(28, new ECB(6, 47), new ECB(34, 48)), new ECBlocks(30, new ECB(46, 24), new ECB(10, 25)), new ECBlocks(30, new ECB(2, 15), new ECB(64, 16))),
436+ new Version(37, new Array(6, 28, 54, 80, 106, 132, 158), new ECBlocks(30, new ECB(17, 122), new ECB(4, 123)), new ECBlocks(28, new ECB(29, 46), new ECB(14, 47)), new ECBlocks(30, new ECB(49, 24), new ECB(10, 25)), new ECBlocks(30, new ECB(24, 15), new ECB(46, 16))),
437+ new Version(38, new Array(6, 32, 58, 84, 110, 136, 162), new ECBlocks(30, new ECB(4, 122), new ECB(18, 123)), new ECBlocks(28, new ECB(13, 46), new ECB(32, 47)), new ECBlocks(30, new ECB(48, 24), new ECB(14, 25)), new ECBlocks(30, new ECB(42, 15), new ECB(32, 16))),
438+ new Version(39, new Array(6, 26, 54, 82, 110, 138, 166), new ECBlocks(30, new ECB(20, 117), new ECB(4, 118)), new ECBlocks(28, new ECB(40, 47), new ECB(7, 48)), new ECBlocks(30, new ECB(43, 24), new ECB(22, 25)), new ECBlocks(30, new ECB(10, 15), new ECB(67, 16))),
439+ new Version(40, new Array(6, 30, 58, 86, 114, 142, 170), new ECBlocks(30, new ECB(19, 118), new ECB(6, 119)), new ECBlocks(28, new ECB(18, 47), new ECB(31, 48)), new ECBlocks(30, new ECB(34, 24), new ECB(34, 25)), new ECBlocks(30, new ECB(20, 15), new ECB(61, 16))));
440+}/*
441+ Ported to JavaScript by Lazar Laszlo 2011
442+
443+ lazarsoft@gmail.com, www.lazarsoft.info
444+
445+*/
446+
447+/*
448+*
449+* Copyright 2007 ZXing authors
450+*
451+* Licensed under the Apache License, Version 2.0 (the "License");
452+* you may not use this file except in compliance with the License.
453+* You may obtain a copy of the License at
454+*
455+* http://www.apache.org/licenses/LICENSE-2.0
456+*
457+* Unless required by applicable law or agreed to in writing, software
458+* distributed under the License is distributed on an "AS IS" BASIS,
459+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
460+* See the License for the specific language governing permissions and
461+* limitations under the License.
462+*/
463+
464+
465+function PerspectiveTransform( a11, a21, a31, a12, a22, a32, a13, a23, a33)
466+{
467+ this.a11 = a11;
468+ this.a12 = a12;
469+ this.a13 = a13;
470+ this.a21 = a21;
471+ this.a22 = a22;
472+ this.a23 = a23;
473+ this.a31 = a31;
474+ this.a32 = a32;
475+ this.a33 = a33;
476+ this.transformPoints1=function( points)
477+ {
478+ var max = points.length;
479+ var a11 = this.a11;
480+ var a12 = this.a12;
481+ var a13 = this.a13;
482+ var a21 = this.a21;
483+ var a22 = this.a22;
484+ var a23 = this.a23;
485+ var a31 = this.a31;
486+ var a32 = this.a32;
487+ var a33 = this.a33;
488+ for (var i = 0; i < max; i += 2)
489+ {
490+ var x = points[i];
491+ var y = points[i + 1];
492+ var denominator = a13 * x + a23 * y + a33;
493+ points[i] = (a11 * x + a21 * y + a31) / denominator;
494+ points[i + 1] = (a12 * x + a22 * y + a32) / denominator;
495+ }
496+ }
497+ this. transformPoints2=function(xValues, yValues)
498+ {
499+ var n = xValues.length;
500+ for (var i = 0; i < n; i++)
501+ {
502+ var x = xValues[i];
503+ var y = yValues[i];
504+ var denominator = this.a13 * x + this.a23 * y + this.a33;
505+ xValues[i] = (this.a11 * x + this.a21 * y + this.a31) / denominator;
506+ yValues[i] = (this.a12 * x + this.a22 * y + this.a32) / denominator;
507+ }
508+ }
509+
510+ this.buildAdjoint=function()
511+ {
512+ // Adjoint is the transpose of the cofactor matrix:
513+ return new PerspectiveTransform(this.a22 * this.a33 - this.a23 * this.a32, this.a23 * this.a31 - this.a21 * this.a33, this.a21 * this.a32 - this.a22 * this.a31, this.a13 * this.a32 - this.a12 * this.a33, this.a11 * this.a33 - this.a13 * this.a31, this.a12 * this.a31 - this.a11 * this.a32, this.a12 * this.a23 - this.a13 * this.a22, this.a13 * this.a21 - this.a11 * this.a23, this.a11 * this.a22 - this.a12 * this.a21);
514+ }
515+ this.times=function( other)
516+ {
517+ return new PerspectiveTransform(this.a11 * other.a11 + this.a21 * other.a12 + this.a31 * other.a13, this.a11 * other.a21 + this.a21 * other.a22 + this.a31 * other.a23, this.a11 * other.a31 + this.a21 * other.a32 + this.a31 * other.a33, this.a12 * other.a11 + this.a22 * other.a12 + this.a32 * other.a13, this.a12 * other.a21 + this.a22 * other.a22 + this.a32 * other.a23, this.a12 * other.a31 + this.a22 * other.a32 + this.a32 * other.a33, this.a13 * other.a11 + this.a23 * other.a12 +this.a33 * other.a13, this.a13 * other.a21 + this.a23 * other.a22 + this.a33 * other.a23, this.a13 * other.a31 + this.a23 * other.a32 + this.a33 * other.a33);
518+ }
519+
520+}
521+
522+PerspectiveTransform.quadrilateralToQuadrilateral=function( x0, y0, x1, y1, x2, y2, x3, y3, x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p)
523+{
524+
525+ var qToS = this.quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
526+ var sToQ = this.squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
527+ return sToQ.times(qToS);
528+}
529+
530+PerspectiveTransform.squareToQuadrilateral=function( x0, y0, x1, y1, x2, y2, x3, y3)
531+{
532+ var dy2 = y3 - y2;
533+ var dy3 = y0 - y1 + y2 - y3;
534+ if (dy2 == 0.0 && dy3 == 0.0)
535+ {
536+ return new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0.0, 0.0, 1.0);
537+ }
538+ else
539+ {
540+ var dx1 = x1 - x2;
541+ var dx2 = x3 - x2;
542+ var dx3 = x0 - x1 + x2 - x3;
543+ var dy1 = y1 - y2;
544+ var denominator = dx1 * dy2 - dx2 * dy1;
545+ var a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
546+ var a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
547+ return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0, a13, a23, 1.0);
548+ }
549+}
550+
551+PerspectiveTransform.quadrilateralToSquare=function( x0, y0, x1, y1, x2, y2, x3, y3)
552+{
553+ // Here, the adjoint serves as the inverse:
554+ return this.squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint();
555+}
556+
557+function DetectorResult(bits, points)
558+{
559+ this.bits = bits;
560+ this.points = points;
561+}
562+
563+
564+function Detector(image)
565+{
566+ this.image=image;
567+ this.resultPointCallback = null;
568+
569+ this.sizeOfBlackWhiteBlackRun=function( fromX, fromY, toX, toY)
570+ {
571+ // Mild variant of Bresenham's algorithm;
572+ // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
573+ var steep = Math.abs(toY - fromY) > Math.abs(toX - fromX);
574+ if (steep)
575+ {
576+ var temp = fromX;
577+ fromX = fromY;
578+ fromY = temp;
579+ temp = toX;
580+ toX = toY;
581+ toY = temp;
582+ }
583+
584+ var dx = Math.abs(toX - fromX);
585+ var dy = Math.abs(toY - fromY);
586+ var error = - dx >> 1;
587+ var ystep = fromY < toY?1:- 1;
588+ var xstep = fromX < toX?1:- 1;
589+ var state = 0; // In black pixels, looking for white, first or second time
590+ for (var x = fromX, y = fromY; x != toX; x += xstep)
591+ {
592+
593+ var realX = steep?y:x;
594+ var realY = steep?x:y;
595+ if (state == 1)
596+ {
597+ // In white pixels, looking for black
598+ if (this.image[realX + realY*qrcode.width])
599+ {
600+ state++;
601+ }
602+ }
603+ else
604+ {
605+ if (!this.image[realX + realY*qrcode.width])
606+ {
607+ state++;
608+ }
609+ }
610+
611+ if (state == 3)
612+ {
613+ // Found black, white, black, and stumbled back onto white; done
614+ var diffX = x - fromX;
615+ var diffY = y - fromY;
616+ return Math.sqrt( (diffX * diffX + diffY * diffY));
617+ }
618+ error += dy;
619+ if (error > 0)
620+ {
621+ if (y == toY)
622+ {
623+ break;
624+ }
625+ y += ystep;
626+ error -= dx;
627+ }
628+ }
629+ var diffX2 = toX - fromX;
630+ var diffY2 = toY - fromY;
631+ return Math.sqrt( (diffX2 * diffX2 + diffY2 * diffY2));
632+ }
633+
634+
635+ this.sizeOfBlackWhiteBlackRunBothWays=function( fromX, fromY, toX, toY)
636+ {
637+
638+ var result = this.sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
639+
640+ // Now count other way -- don't run off image though of course
641+ var scale = 1.0;
642+ var otherToX = fromX - (toX - fromX);
643+ if (otherToX < 0)
644+ {
645+ scale = fromX / (fromX - otherToX);
646+ otherToX = 0;
647+ }
648+ else if (otherToX >= qrcode.width)
649+ {
650+ scale = (qrcode.width - 1 - fromX) / (otherToX - fromX);
651+ otherToX = qrcode.width - 1;
652+ }
653+ var otherToY = Math.floor (fromY - (toY - fromY) * scale);
654+
655+ scale = 1.0;
656+ if (otherToY < 0)
657+ {
658+ scale = fromY / (fromY - otherToY);
659+ otherToY = 0;
660+ }
661+ else if (otherToY >= qrcode.height)
662+ {
663+ scale = (qrcode.height - 1 - fromY) / (otherToY - fromY);
664+ otherToY = qrcode.height - 1;
665+ }
666+ otherToX = Math.floor (fromX + (otherToX - fromX) * scale);
667+
668+ result += this.sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);
669+ return result - 1.0; // -1 because we counted the middle pixel twice
670+ }
671+
672+
673+
674+ this.calculateModuleSizeOneWay=function( pattern, otherPattern)
675+ {
676+ var moduleSizeEst1 = this.sizeOfBlackWhiteBlackRunBothWays(Math.floor( pattern.X), Math.floor( pattern.Y), Math.floor( otherPattern.X), Math.floor(otherPattern.Y));
677+ var moduleSizeEst2 = this.sizeOfBlackWhiteBlackRunBothWays(Math.floor(otherPattern.X), Math.floor(otherPattern.Y), Math.floor( pattern.X), Math.floor(pattern.Y));
678+ if (isNaN(moduleSizeEst1))
679+ {
680+ return moduleSizeEst2 / 7.0;
681+ }
682+ if (isNaN(moduleSizeEst2))
683+ {
684+ return moduleSizeEst1 / 7.0;
685+ }
686+ // Average them, and divide by 7 since we've counted the width of 3 black modules,
687+ // and 1 white and 1 black module on either side. Ergo, divide sum by 14.
688+ return (moduleSizeEst1 + moduleSizeEst2) / 14.0;
689+ }
690+
691+
692+ this.calculateModuleSize=function( topLeft, topRight, bottomLeft)
693+ {
694+ // Take the average
695+ return (this.calculateModuleSizeOneWay(topLeft, topRight) + this.calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0;
696+ }
697+
698+ this.distance=function( pattern1, pattern2)
699+ {
700+ var xDiff = pattern1.X - pattern2.X;
701+ var yDiff = pattern1.Y - pattern2.Y;
702+ return Math.sqrt( (xDiff * xDiff + yDiff * yDiff));
703+ }
704+ this.computeDimension=function( topLeft, topRight, bottomLeft, moduleSize)
705+ {
706+
707+ var tltrCentersDimension = Math.round(this.distance(topLeft, topRight) / moduleSize);
708+ var tlblCentersDimension = Math.round(this.distance(topLeft, bottomLeft) / moduleSize);
709+ var dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7;
710+ switch (dimension & 0x03)
711+ {
712+
713+ // mod 4
714+ case 0:
715+ dimension++;
716+ break;
717+ // 1? do nothing
718+
719+ case 2:
720+ dimension--;
721+ break;
722+
723+ case 3:
724+ throw "Error";
725+ }
726+ return dimension;
727+ }
728+
729+ this.findAlignmentInRegion=function( overallEstModuleSize, estAlignmentX, estAlignmentY, allowanceFactor)
730+ {
731+ // Look for an alignment pattern (3 modules in size) around where it
732+ // should be
733+ var allowance = Math.floor (allowanceFactor * overallEstModuleSize);
734+ var alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance);
735+ var alignmentAreaRightX = Math.min(qrcode.width - 1, estAlignmentX + allowance);
736+ if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3)
737+ {
738+ throw "Error";
739+ }
740+
741+ var alignmentAreaTopY = Math.max(0, estAlignmentY - allowance);
742+ var alignmentAreaBottomY = Math.min(qrcode.height - 1, estAlignmentY + allowance);
743+
744+ var alignmentFinder = new AlignmentPatternFinder(this.image, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX - alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, this.resultPointCallback);
745+ return alignmentFinder.find();
746+ }
747+
748+ this.createTransform=function( topLeft, topRight, bottomLeft, alignmentPattern, dimension)
749+ {
750+ var dimMinusThree = dimension - 3.5;
751+ var bottomRightX;
752+ var bottomRightY;
753+ var sourceBottomRightX;
754+ var sourceBottomRightY;
755+ if (alignmentPattern != null)
756+ {
757+ bottomRightX = alignmentPattern.X;
758+ bottomRightY = alignmentPattern.Y;
759+ sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0;
760+ }
761+ else
762+ {
763+ // Don't have an alignment pattern, just make up the bottom-right point
764+ bottomRightX = (topRight.X - topLeft.X) + bottomLeft.X;
765+ bottomRightY = (topRight.Y - topLeft.Y) + bottomLeft.Y;
766+ sourceBottomRightX = sourceBottomRightY = dimMinusThree;
767+ }
768+
769+ var transform = PerspectiveTransform.quadrilateralToQuadrilateral(3.5, 3.5, dimMinusThree, 3.5, sourceBottomRightX, sourceBottomRightY, 3.5, dimMinusThree, topLeft.X, topLeft.Y, topRight.X, topRight.Y, bottomRightX, bottomRightY, bottomLeft.X, bottomLeft.Y);
770+
771+ return transform;
772+ }
773+
774+ this.sampleGrid=function( image, transform, dimension)
775+ {
776+
777+ var sampler = GridSampler;
778+ return sampler.sampleGrid3(image, dimension, transform);
779+ }
780+
781+ this.processFinderPatternInfo = function( info)
782+ {
783+
784+ var topLeft = info.TopLeft;
785+ var topRight = info.TopRight;
786+ var bottomLeft = info.BottomLeft;
787+
788+ var moduleSize = this.calculateModuleSize(topLeft, topRight, bottomLeft);
789+ if (moduleSize < 1.0)
790+ {
791+ throw "Error";
792+ }
793+ var dimension = this.computeDimension(topLeft, topRight, bottomLeft, moduleSize);
794+ var provisionalVersion = Version.getProvisionalVersionForDimension(dimension);
795+ var modulesBetweenFPCenters = provisionalVersion.DimensionForVersion - 7;
796+
797+ var alignmentPattern = null;
798+ // Anything above version 1 has an alignment pattern
799+ if (provisionalVersion.AlignmentPatternCenters.length > 0)
800+ {
801+
802+ // Guess where a "bottom right" finder pattern would have been
803+ var bottomRightX = topRight.X - topLeft.X + bottomLeft.X;
804+ var bottomRightY = topRight.Y - topLeft.Y + bottomLeft.Y;
805+
806+ // Estimate that alignment pattern is closer by 3 modules
807+ // from "bottom right" to known top left location
808+ var correctionToTopLeft = 1.0 - 3.0 / modulesBetweenFPCenters;
809+ var estAlignmentX = Math.floor (topLeft.X + correctionToTopLeft * (bottomRightX - topLeft.X));
810+ var estAlignmentY = Math.floor (topLeft.Y + correctionToTopLeft * (bottomRightY - topLeft.Y));
811+
812+ // Kind of arbitrary -- expand search radius before giving up
813+ for (var i = 4; i <= 16; i <<= 1)
814+ {
815+ //try
816+ //{
817+ alignmentPattern = this.findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, i);
818+ break;
819+ //}
820+ //catch (re)
821+ //{
822+ // try next round
823+ //}
824+ }
825+ // If we didn't find alignment pattern... well try anyway without it
826+ }
827+
828+ var transform = this.createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);
829+
830+ var bits = this.sampleGrid(this.image, transform, dimension);
831+
832+ var points;
833+ if (alignmentPattern == null)
834+ {
835+ points = new Array(bottomLeft, topLeft, topRight);
836+ }
837+ else
838+ {
839+ points = new Array(bottomLeft, topLeft, topRight, alignmentPattern);
840+ }
841+ return new DetectorResult(bits, points);
842+ }
843+
844+
845+
846+ this.detect=function()
847+ {
848+ var info = new FinderPatternFinder().findFinderPattern(this.image);
849+
850+ return this.processFinderPatternInfo(info);
851+ }
852+}/*
853+ Ported to JavaScript by Lazar Laszlo 2011
854+
855+ lazarsoft@gmail.com, www.lazarsoft.info
856+
857+*/
858+
859+/*
860+*
861+* Copyright 2007 ZXing authors
862+*
863+* Licensed under the Apache License, Version 2.0 (the "License");
864+* you may not use this file except in compliance with the License.
865+* You may obtain a copy of the License at
866+*
867+* http://www.apache.org/licenses/LICENSE-2.0
868+*
869+* Unless required by applicable law or agreed to in writing, software
870+* distributed under the License is distributed on an "AS IS" BASIS,
871+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
872+* See the License for the specific language governing permissions and
873+* limitations under the License.
874+*/
875+
876+
877+var FORMAT_INFO_MASK_QR = 0x5412;
878+var FORMAT_INFO_DECODE_LOOKUP = new Array(new Array(0x5412, 0x00), new Array(0x5125, 0x01), new Array(0x5E7C, 0x02), new Array(0x5B4B, 0x03), new Array(0x45F9, 0x04), new Array(0x40CE, 0x05), new Array(0x4F97, 0x06), new Array(0x4AA0, 0x07), new Array(0x77C4, 0x08), new Array(0x72F3, 0x09), new Array(0x7DAA, 0x0A), new Array(0x789D, 0x0B), new Array(0x662F, 0x0C), new Array(0x6318, 0x0D), new Array(0x6C41, 0x0E), new Array(0x6976, 0x0F), new Array(0x1689, 0x10), new Array(0x13BE, 0x11), new Array(0x1CE7, 0x12), new Array(0x19D0, 0x13), new Array(0x0762, 0x14), new Array(0x0255, 0x15), new Array(0x0D0C, 0x16), new Array(0x083B, 0x17), new Array(0x355F, 0x18), new Array(0x3068, 0x19), new Array(0x3F31, 0x1A), new Array(0x3A06, 0x1B), new Array(0x24B4, 0x1C), new Array(0x2183, 0x1D), new Array(0x2EDA, 0x1E), new Array(0x2BED, 0x1F));
879+var BITS_SET_IN_HALF_BYTE = new Array(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
880+
881+
882+function FormatInformation(formatInfo)
883+{
884+ this.errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
885+ this.dataMask = (formatInfo & 0x07);
886+
887+ this.__defineGetter__("ErrorCorrectionLevel", function()
888+ {
889+ return this.errorCorrectionLevel;
890+ });
891+ this.__defineGetter__("DataMask", function()
892+ {
893+ return this.dataMask;
894+ });
895+ this.GetHashCode=function()
896+ {
897+ return (this.errorCorrectionLevel.ordinal() << 3) | dataMask;
898+ }
899+ this.Equals=function( o)
900+ {
901+ var other = o;
902+ return this.errorCorrectionLevel == other.errorCorrectionLevel && this.dataMask == other.dataMask;
903+ }
904+}
905+
906+FormatInformation.numBitsDiffering=function( a, b)
907+{
908+ a ^= b; // a now has a 1 bit exactly where its bit differs with b's
909+ // Count bits set quickly with a series of lookups:
910+ return BITS_SET_IN_HALF_BYTE[a & 0x0F] + BITS_SET_IN_HALF_BYTE[(URShift(a, 4) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 8) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 12) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 16) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 20) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 24) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 28) & 0x0F)];
911+}
912+
913+FormatInformation.decodeFormatInformation=function( maskedFormatInfo)
914+{
915+ var formatInfo = FormatInformation.doDecodeFormatInformation(maskedFormatInfo);
916+ if (formatInfo != null)
917+ {
918+ return formatInfo;
919+ }
920+ // Should return null, but, some QR codes apparently
921+ // do not mask this info. Try again by actually masking the pattern
922+ // first
923+ return FormatInformation.doDecodeFormatInformation(maskedFormatInfo ^ FORMAT_INFO_MASK_QR);
924+}
925+FormatInformation.doDecodeFormatInformation=function( maskedFormatInfo)
926+{
927+ // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
928+ var bestDifference = 0xffffffff;
929+ var bestFormatInfo = 0;
930+ for (var i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++)
931+ {
932+ var decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i];
933+ var targetInfo = decodeInfo[0];
934+ if (targetInfo == maskedFormatInfo)
935+ {
936+ // Found an exact match
937+ return new FormatInformation(decodeInfo[1]);
938+ }
939+ var bitsDifference = this.numBitsDiffering(maskedFormatInfo, targetInfo);
940+ if (bitsDifference < bestDifference)
941+ {
942+ bestFormatInfo = decodeInfo[1];
943+ bestDifference = bitsDifference;
944+ }
945+ }
946+ // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
947+ // differing means we found a match
948+ if (bestDifference <= 3)
949+ {
950+ return new FormatInformation(bestFormatInfo);
951+ }
952+ return null;
953+}
954+
955+ /*
956+ Ported to JavaScript by Lazar Laszlo 2011
957+
958+ lazarsoft@gmail.com, www.lazarsoft.info
959+
960+*/
961+
962+/*
963+*
964+* Copyright 2007 ZXing authors
965+*
966+* Licensed under the Apache License, Version 2.0 (the "License");
967+* you may not use this file except in compliance with the License.
968+* You may obtain a copy of the License at
969+*
970+* http://www.apache.org/licenses/LICENSE-2.0
971+*
972+* Unless required by applicable law or agreed to in writing, software
973+* distributed under the License is distributed on an "AS IS" BASIS,
974+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
975+* See the License for the specific language governing permissions and
976+* limitations under the License.
977+*/
978+
979+
980+function ErrorCorrectionLevel(ordinal, bits, name)
981+{
982+ this.ordinal_Renamed_Field = ordinal;
983+ this.bits = bits;
984+ this.name = name;
985+ this.__defineGetter__("Bits", function()
986+ {
987+ return this.bits;
988+ });
989+ this.__defineGetter__("Name", function()
990+ {
991+ return this.name;
992+ });
993+ this.ordinal=function()
994+ {
995+ return this.ordinal_Renamed_Field;
996+ }
997+}
998+
999+ErrorCorrectionLevel.forBits=function( bits)
1000+{
1001+ if (bits < 0 || bits >= FOR_BITS.Length)
1002+ {
1003+ throw "ArgumentException";
1004+ }
1005+ return FOR_BITS[bits];
1006+}
1007+
1008+var L = new ErrorCorrectionLevel(0, 0x01, "L");
1009+var M = new ErrorCorrectionLevel(1, 0x00, "M");
1010+var Q = new ErrorCorrectionLevel(2, 0x03, "Q");
1011+var H = new ErrorCorrectionLevel(3, 0x02, "H");
1012+var FOR_BITS = new Array( M, L, H, Q);/*
1013+ Ported to JavaScript by Lazar Laszlo 2011
1014+
1015+ lazarsoft@gmail.com, www.lazarsoft.info
1016+
1017+*/
1018+
1019+/*
1020+*
1021+* Copyright 2007 ZXing authors
1022+*
1023+* Licensed under the Apache License, Version 2.0 (the "License");
1024+* you may not use this file except in compliance with the License.
1025+* You may obtain a copy of the License at
1026+*
1027+* http://www.apache.org/licenses/LICENSE-2.0
1028+*
1029+* Unless required by applicable law or agreed to in writing, software
1030+* distributed under the License is distributed on an "AS IS" BASIS,
1031+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1032+* See the License for the specific language governing permissions and
1033+* limitations under the License.
1034+*/
1035+
1036+
1037+function BitMatrix( width, height)
1038+{
1039+ if(!height)
1040+ height=width;
1041+ if (width < 1 || height < 1)
1042+ {
1043+ throw "Both dimensions must be greater than 0";
1044+ }
1045+ this.width = width;
1046+ this.height = height;
1047+ var rowSize = width >> 5;
1048+ if ((width & 0x1f) != 0)
1049+ {
1050+ rowSize++;
1051+ }
1052+ this.rowSize = rowSize;
1053+ this.bits = new Array(rowSize * height);
1054+ for(var i=0;i<this.bits.length;i++)
1055+ this.bits[i]=0;
1056+
1057+ this.__defineGetter__("Width", function()
1058+ {
1059+ return this.width;
1060+ });
1061+ this.__defineGetter__("Height", function()
1062+ {
1063+ return this.height;
1064+ });
1065+ this.__defineGetter__("Dimension", function()
1066+ {
1067+ if (this.width != this.height)
1068+ {
1069+ throw "Can't call getDimension() on a non-square matrix";
1070+ }
1071+ return this.width;
1072+ });
1073+
1074+ this.get_Renamed=function( x, y)
1075+ {
1076+ var offset = y * this.rowSize + (x >> 5);
1077+ return ((URShift(this.bits[offset], (x & 0x1f))) & 1) != 0;
1078+ }
1079+ this.set_Renamed=function( x, y)
1080+ {
1081+ var offset = y * this.rowSize + (x >> 5);
1082+ this.bits[offset] |= 1 << (x & 0x1f);
1083+ }
1084+ this.flip=function( x, y)
1085+ {
1086+ var offset = y * this.rowSize + (x >> 5);
1087+ this.bits[offset] ^= 1 << (x & 0x1f);
1088+ }
1089+ this.clear=function()
1090+ {
1091+ var max = this.bits.length;
1092+ for (var i = 0; i < max; i++)
1093+ {
1094+ this.bits[i] = 0;
1095+ }
1096+ }
1097+ this.setRegion=function( left, top, width, height)
1098+ {
1099+ if (top < 0 || left < 0)
1100+ {
1101+ throw "Left and top must be nonnegative";
1102+ }
1103+ if (height < 1 || width < 1)
1104+ {
1105+ throw "Height and width must be at least 1";
1106+ }
1107+ var right = left + width;
1108+ var bottom = top + height;
1109+ if (bottom > this.height || right > this.width)
1110+ {
1111+ throw "The region must fit inside the matrix";
1112+ }
1113+ for (var y = top; y < bottom; y++)
1114+ {
1115+ var offset = y * this.rowSize;
1116+ for (var x = left; x < right; x++)
1117+ {
1118+ this.bits[offset + (x >> 5)] |= 1 << (x & 0x1f);
1119+ }
1120+ }
1121+ }
1122+}/*
1123+ Ported to JavaScript by Lazar Laszlo 2011
1124+
1125+ lazarsoft@gmail.com, www.lazarsoft.info
1126+
1127+*/
1128+
1129+/*
1130+*
1131+* Copyright 2007 ZXing authors
1132+*
1133+* Licensed under the Apache License, Version 2.0 (the "License");
1134+* you may not use this file except in compliance with the License.
1135+* You may obtain a copy of the License at
1136+*
1137+* http://www.apache.org/licenses/LICENSE-2.0
1138+*
1139+* Unless required by applicable law or agreed to in writing, software
1140+* distributed under the License is distributed on an "AS IS" BASIS,
1141+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1142+* See the License for the specific language governing permissions and
1143+* limitations under the License.
1144+*/
1145+
1146+
1147+function DataBlock(numDataCodewords, codewords)
1148+{
1149+ this.numDataCodewords = numDataCodewords;
1150+ this.codewords = codewords;
1151+
1152+ this.__defineGetter__("NumDataCodewords", function()
1153+ {
1154+ return this.numDataCodewords;
1155+ });
1156+ this.__defineGetter__("Codewords", function()
1157+ {
1158+ return this.codewords;
1159+ });
1160+}
1161+
1162+DataBlock.getDataBlocks=function(rawCodewords, version, ecLevel)
1163+{
1164+
1165+ if (rawCodewords.length != version.TotalCodewords)
1166+ {
1167+ throw "ArgumentException";
1168+ }
1169+
1170+ // Figure out the number and size of data blocks used by this version and
1171+ // error correction level
1172+ var ecBlocks = version.getECBlocksForLevel(ecLevel);
1173+
1174+ // First count the total number of data blocks
1175+ var totalBlocks = 0;
1176+ var ecBlockArray = ecBlocks.getECBlocks();
1177+ for (var i = 0; i < ecBlockArray.length; i++)
1178+ {
1179+ totalBlocks += ecBlockArray[i].Count;
1180+ }
1181+
1182+ // Now establish DataBlocks of the appropriate size and number of data codewords
1183+ var result = new Array(totalBlocks);
1184+ var numResultBlocks = 0;
1185+ for (var j = 0; j < ecBlockArray.length; j++)
1186+ {
1187+ var ecBlock = ecBlockArray[j];
1188+ for (var i = 0; i < ecBlock.Count; i++)
1189+ {
1190+ var numDataCodewords = ecBlock.DataCodewords;
1191+ var numBlockCodewords = ecBlocks.ECCodewordsPerBlock + numDataCodewords;
1192+ result[numResultBlocks++] = new DataBlock(numDataCodewords, new Array(numBlockCodewords));
1193+ }
1194+ }
1195+
1196+ // All blocks have the same amount of data, except that the last n
1197+ // (where n may be 0) have 1 more byte. Figure out where these start.
1198+ var shorterBlocksTotalCodewords = result[0].codewords.length;
1199+ var longerBlocksStartAt = result.length - 1;
1200+ while (longerBlocksStartAt >= 0)
1201+ {
1202+ var numCodewords = result[longerBlocksStartAt].codewords.length;
1203+ if (numCodewords == shorterBlocksTotalCodewords)
1204+ {
1205+ break;
1206+ }
1207+ longerBlocksStartAt--;
1208+ }
1209+ longerBlocksStartAt++;
1210+
1211+ var shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.ECCodewordsPerBlock;
1212+ // The last elements of result may be 1 element longer;
1213+ // first fill out as many elements as all of them have
1214+ var rawCodewordsOffset = 0;
1215+ for (var i = 0; i < shorterBlocksNumDataCodewords; i++)
1216+ {
1217+ for (var j = 0; j < numResultBlocks; j++)
1218+ {
1219+ result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
1220+ }
1221+ }
1222+ // Fill out the last data block in the longer ones
1223+ for (var j = longerBlocksStartAt; j < numResultBlocks; j++)
1224+ {
1225+ result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
1226+ }
1227+ // Now add in error correction blocks
1228+ var max = result[0].codewords.length;
1229+ for (var i = shorterBlocksNumDataCodewords; i < max; i++)
1230+ {
1231+ for (var j = 0; j < numResultBlocks; j++)
1232+ {
1233+ var iOffset = j < longerBlocksStartAt?i:i + 1;
1234+ result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
1235+ }
1236+ }
1237+ return result;
1238+}
1239+/*
1240+ Ported to JavaScript by Lazar Laszlo 2011
1241+
1242+ lazarsoft@gmail.com, www.lazarsoft.info
1243+
1244+*/
1245+
1246+/*
1247+*
1248+* Copyright 2007 ZXing authors
1249+*
1250+* Licensed under the Apache License, Version 2.0 (the "License");
1251+* you may not use this file except in compliance with the License.
1252+* You may obtain a copy of the License at
1253+*
1254+* http://www.apache.org/licenses/LICENSE-2.0
1255+*
1256+* Unless required by applicable law or agreed to in writing, software
1257+* distributed under the License is distributed on an "AS IS" BASIS,
1258+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1259+* See the License for the specific language governing permissions and
1260+* limitations under the License.
1261+*/
1262+
1263+
1264+function BitMatrixParser(bitMatrix)
1265+{
1266+ var dimension = bitMatrix.Dimension;
1267+ if (dimension < 21 || (dimension & 0x03) != 1)
1268+ {
1269+ throw "Error BitMatrixParser";
1270+ }
1271+ this.bitMatrix = bitMatrix;
1272+ this.parsedVersion = null;
1273+ this.parsedFormatInfo = null;
1274+
1275+ this.copyBit=function( i, j, versionBits)
1276+ {
1277+ return this.bitMatrix.get_Renamed(i, j)?(versionBits << 1) | 0x1:versionBits << 1;
1278+ }
1279+
1280+ this.readFormatInformation=function()
1281+ {
1282+ if (this.parsedFormatInfo != null)
1283+ {
1284+ return this.parsedFormatInfo;
1285+ }
1286+
1287+ // Read top-left format info bits
1288+ var formatInfoBits = 0;
1289+ for (var i = 0; i < 6; i++)
1290+ {
1291+ formatInfoBits = this.copyBit(i, 8, formatInfoBits);
1292+ }
1293+ // .. and skip a bit in the timing pattern ...
1294+ formatInfoBits = this.copyBit(7, 8, formatInfoBits);
1295+ formatInfoBits = this.copyBit(8, 8, formatInfoBits);
1296+ formatInfoBits = this.copyBit(8, 7, formatInfoBits);
1297+ // .. and skip a bit in the timing pattern ...
1298+ for (var j = 5; j >= 0; j--)
1299+ {
1300+ formatInfoBits = this.copyBit(8, j, formatInfoBits);
1301+ }
1302+
1303+ this.parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
1304+ if (this.parsedFormatInfo != null)
1305+ {
1306+ return this.parsedFormatInfo;
1307+ }
1308+
1309+ // Hmm, failed. Try the top-right/bottom-left pattern
1310+ var dimension = this.bitMatrix.Dimension;
1311+ formatInfoBits = 0;
1312+ var iMin = dimension - 8;
1313+ for (var i = dimension - 1; i >= iMin; i--)
1314+ {
1315+ formatInfoBits = this.copyBit(i, 8, formatInfoBits);
1316+ }
1317+ for (var j = dimension - 7; j < dimension; j++)
1318+ {
1319+ formatInfoBits = this.copyBit(8, j, formatInfoBits);
1320+ }
1321+
1322+ this.parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
1323+ if (this.parsedFormatInfo != null)
1324+ {
1325+ return this.parsedFormatInfo;
1326+ }
1327+ throw "Error readFormatInformation";
1328+ }
1329+ this.readVersion=function()
1330+ {
1331+
1332+ if (this.parsedVersion != null)
1333+ {
1334+ return this.parsedVersion;
1335+ }
1336+
1337+ var dimension = this.bitMatrix.Dimension;
1338+
1339+ var provisionalVersion = (dimension - 17) >> 2;
1340+ if (provisionalVersion <= 6)
1341+ {
1342+ return Version.getVersionForNumber(provisionalVersion);
1343+ }
1344+
1345+ // Read top-right version info: 3 wide by 6 tall
1346+ var versionBits = 0;
1347+ var ijMin = dimension - 11;
1348+ for (var j = 5; j >= 0; j--)
1349+ {
1350+ for (var i = dimension - 9; i >= ijMin; i--)
1351+ {
1352+ versionBits = this.copyBit(i, j, versionBits);
1353+ }
1354+ }
1355+
1356+ this.parsedVersion = Version.decodeVersionInformation(versionBits);
1357+ if (this.parsedVersion != null && this.parsedVersion.DimensionForVersion == dimension)
1358+ {
1359+ return this.parsedVersion;
1360+ }
1361+
1362+ // Hmm, failed. Try bottom left: 6 wide by 3 tall
1363+ versionBits = 0;
1364+ for (var i = 5; i >= 0; i--)
1365+ {
1366+ for (var j = dimension - 9; j >= ijMin; j--)
1367+ {
1368+ versionBits = this.copyBit(i, j, versionBits);
1369+ }
1370+ }
1371+
1372+ this.parsedVersion = Version.decodeVersionInformation(versionBits);
1373+ if (this.parsedVersion != null && this.parsedVersion.DimensionForVersion == dimension)
1374+ {
1375+ return this.parsedVersion;
1376+ }
1377+ throw "Error readVersion";
1378+ }
1379+ this.readCodewords=function()
1380+ {
1381+
1382+ var formatInfo = this.readFormatInformation();
1383+ var version = this.readVersion();
1384+
1385+ // Get the data mask for the format used in this QR Code. This will exclude
1386+ // some bits from reading as we wind through the bit matrix.
1387+ var dataMask = DataMask.forReference( formatInfo.DataMask);
1388+ var dimension = this.bitMatrix.Dimension;
1389+ dataMask.unmaskBitMatrix(this.bitMatrix, dimension);
1390+
1391+ var functionPattern = version.buildFunctionPattern();
1392+
1393+ var readingUp = true;
1394+ var result = new Array(version.TotalCodewords);
1395+ var resultOffset = 0;
1396+ var currentByte = 0;
1397+ var bitsRead = 0;
1398+ // Read columns in pairs, from right to left
1399+ for (var j = dimension - 1; j > 0; j -= 2)
1400+ {
1401+ if (j == 6)
1402+ {
1403+ // Skip whole column with vertical alignment pattern;
1404+ // saves time and makes the other code proceed more cleanly
1405+ j--;
1406+ }
1407+ // Read alternatingly from bottom to top then top to bottom
1408+ for (var count = 0; count < dimension; count++)
1409+ {
1410+ var i = readingUp?dimension - 1 - count:count;
1411+ for (var col = 0; col < 2; col++)
1412+ {
1413+ // Ignore bits covered by the function pattern
1414+ if (!functionPattern.get_Renamed(j - col, i))
1415+ {
1416+ // Read a bit
1417+ bitsRead++;
1418+ currentByte <<= 1;
1419+ if (this.bitMatrix.get_Renamed(j - col, i))
1420+ {
1421+ currentByte |= 1;
1422+ }
1423+ // If we've made a whole byte, save it off
1424+ if (bitsRead == 8)
1425+ {
1426+ result[resultOffset++] = currentByte;
1427+ bitsRead = 0;
1428+ currentByte = 0;
1429+ }
1430+ }
1431+ }
1432+ }
1433+ readingUp ^= true; // readingUp = !readingUp; // switch directions
1434+ }
1435+ if (resultOffset != version.TotalCodewords)
1436+ {
1437+ throw "Error readCodewords";
1438+ }
1439+ return result;
1440+ }
1441+}/*
1442+ Ported to JavaScript by Lazar Laszlo 2011
1443+
1444+ lazarsoft@gmail.com, www.lazarsoft.info
1445+
1446+*/
1447+
1448+/*
1449+*
1450+* Copyright 2007 ZXing authors
1451+*
1452+* Licensed under the Apache License, Version 2.0 (the "License");
1453+* you may not use this file except in compliance with the License.
1454+* You may obtain a copy of the License at
1455+*
1456+* http://www.apache.org/licenses/LICENSE-2.0
1457+*
1458+* Unless required by applicable law or agreed to in writing, software
1459+* distributed under the License is distributed on an "AS IS" BASIS,
1460+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1461+* See the License for the specific language governing permissions and
1462+* limitations under the License.
1463+*/
1464+
1465+
1466+var DataMask = {};
1467+
1468+DataMask.forReference = function(reference)
1469+{
1470+ if (reference < 0 || reference > 7)
1471+ {
1472+ throw "System.ArgumentException";
1473+ }
1474+ return DataMask.DATA_MASKS[reference];
1475+}
1476+
1477+function DataMask000()
1478+{
1479+ this.unmaskBitMatrix=function(bits, dimension)
1480+ {
1481+ for (var i = 0; i < dimension; i++)
1482+ {
1483+ for (var j = 0; j < dimension; j++)
1484+ {
1485+ if (this.isMasked(i, j))
1486+ {
1487+ bits.flip(j, i);
1488+ }
1489+ }
1490+ }
1491+ }
1492+ this.isMasked=function( i, j)
1493+ {
1494+ return ((i + j) & 0x01) == 0;
1495+ }
1496+}
1497+
1498+function DataMask001()
1499+{
1500+ this.unmaskBitMatrix=function(bits, dimension)
1501+ {
1502+ for (var i = 0; i < dimension; i++)
1503+ {
1504+ for (var j = 0; j < dimension; j++)
1505+ {
1506+ if (this.isMasked(i, j))
1507+ {
1508+ bits.flip(j, i);
1509+ }
1510+ }
1511+ }
1512+ }
1513+ this.isMasked=function( i, j)
1514+ {
1515+ return (i & 0x01) == 0;
1516+ }
1517+}
1518+
1519+function DataMask010()
1520+{
1521+ this.unmaskBitMatrix=function(bits, dimension)
1522+ {
1523+ for (var i = 0; i < dimension; i++)
1524+ {
1525+ for (var j = 0; j < dimension; j++)
1526+ {
1527+ if (this.isMasked(i, j))
1528+ {
1529+ bits.flip(j, i);
1530+ }
1531+ }
1532+ }
1533+ }
1534+ this.isMasked=function( i, j)
1535+ {
1536+ return j % 3 == 0;
1537+ }
1538+}
1539+
1540+function DataMask011()
1541+{
1542+ this.unmaskBitMatrix=function(bits, dimension)
1543+ {
1544+ for (var i = 0; i < dimension; i++)
1545+ {
1546+ for (var j = 0; j < dimension; j++)
1547+ {
1548+ if (this.isMasked(i, j))
1549+ {
1550+ bits.flip(j, i);
1551+ }
1552+ }
1553+ }
1554+ }
1555+ this.isMasked=function( i, j)
1556+ {
1557+ return (i + j) % 3 == 0;
1558+ }
1559+}
1560+
1561+function DataMask100()
1562+{
1563+ this.unmaskBitMatrix=function(bits, dimension)
1564+ {
1565+ for (var i = 0; i < dimension; i++)
1566+ {
1567+ for (var j = 0; j < dimension; j++)
1568+ {
1569+ if (this.isMasked(i, j))
1570+ {
1571+ bits.flip(j, i);
1572+ }
1573+ }
1574+ }
1575+ }
1576+ this.isMasked=function( i, j)
1577+ {
1578+ return (((URShift(i, 1)) + (j / 3)) & 0x01) == 0;
1579+ }
1580+}
1581+
1582+function DataMask101()
1583+{
1584+ this.unmaskBitMatrix=function(bits, dimension)
1585+ {
1586+ for (var i = 0; i < dimension; i++)
1587+ {
1588+ for (var j = 0; j < dimension; j++)
1589+ {
1590+ if (this.isMasked(i, j))
1591+ {
1592+ bits.flip(j, i);
1593+ }
1594+ }
1595+ }
1596+ }
1597+ this.isMasked=function( i, j)
1598+ {
1599+ var temp = i * j;
1600+ return (temp & 0x01) + (temp % 3) == 0;
1601+ }
1602+}
1603+
1604+function DataMask110()
1605+{
1606+ this.unmaskBitMatrix=function(bits, dimension)
1607+ {
1608+ for (var i = 0; i < dimension; i++)
1609+ {
1610+ for (var j = 0; j < dimension; j++)
1611+ {
1612+ if (this.isMasked(i, j))
1613+ {
1614+ bits.flip(j, i);
1615+ }
1616+ }
1617+ }
1618+ }
1619+ this.isMasked=function( i, j)
1620+ {
1621+ var temp = i * j;
1622+ return (((temp & 0x01) + (temp % 3)) & 0x01) == 0;
1623+ }
1624+}
1625+function DataMask111()
1626+{
1627+ this.unmaskBitMatrix=function(bits, dimension)
1628+ {
1629+ for (var i = 0; i < dimension; i++)
1630+ {
1631+ for (var j = 0; j < dimension; j++)
1632+ {
1633+ if (this.isMasked(i, j))
1634+ {
1635+ bits.flip(j, i);
1636+ }
1637+ }
1638+ }
1639+ }
1640+ this.isMasked=function( i, j)
1641+ {
1642+ return ((((i + j) & 0x01) + ((i * j) % 3)) & 0x01) == 0;
1643+ }
1644+}
1645+
1646+DataMask.DATA_MASKS = new Array(new DataMask000(), new DataMask001(), new DataMask010(), new DataMask011(), new DataMask100(), new DataMask101(), new DataMask110(), new DataMask111());
1647+
1648+/*
1649+ Ported to JavaScript by Lazar Laszlo 2011
1650+
1651+ lazarsoft@gmail.com, www.lazarsoft.info
1652+
1653+*/
1654+
1655+/*
1656+*
1657+* Copyright 2007 ZXing authors
1658+*
1659+* Licensed under the Apache License, Version 2.0 (the "License");
1660+* you may not use this file except in compliance with the License.
1661+* You may obtain a copy of the License at
1662+*
1663+* http://www.apache.org/licenses/LICENSE-2.0
1664+*
1665+* Unless required by applicable law or agreed to in writing, software
1666+* distributed under the License is distributed on an "AS IS" BASIS,
1667+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1668+* See the License for the specific language governing permissions and
1669+* limitations under the License.
1670+*/
1671+
1672+
1673+function ReedSolomonDecoder(field)
1674+{
1675+ this.field = field;
1676+ this.decode=function(received, twoS)
1677+ {
1678+ var poly = new GF256Poly(this.field, received);
1679+ var syndromeCoefficients = new Array(twoS);
1680+ for(var i=0;i<syndromeCoefficients.length;i++)syndromeCoefficients[i]=0;
1681+ var dataMatrix = false;//this.field.Equals(GF256.DATA_MATRIX_FIELD);
1682+ var noError = true;
1683+ for (var i = 0; i < twoS; i++)
1684+ {
1685+ // Thanks to sanfordsquires for this fix:
1686+ var eval = poly.evaluateAt(this.field.exp(dataMatrix?i + 1:i));
1687+ syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval;
1688+ if (eval != 0)
1689+ {
1690+ noError = false;
1691+ }
1692+ }
1693+ if (noError)
1694+ {
1695+ return ;
1696+ }
1697+ var syndrome = new GF256Poly(this.field, syndromeCoefficients);
1698+ var sigmaOmega = this.runEuclideanAlgorithm(this.field.buildMonomial(twoS, 1), syndrome, twoS);
1699+ var sigma = sigmaOmega[0];
1700+ var omega = sigmaOmega[1];
1701+ var errorLocations = this.findErrorLocations(sigma);
1702+ var errorMagnitudes = this.findErrorMagnitudes(omega, errorLocations, dataMatrix);
1703+ for (var i = 0; i < errorLocations.length; i++)
1704+ {
1705+ var position = received.length - 1 - this.field.log(errorLocations[i]);
1706+ if (position < 0)
1707+ {
1708+ throw "ReedSolomonException Bad error location";
1709+ }
1710+ received[position] = GF256.addOrSubtract(received[position], errorMagnitudes[i]);
1711+ }
1712+ }
1713+
1714+ this.runEuclideanAlgorithm=function( a, b, R)
1715+ {
1716+ // Assume a's degree is >= b's
1717+ if (a.Degree < b.Degree)
1718+ {
1719+ var temp = a;
1720+ a = b;
1721+ b = temp;
1722+ }
1723+
1724+ var rLast = a;
1725+ var r = b;
1726+ var sLast = this.field.One;
1727+ var s = this.field.Zero;
1728+ var tLast = this.field.Zero;
1729+ var t = this.field.One;
1730+
1731+ // Run Euclidean algorithm until r's degree is less than R/2
1732+ while (r.Degree >= Math.floor(R / 2))
1733+ {
1734+ var rLastLast = rLast;
1735+ var sLastLast = sLast;
1736+ var tLastLast = tLast;
1737+ rLast = r;
1738+ sLast = s;
1739+ tLast = t;
1740+
1741+ // Divide rLastLast by rLast, with quotient in q and remainder in r
1742+ if (rLast.Zero)
1743+ {
1744+ // Oops, Euclidean algorithm already terminated?
1745+ throw "r_{i-1} was zero";
1746+ }
1747+ r = rLastLast;
1748+ var q = this.field.Zero;
1749+ var denominatorLeadingTerm = rLast.getCoefficient(rLast.Degree);
1750+ var dltInverse = this.field.inverse(denominatorLeadingTerm);
1751+ while (r.Degree >= rLast.Degree && !r.Zero)
1752+ {
1753+ var degreeDiff = r.Degree - rLast.Degree;
1754+ var scale = this.field.multiply(r.getCoefficient(r.Degree), dltInverse);
1755+ q = q.addOrSubtract(this.field.buildMonomial(degreeDiff, scale));
1756+ r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));
1757+ //r.EXE();
1758+ }
1759+
1760+ s = q.multiply1(sLast).addOrSubtract(sLastLast);
1761+ t = q.multiply1(tLast).addOrSubtract(tLastLast);
1762+ }
1763+
1764+ var sigmaTildeAtZero = t.getCoefficient(0);
1765+ if (sigmaTildeAtZero == 0)
1766+ {
1767+ throw "ReedSolomonException sigmaTilde(0) was zero";
1768+ }
1769+
1770+ var inverse = this.field.inverse(sigmaTildeAtZero);
1771+ var sigma = t.multiply2(inverse);
1772+ var omega = r.multiply2(inverse);
1773+ return new Array(sigma, omega);
1774+ }
1775+ this.findErrorLocations=function( errorLocator)
1776+ {
1777+ // This is a direct application of Chien's search
1778+ var numErrors = errorLocator.Degree;
1779+ if (numErrors == 1)
1780+ {
1781+ // shortcut
1782+ return new Array(errorLocator.getCoefficient(1));
1783+ }
1784+ var result = new Array(numErrors);
1785+ var e = 0;
1786+ for (var i = 1; i < 256 && e < numErrors; i++)
1787+ {
1788+ if (errorLocator.evaluateAt(i) == 0)
1789+ {
1790+ result[e] = this.field.inverse(i);
1791+ e++;
1792+ }
1793+ }
1794+ if (e != numErrors)
1795+ {
1796+ throw "Error locator degree does not match number of roots";
1797+ }
1798+ return result;
1799+ }
1800+ this.findErrorMagnitudes=function( errorEvaluator, errorLocations, dataMatrix)
1801+ {
1802+ // This is directly applying Forney's Formula
1803+ var s = errorLocations.length;
1804+ var result = new Array(s);
1805+ for (var i = 0; i < s; i++)
1806+ {
1807+ var xiInverse = this.field.inverse(errorLocations[i]);
1808+ var denominator = 1;
1809+ for (var j = 0; j < s; j++)
1810+ {
1811+ if (i != j)
1812+ {
1813+ denominator = this.field.multiply(denominator, GF256.addOrSubtract(1, this.field.multiply(errorLocations[j], xiInverse)));
1814+ }
1815+ }
1816+ result[i] = this.field.multiply(errorEvaluator.evaluateAt(xiInverse), this.field.inverse(denominator));
1817+ // Thanks to sanfordsquires for this fix:
1818+ if (dataMatrix)
1819+ {
1820+ result[i] = this.field.multiply(result[i], xiInverse);
1821+ }
1822+ }
1823+ return result;
1824+ }
1825+}/*
1826+ Ported to JavaScript by Lazar Laszlo 2011
1827+
1828+ lazarsoft@gmail.com, www.lazarsoft.info
1829+
1830+*/
1831+
1832+/*
1833+*
1834+* Copyright 2007 ZXing authors
1835+*
1836+* Licensed under the Apache License, Version 2.0 (the "License");
1837+* you may not use this file except in compliance with the License.
1838+* You may obtain a copy of the License at
1839+*
1840+* http://www.apache.org/licenses/LICENSE-2.0
1841+*
1842+* Unless required by applicable law or agreed to in writing, software
1843+* distributed under the License is distributed on an "AS IS" BASIS,
1844+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1845+* See the License for the specific language governing permissions and
1846+* limitations under the License.
1847+*/
1848+
1849+
1850+function GF256Poly(field, coefficients)
1851+{
1852+ if (coefficients == null || coefficients.length == 0)
1853+ {
1854+ throw "System.ArgumentException";
1855+ }
1856+ this.field = field;
1857+ var coefficientsLength = coefficients.length;
1858+ if (coefficientsLength > 1 && coefficients[0] == 0)
1859+ {
1860+ // Leading term must be non-zero for anything except the constant polynomial "0"
1861+ var firstNonZero = 1;
1862+ while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0)
1863+ {
1864+ firstNonZero++;
1865+ }
1866+ if (firstNonZero == coefficientsLength)
1867+ {
1868+ this.coefficients = field.Zero.coefficients;
1869+ }
1870+ else
1871+ {
1872+ this.coefficients = new Array(coefficientsLength - firstNonZero);
1873+ for(var i=0;i<this.coefficients.length;i++)this.coefficients[i]=0;
1874+ //Array.Copy(coefficients, firstNonZero, this.coefficients, 0, this.coefficients.length);
1875+ for(var ci=0;ci<this.coefficients.length;ci++)this.coefficients[ci]=coefficients[firstNonZero+ci];
1876+ }
1877+ }
1878+ else
1879+ {
1880+ this.coefficients = coefficients;
1881+ }
1882+
1883+ this.__defineGetter__("Zero", function()
1884+ {
1885+ return this.coefficients[0] == 0;
1886+ });
1887+ this.__defineGetter__("Degree", function()
1888+ {
1889+ return this.coefficients.length - 1;
1890+ });
1891+ this.__defineGetter__("Coefficients", function()
1892+ {
1893+ return this.coefficients;
1894+ });
1895+
1896+ this.getCoefficient=function( degree)
1897+ {
1898+ return this.coefficients[this.coefficients.length - 1 - degree];
1899+ }
1900+
1901+ this.evaluateAt=function( a)
1902+ {
1903+ if (a == 0)
1904+ {
1905+ // Just return the x^0 coefficient
1906+ return this.getCoefficient(0);
1907+ }
1908+ var size = this.coefficients.length;
1909+ if (a == 1)
1910+ {
1911+ // Just the sum of the coefficients
1912+ var result = 0;
1913+ for (var i = 0; i < size; i++)
1914+ {
1915+ result = GF256.addOrSubtract(result, this.coefficients[i]);
1916+ }
1917+ return result;
1918+ }
1919+ var result2 = this.coefficients[0];
1920+ for (var i = 1; i < size; i++)
1921+ {
1922+ result2 = GF256.addOrSubtract(this.field.multiply(a, result2), this.coefficients[i]);
1923+ }
1924+ return result2;
1925+ }
1926+
1927+ this.addOrSubtract=function( other)
1928+ {
1929+ if (this.field != other.field)
1930+ {
1931+ throw "GF256Polys do not have same GF256 field";
1932+ }
1933+ if (this.Zero)
1934+ {
1935+ return other;
1936+ }
1937+ if (other.Zero)
1938+ {
1939+ return this;
1940+ }
1941+
1942+ var smallerCoefficients = this.coefficients;
1943+ var largerCoefficients = other.coefficients;
1944+ if (smallerCoefficients.length > largerCoefficients.length)
1945+ {
1946+ var temp = smallerCoefficients;
1947+ smallerCoefficients = largerCoefficients;
1948+ largerCoefficients = temp;
1949+ }
1950+ var sumDiff = new Array(largerCoefficients.length);
1951+ var lengthDiff = largerCoefficients.length - smallerCoefficients.length;
1952+ // Copy high-order terms only found in higher-degree polynomial's coefficients
1953+ //Array.Copy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
1954+ for(var ci=0;ci<lengthDiff;ci++)sumDiff[ci]=largerCoefficients[ci];
1955+
1956+ for (var i = lengthDiff; i < largerCoefficients.length; i++)
1957+ {
1958+ sumDiff[i] = GF256.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
1959+ }
1960+
1961+ return new GF256Poly(field, sumDiff);
1962+ }
1963+ this.multiply1=function( other)
1964+ {
1965+ if (this.field!=other.field)
1966+ {
1967+ throw "GF256Polys do not have same GF256 field";
1968+ }
1969+ if (this.Zero || other.Zero)
1970+ {
1971+ return this.field.Zero;
1972+ }
1973+ var aCoefficients = this.coefficients;
1974+ var aLength = aCoefficients.length;
1975+ var bCoefficients = other.coefficients;
1976+ var bLength = bCoefficients.length;
1977+ var product = new Array(aLength + bLength - 1);
1978+ for (var i = 0; i < aLength; i++)
1979+ {
1980+ var aCoeff = aCoefficients[i];
1981+ for (var j = 0; j < bLength; j++)
1982+ {
1983+ product[i + j] = GF256.addOrSubtract(product[i + j], this.field.multiply(aCoeff, bCoefficients[j]));
1984+ }
1985+ }
1986+ return new GF256Poly(this.field, product);
1987+ }
1988+ this.multiply2=function( scalar)
1989+ {
1990+ if (scalar == 0)
1991+ {
1992+ return this.field.Zero;
1993+ }
1994+ if (scalar == 1)
1995+ {
1996+ return this;
1997+ }
1998+ var size = this.coefficients.length;
1999+ var product = new Array(size);
2000+ for (var i = 0; i < size; i++)
2001+ {
2002+ product[i] = this.field.multiply(this.coefficients[i], scalar);
2003+ }
2004+ return new GF256Poly(this.field, product);
2005+ }
2006+ this.multiplyByMonomial=function( degree, coefficient)
2007+ {
2008+ if (degree < 0)
2009+ {
2010+ throw "System.ArgumentException";
2011+ }
2012+ if (coefficient == 0)
2013+ {
2014+ return this.field.Zero;
2015+ }
2016+ var size = this.coefficients.length;
2017+ var product = new Array(size + degree);
2018+ for(var i=0;i<product.length;i++)product[i]=0;
2019+ for (var i = 0; i < size; i++)
2020+ {
2021+ product[i] = this.field.multiply(this.coefficients[i], coefficient);
2022+ }
2023+ return new GF256Poly(this.field, product);
2024+ }
2025+ this.divide=function( other)
2026+ {
2027+ if (this.field!=other.field)
2028+ {
2029+ throw "GF256Polys do not have same GF256 field";
2030+ }
2031+ if (other.Zero)
2032+ {
2033+ throw "Divide by 0";
2034+ }
2035+
2036+ var quotient = this.field.Zero;
2037+ var remainder = this;
2038+
2039+ var denominatorLeadingTerm = other.getCoefficient(other.Degree);
2040+ var inverseDenominatorLeadingTerm = this.field.inverse(denominatorLeadingTerm);
2041+
2042+ while (remainder.Degree >= other.Degree && !remainder.Zero)
2043+ {
2044+ var degreeDifference = remainder.Degree - other.Degree;
2045+ var scale = this.field.multiply(remainder.getCoefficient(remainder.Degree), inverseDenominatorLeadingTerm);
2046+ var term = other.multiplyByMonomial(degreeDifference, scale);
2047+ var iterationQuotient = this.field.buildMonomial(degreeDifference, scale);
2048+ quotient = quotient.addOrSubtract(iterationQuotient);
2049+ remainder = remainder.addOrSubtract(term);
2050+ }
2051+
2052+ return new Array(quotient, remainder);
2053+ }
2054+}/*
2055+ Ported to JavaScript by Lazar Laszlo 2011
2056+
2057+ lazarsoft@gmail.com, www.lazarsoft.info
2058+
2059+*/
2060+
2061+/*
2062+*
2063+* Copyright 2007 ZXing authors
2064+*
2065+* Licensed under the Apache License, Version 2.0 (the "License");
2066+* you may not use this file except in compliance with the License.
2067+* You may obtain a copy of the License at
2068+*
2069+* http://www.apache.org/licenses/LICENSE-2.0
2070+*
2071+* Unless required by applicable law or agreed to in writing, software
2072+* distributed under the License is distributed on an "AS IS" BASIS,
2073+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2074+* See the License for the specific language governing permissions and
2075+* limitations under the License.
2076+*/
2077+
2078+
2079+function GF256( primitive)
2080+{
2081+ this.expTable = new Array(256);
2082+ this.logTable = new Array(256);
2083+ var x = 1;
2084+ for (var i = 0; i < 256; i++)
2085+ {
2086+ this.expTable[i] = x;
2087+ x <<= 1; // x = x * 2; we're assuming the generator alpha is 2
2088+ if (x >= 0x100)
2089+ {
2090+ x ^= primitive;
2091+ }
2092+ }
2093+ for (var i = 0; i < 255; i++)
2094+ {
2095+ this.logTable[this.expTable[i]] = i;
2096+ }
2097+ // logTable[0] == 0 but this should never be used
2098+ var at0=new Array(1);at0[0]=0;
2099+ this.zero = new GF256Poly(this, new Array(at0));
2100+ var at1=new Array(1);at1[0]=1;
2101+ this.one = new GF256Poly(this, new Array(at1));
2102+
2103+ this.__defineGetter__("Zero", function()
2104+ {
2105+ return this.zero;
2106+ });
2107+ this.__defineGetter__("One", function()
2108+ {
2109+ return this.one;
2110+ });
2111+ this.buildMonomial=function( degree, coefficient)
2112+ {
2113+ if (degree < 0)
2114+ {
2115+ throw "System.ArgumentException";
2116+ }
2117+ if (coefficient == 0)
2118+ {
2119+ return zero;
2120+ }
2121+ var coefficients = new Array(degree + 1);
2122+ for(var i=0;i<coefficients.length;i++)coefficients[i]=0;
2123+ coefficients[0] = coefficient;
2124+ return new GF256Poly(this, coefficients);
2125+ }
2126+ this.exp=function( a)
2127+ {
2128+ return this.expTable[a];
2129+ }
2130+ this.log=function( a)
2131+ {
2132+ if (a == 0)
2133+ {
2134+ throw "System.ArgumentException";
2135+ }
2136+ return this.logTable[a];
2137+ }
2138+ this.inverse=function( a)
2139+ {
2140+ if (a == 0)
2141+ {
2142+ throw "System.ArithmeticException";
2143+ }
2144+ return this.expTable[255 - this.logTable[a]];
2145+ }
2146+ this.multiply=function( a, b)
2147+ {
2148+ if (a == 0 || b == 0)
2149+ {
2150+ return 0;
2151+ }
2152+ if (a == 1)
2153+ {
2154+ return b;
2155+ }
2156+ if (b == 1)
2157+ {
2158+ return a;
2159+ }
2160+ return this.expTable[(this.logTable[a] + this.logTable[b]) % 255];
2161+ }
2162+}
2163+
2164+GF256.QR_CODE_FIELD = new GF256(0x011D);
2165+GF256.DATA_MATRIX_FIELD = new GF256(0x012D);
2166+
2167+GF256.addOrSubtract=function( a, b)
2168+{
2169+ return a ^ b;
2170+}/*
2171+ Ported to JavaScript by Lazar Laszlo 2011
2172+
2173+ lazarsoft@gmail.com, www.lazarsoft.info
2174+
2175+*/
2176+
2177+/*
2178+*
2179+* Copyright 2007 ZXing authors
2180+*
2181+* Licensed under the Apache License, Version 2.0 (the "License");
2182+* you may not use this file except in compliance with the License.
2183+* You may obtain a copy of the License at
2184+*
2185+* http://www.apache.org/licenses/LICENSE-2.0
2186+*
2187+* Unless required by applicable law or agreed to in writing, software
2188+* distributed under the License is distributed on an "AS IS" BASIS,
2189+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2190+* See the License for the specific language governing permissions and
2191+* limitations under the License.
2192+*/
2193+
2194+
2195+var Decoder={};
2196+Decoder.rsDecoder = new ReedSolomonDecoder(GF256.QR_CODE_FIELD);
2197+
2198+Decoder.correctErrors=function( codewordBytes, numDataCodewords)
2199+{
2200+ var numCodewords = codewordBytes.length;
2201+ // First read into an array of ints
2202+ var codewordsInts = new Array(numCodewords);
2203+ for (var i = 0; i < numCodewords; i++)
2204+ {
2205+ codewordsInts[i] = codewordBytes[i] & 0xFF;
2206+ }
2207+ var numECCodewords = codewordBytes.length - numDataCodewords;
2208+ try
2209+ {
2210+ Decoder.rsDecoder.decode(codewordsInts, numECCodewords);
2211+ //var corrector = new ReedSolomon(codewordsInts, numECCodewords);
2212+ //corrector.correct();
2213+ }
2214+ catch ( rse)
2215+ {
2216+ throw rse;
2217+ }
2218+ // Copy back into array of bytes -- only need to worry about the bytes that were data
2219+ // We don't care about errors in the error-correction codewords
2220+ for (var i = 0; i < numDataCodewords; i++)
2221+ {
2222+ codewordBytes[i] = codewordsInts[i];
2223+ }
2224+}
2225+
2226+Decoder.decode=function(bits)
2227+{
2228+ var parser = new BitMatrixParser(bits);
2229+ var version = parser.readVersion();
2230+ var ecLevel = parser.readFormatInformation().ErrorCorrectionLevel;
2231+
2232+ // Read codewords
2233+ var codewords = parser.readCodewords();
2234+
2235+ // Separate into data blocks
2236+ var dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);
2237+
2238+ // Count total number of data bytes
2239+ var totalBytes = 0;
2240+ for (var i = 0; i < dataBlocks.Length; i++)
2241+ {
2242+ totalBytes += dataBlocks[i].NumDataCodewords;
2243+ }
2244+ var resultBytes = new Array(totalBytes);
2245+ var resultOffset = 0;
2246+
2247+ // Error-correct and copy data blocks together into a stream of bytes
2248+ for (var j = 0; j < dataBlocks.length; j++)
2249+ {
2250+ var dataBlock = dataBlocks[j];
2251+ var codewordBytes = dataBlock.Codewords;
2252+ var numDataCodewords = dataBlock.NumDataCodewords;
2253+ Decoder.correctErrors(codewordBytes, numDataCodewords);
2254+ for (var i = 0; i < numDataCodewords; i++)
2255+ {
2256+ resultBytes[resultOffset++] = codewordBytes[i];
2257+ }
2258+ }
2259+
2260+ // Decode the contents of that stream of bytes
2261+ var reader = new QRCodeDataBlockReader(resultBytes, version.VersionNumber, ecLevel.Bits);
2262+ return reader;
2263+ //return DecodedBitStreamParser.decode(resultBytes, version, ecLevel);
2264+}
2265+/*
2266+ Copyright 2011 Lazar Laszlo (lazarsoft@gmail.com, www.lazarsoft.info)
2267+
2268+ Licensed under the Apache License, Version 2.0 (the "License");
2269+ you may not use this file except in compliance with the License.
2270+ You may obtain a copy of the License at
2271+
2272+ http://www.apache.org/licenses/LICENSE-2.0
2273+
2274+ Unless required by applicable law or agreed to in writing, software
2275+ distributed under the License is distributed on an "AS IS" BASIS,
2276+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2277+ See the License for the specific language governing permissions and
2278+ limitations under the License.
2279+*/
2280+
2281+
2282+var qrcode = {};
2283+qrcode.imagedata = null;
2284+qrcode.width = 0;
2285+qrcode.height = 0;
2286+qrcode.qrCodeSymbol = null;
2287+qrcode.debug = false;
2288+
2289+qrcode.sizeOfDataLengthInfo = [ [ 10, 9, 8, 8 ], [ 12, 11, 16, 10 ], [ 14, 13, 16, 12 ] ];
2290+
2291+qrcode.callback = null;
2292+
2293+qrcode.decode = function(src){
2294+
2295+ if(arguments.length==0)
2296+ {
2297+ var canvas_qr = document.getElementById("qr-canvas");
2298+ var context = canvas_qr.getContext('2d');
2299+ qrcode.width = canvas_qr.width;
2300+ qrcode.height = canvas_qr.height;
2301+ qrcode.imagedata = context.getImageData(0, 0, qrcode.width, qrcode.height);
2302+ qrcode.result = qrcode.process(context);
2303+ if(qrcode.callback!=null)
2304+ qrcode.callback(qrcode.result);
2305+ return qrcode.result;
2306+ }
2307+ else
2308+ {
2309+ var image = new Image();
2310+ image.onload=function(){
2311+ //var canvas_qr = document.getElementById("qr-canvas");
2312+ var canvas_qr = document.createElement('canvas');
2313+ var context = canvas_qr.getContext('2d');
2314+ var canvas_out = document.getElementById("out-canvas");
2315+ if(canvas_out!=null)
2316+ {
2317+ var outctx = canvas_out.getContext('2d');
2318+ outctx.clearRect(0, 0, 320, 240);
2319+ outctx.drawImage(image, 0, 0, 320, 240);
2320+ }
2321+ canvas_qr.width = image.width;
2322+ canvas_qr.height = image.height;
2323+ context.drawImage(image, 0, 0);
2324+ qrcode.width = image.width;
2325+ qrcode.height = image.height;
2326+ try{
2327+ qrcode.imagedata = context.getImageData(0, 0, image.width, image.height);
2328+ }catch(e){
2329+ qrcode.result = "Cross domain image reading not supported in your browser! Save it to your computer then drag and drop the file!";
2330+ if(qrcode.callback!=null)
2331+ qrcode.callback(qrcode.result);
2332+ return;
2333+ }
2334+
2335+ try
2336+ {
2337+ qrcode.result = qrcode.process(context);
2338+ }
2339+ catch(e)
2340+ {
2341+ console.log(e);
2342+ qrcode.result = "error decoding QR Code";
2343+ }
2344+ if(qrcode.callback!=null)
2345+ qrcode.callback(qrcode.result);
2346+ }
2347+ image.src = src;
2348+ }
2349+}
2350+
2351+qrcode.decode_utf8 = function ( s )
2352+{
2353+ return decodeURIComponent( escape( s ) );
2354+}
2355+
2356+qrcode.process = function(ctx){
2357+
2358+ var start = new Date().getTime();
2359+
2360+ var image = qrcode.grayScaleToBitmap(qrcode.grayscale());
2361+ //var image = qrcode.binarize(128);
2362+
2363+ if(qrcode.debug)
2364+ {
2365+ for (var y = 0; y < qrcode.height; y++)
2366+ {
2367+ for (var x = 0; x < qrcode.width; x++)
2368+ {
2369+ var point = (x * 4) + (y * qrcode.width * 4);
2370+ qrcode.imagedata.data[point] = image[x+y*qrcode.width]?0:0;
2371+ qrcode.imagedata.data[point+1] = image[x+y*qrcode.width]?0:0;
2372+ qrcode.imagedata.data[point+2] = image[x+y*qrcode.width]?255:0;
2373+ }
2374+ }
2375+ ctx.putImageData(qrcode.imagedata, 0, 0);
2376+ }
2377+
2378+ //var finderPatternInfo = new FinderPatternFinder().findFinderPattern(image);
2379+
2380+ var detector = new Detector(image);
2381+
2382+ var qRCodeMatrix = detector.detect();
2383+
2384+ /*for (var y = 0; y < qRCodeMatrix.bits.Height; y++)
2385+ {
2386+ for (var x = 0; x < qRCodeMatrix.bits.Width; x++)
2387+ {
2388+ var point = (x * 4*2) + (y*2 * qrcode.width * 4);
2389+ qrcode.imagedata.data[point] = qRCodeMatrix.bits.get_Renamed(x,y)?0:0;
2390+ qrcode.imagedata.data[point+1] = qRCodeMatrix.bits.get_Renamed(x,y)?0:0;
2391+ qrcode.imagedata.data[point+2] = qRCodeMatrix.bits.get_Renamed(x,y)?255:0;
2392+ }
2393+ }*/
2394+ if(qrcode.debug)
2395+ ctx.putImageData(qrcode.imagedata, 0, 0);
2396+
2397+ var reader = Decoder.decode(qRCodeMatrix.bits);
2398+ var data = reader.DataByte;
2399+ var str="";
2400+ for(var i=0;i<data.length;i++)
2401+ {
2402+ for(var j=0;j<data[i].length;j++)
2403+ str+=String.fromCharCode(data[i][j]);
2404+ }
2405+
2406+ var end = new Date().getTime();
2407+ var time = end - start;
2408+ console.log(time);
2409+
2410+ return qrcode.decode_utf8(str);
2411+ //alert("Time:" + time + " Code: "+str);
2412+}
2413+
2414+qrcode.getPixel = function(x,y){
2415+ if (qrcode.width < x) {
2416+ throw "point error";
2417+ }
2418+ if (qrcode.height < y) {
2419+ throw "point error";
2420+ }
2421+ var point = (x * 4) + (y * qrcode.width * 4);
2422+ var p = (qrcode.imagedata.data[point]*33 + qrcode.imagedata.data[point + 1]*34 + qrcode.imagedata.data[point + 2]*33)/100;
2423+ return p;
2424+}
2425+
2426+qrcode.binarize = function(th){
2427+ var ret = new Array(qrcode.width*qrcode.height);
2428+ for (var y = 0; y < qrcode.height; y++)
2429+ {
2430+ for (var x = 0; x < qrcode.width; x++)
2431+ {
2432+ var gray = qrcode.getPixel(x, y);
2433+
2434+ ret[x+y*qrcode.width] = gray<=th?true:false;
2435+ }
2436+ }
2437+ return ret;
2438+}
2439+
2440+qrcode.getMiddleBrightnessPerArea=function(image)
2441+{
2442+ var numSqrtArea = 4;
2443+ //obtain middle brightness((min + max) / 2) per area
2444+ var areaWidth = Math.floor(qrcode.width / numSqrtArea);
2445+ var areaHeight = Math.floor(qrcode.height / numSqrtArea);
2446+ var minmax = new Array(numSqrtArea);
2447+ for (var i = 0; i < numSqrtArea; i++)
2448+ {
2449+ minmax[i] = new Array(numSqrtArea);
2450+ for (var i2 = 0; i2 < numSqrtArea; i2++)
2451+ {
2452+ minmax[i][i2] = new Array(0,0);
2453+ }
2454+ }
2455+ for (var ay = 0; ay < numSqrtArea; ay++)
2456+ {
2457+ for (var ax = 0; ax < numSqrtArea; ax++)
2458+ {
2459+ minmax[ax][ay][0] = 0xFF;
2460+ for (var dy = 0; dy < areaHeight; dy++)
2461+ {
2462+ for (var dx = 0; dx < areaWidth; dx++)
2463+ {
2464+ var target = image[areaWidth * ax + dx+(areaHeight * ay + dy)*qrcode.width];
2465+ if (target < minmax[ax][ay][0])
2466+ minmax[ax][ay][0] = target;
2467+ if (target > minmax[ax][ay][1])
2468+ minmax[ax][ay][1] = target;
2469+ }
2470+ }
2471+ //minmax[ax][ay][0] = (minmax[ax][ay][0] + minmax[ax][ay][1]) / 2;
2472+ }
2473+ }
2474+ var middle = new Array(numSqrtArea);
2475+ for (var i3 = 0; i3 < numSqrtArea; i3++)
2476+ {
2477+ middle[i3] = new Array(numSqrtArea);
2478+ }
2479+ for (var ay = 0; ay < numSqrtArea; ay++)
2480+ {
2481+ for (var ax = 0; ax < numSqrtArea; ax++)
2482+ {
2483+ middle[ax][ay] = Math.floor((minmax[ax][ay][0] + minmax[ax][ay][1]) / 2);
2484+ //Console.out.print(middle[ax][ay] + ",");
2485+ }
2486+ //Console.out.println("");
2487+ }
2488+ //Console.out.println("");
2489+
2490+ return middle;
2491+}
2492+
2493+qrcode.grayScaleToBitmap=function(grayScale)
2494+{
2495+ var middle = qrcode.getMiddleBrightnessPerArea(grayScale);
2496+ var sqrtNumArea = middle.length;
2497+ var areaWidth = Math.floor(qrcode.width / sqrtNumArea);
2498+ var areaHeight = Math.floor(qrcode.height / sqrtNumArea);
2499+ var bitmap = new Array(qrcode.height*qrcode.width);
2500+
2501+ for (var ay = 0; ay < sqrtNumArea; ay++)
2502+ {
2503+ for (var ax = 0; ax < sqrtNumArea; ax++)
2504+ {
2505+ for (var dy = 0; dy < areaHeight; dy++)
2506+ {
2507+ for (var dx = 0; dx < areaWidth; dx++)
2508+ {
2509+ bitmap[areaWidth * ax + dx+ (areaHeight * ay + dy)*qrcode.width] = (grayScale[areaWidth * ax + dx+ (areaHeight * ay + dy)*qrcode.width] < middle[ax][ay])?true:false;
2510+ }
2511+ }
2512+ }
2513+ }
2514+ return bitmap;
2515+}
2516+
2517+qrcode.grayscale = function(){
2518+ var ret = new Array(qrcode.width*qrcode.height);
2519+ for (var y = 0; y < qrcode.height; y++)
2520+ {
2521+ for (var x = 0; x < qrcode.width; x++)
2522+ {
2523+ var gray = qrcode.getPixel(x, y);
2524+
2525+ ret[x+y*qrcode.width] = gray;
2526+ }
2527+ }
2528+ return ret;
2529+}
2530+
2531+
2532+
2533+
2534+function URShift( number, bits)
2535+{
2536+ if (number >= 0)
2537+ return number >> bits;
2538+ else
2539+ return (number >> bits) + (2 << ~bits);
2540+}
2541+
2542+
2543+Array.prototype.remove = function(from, to) {
2544+ var rest = this.slice((to || from) + 1 || this.length);
2545+ this.length = from < 0 ? this.length + from : from;
2546+ return this.push.apply(this, rest);
2547+};
2548+/*
2549+ Ported to JavaScript by Lazar Laszlo 2011
2550+
2551+ lazarsoft@gmail.com, www.lazarsoft.info
2552+
2553+*/
2554+
2555+/*
2556+*
2557+* Copyright 2007 ZXing authors
2558+*
2559+* Licensed under the Apache License, Version 2.0 (the "License");
2560+* you may not use this file except in compliance with the License.
2561+* You may obtain a copy of the License at
2562+*
2563+* http://www.apache.org/licenses/LICENSE-2.0
2564+*
2565+* Unless required by applicable law or agreed to in writing, software
2566+* distributed under the License is distributed on an "AS IS" BASIS,
2567+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2568+* See the License for the specific language governing permissions and
2569+* limitations under the License.
2570+*/
2571+
2572+
2573+var MIN_SKIP = 3;
2574+var MAX_MODULES = 57;
2575+var INTEGER_MATH_SHIFT = 8;
2576+var CENTER_QUORUM = 2;
2577+
2578+qrcode.orderBestPatterns=function(patterns)
2579+ {
2580+
2581+ function distance( pattern1, pattern2)
2582+ {
2583+ var xDiff = pattern1.X - pattern2.X;
2584+ var yDiff = pattern1.Y - pattern2.Y;
2585+ return Math.sqrt( (xDiff * xDiff + yDiff * yDiff));
2586+ }
2587+
2588+ /// <summary> Returns the z component of the cross product between vectors BC and BA.</summary>
2589+ function crossProductZ( pointA, pointB, pointC)
2590+ {
2591+ var bX = pointB.x;
2592+ var bY = pointB.y;
2593+ return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));
2594+ }
2595+
2596+
2597+ // Find distances between pattern centers
2598+ var zeroOneDistance = distance(patterns[0], patterns[1]);
2599+ var oneTwoDistance = distance(patterns[1], patterns[2]);
2600+ var zeroTwoDistance = distance(patterns[0], patterns[2]);
2601+
2602+ var pointA, pointB, pointC;
2603+ // Assume one closest to other two is B; A and C will just be guesses at first
2604+ if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance)
2605+ {
2606+ pointB = patterns[0];
2607+ pointA = patterns[1];
2608+ pointC = patterns[2];
2609+ }
2610+ else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance)
2611+ {
2612+ pointB = patterns[1];
2613+ pointA = patterns[0];
2614+ pointC = patterns[2];
2615+ }
2616+ else
2617+ {
2618+ pointB = patterns[2];
2619+ pointA = patterns[0];
2620+ pointC = patterns[1];
2621+ }
2622+
2623+ // Use cross product to figure out whether A and C are correct or flipped.
2624+ // This asks whether BC x BA has a positive z component, which is the arrangement
2625+ // we want for A, B, C. If it's negative, then we've got it flipped around and
2626+ // should swap A and C.
2627+ if (crossProductZ(pointA, pointB, pointC) < 0.0)
2628+ {
2629+ var temp = pointA;
2630+ pointA = pointC;
2631+ pointC = temp;
2632+ }
2633+
2634+ patterns[0] = pointA;
2635+ patterns[1] = pointB;
2636+ patterns[2] = pointC;
2637+ }
2638+
2639+
2640+function FinderPattern(posX, posY, estimatedModuleSize)
2641+{
2642+ this.x=posX;
2643+ this.y=posY;
2644+ this.count = 1;
2645+ this.estimatedModuleSize = estimatedModuleSize;
2646+
2647+ this.__defineGetter__("EstimatedModuleSize", function()
2648+ {
2649+ return this.estimatedModuleSize;
2650+ });
2651+ this.__defineGetter__("Count", function()
2652+ {
2653+ return this.count;
2654+ });
2655+ this.__defineGetter__("X", function()
2656+ {
2657+ return this.x;
2658+ });
2659+ this.__defineGetter__("Y", function()
2660+ {
2661+ return this.y;
2662+ });
2663+ this.incrementCount = function()
2664+ {
2665+ this.count++;
2666+ }
2667+ this.aboutEquals=function( moduleSize, i, j)
2668+ {
2669+ if (Math.abs(i - this.y) <= moduleSize && Math.abs(j - this.x) <= moduleSize)
2670+ {
2671+ var moduleSizeDiff = Math.abs(moduleSize - this.estimatedModuleSize);
2672+ return moduleSizeDiff <= 1.0 || moduleSizeDiff / this.estimatedModuleSize <= 1.0;
2673+ }
2674+ return false;
2675+ }
2676+
2677+}
2678+
2679+function FinderPatternInfo(patternCenters)
2680+{
2681+ this.bottomLeft = patternCenters[0];
2682+ this.topLeft = patternCenters[1];
2683+ this.topRight = patternCenters[2];
2684+ this.__defineGetter__("BottomLeft", function()
2685+ {
2686+ return this.bottomLeft;
2687+ });
2688+ this.__defineGetter__("TopLeft", function()
2689+ {
2690+ return this.topLeft;
2691+ });
2692+ this.__defineGetter__("TopRight", function()
2693+ {
2694+ return this.topRight;
2695+ });
2696+}
2697+
2698+function FinderPatternFinder()
2699+{
2700+ this.image=null;
2701+ this.possibleCenters = [];
2702+ this.hasSkipped = false;
2703+ this.crossCheckStateCount = new Array(0,0,0,0,0);
2704+ this.resultPointCallback = null;
2705+
2706+ this.__defineGetter__("CrossCheckStateCount", function()
2707+ {
2708+ this.crossCheckStateCount[0] = 0;
2709+ this.crossCheckStateCount[1] = 0;
2710+ this.crossCheckStateCount[2] = 0;
2711+ this.crossCheckStateCount[3] = 0;
2712+ this.crossCheckStateCount[4] = 0;
2713+ return this.crossCheckStateCount;
2714+ });
2715+
2716+ this.foundPatternCross=function( stateCount)
2717+ {
2718+ var totalModuleSize = 0;
2719+ for (var i = 0; i < 5; i++)
2720+ {
2721+ var count = stateCount[i];
2722+ if (count == 0)
2723+ {
2724+ return false;
2725+ }
2726+ totalModuleSize += count;
2727+ }
2728+ if (totalModuleSize < 7)
2729+ {
2730+ return false;
2731+ }
2732+ var moduleSize = Math.floor((totalModuleSize << INTEGER_MATH_SHIFT) / 7);
2733+ var maxVariance = Math.floor(moduleSize / 2);
2734+ // Allow less than 50% variance from 1-1-3-1-1 proportions
2735+ return Math.abs(moduleSize - (stateCount[0] << INTEGER_MATH_SHIFT)) < maxVariance && Math.abs(moduleSize - (stateCount[1] << INTEGER_MATH_SHIFT)) < maxVariance && Math.abs(3 * moduleSize - (stateCount[2] << INTEGER_MATH_SHIFT)) < 3 * maxVariance && Math.abs(moduleSize - (stateCount[3] << INTEGER_MATH_SHIFT)) < maxVariance && Math.abs(moduleSize - (stateCount[4] << INTEGER_MATH_SHIFT)) < maxVariance;
2736+ }
2737+ this.centerFromEnd=function( stateCount, end)
2738+ {
2739+ return (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0;
2740+ }
2741+ this.crossCheckVertical=function( startI, centerJ, maxCount, originalStateCountTotal)
2742+ {
2743+ var image = this.image;
2744+
2745+ var maxI = qrcode.height;
2746+ var stateCount = this.CrossCheckStateCount;
2747+
2748+ // Start counting up from center
2749+ var i = startI;
2750+ while (i >= 0 && image[centerJ + i*qrcode.width])
2751+ {
2752+ stateCount[2]++;
2753+ i--;
2754+ }
2755+ if (i < 0)
2756+ {
2757+ return NaN;
2758+ }
2759+ while (i >= 0 && !image[centerJ +i*qrcode.width] && stateCount[1] <= maxCount)
2760+ {
2761+ stateCount[1]++;
2762+ i--;
2763+ }
2764+ // If already too many modules in this state or ran off the edge:
2765+ if (i < 0 || stateCount[1] > maxCount)
2766+ {
2767+ return NaN;
2768+ }
2769+ while (i >= 0 && image[centerJ + i*qrcode.width] && stateCount[0] <= maxCount)
2770+ {
2771+ stateCount[0]++;
2772+ i--;
2773+ }
2774+ if (stateCount[0] > maxCount)
2775+ {
2776+ return NaN;
2777+ }
2778+
2779+ // Now also count down from center
2780+ i = startI + 1;
2781+ while (i < maxI && image[centerJ +i*qrcode.width])
2782+ {
2783+ stateCount[2]++;
2784+ i++;
2785+ }
2786+ if (i == maxI)
2787+ {
2788+ return NaN;
2789+ }
2790+ while (i < maxI && !image[centerJ + i*qrcode.width] && stateCount[3] < maxCount)
2791+ {
2792+ stateCount[3]++;
2793+ i++;
2794+ }
2795+ if (i == maxI || stateCount[3] >= maxCount)
2796+ {
2797+ return NaN;
2798+ }
2799+ while (i < maxI && image[centerJ + i*qrcode.width] && stateCount[4] < maxCount)
2800+ {
2801+ stateCount[4]++;
2802+ i++;
2803+ }
2804+ if (stateCount[4] >= maxCount)
2805+ {
2806+ return NaN;
2807+ }
2808+
2809+ // If we found a finder-pattern-like section, but its size is more than 40% different than
2810+ // the original, assume it's a false positive
2811+ var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
2812+ if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal)
2813+ {
2814+ return NaN;
2815+ }
2816+
2817+ return this.foundPatternCross(stateCount)?this.centerFromEnd(stateCount, i):NaN;
2818+ }
2819+ this.crossCheckHorizontal=function( startJ, centerI, maxCount, originalStateCountTotal)
2820+ {
2821+ var image = this.image;
2822+
2823+ var maxJ = qrcode.width;
2824+ var stateCount = this.CrossCheckStateCount;
2825+
2826+ var j = startJ;
2827+ while (j >= 0 && image[j+ centerI*qrcode.width])
2828+ {
2829+ stateCount[2]++;
2830+ j--;
2831+ }
2832+ if (j < 0)
2833+ {
2834+ return NaN;
2835+ }
2836+ while (j >= 0 && !image[j+ centerI*qrcode.width] && stateCount[1] <= maxCount)
2837+ {
2838+ stateCount[1]++;
2839+ j--;
2840+ }
2841+ if (j < 0 || stateCount[1] > maxCount)
2842+ {
2843+ return NaN;
2844+ }
2845+ while (j >= 0 && image[j+ centerI*qrcode.width] && stateCount[0] <= maxCount)
2846+ {
2847+ stateCount[0]++;
2848+ j--;
2849+ }
2850+ if (stateCount[0] > maxCount)
2851+ {
2852+ return NaN;
2853+ }
2854+
2855+ j = startJ + 1;
2856+ while (j < maxJ && image[j+ centerI*qrcode.width])
2857+ {
2858+ stateCount[2]++;
2859+ j++;
2860+ }
2861+ if (j == maxJ)
2862+ {
2863+ return NaN;
2864+ }
2865+ while (j < maxJ && !image[j+ centerI*qrcode.width] && stateCount[3] < maxCount)
2866+ {
2867+ stateCount[3]++;
2868+ j++;
2869+ }
2870+ if (j == maxJ || stateCount[3] >= maxCount)
2871+ {
2872+ return NaN;
2873+ }
2874+ while (j < maxJ && image[j+ centerI*qrcode.width] && stateCount[4] < maxCount)
2875+ {
2876+ stateCount[4]++;
2877+ j++;
2878+ }
2879+ if (stateCount[4] >= maxCount)
2880+ {
2881+ return NaN;
2882+ }
2883+
2884+ // If we found a finder-pattern-like section, but its size is significantly different than
2885+ // the original, assume it's a false positive
2886+ var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
2887+ if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal)
2888+ {
2889+ return NaN;
2890+ }
2891+
2892+ return this.foundPatternCross(stateCount)?this.centerFromEnd(stateCount, j):NaN;
2893+ }
2894+ this.handlePossibleCenter=function( stateCount, i, j)
2895+ {
2896+ var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
2897+ var centerJ = this.centerFromEnd(stateCount, j); //float
2898+ var centerI = this.crossCheckVertical(i, Math.floor( centerJ), stateCount[2], stateCountTotal); //float
2899+ if (!isNaN(centerI))
2900+ {
2901+ // Re-cross check
2902+ centerJ = this.crossCheckHorizontal(Math.floor( centerJ), Math.floor( centerI), stateCount[2], stateCountTotal);
2903+ if (!isNaN(centerJ))
2904+ {
2905+ var estimatedModuleSize = stateCountTotal / 7.0;
2906+ var found = false;
2907+ var max = this.possibleCenters.length;
2908+ for (var index = 0; index < max; index++)
2909+ {
2910+ var center = this.possibleCenters[index];
2911+ // Look for about the same center and module size:
2912+ if (center.aboutEquals(estimatedModuleSize, centerI, centerJ))
2913+ {
2914+ center.incrementCount();
2915+ found = true;
2916+ break;
2917+ }
2918+ }
2919+ if (!found)
2920+ {
2921+ var point = new FinderPattern(centerJ, centerI, estimatedModuleSize);
2922+ this.possibleCenters.push(point);
2923+ if (this.resultPointCallback != null)
2924+ {
2925+ this.resultPointCallback.foundPossibleResultPoint(point);
2926+ }
2927+ }
2928+ return true;
2929+ }
2930+ }
2931+ return false;
2932+ }
2933+
2934+ this.selectBestPatterns=function()
2935+ {
2936+
2937+ var startSize = this.possibleCenters.length;
2938+ if (startSize < 3)
2939+ {
2940+ // Couldn't find enough finder patterns
2941+ throw "Couldn't find enough finder patterns (found " + startSize + ")";
2942+ }
2943+
2944+ // Filter outlier possibilities whose module size is too different
2945+ if (startSize > 3)
2946+ {
2947+ // But we can only afford to do so if we have at least 4 possibilities to choose from
2948+ var totalModuleSize = 0.0;
2949+ for (var i = 0; i < startSize; i++)
2950+ {
2951+ totalModuleSize += this.possibleCenters[i].EstimatedModuleSize;
2952+ }
2953+ var average = totalModuleSize / startSize;
2954+ for (var i = 0; i < this.possibleCenters.length && this.possibleCenters.length > 3; i++)
2955+ {
2956+ var pattern = this.possibleCenters[i];
2957+ if (Math.abs(pattern.EstimatedModuleSize - average) > 0.2 * average)
2958+ {
2959+ this.possibleCenters.remove(i);
2960+ i--;
2961+ }
2962+ }
2963+ }
2964+
2965+ if (this.possibleCenters.Count > 3)
2966+ {
2967+ // Throw away all but those first size candidate points we found.
2968+ //Collections.insertionSort(possibleCenters, new CenterComparator());
2969+ //SupportClass.SetCapacity(possibleCenters, 3);
2970+ }
2971+
2972+ return new Array( this.possibleCenters[0], this.possibleCenters[1], this.possibleCenters[2]);
2973+ }
2974+
2975+ this.findRowSkip=function()
2976+ {
2977+ var max = this.possibleCenters.length;
2978+ if (max <= 1)
2979+ {
2980+ return 0;
2981+ }
2982+ var firstConfirmedCenter = null;
2983+ for (var i = 0; i < max; i++)
2984+ {
2985+ var center = this.possibleCenters[i];
2986+ if (center.Count >= CENTER_QUORUM)
2987+ {
2988+ if (firstConfirmedCenter == null)
2989+ {
2990+ firstConfirmedCenter = center;
2991+ }
2992+ else
2993+ {
2994+ // We have two confirmed centers
2995+ // How far down can we skip before resuming looking for the next
2996+ // pattern? In the worst case, only the difference between the
2997+ // difference in the x / y coordinates of the two centers.
2998+ // This is the case where you find top left last.
2999+ this.hasSkipped = true;
3000+ return Math.floor ((Math.abs(firstConfirmedCenter.X - center.X) - Math.abs(firstConfirmedCenter.Y - center.Y)) / 2);
3001+ }
3002+ }
3003+ }
3004+ return 0;
3005+ }
3006+
3007+ this.haveMultiplyConfirmedCenters=function()
3008+ {
3009+ var confirmedCount = 0;
3010+ var totalModuleSize = 0.0;
3011+ var max = this.possibleCenters.length;
3012+ for (var i = 0; i < max; i++)
3013+ {
3014+ var pattern = this.possibleCenters[i];
3015+ if (pattern.Count >= CENTER_QUORUM)
3016+ {
3017+ confirmedCount++;
3018+ totalModuleSize += pattern.EstimatedModuleSize;
3019+ }
3020+ }
3021+ if (confirmedCount < 3)
3022+ {
3023+ return false;
3024+ }
3025+ // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
3026+ // and that we need to keep looking. We detect this by asking if the estimated module sizes
3027+ // vary too much. We arbitrarily say that when the total deviation from average exceeds
3028+ // 5% of the total module size estimates, it's too much.
3029+ var average = totalModuleSize / max;
3030+ var totalDeviation = 0.0;
3031+ for (var i = 0; i < max; i++)
3032+ {
3033+ pattern = this.possibleCenters[i];
3034+ totalDeviation += Math.abs(pattern.EstimatedModuleSize - average);
3035+ }
3036+ return totalDeviation <= 0.05 * totalModuleSize;
3037+ }
3038+
3039+ this.findFinderPattern = function(image){
3040+ var tryHarder = false;
3041+ this.image=image;
3042+ var maxI = qrcode.height;
3043+ var maxJ = qrcode.width;
3044+ var iSkip = Math.floor((3 * maxI) / (4 * MAX_MODULES));
3045+ if (iSkip < MIN_SKIP || tryHarder)
3046+ {
3047+ iSkip = MIN_SKIP;
3048+ }
3049+
3050+ var done = false;
3051+ var stateCount = new Array(5);
3052+ for (var i = iSkip - 1; i < maxI && !done; i += iSkip)
3053+ {
3054+ // Get a row of black/white values
3055+ stateCount[0] = 0;
3056+ stateCount[1] = 0;
3057+ stateCount[2] = 0;
3058+ stateCount[3] = 0;
3059+ stateCount[4] = 0;
3060+ var currentState = 0;
3061+ for (var j = 0; j < maxJ; j++)
3062+ {
3063+ if (image[j+i*qrcode.width] )
3064+ {
3065+ // Black pixel
3066+ if ((currentState & 1) == 1)
3067+ {
3068+ // Counting white pixels
3069+ currentState++;
3070+ }
3071+ stateCount[currentState]++;
3072+ }
3073+ else
3074+ {
3075+ // White pixel
3076+ if ((currentState & 1) == 0)
3077+ {
3078+ // Counting black pixels
3079+ if (currentState == 4)
3080+ {
3081+ // A winner?
3082+ if (this.foundPatternCross(stateCount))
3083+ {
3084+ // Yes
3085+ var confirmed = this.handlePossibleCenter(stateCount, i, j);
3086+ if (confirmed)
3087+ {
3088+ // Start examining every other line. Checking each line turned out to be too
3089+ // expensive and didn't improve performance.
3090+ iSkip = 2;
3091+ if (this.hasSkipped)
3092+ {
3093+ done = this.haveMultiplyConfirmedCenters();
3094+ }
3095+ else
3096+ {
3097+ var rowSkip = this.findRowSkip();
3098+ if (rowSkip > stateCount[2])
3099+ {
3100+ // Skip rows between row of lower confirmed center
3101+ // and top of presumed third confirmed center
3102+ // but back up a bit to get a full chance of detecting
3103+ // it, entire width of center of finder pattern
3104+
3105+ // Skip by rowSkip, but back off by stateCount[2] (size of last center
3106+ // of pattern we saw) to be conservative, and also back off by iSkip which
3107+ // is about to be re-added
3108+ i += rowSkip - stateCount[2] - iSkip;
3109+ j = maxJ - 1;
3110+ }
3111+ }
3112+ }
3113+ else
3114+ {
3115+ // Advance to next black pixel
3116+ do
3117+ {
3118+ j++;
3119+ }
3120+ while (j < maxJ && !image[j + i*qrcode.width]);
3121+ j--; // back up to that last white pixel
3122+ }
3123+ // Clear state to start looking again
3124+ currentState = 0;
3125+ stateCount[0] = 0;
3126+ stateCount[1] = 0;
3127+ stateCount[2] = 0;
3128+ stateCount[3] = 0;
3129+ stateCount[4] = 0;
3130+ }
3131+ else
3132+ {
3133+ // No, shift counts back by two
3134+ stateCount[0] = stateCount[2];
3135+ stateCount[1] = stateCount[3];
3136+ stateCount[2] = stateCount[4];
3137+ stateCount[3] = 1;
3138+ stateCount[4] = 0;
3139+ currentState = 3;
3140+ }
3141+ }
3142+ else
3143+ {
3144+ stateCount[++currentState]++;
3145+ }
3146+ }
3147+ else
3148+ {
3149+ // Counting white pixels
3150+ stateCount[currentState]++;
3151+ }
3152+ }
3153+ }
3154+ if (this.foundPatternCross(stateCount))
3155+ {
3156+ var confirmed = this.handlePossibleCenter(stateCount, i, maxJ);
3157+ if (confirmed)
3158+ {
3159+ iSkip = stateCount[0];
3160+ if (this.hasSkipped)
3161+ {
3162+ // Found a third one
3163+ done = haveMultiplyConfirmedCenters();
3164+ }
3165+ }
3166+ }
3167+ }
3168+
3169+ var patternInfo = this.selectBestPatterns();
3170+ qrcode.orderBestPatterns(patternInfo);
3171+
3172+ return new FinderPatternInfo(patternInfo);
3173+ };
3174+}/*
3175+ Ported to JavaScript by Lazar Laszlo 2011
3176+
3177+ lazarsoft@gmail.com, www.lazarsoft.info
3178+
3179+*/
3180+
3181+/*
3182+*
3183+* Copyright 2007 ZXing authors
3184+*
3185+* Licensed under the Apache License, Version 2.0 (the "License");
3186+* you may not use this file except in compliance with the License.
3187+* You may obtain a copy of the License at
3188+*
3189+* http://www.apache.org/licenses/LICENSE-2.0
3190+*
3191+* Unless required by applicable law or agreed to in writing, software
3192+* distributed under the License is distributed on an "AS IS" BASIS,
3193+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3194+* See the License for the specific language governing permissions and
3195+* limitations under the License.
3196+*/
3197+
3198+
3199+function AlignmentPattern(posX, posY, estimatedModuleSize)
3200+{
3201+ this.x=posX;
3202+ this.y=posY;
3203+ this.count = 1;
3204+ this.estimatedModuleSize = estimatedModuleSize;
3205+
3206+ this.__defineGetter__("EstimatedModuleSize", function()
3207+ {
3208+ return this.estimatedModuleSize;
3209+ });
3210+ this.__defineGetter__("Count", function()
3211+ {
3212+ return this.count;
3213+ });
3214+ this.__defineGetter__("X", function()
3215+ {
3216+ return Math.floor(this.x);
3217+ });
3218+ this.__defineGetter__("Y", function()
3219+ {
3220+ return Math.floor(this.y);
3221+ });
3222+ this.incrementCount = function()
3223+ {
3224+ this.count++;
3225+ }
3226+ this.aboutEquals=function( moduleSize, i, j)
3227+ {
3228+ if (Math.abs(i - this.y) <= moduleSize && Math.abs(j - this.x) <= moduleSize)
3229+ {
3230+ var moduleSizeDiff = Math.abs(moduleSize - this.estimatedModuleSize);
3231+ return moduleSizeDiff <= 1.0 || moduleSizeDiff / this.estimatedModuleSize <= 1.0;
3232+ }
3233+ return false;
3234+ }
3235+
3236+}
3237+
3238+function AlignmentPatternFinder( image, startX, startY, width, height, moduleSize, resultPointCallback)
3239+{
3240+ this.image = image;
3241+ this.possibleCenters = new Array();
3242+ this.startX = startX;
3243+ this.startY = startY;
3244+ this.width = width;
3245+ this.height = height;
3246+ this.moduleSize = moduleSize;
3247+ this.crossCheckStateCount = new Array(0,0,0);
3248+ this.resultPointCallback = resultPointCallback;
3249+
3250+ this.centerFromEnd=function(stateCount, end)
3251+ {
3252+ return (end - stateCount[2]) - stateCount[1] / 2.0;
3253+ }
3254+ this.foundPatternCross = function(stateCount)
3255+ {
3256+ var moduleSize = this.moduleSize;
3257+ var maxVariance = moduleSize / 2.0;
3258+ for (var i = 0; i < 3; i++)
3259+ {
3260+ if (Math.abs(moduleSize - stateCount[i]) >= maxVariance)
3261+ {
3262+ return false;
3263+ }
3264+ }
3265+ return true;
3266+ }
3267+
3268+ this.crossCheckVertical=function( startI, centerJ, maxCount, originalStateCountTotal)
3269+ {
3270+ var image = this.image;
3271+
3272+ var maxI = qrcode.height;
3273+ var stateCount = this.crossCheckStateCount;
3274+ stateCount[0] = 0;
3275+ stateCount[1] = 0;
3276+ stateCount[2] = 0;
3277+
3278+ // Start counting up from center
3279+ var i = startI;
3280+ while (i >= 0 && image[centerJ + i*qrcode.width] && stateCount[1] <= maxCount)
3281+ {
3282+ stateCount[1]++;
3283+ i--;
3284+ }
3285+ // If already too many modules in this state or ran off the edge:
3286+ if (i < 0 || stateCount[1] > maxCount)
3287+ {
3288+ return NaN;
3289+ }
3290+ while (i >= 0 && !image[centerJ + i*qrcode.width] && stateCount[0] <= maxCount)
3291+ {
3292+ stateCount[0]++;
3293+ i--;
3294+ }
3295+ if (stateCount[0] > maxCount)
3296+ {
3297+ return NaN;
3298+ }
3299+
3300+ // Now also count down from center
3301+ i = startI + 1;
3302+ while (i < maxI && image[centerJ + i*qrcode.width] && stateCount[1] <= maxCount)
3303+ {
3304+ stateCount[1]++;
3305+ i++;
3306+ }
3307+ if (i == maxI || stateCount[1] > maxCount)
3308+ {
3309+ return NaN;
3310+ }
3311+ while (i < maxI && !image[centerJ + i*qrcode.width] && stateCount[2] <= maxCount)
3312+ {
3313+ stateCount[2]++;
3314+ i++;
3315+ }
3316+ if (stateCount[2] > maxCount)
3317+ {
3318+ return NaN;
3319+ }
3320+
3321+ var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
3322+ if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal)
3323+ {
3324+ return NaN;
3325+ }
3326+
3327+ return this.foundPatternCross(stateCount)?this.centerFromEnd(stateCount, i):NaN;
3328+ }
3329+
3330+ this.handlePossibleCenter=function( stateCount, i, j)
3331+ {
3332+ var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
3333+ var centerJ = this.centerFromEnd(stateCount, j);
3334+ var centerI = this.crossCheckVertical(i, Math.floor (centerJ), 2 * stateCount[1], stateCountTotal);
3335+ if (!isNaN(centerI))
3336+ {
3337+ var estimatedModuleSize = (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0;
3338+ var max = this.possibleCenters.length;
3339+ for (var index = 0; index < max; index++)
3340+ {
3341+ var center = this.possibleCenters[index];
3342+ // Look for about the same center and module size:
3343+ if (center.aboutEquals(estimatedModuleSize, centerI, centerJ))
3344+ {
3345+ return new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
3346+ }
3347+ }
3348+ // Hadn't found this before; save it
3349+ var point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
3350+ this.possibleCenters.push(point);
3351+ if (this.resultPointCallback != null)
3352+ {
3353+ this.resultPointCallback.foundPossibleResultPoint(point);
3354+ }
3355+ }
3356+ return null;
3357+ }
3358+
3359+ this.find = function()
3360+ {
3361+ var startX = this.startX;
3362+ var height = this.height;
3363+ var maxJ = startX + width;
3364+ var middleI = startY + (height >> 1);
3365+ // We are looking for black/white/black modules in 1:1:1 ratio;
3366+ // this tracks the number of black/white/black modules seen so far
3367+ var stateCount = new Array(0,0,0);
3368+ for (var iGen = 0; iGen < height; iGen++)
3369+ {
3370+ // Search from middle outwards
3371+ var i = middleI + ((iGen & 0x01) == 0?((iGen + 1) >> 1):- ((iGen + 1) >> 1));
3372+ stateCount[0] = 0;
3373+ stateCount[1] = 0;
3374+ stateCount[2] = 0;
3375+ var j = startX;
3376+ // Burn off leading white pixels before anything else; if we start in the middle of
3377+ // a white run, it doesn't make sense to count its length, since we don't know if the
3378+ // white run continued to the left of the start point
3379+ while (j < maxJ && !image[j + qrcode.width* i])
3380+ {
3381+ j++;
3382+ }
3383+ var currentState = 0;
3384+ while (j < maxJ)
3385+ {
3386+ if (image[j + i*qrcode.width])
3387+ {
3388+ // Black pixel
3389+ if (currentState == 1)
3390+ {
3391+ // Counting black pixels
3392+ stateCount[currentState]++;
3393+ }
3394+ else
3395+ {
3396+ // Counting white pixels
3397+ if (currentState == 2)
3398+ {
3399+ // A winner?
3400+ if (this.foundPatternCross(stateCount))
3401+ {
3402+ // Yes
3403+ var confirmed = this.handlePossibleCenter(stateCount, i, j);
3404+ if (confirmed != null)
3405+ {
3406+ return confirmed;
3407+ }
3408+ }
3409+ stateCount[0] = stateCount[2];
3410+ stateCount[1] = 1;
3411+ stateCount[2] = 0;
3412+ currentState = 1;
3413+ }
3414+ else
3415+ {
3416+ stateCount[++currentState]++;
3417+ }
3418+ }
3419+ }
3420+ else
3421+ {
3422+ // White pixel
3423+ if (currentState == 1)
3424+ {
3425+ // Counting black pixels
3426+ currentState++;
3427+ }
3428+ stateCount[currentState]++;
3429+ }
3430+ j++;
3431+ }
3432+ if (this.foundPatternCross(stateCount))
3433+ {
3434+ var confirmed = this.handlePossibleCenter(stateCount, i, maxJ);
3435+ if (confirmed != null)
3436+ {
3437+ return confirmed;
3438+ }
3439+ }
3440+ }
3441+
3442+ // Hmm, nothing we saw was observed and confirmed twice. If we had
3443+ // any guess at all, return it.
3444+ if (!(this.possibleCenters.length == 0))
3445+ {
3446+ return this.possibleCenters[0];
3447+ }
3448+
3449+ throw "Couldn't find enough alignment patterns";
3450+ }
3451+
3452+}/*
3453+ Ported to JavaScript by Lazar Laszlo 2011
3454+
3455+ lazarsoft@gmail.com, www.lazarsoft.info
3456+
3457+*/
3458+
3459+/*
3460+*
3461+* Copyright 2007 ZXing authors
3462+*
3463+* Licensed under the Apache License, Version 2.0 (the "License");
3464+* you may not use this file except in compliance with the License.
3465+* You may obtain a copy of the License at
3466+*
3467+* http://www.apache.org/licenses/LICENSE-2.0
3468+*
3469+* Unless required by applicable law or agreed to in writing, software
3470+* distributed under the License is distributed on an "AS IS" BASIS,
3471+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3472+* See the License for the specific language governing permissions and
3473+* limitations under the License.
3474+*/
3475+
3476+
3477+function QRCodeDataBlockReader(blocks, version, numErrorCorrectionCode)
3478+{
3479+ this.blockPointer = 0;
3480+ this.bitPointer = 7;
3481+ this.dataLength = 0;
3482+ this.blocks = blocks;
3483+ this.numErrorCorrectionCode = numErrorCorrectionCode;
3484+ if (version <= 9)
3485+ this.dataLengthMode = 0;
3486+ else if (version >= 10 && version <= 26)
3487+ this.dataLengthMode = 1;
3488+ else if (version >= 27 && version <= 40)
3489+ this.dataLengthMode = 2;
3490+
3491+ this.getNextBits = function( numBits)
3492+ {
3493+ var bits = 0;
3494+ if (numBits < this.bitPointer + 1)
3495+ {
3496+ // next word fits into current data block
3497+ var mask = 0;
3498+ for (var i = 0; i < numBits; i++)
3499+ {
3500+ mask += (1 << i);
3501+ }
3502+ mask <<= (this.bitPointer - numBits + 1);
3503+
3504+ bits = (this.blocks[this.blockPointer] & mask) >> (this.bitPointer - numBits + 1);
3505+ this.bitPointer -= numBits;
3506+ return bits;
3507+ }
3508+ else if (numBits < this.bitPointer + 1 + 8)
3509+ {
3510+ // next word crosses 2 data blocks
3511+ var mask1 = 0;
3512+ for (var i = 0; i < this.bitPointer + 1; i++)
3513+ {
3514+ mask1 += (1 << i);
3515+ }
3516+ bits = (this.blocks[this.blockPointer] & mask1) << (numBits - (this.bitPointer + 1));
3517+ this.blockPointer++;
3518+ bits += ((this.blocks[this.blockPointer]) >> (8 - (numBits - (this.bitPointer + 1))));
3519+
3520+ this.bitPointer = this.bitPointer - numBits % 8;
3521+ if (this.bitPointer < 0)
3522+ {
3523+ this.bitPointer = 8 + this.bitPointer;
3524+ }
3525+ return bits;
3526+ }
3527+ else if (numBits < this.bitPointer + 1 + 16)
3528+ {
3529+ // next word crosses 3 data blocks
3530+ var mask1 = 0; // mask of first block
3531+ var mask3 = 0; // mask of 3rd block
3532+ //bitPointer + 1 : number of bits of the 1st block
3533+ //8 : number of the 2nd block (note that use already 8bits because next word uses 3 data blocks)
3534+ //numBits - (bitPointer + 1 + 8) : number of bits of the 3rd block
3535+ for (var i = 0; i < this.bitPointer + 1; i++)
3536+ {
3537+ mask1 += (1 << i);
3538+ }
3539+ var bitsFirstBlock = (this.blocks[this.blockPointer] & mask1) << (numBits - (this.bitPointer + 1));
3540+ this.blockPointer++;
3541+
3542+ var bitsSecondBlock = this.blocks[this.blockPointer] << (numBits - (this.bitPointer + 1 + 8));
3543+ this.blockPointer++;
3544+
3545+ for (var i = 0; i < numBits - (this.bitPointer + 1 + 8); i++)
3546+ {
3547+ mask3 += (1 << i);
3548+ }
3549+ mask3 <<= 8 - (numBits - (this.bitPointer + 1 + 8));
3550+ var bitsThirdBlock = (this.blocks[this.blockPointer] & mask3) >> (8 - (numBits - (this.bitPointer + 1 + 8)));
3551+
3552+ bits = bitsFirstBlock + bitsSecondBlock + bitsThirdBlock;
3553+ this.bitPointer = this.bitPointer - (numBits - 8) % 8;
3554+ if (this.bitPointer < 0)
3555+ {
3556+ this.bitPointer = 8 + this.bitPointer;
3557+ }
3558+ return bits;
3559+ }
3560+ else
3561+ {
3562+ return 0;
3563+ }
3564+ }
3565+ this.NextMode=function()
3566+ {
3567+ if ((this.blockPointer > this.blocks.length - this.numErrorCorrectionCode - 2))
3568+ return 0;
3569+ else
3570+ return this.getNextBits(4);
3571+ }
3572+ this.getDataLength=function( modeIndicator)
3573+ {
3574+ var index = 0;
3575+ while (true)
3576+ {
3577+ if ((modeIndicator >> index) == 1)
3578+ break;
3579+ index++;
3580+ }
3581+
3582+ return this.getNextBits(qrcode.sizeOfDataLengthInfo[this.dataLengthMode][index]);
3583+ }
3584+ this.getRomanAndFigureString=function( dataLength)
3585+ {
3586+ var length = dataLength;
3587+ var intData = 0;
3588+ var strData = "";
3589+ var tableRomanAndFigure = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':');
3590+ do
3591+ {
3592+ if (length > 1)
3593+ {
3594+ intData = this.getNextBits(11);
3595+ var firstLetter = Math.floor(intData / 45);
3596+ var secondLetter = intData % 45;
3597+ strData += tableRomanAndFigure[firstLetter];
3598+ strData += tableRomanAndFigure[secondLetter];
3599+ length -= 2;
3600+ }
3601+ else if (length == 1)
3602+ {
3603+ intData = this.getNextBits(6);
3604+ strData += tableRomanAndFigure[intData];
3605+ length -= 1;
3606+ }
3607+ }
3608+ while (length > 0);
3609+
3610+ return strData;
3611+ }
3612+ this.getFigureString=function( dataLength)
3613+ {
3614+ var length = dataLength;
3615+ var intData = 0;
3616+ var strData = "";
3617+ do
3618+ {
3619+ if (length >= 3)
3620+ {
3621+ intData = this.getNextBits(10);
3622+ if (intData < 100)
3623+ strData += "0";
3624+ if (intData < 10)
3625+ strData += "0";
3626+ length -= 3;
3627+ }
3628+ else if (length == 2)
3629+ {
3630+ intData = this.getNextBits(7);
3631+ if (intData < 10)
3632+ strData += "0";
3633+ length -= 2;
3634+ }
3635+ else if (length == 1)
3636+ {
3637+ intData = this.getNextBits(4);
3638+ length -= 1;
3639+ }
3640+ strData += intData;
3641+ }
3642+ while (length > 0);
3643+
3644+ return strData;
3645+ }
3646+ this.get8bitByteArray=function( dataLength)
3647+ {
3648+ var length = dataLength;
3649+ var intData = 0;
3650+ var output = new Array();
3651+
3652+ do
3653+ {
3654+ intData = this.getNextBits(8);
3655+ output.push( intData);
3656+ length--;
3657+ }
3658+ while (length > 0);
3659+ return output;
3660+ }
3661+ this.getKanjiString=function( dataLength)
3662+ {
3663+ var length = dataLength;
3664+ var intData = 0;
3665+ var unicodeString = "";
3666+ do
3667+ {
3668+ intData = getNextBits(13);
3669+ var lowerByte = intData % 0xC0;
3670+ var higherByte = intData / 0xC0;
3671+
3672+ var tempWord = (higherByte << 8) + lowerByte;
3673+ var shiftjisWord = 0;
3674+ if (tempWord + 0x8140 <= 0x9FFC)
3675+ {
3676+ // between 8140 - 9FFC on Shift_JIS character set
3677+ shiftjisWord = tempWord + 0x8140;
3678+ }
3679+ else
3680+ {
3681+ // between E040 - EBBF on Shift_JIS character set
3682+ shiftjisWord = tempWord + 0xC140;
3683+ }
3684+
3685+ //var tempByte = new Array(0,0);
3686+ //tempByte[0] = (sbyte) (shiftjisWord >> 8);
3687+ //tempByte[1] = (sbyte) (shiftjisWord & 0xFF);
3688+ //unicodeString += new String(SystemUtils.ToCharArray(SystemUtils.ToByteArray(tempByte)));
3689+ unicodeString += String.fromCharCode(shiftjisWord);
3690+ length--;
3691+ }
3692+ while (length > 0);
3693+
3694+
3695+ return unicodeString;
3696+ }
3697+
3698+ this.__defineGetter__("DataByte", function()
3699+ {
3700+ var output = new Array();
3701+ var MODE_NUMBER = 1;
3702+ var MODE_ROMAN_AND_NUMBER = 2;
3703+ var MODE_8BIT_BYTE = 4;
3704+ var MODE_KANJI = 8;
3705+ do
3706+ {
3707+ var mode = this.NextMode();
3708+ //canvas.println("mode: " + mode);
3709+ if (mode == 0)
3710+ {
3711+ if (output.length > 0)
3712+ break;
3713+ else
3714+ throw "Empty data block";
3715+ }
3716+ //if (mode != 1 && mode != 2 && mode != 4 && mode != 8)
3717+ // break;
3718+ //}
3719+ if (mode != MODE_NUMBER && mode != MODE_ROMAN_AND_NUMBER && mode != MODE_8BIT_BYTE && mode != MODE_KANJI)
3720+ {
3721+ /* canvas.println("Invalid mode: " + mode);
3722+ mode = guessMode(mode);
3723+ canvas.println("Guessed mode: " + mode); */
3724+ throw "Invalid mode: " + mode + " in (block:" + this.blockPointer + " bit:" + this.bitPointer + ")";
3725+ }
3726+ var dataLength = this.getDataLength(mode);
3727+ if (dataLength < 1)
3728+ throw "Invalid data length: " + dataLength;
3729+ //canvas.println("length: " + dataLength);
3730+ switch (mode)
3731+ {
3732+
3733+ case MODE_NUMBER:
3734+ //canvas.println("Mode: Figure");
3735+ var temp_str = this.getFigureString(dataLength);
3736+ var ta = new Array(temp_str.length);
3737+ for(var j=0;j<temp_str.length;j++)
3738+ ta[j]=temp_str.charCodeAt(j);
3739+ output.push(ta);
3740+ break;
3741+
3742+ case MODE_ROMAN_AND_NUMBER:
3743+ //canvas.println("Mode: Roman&Figure");
3744+ var temp_str = this.getRomanAndFigureString(dataLength);
3745+ var ta = new Array(temp_str.length);
3746+ for(var j=0;j<temp_str.length;j++)
3747+ ta[j]=temp_str.charCodeAt(j);
3748+ output.push(ta );
3749+ //output.Write(SystemUtils.ToByteArray(temp_sbyteArray2), 0, temp_sbyteArray2.Length);
3750+ break;
3751+
3752+ case MODE_8BIT_BYTE:
3753+ //canvas.println("Mode: 8bit Byte");
3754+ //sbyte[] temp_sbyteArray3;
3755+ var temp_sbyteArray3 = this.get8bitByteArray(dataLength);
3756+ output.push(temp_sbyteArray3);
3757+ //output.Write(SystemUtils.ToByteArray(temp_sbyteArray3), 0, temp_sbyteArray3.Length);
3758+ break;
3759+
3760+ case MODE_KANJI:
3761+ //canvas.println("Mode: Kanji");
3762+ //sbyte[] temp_sbyteArray4;
3763+ //temp_sbyteArray4 = SystemUtils.ToSByteArray(SystemUtils.ToByteArray(getKanjiString(dataLength)));
3764+ //output.Write(SystemUtils.ToByteArray(temp_sbyteArray4), 0, temp_sbyteArray4.Length);
3765+ var temp_str = this.getKanjiString(dataLength);
3766+ output.push(temp_str);
3767+ break;
3768+ }
3769+ //
3770+ //canvas.println("DataLength: " + dataLength);
3771+ //Console.out.println(dataString);
3772+ }
3773+ while (true);
3774+ return output;
3775+ });
3776+}
3777
3778=== modified file 'qreator-touch.qml'
3779--- qreator-touch.qml 2013-06-15 07:59:41 +0000
3780+++ qreator-touch.qml 2013-06-16 10:03:26 +0000
3781@@ -1,23 +1,30 @@
3782 import QtQuick 2.0
3783 import Ubuntu.Components 0.1
3784-
3785-import Ubuntu.Components.ListItems 0.1 as ListItem
3786+import Ubuntu.Components.ListItems 0.1 as ListItems
3787 import Ubuntu.Components.Popups 0.1
3788-import QtGraphicalEffects 1.0
3789
3790 import "qreator"
3791
3792 MainView {
3793- // We define objectName for functional testing purposes
3794- // (autopilot-qt5)
3795 objectName: "mainView"
3796 applicationName: "qreator-touch"
3797+ automaticOrientation: true
3798+ property bool debugMode: false
3799+ property real pageSpacing: units.gu(6)
3800 id: root
3801
3802- width: units.gu(50)
3803- height: units.gu(75)
3804-
3805- property real pageSpacing: units.gu(6)
3806+ width: units.gu(80)
3807+ height: units.gu(80)
3808+
3809+ function debugLog(msg) {
3810+ /* Display debug messages in the debug tab. Useful to present debug messages
3811+ in a device
3812+
3813+ @msg: string with the debug message to display
3814+ */
3815+ debugLogger.text += msg + "\n"
3816+ console.log(msg)
3817+ }
3818
3819 Tabs {
3820 id: tabs
3821@@ -26,7 +33,7 @@
3822 objectName: "tabScanner"
3823 title: i18n.tr("Scan")
3824 page: Scanner {
3825- id: scannerPage
3826+ id: pageScanner
3827 }
3828 }
3829
3830@@ -34,17 +41,43 @@
3831 objectName: "tabQreator"
3832 title: i18n.tr("Create")
3833 page: Creator {
3834- id: creatorPage
3835+ id: pageCreator
3836 }
3837 }
3838
3839 Tab {
3840- objectName: "tabHistory"
3841- title: i18n.tr("History")
3842- page: History {
3843- id: historyPage
3844+ objectName: "TabDebug"
3845+ title: i18n.tr("Debug")
3846+
3847+ // FIXME: this property seems to have a weird effect: when set to
3848+ // true, the tab contents are always visible, also when other tabs
3849+ // are loaded
3850+ //visible: root.debugMode? true : false
3851+
3852+ page: Page {
3853+ Column {
3854+ anchors.fill: parent
3855+
3856+ spacing: units.gu(1)
3857+
3858+ TextArea {
3859+ id: debugLogger
3860+ width: parent.width
3861+ height: parent.height - parent.spacing - buttonClear.height
3862+
3863+ readOnly: true
3864+ text: ""
3865+ }
3866+
3867+ Button {
3868+ id: buttonClear
3869+ text: "Clear"
3870+ width: parent.width
3871+
3872+ onClicked: debugLogger.text = "";
3873+ }
3874+ }
3875 }
3876 }
3877 }
3878 }
3879-
3880
3881=== renamed file 'qreator.qmlproject' => 'qreator-touch.qmlproject'
3882=== modified file 'qreator/Creator.qml'
3883--- qreator/Creator.qml 2013-06-15 07:59:41 +0000
3884+++ qreator/Creator.qml 2013-06-16 10:03:26 +0000
3885@@ -52,22 +52,5 @@
3886 }
3887 }
3888
3889- tools: ToolbarActions {
3890- Action {
3891- text: i18n.tr("About")
3892- iconSource: Qt.resolvedUrl("../img/about.png")
3893- onTriggered: pageStack.push(pageAbout)
3894- }
3895- Action {
3896- text: i18n.tr("History")
3897- iconSource: Qt.resolvedUrl("../img/history.png")
3898- onTriggered: pageStack.push(pageHistory)
3899- }
3900- Action {
3901- text: i18n.tr("Settings")
3902- iconSource: Qt.resolvedUrl("../img/settings.png")
3903- onTriggered: pageStack.push(pageSettings)
3904- }
3905- }
3906 }
3907 }
3908
3909=== added file 'qreator/Decoder.qml'
3910--- qreator/Decoder.qml 1970-01-01 00:00:00 +0000
3911+++ qreator/Decoder.qml 2013-06-16 10:03:26 +0000
3912@@ -0,0 +1,68 @@
3913+import QtQuick 2.0
3914+import Ubuntu.Components 0.1
3915+import QZXing 1.2
3916+import "../js/jsqrcode-combined.js" as QR
3917+
3918+Item {
3919+
3920+ // This property allows switching between decoding backends:
3921+ // true: uses the JavaScript decoding backend
3922+ // false: uses the C++ decoding backend
3923+ property bool useJsbackend: false
3924+
3925+ signal decodingSuccess(string tag)
3926+ signal decodingStart()
3927+ signal decodingFinish(bool success)
3928+
3929+ function decode(imageUrl) {
3930+ if (!useJsbackend) {
3931+ decoder.decodeImageQML(imageUrl);
3932+ } else {
3933+ decodingcanvas.imageurl = imageUrl;
3934+ decodingcanvas.loadImage(imageUrl);
3935+ }
3936+ }
3937+
3938+ // For the C++ decoding backend
3939+ QZXing {
3940+ id: decoder
3941+ onTagFound: {
3942+ decodingSuccess(tag);
3943+ }
3944+
3945+ onDecodingStarted: {
3946+ decodingStart();
3947+ }
3948+
3949+ onDecodingFinished: {
3950+ decodingFinish(succeeded);
3951+ }
3952+ }
3953+
3954+ // For the JavaScript decoding backend
3955+ Canvas {
3956+ id: decodingcanvas
3957+ visible: false
3958+ property string imageurl
3959+
3960+ onImageLoaded: {
3961+ debugLog("Loaded image:" + imageurl);
3962+ var context = decodingcanvas.getContext("2d");
3963+ QR.qrcode.imagedata = context.createImageData(imageurl);
3964+ QR.qrcode.width = QR.qrcode.imagedata.width;
3965+ QR.qrcode.height = QR.qrcode.imagedata.height;
3966+
3967+ decodingStart();
3968+ try {
3969+ var result = QR.qrcode.process(context);
3970+ decodingSuccess(result);
3971+ decodingFinish(true);
3972+ debugLog("Result: " + result);
3973+ }
3974+ catch(e) {
3975+ debugLog("Could not decode: " + e );
3976+ decodingFinish(false);
3977+ }
3978+ }
3979+ }
3980+}
3981
3982=== added file 'qreator/DeviceOrientation.qml'
3983--- qreator/DeviceOrientation.qml 1970-01-01 00:00:00 +0000
3984+++ qreator/DeviceOrientation.qml 2013-06-16 10:03:26 +0000
3985@@ -0,0 +1,37 @@
3986+/*
3987+ * Copyright 2013 Canonical Ltd.
3988+ *
3989+ * This program is free software; you can redistribute it and/or modify
3990+ * it under the terms of the GNU Lesser General Public License as published by
3991+ * the Free Software Foundation; version 3.
3992+ *
3993+ * This program is distributed in the hope that it will be useful,
3994+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3995+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3996+ * GNU Lesser General Public License for more details.
3997+ *
3998+ * You should have received a copy of the GNU Lesser General Public License
3999+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4000+ */
4001+
4002+import QtQuick 2.0
4003+import QtQuick.Window 2.0
4004+
4005+// We must use Item element because Screen component does not works with QtObject
4006+Item {
4007+ property string naturalOrientation: Screen.primaryOrientation == Qt.LandscapeOrientation ? "landscape" : "portrait"
4008+
4009+ /* Is the device currently rotated to be in lanscape orientation ? */
4010+ property bool isLandscape: Screen.orientation == Qt.LandscapeOrientation ||
4011+ Screen.orientation == Qt.InvertedLandscapeOrientation
4012+
4013+ /* Is the device currently rotated upside down ? */
4014+ property bool isInverted: Screen.orientation == Qt.InvertedLandscapeOrientation ||
4015+ Screen.orientation == Qt.InvertedPortraitOrientation
4016+
4017+ /* The rotation angle in 90 degrees increments with respect to the device being in its
4018+ default position */
4019+ property int rotationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
4020+
4021+ visible: false
4022+}
4023
4024=== modified file 'qreator/History.qml'
4025--- qreator/History.qml 2013-06-15 07:59:41 +0000
4026+++ qreator/History.qml 2013-06-16 10:03:26 +0000
4027@@ -2,8 +2,8 @@
4028 import Ubuntu.Components 0.1
4029
4030 Page {
4031- id: pageRoot
4032- title: i18n.tr("Scan")
4033+ id: pageHistory
4034+ title: i18n.tr("History")
4035 visible: false
4036
4037 }
4038
4039=== modified file 'qreator/QrCodeCanvas.qml'
4040--- qreator/QrCodeCanvas.qml 2013-06-15 07:59:41 +0000
4041+++ qreator/QrCodeCanvas.qml 2013-06-16 10:03:26 +0000
4042@@ -13,7 +13,7 @@
4043 RectangularGlow {
4044 id: effect
4045 anchors.fill: qrcanvas
4046- glowRadius: 10
4047+ glowRadius: units.gu(1)
4048 spread: 0.2
4049 color: "#DD4814"
4050 cornerRadius: glowRadius
4051@@ -28,15 +28,15 @@
4052 onPaint: drawQrCode(container.text)
4053 }
4054
4055- function requestQrCodePaint(textme) {
4056- container.text = textme;
4057+ function requestQrCodePaint(qrcodetext) {
4058+ container.text = qrcodetext;
4059 qrcanvas.requestPaint();
4060 }
4061
4062 function drawQrCode (textdata) {
4063 var ctx = qrcanvas.getContext('2d');
4064
4065- var pixel_size = 8;
4066+ var pixel_size = units.gu(1);
4067 // Determines the overall dimensions of the symbol (1..10)
4068 // We choose Version 4 (33×33).
4069 var QRCodeVersion = 4;
4070
4071=== added file 'qreator/QrCodeDialog.qml'
4072--- qreator/QrCodeDialog.qml 1970-01-01 00:00:00 +0000
4073+++ qreator/QrCodeDialog.qml 2013-06-16 10:03:26 +0000
4074@@ -0,0 +1,44 @@
4075+import QtQuick 2.0
4076+import Ubuntu.Components 0.1
4077+import Ubuntu.Components.Popups 0.1
4078+
4079+Dialog {
4080+ id: qrcodedialog
4081+ title: ""
4082+ text: ""
4083+ property bool decodingSuccess: false
4084+
4085+ onTextChanged: {
4086+ fakeClipboard.text = text;
4087+ }
4088+
4089+ Button {
4090+ text: i18n.tr("Open")
4091+ color: "#DD4814"
4092+ visible: decodingSuccess ? true:false
4093+ onClicked: PopupUtils.close(qrcodedialog);
4094+ }
4095+
4096+ Button {
4097+ text: i18n.tr("Copy to clipboard")
4098+ visible: decodingSuccess ? true:false
4099+ onClicked: {
4100+ fakeClipboard.selectAll();
4101+ fakeClipboard.copy();
4102+ PopupUtils.close(qrcodedialog);
4103+ }
4104+ }
4105+
4106+ Button {
4107+ text: decodingSuccess ? i18n.tr("Cancel"):i18n.tr("OK")
4108+ onClicked: PopupUtils.close(qrcodedialog);
4109+ }
4110+
4111+ TextField {
4112+ // There is no QML clipboard API, so we use the Textfield's
4113+ // copy() method to indirectly access the clipboard
4114+ id: fakeClipboard
4115+ visible: false
4116+ }
4117+
4118+}
4119
4120=== modified file 'qreator/QrCodeToolbar.qml'
4121--- qreator/QrCodeToolbar.qml 2013-06-15 07:59:41 +0000
4122+++ qreator/QrCodeToolbar.qml 2013-06-16 10:03:26 +0000
4123@@ -1,6 +1,17 @@
4124 import QtQuick 2.0
4125 import Ubuntu.Components 0.1
4126-import QtQuick.LocalStorage 2.0
4127
4128 ToolbarActions {
4129-}
4130\ No newline at end of file
4131+ Action {
4132+ text: i18n.tr("Edit")
4133+ iconSource: Qt.resolvedUrl("../img/edit.png")
4134+ }
4135+ Action {
4136+ text: i18n.tr("Save")
4137+ iconSource: Qt.resolvedUrl("../img/save.png")
4138+ }
4139+ Action {
4140+ text: i18n.tr("Share")
4141+ iconSource: Qt.resolvedUrl("../img/share.png")
4142+ }
4143+}
4144
4145=== modified file 'qreator/Scanner.qml'
4146--- qreator/Scanner.qml 2013-06-15 07:59:41 +0000
4147+++ qreator/Scanner.qml 2013-06-16 10:03:26 +0000
4148@@ -1,13 +1,189 @@
4149 import QtQuick 2.0
4150 import Ubuntu.Components 0.1
4151-
4152-import Ubuntu.Components.ListItems 0.1 as ListItem
4153 import Ubuntu.Components.Popups 0.1
4154-import QtGraphicalEffects 1.0
4155-
4156-Page {
4157- id: pageRoot
4158- title: i18n.tr("Scan")
4159- visible: false
4160-
4161+import QtMultimedia 5.0
4162+
4163+Item {
4164+ anchors.fill: parent
4165+
4166+ /*
4167+ Connections {
4168+ target: Qt.application
4169+ // FIXME: this paints the video output on top of the canvas
4170+ onActiveChanged: (Qt.application.active) ? camera.start() : camera.stop();
4171+ }*/
4172+
4173+ Page {
4174+ id: pagePreview
4175+
4176+ onWidthChanged: canvas.requestPaint();
4177+ onHeightChanged: canvas.requestPaint();
4178+
4179+ tools: ToolbarActions {
4180+ Action {
4181+ text: i18n.tr("Decode")
4182+ iconSource: Qt.resolvedUrl("../img/camera.png")
4183+ onTriggered: captureImage();
4184+ }
4185+ Action {
4186+ text: i18n.tr("Upload")
4187+ iconSource: Qt.resolvedUrl("../img/add.png")
4188+ onTriggered: PopupUtils.open(uploader);
4189+ }
4190+ }
4191+
4192+ VideoOutput {
4193+ id: videoPreview
4194+ source: camera
4195+ anchors {
4196+ top: parent.top
4197+ horizontalCenter: parent.horizontalCenter
4198+ }
4199+ //width: parent.width
4200+ //height: parent.width
4201+
4202+ focus: visible
4203+
4204+ /* This rotation needs to be applied since the camera hardware in the
4205+ Galaxy Nexus phone is mounted at an angle inside the device, so the video
4206+ feed is rotated too.
4207+ FIXME: This should come from a system configuration option so that we
4208+ don't have to have a different codebase for each different device we want
4209+ to run on */
4210+ orientation: device.naturalOrientation === "portrait" ? -90 : 0
4211+ }
4212+
4213+ Canvas {
4214+ id: canvas
4215+ property string imageurl
4216+ width: videoPreview.width
4217+ height: videoPreview.height
4218+ anchors.fill: videoPreview
4219+ visible: true
4220+
4221+ onPaint: {
4222+ var ctx = canvas.getContext('2d');
4223+ var captureWidth = units.gu(30);
4224+ var captureHeight = units.gu(30);
4225+
4226+ ctx.clearRect(0, 0, width, height);
4227+
4228+ ctx.fillStyle = "rgba(0, 0, 0, .5)";
4229+
4230+ console.log("Canvas width: " + width)
4231+ console.log("Canvas height: " + height)
4232+
4233+ var originX = (width / 2) - (captureWidth / 2);
4234+ var originY = (height / 2) - (captureHeight / 2);
4235+
4236+ ctx.fillRect(0, 0, width, height);
4237+
4238+ // Origin, a width, and a height
4239+ ctx.clearRect(originX, originY, captureWidth, captureHeight);
4240+ }
4241+ }
4242+ }
4243+
4244+ Decoder {
4245+ id: decoder
4246+ onDecodingSuccess: {
4247+ debugLog("Tag found: " + tag);
4248+ var resultTitle = i18n.tr("Your code contents");
4249+ var resultMessage = tag;
4250+ showResult(resultMessage, resultTitle, true);
4251+ }
4252+
4253+ onDecodingStart: {
4254+ debugLog("Decoding started");
4255+ }
4256+
4257+ onDecodingFinish: {
4258+ debugLog("Decoding finished " + (success==true ? "successfully" : "unsuccessfully"));
4259+ if (!success) {
4260+ // This dialog will go away as soon as we implement automatic scanning
4261+ // But we might want to keep it for providing feedback when manually uploading
4262+ // images to decode
4263+ var resultTitle = i18n.tr("Oops!");
4264+ var resultMessage = i18n.tr("The code's content could not be decoded");
4265+ showResult(resultMessage, resultTitle, false);
4266+ }
4267+ }
4268+ }
4269+
4270+ Component {
4271+ id: uploader
4272+
4273+ UploadImageDialog {
4274+
4275+ onAccepted: {
4276+ if (inputText !== '') {
4277+ console.log("Uploaded image", inputText);
4278+ decoder.decode(inputText);
4279+ } else {
4280+ console.log("Empty image path, ignored")
4281+ }
4282+ }
4283+
4284+ }
4285+ }
4286+
4287+ Camera {
4288+ id: camera
4289+ flash.mode: Camera.FlashOff
4290+ captureMode: Camera.CaptureStillImage
4291+ focus.focusMode: Camera.FocusAuto
4292+ exposure.exposureMode: Camera.ExposureAuto
4293+
4294+ imageCapture {
4295+ //resolution: Qt.size(640, 480)
4296+
4297+ onImageSaved: {
4298+ debugLog("Picture saved as " + path);
4299+ decoder.decode(path);
4300+ }
4301+ }
4302+ }
4303+
4304+ DeviceOrientation {
4305+ id: device
4306+ }
4307+
4308+ function showResult(resultMessage, resultTitle, decodingSuccess) {
4309+ /* Shows the decoding result in a dialog
4310+
4311+ @resultMessage: Main message to display
4312+ @resultTitle: Title of the dialog
4313+ @decodingSuccess: True if the decoding was successful
4314+ */
4315+
4316+ PopupUtils.open(Qt.resolvedUrl("QrCodeDialog.qml"), pagePreview,
4317+ {
4318+ text: resultMessage,
4319+ title: resultTitle,
4320+ decodingSuccess: decodingSuccess
4321+ })
4322+ }
4323+
4324+ function captureImage(){
4325+ /*
4326+ FIXME: we need to decide whether to save the image at all on the
4327+ file system (perhaps by intercepting onImageCaptured from the
4328+ Camera), to save files individually or to always overwrite the same
4329+ picture as a workaround (using captureToLocation)
4330+
4331+ The later seems to work on the desktop, but it inexplicably crashes
4332+ the camera when doing it on a device.
4333+
4334+ Another option to complement it could be to delete the saved image from
4335+ C++: QFile::remove(const QString &fileName);
4336+
4337+ For now, we'll simply save all images
4338+ */
4339+
4340+ //var captureLocation = "/tmp/qrcode.jpg";
4341+ //camera.imageCapture.captureToLocation(captureLocation);
4342+
4343+ camera.imageCapture.capture()
4344+
4345+ }
4346 }
4347
4348=== added file 'qreator/UploadImageDialog.qml'
4349--- qreator/UploadImageDialog.qml 1970-01-01 00:00:00 +0000
4350+++ qreator/UploadImageDialog.qml 2013-06-16 10:03:26 +0000
4351@@ -0,0 +1,37 @@
4352+import QtQuick 2.0
4353+import Ubuntu.Components 0.1
4354+import Ubuntu.Components.Popups 0.1
4355+
4356+Dialog {
4357+ id: root
4358+
4359+ title: i18n.tr("Upload image")
4360+ text: i18n.tr("Enter the path to an image to decode")
4361+
4362+ property alias inputText: input.text
4363+
4364+ signal accepted
4365+ signal rejected
4366+
4367+ TextField {
4368+ id: input
4369+ focus: true
4370+ }
4371+
4372+ Button {
4373+ text: i18n.tr("Decode image")
4374+ color: "#DD4814"
4375+ onClicked: {
4376+ accepted()
4377+ PopupUtils.close(root)
4378+ }
4379+ }
4380+
4381+ Button {
4382+ text: i18n.tr("Cancel")
4383+ onClicked: {
4384+ rejected()
4385+ PopupUtils.close(root)
4386+ }
4387+ }
4388+}

Subscribers

People subscribed via source and target branches