/*
Ported to JavaScript by Lazar Laszlo 2011
[email protected], www.lazarsoft.info
*/
/*
*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var MIN_SKIP = 3;
var MAX_MODULES = 57;
var INTEGER_MATH_SHIFT = 8;
var CENTER_QUORUM = 2;
qrcode.orderBestPatterns=function(patterns)
{
function distance( pattern1, pattern2)
{
xDiff = pattern1.X - pattern2.X;
yDiff = pattern1.Y - pattern2.Y;
return Math.sqrt( (xDiff * xDiff + yDiff * yDiff));
}
/// <summary> Returns the z component of the cross product between vectors BC and BA.</summary>
function crossProductZ( pointA, pointB, pointC)
{
var bX = pointB.x;
var bY = pointB.y;
return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));
}
// Find distances between pattern centers
var zeroOneDistance = distance(patterns[0], patterns[1]);
var oneTwoDistance = distance(patterns[1], patterns[2]);
var zeroTwoDistance = distance(patterns[0], patterns[2]);
var pointA, pointB, pointC;
// Assume one closest to other two is B; A and C will just be guesses at first
if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance)
{
pointB = patterns[0];
pointA = patterns[1];
pointC = patterns[2];
}
else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance)
{
pointB = patterns[1];
pointA = patterns[0];
pointC = patterns[2];
}
else
{
pointB = patterns[2];
pointA = patterns[0];
pointC = patterns[1];
}
// Use cross product to figure out whether A and C are correct or flipped.
// This asks whether BC x BA has a positive z component, which is the arrangement
// we want for A, B, C. If it's negative, then we've got it flipped around and
// should swap A and C.
if (crossProductZ(pointA, pointB, pointC) < 0.0)
{
var temp = pointA;
pointA = pointC;
pointC = temp;
}
patterns[0] = pointA;
patterns[1] = pointB;
patterns[2] = pointC;
}
function FinderPattern(posX, posY, estimatedModuleSize)
{
this.x=posX;
this.y=posY;
this.count = 1;
this.estimatedModuleSize = estimatedModuleSize;
this.__defineGetter__("EstimatedModuleSize", function()
{
return this.estimatedModuleSize;
});
this.__defineGetter__("Count", function()
{
return this.count;
});
this.__defineGetter__("X", function()
{
return this.x;
});
this.__defineGetter__("Y", function()
{
return this.y;
});
this.incrementCount = function()
{
this.count++;
}
this.aboutEquals=function( moduleSize, i, j)
{
if (Math.abs(i - this.y) <= moduleSize && Math.abs(j - this.x) <= moduleSize)
{
var moduleSizeDiff = Math.abs(moduleSize - this.estimatedModuleSize);
return moduleSizeDiff <= 1.0 || moduleSizeDiff / this.estimatedModuleSize <= 1.0;
}
return false;
}
}
function FinderPatternInfo(patternCenters)
{
this.bottomLeft = patternCenters[0];
this.topLeft = patternCenters[1];
this.topRight = patternCenters[2];
this.__defineGetter__("BottomLeft", function()
{
return this.bottomLeft;
});
this.__defineGetter__("TopLeft", function()
{
return this.topLeft;
});
this.__defineGetter__("TopRight", function()
{
return this.topRight;
});
}
function FinderPatternFinder()
{
this.image=null;
this.possibleCenters = [];
this.hasSkipped = false;
this.crossCheckStateCount = new Array(0,0,0,0,0);
this.resultPointCallback = null;
this.__defineGetter__("CrossCheckStateCount", function()
{
this.crossCheckStateCount[0] = 0;
this.crossCheckStateCount[1] = 0;
this.crossCheckStateCount[2] = 0;
this.crossCheckStateCount[3] = 0;
this.crossCheckStateCount[4] = 0;
return this.crossCheckStateCount;
});
this.foundPatternCross=function( stateCount)
{
var totalModuleSize = 0;
for (var i = 0; i < 5; i++)
{
var count = stateCount[i];
if (count == 0)
{
return false;
}
totalModuleSize += count;
}
if (totalModuleSize < 7)
{
return false;
}
var moduleSize = Math.floor((totalModuleSize << INTEGER_MATH_SHIFT) / 7);
var maxVariance = Math.floor(moduleSize / 2);
// Allow less than 50% variance from 1-1-3-1-1 proportions
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;
}
this.centerFromEnd=function( stateCount, end)
{
return (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0;
}
this.crossCheckVertical=function( startI, centerJ, maxCount, originalStateCountTotal)
{
var image = this.image;
var maxI = qrcode.height;
var stateCount = this.CrossCheckStateCount;
// Start counting up from center
var i = startI;
while (i >= 0 && image[centerJ + i*qrcode.width])
{
stateCount[2]++;
i--;
}
if (i < 0)
{
return NaN;
}
while (i >= 0 && !image[centerJ +i*qrcode.width] && stateCount[1] <= maxCount)
{
stateCount[1]++;
i--;
}
// If already too many modules in this state or ran off the edge:
if (i < 0 || stateCount[1] > maxCount)
{
return NaN;
}
while (i >= 0 && image[centerJ + i*qrcode.width] && stateCount[0] <= maxCount)
{
stateCount[0]++;
i--;
}
if (stateCount[0] > maxCount)
{
return NaN;
}
// Now also count down from center
i = startI + 1;
while (i < maxI && image[centerJ +i*qrcode.width])
{
stateCount[2]++;
i++;
}
if (i == maxI)
{
return NaN;
}
while (i < maxI && !image[centerJ + i*qrcode.width] && stateCount[3] < maxCount)
{
stateCount[3]++;
i++;
}
if (i == maxI || stateCount[3] >= maxCount)
{
return NaN;
}
while (i < maxI && image[centerJ + i*qrcode.width] && stateCount[4] < maxCount)
{
stateCount[4]++;
i++;
}
if (stateCount[4] >= maxCount)
{
return NaN;
}
// If we found a finder-pattern-like section, but its size is more than 40% different than
// the original, assume it's a false positive
var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal)
{
return NaN;
}
return this.foundPatternCross(stateCount)?this.centerFromEnd(stateCount, i):NaN;
}
this.crossCheckHorizontal=function( startJ, centerI, maxCount, originalStateCountTotal)
{
var image = this.image;
var maxJ = qrcode.width;
var stateCount = this.CrossCheckStateCount;
var j = startJ;
while (j >= 0 && image[j+ centerI*qrcode.width])
{
stateCount[2]++;
j--;
}
if (j < 0)
{
return NaN;
}
while (j >= 0 && !image[j+ centerI*qrcode.width] && stateCount[1] <= maxCount)
{
stateCount[1]++;
j--;
}
if (j < 0 || stateCount[1] > maxCount)