FormatInformation.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <?php
  2. /*
  3. * Copyright 2007 ZXing authors
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. namespace Zxing\Qrcode\Decoder;
  18. /**
  19. * <p>Encapsulates a QR Code's format information, including the data mask used and
  20. * error correction level.</p>
  21. *
  22. * @author Sean Owen
  23. * @see DataMask
  24. * @see ErrorCorrectionLevel
  25. */
  26. final class FormatInformation {
  27. public static $FORMAT_INFO_MASK_QR;
  28. /**
  29. * See ISO 18004:2006, Annex C, Table C.1
  30. */
  31. public static $FORMAT_INFO_DECODE_LOOKUP;
  32. /**
  33. * Offset i holds the number of 1 bits in the binary representation of i
  34. */
  35. private static $BITS_SET_IN_HALF_BYTE;
  36. private $errorCorrectionLevel;
  37. private $dataMask;
  38. public static function Init(){
  39. self::$FORMAT_INFO_MASK_QR= 0x5412;
  40. self::$BITS_SET_IN_HALF_BYTE = array(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
  41. self::$FORMAT_INFO_DECODE_LOOKUP = array(
  42. array(0x5412, 0x00),
  43. array (0x5125, 0x01),
  44. array(0x5E7C, 0x02),
  45. array(0x5B4B, 0x03),
  46. array(0x45F9, 0x04),
  47. array(0x40CE, 0x05),
  48. array(0x4F97, 0x06),
  49. array(0x4AA0, 0x07),
  50. array(0x77C4, 0x08),
  51. array(0x72F3, 0x09),
  52. array(0x7DAA, 0x0A),
  53. array(0x789D, 0x0B),
  54. array(0x662F, 0x0C),
  55. array(0x6318, 0x0D),
  56. array(0x6C41, 0x0E),
  57. array(0x6976, 0x0F),
  58. array(0x1689, 0x10),
  59. array(0x13BE, 0x11),
  60. array(0x1CE7, 0x12),
  61. array(0x19D0, 0x13),
  62. array(0x0762, 0x14),
  63. array(0x0255, 0x15),
  64. array(0x0D0C, 0x16),
  65. array(0x083B, 0x17),
  66. array(0x355F, 0x18),
  67. array(0x3068, 0x19),
  68. array(0x3F31, 0x1A),
  69. array(0x3A06, 0x1B),
  70. array(0x24B4, 0x1C),
  71. array(0x2183, 0x1D),
  72. array(0x2EDA, 0x1E),
  73. array(0x2BED, 0x1F),
  74. );
  75. }
  76. private function __construct($formatInfo) {
  77. // Bits 3,4
  78. $this->errorCorrectionLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x03);
  79. // Bottom 3 bits
  80. $this->dataMask = ($formatInfo & 0x07);//(byte)
  81. }
  82. static function numBitsDiffering($a, $b) {
  83. $a ^= $b; // a now has a 1 bit exactly where its bit differs with b's
  84. // Count bits set quickly with a series of lookups:
  85. return self::$BITS_SET_IN_HALF_BYTE[$a & 0x0F] +
  86. self::$BITS_SET_IN_HALF_BYTE[intval(uRShift($a, 4) & 0x0F)] +
  87. self::$BITS_SET_IN_HALF_BYTE[(uRShift($a ,8) & 0x0F)] +
  88. self::$BITS_SET_IN_HALF_BYTE[(uRShift($a , 12) & 0x0F)] +
  89. self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 16) & 0x0F)] +
  90. self::$BITS_SET_IN_HALF_BYTE[(uRShift($a , 20) & 0x0F)] +
  91. self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 24) & 0x0F)] +
  92. self::$BITS_SET_IN_HALF_BYTE[(uRShift($a ,28) & 0x0F)];
  93. }
  94. /**
  95. * @param maskedFormatInfo1; format info indicator, with mask still applied
  96. * @param maskedFormatInfo2; second copy of same info; both are checked at the same time
  97. * to establish best match
  98. * @return information about the format it specifies, or {@code null}
  99. * if doesn't seem to match any known pattern
  100. */
  101. static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) {
  102. $formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);
  103. if ($formatInfo != null) {
  104. return $formatInfo;
  105. }
  106. // Should return null, but, some QR codes apparently
  107. // do not mask this info. Try again by actually masking the pattern
  108. // first
  109. return self::doDecodeFormatInformation($maskedFormatInfo1 ^ self::$FORMAT_INFO_MASK_QR,
  110. $maskedFormatInfo2 ^ self::$FORMAT_INFO_MASK_QR);
  111. }
  112. private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) {
  113. // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
  114. $bestDifference = PHP_INT_MAX;
  115. $bestFormatInfo = 0;
  116. foreach (self::$FORMAT_INFO_DECODE_LOOKUP as $decodeInfo ) {
  117. $targetInfo = $decodeInfo[0];
  118. if ($targetInfo == $maskedFormatInfo1 || $targetInfo == $maskedFormatInfo2) {
  119. // Found an exact match
  120. return new FormatInformation($decodeInfo[1]);
  121. }
  122. $bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo);
  123. if ($bitsDifference < $bestDifference) {
  124. $bestFormatInfo = $decodeInfo[1];
  125. $bestDifference = $bitsDifference;
  126. }
  127. if ($maskedFormatInfo1 != $maskedFormatInfo2) {
  128. // also try the other option
  129. $bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo);
  130. if ($bitsDifference < $bestDifference) {
  131. $bestFormatInfo = $decodeInfo[1];
  132. $bestDifference = $bitsDifference;
  133. }
  134. }
  135. }
  136. // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
  137. // differing means we found a match
  138. if ($bestDifference <= 3) {
  139. return new FormatInformation($bestFormatInfo);
  140. }
  141. return null;
  142. }
  143. function getErrorCorrectionLevel() {
  144. return $this->errorCorrectionLevel;
  145. }
  146. function getDataMask() {
  147. return $this->dataMask;
  148. }
  149. //@Override
  150. public function hashCode() {
  151. return ($this->errorCorrectionLevel->ordinal() << 3) | intval($this->dataMask);
  152. }
  153. //@Override
  154. public function equals($o) {
  155. if (!($o instanceof FormatInformation)) {
  156. return false;
  157. }
  158. $other =$o;
  159. return $this->errorCorrectionLevel == $other->errorCorrectionLevel &&
  160. $this->dataMask == $other->dataMask;
  161. }
  162. }
  163. FormatInformation::Init();