not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

650 lines
17 KiB

  1. /*
  2. Ported to JavaScript by Lazar Laszlo 2011
  3. lazarsoft@gmail.com, www.lazarsoft.info
  4. */
  5. /*
  6. *
  7. * Copyright 2007 ZXing authors
  8. *
  9. * Licensed under the Apache License, Version 2.0 (the "License");
  10. * you may not use this file except in compliance with the License.
  11. * You may obtain a copy of the License at
  12. *
  13. * http://www.apache.org/licenses/LICENSE-2.0
  14. *
  15. * Unless required by applicable law or agreed to in writing, software
  16. * distributed under the License is distributed on an "AS IS" BASIS,
  17. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. * See the License for the specific language governing permissions and
  19. * limitations under the License.
  20. */
  21. var MIN_SKIP = 3;
  22. var MAX_MODULES = 57;
  23. var INTEGER_MATH_SHIFT = 8;
  24. var CENTER_QUORUM = 2;
  25. qrcode.orderBestPatterns=function(patterns)
  26. {
  27. function distance( pattern1, pattern2)
  28. {
  29. var xDiff = pattern1.X - pattern2.X;
  30. var yDiff = pattern1.Y - pattern2.Y;
  31. return Math.sqrt( (xDiff * xDiff + yDiff * yDiff));
  32. }
  33. /// <summary> Returns the z component of the cross product between vectors BC and BA.</summary>
  34. function crossProductZ( pointA, pointB, pointC)
  35. {
  36. var bX = pointB.x;
  37. var bY = pointB.y;
  38. return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));
  39. }
  40. // Find distances between pattern centers
  41. var zeroOneDistance = distance(patterns[0], patterns[1]);
  42. var oneTwoDistance = distance(patterns[1], patterns[2]);
  43. var zeroTwoDistance = distance(patterns[0], patterns[2]);
  44. var pointA, pointB, pointC;
  45. // Assume one closest to other two is B; A and C will just be guesses at first
  46. if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance)
  47. {
  48. pointB = patterns[0];
  49. pointA = patterns[1];
  50. pointC = patterns[2];
  51. }
  52. else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance)
  53. {
  54. pointB = patterns[1];
  55. pointA = patterns[0];
  56. pointC = patterns[2];
  57. }
  58. else
  59. {
  60. pointB = patterns[2];
  61. pointA = patterns[0];
  62. pointC = patterns[1];
  63. }
  64. // Use cross product to figure out whether A and C are correct or flipped.
  65. // This asks whether BC x BA has a positive z component, which is the arrangement
  66. // we want for A, B, C. If it's negative, then we've got it flipped around and
  67. // should swap A and C.
  68. if (crossProductZ(pointA, pointB, pointC) < 0.0)
  69. {
  70. var temp = pointA;
  71. pointA = pointC;
  72. pointC = temp;
  73. }
  74. patterns[0] = pointA;
  75. patterns[1] = pointB;
  76. patterns[2] = pointC;
  77. }
  78. function FinderPattern(posX, posY, estimatedModuleSize)
  79. {
  80. this.x=posX;
  81. this.y=posY;
  82. this.count = 1;
  83. this.estimatedModuleSize = estimatedModuleSize;
  84. this.__defineGetter__("EstimatedModuleSize", function()
  85. {
  86. return this.estimatedModuleSize;
  87. });
  88. this.__defineGetter__("Count", function()
  89. {
  90. return this.count;
  91. });
  92. this.__defineGetter__("X", function()
  93. {
  94. return this.x;
  95. });
  96. this.__defineGetter__("Y", function()
  97. {
  98. return this.y;
  99. });
  100. this.incrementCount = function()
  101. {
  102. this.count++;
  103. }
  104. this.aboutEquals=function( moduleSize, i, j)
  105. {
  106. if (Math.abs(i - this.y) <= moduleSize && Math.abs(j - this.x) <= moduleSize)
  107. {
  108. var moduleSizeDiff = Math.abs(moduleSize - this.estimatedModuleSize);
  109. return moduleSizeDiff <= 1.0 || moduleSizeDiff / this.estimatedModuleSize <= 1.0;
  110. }
  111. return false;
  112. }
  113. }
  114. function FinderPatternInfo(patternCenters)
  115. {
  116. this.bottomLeft = patternCenters[0];
  117. this.topLeft = patternCenters[1];
  118. this.topRight = patternCenters[2];
  119. this.__defineGetter__("BottomLeft", function()
  120. {
  121. return this.bottomLeft;
  122. });
  123. this.__defineGetter__("TopLeft", function()
  124. {
  125. return this.topLeft;
  126. });
  127. this.__defineGetter__("TopRight", function()
  128. {
  129. return this.topRight;
  130. });
  131. }
  132. function FinderPatternFinder()
  133. {
  134. this.image=null;
  135. this.possibleCenters = [];
  136. this.hasSkipped = false;
  137. this.crossCheckStateCount = new Array(0,0,0,0,0);
  138. this.resultPointCallback = null;
  139. this.__defineGetter__("CrossCheckStateCount", function()
  140. {
  141. this.crossCheckStateCount[0] = 0;
  142. this.crossCheckStateCount[1] = 0;
  143. this.crossCheckStateCount[2] = 0;
  144. this.crossCheckStateCount[3] = 0;
  145. this.crossCheckStateCount[4] = 0;
  146. return this.crossCheckStateCount;
  147. });
  148. this.foundPatternCross=function( stateCount)
  149. {
  150. var totalModuleSize = 0;
  151. for (var i = 0; i < 5; i++)
  152. {
  153. var count = stateCount[i];
  154. if (count == 0)
  155. {
  156. return false;
  157. }
  158. totalModuleSize += count;
  159. }
  160. if (totalModuleSize < 7)
  161. {
  162. return false;
  163. }
  164. var moduleSize = Math.floor((totalModuleSize << INTEGER_MATH_SHIFT) / 7);
  165. var maxVariance = Math.floor(moduleSize / 2);
  166. // Allow less than 50% variance from 1-1-3-1-1 proportions
  167. 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;
  168. }
  169. this.centerFromEnd=function( stateCount, end)
  170. {
  171. return (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0;
  172. }
  173. this.crossCheckVertical=function( startI, centerJ, maxCount, originalStateCountTotal)
  174. {
  175. var image = this.image;
  176. var maxI = qrcode.height;
  177. var stateCount = this.CrossCheckStateCount;
  178. // Start counting up from center
  179. var i = startI;
  180. while (i >= 0 && image[centerJ + i*qrcode.width])
  181. {
  182. stateCount[2]++;
  183. i--;
  184. }
  185. if (i < 0)
  186. {
  187. return NaN;
  188. }
  189. while (i >= 0 && !image[centerJ +i*qrcode.width] && stateCount[1] <= maxCount)
  190. {
  191. stateCount[1]++;
  192. i--;
  193. }
  194. // If already too many modules in this state or ran off the edge:
  195. if (i < 0 || stateCount[1] > maxCount)
  196. {
  197. return NaN;
  198. }
  199. while (i >= 0 && image[centerJ + i*qrcode.width] && stateCount[0] <= maxCount)
  200. {
  201. stateCount[0]++;
  202. i--;
  203. }
  204. if (stateCount[0] > maxCount)
  205. {
  206. return NaN;
  207. }
  208. // Now also count down from center
  209. i = startI + 1;
  210. while (i < maxI && image[centerJ +i*qrcode.width])
  211. {
  212. stateCount[2]++;
  213. i++;
  214. }
  215. if (i == maxI)
  216. {
  217. return NaN;
  218. }
  219. while (i < maxI && !image[centerJ + i*qrcode.width] && stateCount[3] < maxCount)
  220. {
  221. stateCount[3]++;
  222. i++;
  223. }
  224. if (i == maxI || stateCount[3] >= maxCount)
  225. {
  226. return NaN;
  227. }
  228. while (i < maxI && image[centerJ + i*qrcode.width] && stateCount[4] < maxCount)
  229. {
  230. stateCount[4]++;
  231. i++;
  232. }
  233. if (stateCount[4] >= maxCount)
  234. {
  235. return NaN;
  236. }
  237. // If we found a finder-pattern-like section, but its size is more than 40% different than
  238. // the original, assume it's a false positive
  239. var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
  240. if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal)
  241. {
  242. return NaN;
  243. }
  244. return this.foundPatternCross(stateCount)?this.centerFromEnd(stateCount, i):NaN;
  245. }
  246. this.crossCheckHorizontal=function( startJ, centerI, maxCount, originalStateCountTotal)
  247. {
  248. var image = this.image;
  249. var maxJ = qrcode.width;
  250. var stateCount = this.CrossCheckStateCount;
  251. var j = startJ;
  252. while (j >= 0 && image[j+ centerI*qrcode.width])
  253. {
  254. stateCount[2]++;
  255. j--;
  256. }
  257. if (j < 0)
  258. {
  259. return NaN;
  260. }
  261. while (j >= 0 && !image[j+ centerI*qrcode.width] && stateCount[1] <= maxCount)
  262. {
  263. stateCount[1]++;
  264. j--;
  265. }
  266. if (j < 0 || stateCount[1] > maxCount)
  267. {
  268. return NaN;
  269. }
  270. while (j >= 0 && image[j+ centerI*qrcode.width] && stateCount[0] <= maxCount)
  271. {
  272. stateCount[0]++;
  273. j--;
  274. }
  275. if (stateCount[0] > maxCount)
  276. {
  277. return NaN;
  278. }
  279. j = startJ + 1;
  280. while (j < maxJ && image[j+ centerI*qrcode.width])
  281. {
  282. stateCount[2]++;
  283. j++;
  284. }
  285. if (j == maxJ)
  286. {
  287. return NaN;
  288. }
  289. while (j < maxJ && !image[j+ centerI*qrcode.width] && stateCount[3] < maxCount)
  290. {
  291. stateCount[3]++;
  292. j++;
  293. }
  294. if (j == maxJ || stateCount[3] >= maxCount)
  295. {
  296. return NaN;
  297. }
  298. while (j < maxJ && image[j+ centerI*qrcode.width] && stateCount[4] < maxCount)
  299. {
  300. stateCount[4]++;
  301. j++;
  302. }
  303. if (stateCount[4] >= maxCount)
  304. {
  305. return NaN;
  306. }
  307. // If we found a finder-pattern-like section, but its size is significantly different than
  308. // the original, assume it's a false positive
  309. var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
  310. if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal)
  311. {
  312. return NaN;
  313. }
  314. return this.foundPatternCross(stateCount)?this.centerFromEnd(stateCount, j):NaN;
  315. }
  316. this.handlePossibleCenter=function( stateCount, i, j)
  317. {
  318. var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
  319. var centerJ = this.centerFromEnd(stateCount, j); //float
  320. var centerI = this.crossCheckVertical(i, Math.floor( centerJ), stateCount[2], stateCountTotal); //float
  321. if (!isNaN(centerI))
  322. {
  323. // Re-cross check
  324. centerJ = this.crossCheckHorizontal(Math.floor( centerJ), Math.floor( centerI), stateCount[2], stateCountTotal);
  325. if (!isNaN(centerJ))
  326. {
  327. var estimatedModuleSize = stateCountTotal / 7.0;
  328. var found = false;
  329. var max = this.possibleCenters.length;
  330. for (var index = 0; index < max; index++)
  331. {
  332. var center = this.possibleCenters[index];
  333. // Look for about the same center and module size:
  334. if (center.aboutEquals(estimatedModuleSize, centerI, centerJ))
  335. {
  336. center.incrementCount();
  337. found = true;
  338. break;
  339. }
  340. }
  341. if (!found)
  342. {
  343. var point = new FinderPattern(centerJ, centerI, estimatedModuleSize);
  344. this.possibleCenters.push(point);
  345. if (this.resultPointCallback != null)
  346. {
  347. this.resultPointCallback.foundPossibleResultPoint(point);
  348. }
  349. }
  350. return true;
  351. }
  352. }
  353. return false;
  354. }
  355. this.selectBestPatterns=function()
  356. {
  357. var startSize = this.possibleCenters.length;
  358. if (startSize < 3)
  359. {
  360. // Couldn't find enough finder patterns
  361. throw "Couldn't find enough finder patterns (found " + startSize + ")"
  362. }
  363. // Filter outlier possibilities whose module size is too different
  364. if (startSize > 3)
  365. {
  366. // But we can only afford to do so if we have at least 4 possibilities to choose from
  367. var totalModuleSize = 0.0;
  368. var square = 0.0;
  369. for (var i = 0; i < startSize; i++)
  370. {
  371. //totalModuleSize += this.possibleCenters[i].EstimatedModuleSize;
  372. var centerValue=this.possibleCenters[i].EstimatedModuleSize;
  373. totalModuleSize += centerValue;
  374. square += (centerValue * centerValue);
  375. }
  376. var average = totalModuleSize / startSize;
  377. this.possibleCenters.sort(function(center1,center2) {
  378. var dA=Math.abs(center2.EstimatedModuleSize - average);
  379. var dB=Math.abs(center1.EstimatedModuleSize - average);
  380. if (dA < dB) {
  381. return (-1);
  382. } else if (dA == dB) {
  383. return 0;
  384. } else {
  385. return 1;
  386. }
  387. });
  388. var stdDev = Math.sqrt(square / startSize - average * average);
  389. var limit = Math.max(0.2 * average, stdDev);
  390. //for (var i = 0; i < this.possibleCenters.length && this.possibleCenters.length > 3; i++)
  391. for (var i = this.possibleCenters.length - 1; i >= 0 ; i--)
  392. {
  393. var pattern = this.possibleCenters[i];
  394. //if (Math.abs(pattern.EstimatedModuleSize - average) > 0.2 * average)
  395. if (Math.abs(pattern.EstimatedModuleSize - average) > limit)
  396. {
  397. //this.possibleCenters.remove(i);
  398. this.possibleCenters.splice(i,1);
  399. //i--;
  400. }
  401. }
  402. }
  403. if (this.possibleCenters.length > 3)
  404. {
  405. // Throw away all but those first size candidate points we found.
  406. this.possibleCenters.sort(function(a, b){
  407. if (a.count > b.count){return -1;}
  408. if (a.count < b.count){return 1;}
  409. return 0;
  410. });
  411. }
  412. return new Array( this.possibleCenters[0], this.possibleCenters[1], this.possibleCenters[2]);
  413. }
  414. this.findRowSkip=function()
  415. {
  416. var max = this.possibleCenters.length;
  417. if (max <= 1)
  418. {
  419. return 0;
  420. }
  421. var firstConfirmedCenter = null;
  422. for (var i = 0; i < max; i++)
  423. {
  424. var center = this.possibleCenters[i];
  425. if (center.Count >= CENTER_QUORUM)
  426. {
  427. if (firstConfirmedCenter == null)
  428. {
  429. firstConfirmedCenter = center;
  430. }
  431. else
  432. {
  433. // We have two confirmed centers
  434. // How far down can we skip before resuming looking for the next
  435. // pattern? In the worst case, only the difference between the
  436. // difference in the x / y coordinates of the two centers.
  437. // This is the case where you find top left last.
  438. this.hasSkipped = true;
  439. return Math.floor ((Math.abs(firstConfirmedCenter.X - center.X) - Math.abs(firstConfirmedCenter.Y - center.Y)) / 2);
  440. }
  441. }
  442. }
  443. return 0;
  444. }
  445. this.haveMultiplyConfirmedCenters=function()
  446. {
  447. var confirmedCount = 0;
  448. var totalModuleSize = 0.0;
  449. var max = this.possibleCenters.length;
  450. for (var i = 0; i < max; i++)
  451. {
  452. var pattern = this.possibleCenters[i];
  453. if (pattern.Count >= CENTER_QUORUM)
  454. {
  455. confirmedCount++;
  456. totalModuleSize += pattern.EstimatedModuleSize;
  457. }
  458. }
  459. if (confirmedCount < 3)
  460. {
  461. return false;
  462. }
  463. // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
  464. // and that we need to keep looking. We detect this by asking if the estimated module sizes
  465. // vary too much. We arbitrarily say that when the total deviation from average exceeds
  466. // 5% of the total module size estimates, it's too much.
  467. var average = totalModuleSize / max;
  468. var totalDeviation = 0.0;
  469. for (var i = 0; i < max; i++)
  470. {
  471. pattern = this.possibleCenters[i];
  472. totalDeviation += Math.abs(pattern.EstimatedModuleSize - average);
  473. }
  474. return totalDeviation <= 0.05 * totalModuleSize;
  475. }
  476. this.findFinderPattern = function(image){
  477. var tryHarder = false;
  478. this.image=image;
  479. var maxI = qrcode.height;
  480. var maxJ = qrcode.width;
  481. var iSkip = Math.floor((3 * maxI) / (4 * MAX_MODULES));
  482. if (iSkip < MIN_SKIP || tryHarder)
  483. {
  484. iSkip = MIN_SKIP;
  485. }
  486. var done = false;
  487. var stateCount = new Array(5);
  488. for (var i = iSkip - 1; i < maxI && !done; i += iSkip)
  489. {
  490. // Get a row of black/white values
  491. stateCount[0] = 0;
  492. stateCount[1] = 0;
  493. stateCount[2] = 0;
  494. stateCount[3] = 0;
  495. stateCount[4] = 0;
  496. var currentState = 0;
  497. for (var j = 0; j < maxJ; j++)
  498. {
  499. if (image[j+i*qrcode.width] )
  500. {
  501. // Black pixel
  502. if ((currentState & 1) == 1)
  503. {
  504. // Counting white pixels
  505. currentState++;
  506. }
  507. stateCount[currentState]++;
  508. }
  509. else
  510. {
  511. // White pixel
  512. if ((currentState & 1) == 0)
  513. {
  514. // Counting black pixels
  515. if (currentState == 4)
  516. {
  517. // A winner?
  518. if (this.foundPatternCross(stateCount))
  519. {
  520. // Yes
  521. var confirmed = this.handlePossibleCenter(stateCount, i, j);
  522. if (confirmed)
  523. {
  524. // Start examining every other line. Checking each line turned out to be too
  525. // expensive and didn't improve performance.
  526. iSkip = 2;
  527. if (this.hasSkipped)
  528. {
  529. done = this.haveMultiplyConfirmedCenters();
  530. }
  531. else
  532. {
  533. var rowSkip = this.findRowSkip();
  534. if (rowSkip > stateCount[2])
  535. {
  536. // Skip rows between row of lower confirmed center
  537. // and top of presumed third confirmed center
  538. // but back up a bit to get a full chance of detecting
  539. // it, entire width of center of finder pattern
  540. // Skip by rowSkip, but back off by stateCount[2] (size of last center
  541. // of pattern we saw) to be conservative, and also back off by iSkip which
  542. // is about to be re-added
  543. i += rowSkip - stateCount[2] - iSkip;
  544. j = maxJ - 1;
  545. }
  546. }
  547. }
  548. else
  549. {
  550. // Advance to next black pixel
  551. do
  552. {
  553. j++;
  554. }
  555. while (j < maxJ && !image[j + i*qrcode.width]);
  556. j--; // back up to that last white pixel
  557. }
  558. // Clear state to start looking again
  559. currentState = 0;
  560. stateCount[0] = 0;
  561. stateCount[1] = 0;
  562. stateCount[2] = 0;
  563. stateCount[3] = 0;
  564. stateCount[4] = 0;
  565. }
  566. else
  567. {
  568. // No, shift counts back by two
  569. stateCount[0] = stateCount[2];
  570. stateCount[1] = stateCount[3];
  571. stateCount[2] = stateCount[4];
  572. stateCount[3] = 1;
  573. stateCount[4] = 0;
  574. currentState = 3;
  575. }
  576. }
  577. else
  578. {
  579. stateCount[++currentState]++;
  580. }
  581. }
  582. else
  583. {
  584. // Counting white pixels
  585. stateCount[currentState]++;
  586. }
  587. }
  588. }
  589. if (this.foundPatternCross(stateCount))
  590. {
  591. var confirmed = this.handlePossibleCenter(stateCount, i, maxJ);
  592. if (confirmed)
  593. {
  594. iSkip = stateCount[0];
  595. if (this.hasSkipped)
  596. {
  597. // Found a third one
  598. done = this.haveMultiplyConfirmedCenters();
  599. }
  600. }
  601. }
  602. }
  603. var patternInfo = this.selectBestPatterns();
  604. qrcode.orderBestPatterns(patternInfo);
  605. return new FinderPatternInfo(patternInfo);
  606. };
  607. }