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

Subscribers

People subscribed via source and target branches