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

Decoder.cs 7.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /*
  2. * Copyright 2007 ZXing authors
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. using System.Collections.Generic;
  17. using ZXing.Common;
  18. using ZXing.Common.ReedSolomon;
  19. namespace ZXing.QrCode.Internal
  20. {
  21. /// <summary>
  22. /// <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
  23. /// the QR Code from an image.</p>
  24. /// </summary>
  25. /// <author>
  26. /// Sean Owen
  27. /// </author>
  28. public sealed class Decoder
  29. {
  30. private readonly ReedSolomonDecoder rsDecoder;
  31. /// <summary>
  32. /// Initializes a new instance of the <see cref="Decoder"/> class.
  33. /// </summary>
  34. public Decoder()
  35. {
  36. rsDecoder = new ReedSolomonDecoder(GenericGF.QR_CODE_FIELD_256);
  37. }
  38. /// <summary>
  39. /// <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
  40. /// "true" is taken to mean a black module.</p>
  41. /// </summary>
  42. /// <param name="image">booleans representing white/black QR Code modules</param>
  43. /// <param name="hints">The hints.</param>
  44. /// <returns>
  45. /// text and bytes encoded within the QR Code
  46. /// </returns>
  47. public DecoderResult decode(bool[][] image, IDictionary<DecodeHintType, object> hints)
  48. {
  49. var dimension = image.Length;
  50. var bits = new BitMatrix(dimension);
  51. for (int i = 0; i < dimension; i++)
  52. {
  53. for (int j = 0; j < dimension; j++)
  54. {
  55. bits[j, i] = image[i][j];
  56. }
  57. }
  58. return decode(bits, hints);
  59. }
  60. /// <summary>
  61. /// <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
  62. /// </summary>
  63. /// <param name="bits">booleans representing white/black QR Code modules</param>
  64. /// <param name="hints">The hints.</param>
  65. /// <returns>
  66. /// text and bytes encoded within the QR Code
  67. /// </returns>
  68. public DecoderResult decode(BitMatrix bits, IDictionary<DecodeHintType, object> hints)
  69. {
  70. // Construct a parser and read version, error-correction level
  71. var parser = BitMatrixParser.createBitMatrixParser(bits);
  72. if (parser == null)
  73. return null;
  74. var result = decode(parser, hints);
  75. if (result == null)
  76. {
  77. // Revert the bit matrix
  78. parser.remask();
  79. // Will be attempting a mirrored reading of the version and format info.
  80. parser.setMirror(true);
  81. // Preemptively read the version.
  82. var version = parser.readVersion();
  83. if (version == null)
  84. return null;
  85. // Preemptively read the format information.
  86. var formatinfo = parser.readFormatInformation();
  87. if (formatinfo == null)
  88. return null;
  89. /*
  90. * Since we're here, this means we have successfully detected some kind
  91. * of version and format information when mirrored. This is a good sign,
  92. * that the QR code may be mirrored, and we should try once more with a
  93. * mirrored content.
  94. */
  95. // Prepare for a mirrored reading.
  96. parser.mirror();
  97. result = decode(parser, hints);
  98. if (result != null)
  99. {
  100. // Success! Notify the caller that the code was mirrored.
  101. result.Other = new QRCodeDecoderMetaData(true);
  102. }
  103. }
  104. return result;
  105. }
  106. private DecoderResult decode(BitMatrixParser parser, IDictionary<DecodeHintType, object> hints)
  107. {
  108. Version version = parser.readVersion();
  109. if (version == null)
  110. return null;
  111. var formatinfo = parser.readFormatInformation();
  112. if (formatinfo == null)
  113. return null;
  114. ErrorCorrectionLevel ecLevel = formatinfo.ErrorCorrectionLevel;
  115. // Read codewords
  116. byte[] codewords = parser.readCodewords();
  117. if (codewords == null)
  118. return null;
  119. // Separate into data blocks
  120. DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);
  121. // Count total number of data bytes
  122. int totalBytes = 0;
  123. foreach (var dataBlock in dataBlocks)
  124. {
  125. totalBytes += dataBlock.NumDataCodewords;
  126. }
  127. byte[] resultBytes = new byte[totalBytes];
  128. int resultOffset = 0;
  129. // Error-correct and copy data blocks together into a stream of bytes
  130. foreach (var dataBlock in dataBlocks)
  131. {
  132. byte[] codewordBytes = dataBlock.Codewords;
  133. int numDataCodewords = dataBlock.NumDataCodewords;
  134. if (!correctErrors(codewordBytes, numDataCodewords))
  135. return null;
  136. for (int i = 0; i < numDataCodewords; i++)
  137. {
  138. resultBytes[resultOffset++] = codewordBytes[i];
  139. }
  140. }
  141. // Decode the contents of that stream of bytes
  142. return DecodedBitStreamParser.decode(resultBytes, version, ecLevel, hints);
  143. }
  144. /// <summary>
  145. /// <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
  146. /// correct the errors in-place using Reed-Solomon error correction.</p>
  147. /// </summary>
  148. /// <param name="codewordBytes">data and error correction codewords</param>
  149. /// <param name="numDataCodewords">number of codewords that are data bytes</param>
  150. /// <returns></returns>
  151. private bool correctErrors(byte[] codewordBytes, int numDataCodewords)
  152. {
  153. int numCodewords = codewordBytes.Length;
  154. // First read into an array of ints
  155. int[] codewordsInts = new int[numCodewords];
  156. for (int i = 0; i < numCodewords; i++)
  157. {
  158. codewordsInts[i] = codewordBytes[i] & 0xFF;
  159. }
  160. int numECCodewords = codewordBytes.Length - numDataCodewords;
  161. if (!rsDecoder.decode(codewordsInts, numECCodewords))
  162. return false;
  163. // Copy back into array of bytes -- only need to worry about the bytes that were data
  164. // We don't care about errors in the error-correction codewords
  165. for (int i = 0; i < numDataCodewords; i++)
  166. {
  167. codewordBytes[i] = (byte)codewordsInts[i];
  168. }
  169. return true;
  170. }
  171. }
  172. }