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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. * Copyright 2012 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. #import "ZXBarcodeFormat.h"
  17. #import "ZXBinaryBitmap.h"
  18. #import "ZXBitMatrix.h"
  19. #import "ZXDecodeHints.h"
  20. #import "ZXDecoderResult.h"
  21. #import "ZXDetectorResult.h"
  22. #import "ZXErrors.h"
  23. #import "ZXIntArray.h"
  24. #import "ZXQRCodeDecoder.h"
  25. #import "ZXQRCodeDecoderMetaData.h"
  26. #import "ZXQRCodeDetector.h"
  27. #import "ZXQRCodeReader.h"
  28. #import "ZXResult.h"
  29. @implementation ZXQRCodeReader
  30. - (id)init {
  31. if (self = [super init]) {
  32. _decoder = [[ZXQRCodeDecoder alloc] init];
  33. }
  34. return self;
  35. }
  36. /**
  37. * Locates and decodes a QR code in an image.
  38. *
  39. * @return a String representing the content encoded by the QR code
  40. * @throws NotFoundException if a QR code cannot be found
  41. * @throws FormatException if a QR code cannot be decoded
  42. * @throws ChecksumException if error correction fails
  43. */
  44. - (ZXResult *)decode:(ZXBinaryBitmap *)image error:(NSError **)error {
  45. return [self decode:image hints:nil error:error];
  46. }
  47. - (ZXResult *)decode:(ZXBinaryBitmap *)image hints:(ZXDecodeHints *)hints error:(NSError **)error {
  48. ZXDecoderResult *decoderResult;
  49. NSMutableArray *points;
  50. ZXBitMatrix *matrix = [image blackMatrixWithError:error];
  51. if (!matrix) {
  52. return nil;
  53. }
  54. if (hints != nil && hints.pureBarcode) {
  55. ZXBitMatrix *bits = [self extractPureBits:matrix];
  56. if (!bits) {
  57. if (error) *error = ZXNotFoundErrorInstance();
  58. return nil;
  59. }
  60. decoderResult = [self.decoder decodeMatrix:bits hints:hints error:error];
  61. if (!decoderResult) {
  62. return nil;
  63. }
  64. points = [NSMutableArray array];
  65. } else {
  66. ZXDetectorResult *detectorResult = [[[ZXQRCodeDetector alloc] initWithImage:matrix] detect:hints error:error];
  67. if (!detectorResult) {
  68. return nil;
  69. }
  70. decoderResult = [self.decoder decodeMatrix:[detectorResult bits] hints:hints error:error];
  71. if (!decoderResult) {
  72. return nil;
  73. }
  74. points = [[detectorResult points] mutableCopy];
  75. }
  76. // If the code was mirrored: swap the bottom-left and the top-right points.
  77. if ([decoderResult.other isKindOfClass:[ZXQRCodeDecoderMetaData class]]) {
  78. [(ZXQRCodeDecoderMetaData *)decoderResult.other applyMirroredCorrection:points];
  79. }
  80. ZXResult *result = [ZXResult resultWithText:decoderResult.text
  81. rawBytes:decoderResult.rawBytes
  82. resultPoints:points
  83. format:kBarcodeFormatQRCode];
  84. NSMutableArray *byteSegments = decoderResult.byteSegments;
  85. if (byteSegments != nil) {
  86. [result putMetadata:kResultMetadataTypeByteSegments value:byteSegments];
  87. }
  88. NSString *ecLevel = decoderResult.ecLevel;
  89. if (ecLevel != nil) {
  90. [result putMetadata:kResultMetadataTypeErrorCorrectionLevel value:ecLevel];
  91. }
  92. if ([decoderResult hasStructuredAppend]) {
  93. [result putMetadata:kResultMetadataTypeStructuredAppendSequence
  94. value:@(decoderResult.structuredAppendSequenceNumber)];
  95. [result putMetadata:kResultMetadataTypeStructuredAppendParity
  96. value:@(decoderResult.structuredAppendParity)];
  97. }
  98. return result;
  99. }
  100. - (void)reset {
  101. // do nothing
  102. }
  103. /**
  104. * This method detects a code in a "pure" image -- that is, pure monochrome image
  105. * which contains only an unrotated, unskewed, image of a code, with some white border
  106. * around it. This is a specialized method that works exceptionally fast in this special
  107. * case.
  108. */
  109. - (ZXBitMatrix *)extractPureBits:(ZXBitMatrix *)image {
  110. ZXIntArray *leftTopBlack = image.topLeftOnBit;
  111. ZXIntArray *rightBottomBlack = image.bottomRightOnBit;
  112. if (leftTopBlack == nil || rightBottomBlack == nil) {
  113. return nil;
  114. }
  115. float moduleSize = [self moduleSize:leftTopBlack image:image];
  116. if (moduleSize == -1) {
  117. return nil;
  118. }
  119. int top = leftTopBlack.array[1];
  120. int bottom = rightBottomBlack.array[1];
  121. int left = leftTopBlack.array[0];
  122. int right = rightBottomBlack.array[0];
  123. // Sanity check!
  124. if (left >= right || top >= bottom) {
  125. return nil;
  126. }
  127. if (bottom - top != right - left) {
  128. // Special case, where bottom-right module wasn't black so we found something else in the last row
  129. // Assume it's a square, so use height as the width
  130. right = left + (bottom - top);
  131. }
  132. int matrixWidth = round((right - left + 1) / moduleSize);
  133. int matrixHeight = round((bottom - top + 1) / moduleSize);
  134. if (matrixWidth <= 0 || matrixHeight <= 0) {
  135. return nil;
  136. }
  137. if (matrixHeight != matrixWidth) {
  138. return nil;
  139. }
  140. int nudge = (int) (moduleSize / 2.0f);
  141. top += nudge;
  142. left += nudge;
  143. // But careful that this does not sample off the edge
  144. // "right" is the farthest-right valid pixel location -- right+1 is not necessarily
  145. // This is positive by how much the inner x loop below would be too large
  146. int nudgedTooFarRight = left + (int) ((matrixWidth - 1) * moduleSize) - right;
  147. if (nudgedTooFarRight > 0) {
  148. if (nudgedTooFarRight > nudge) {
  149. // Neither way fits; abort
  150. return nil;
  151. }
  152. left -= nudgedTooFarRight;
  153. }
  154. // See logic above
  155. int nudgedTooFarDown = top + (int) ((matrixHeight - 1) * moduleSize) - bottom;
  156. if (nudgedTooFarDown > 0) {
  157. if (nudgedTooFarDown > nudge) {
  158. // Neither way fits; abort
  159. return nil;
  160. }
  161. top -= nudgedTooFarDown;
  162. }
  163. // Now just read off the bits
  164. ZXBitMatrix *bits = [[ZXBitMatrix alloc] initWithWidth:matrixWidth height:matrixHeight];
  165. for (int y = 0; y < matrixHeight; y++) {
  166. int iOffset = top + (int) (y * moduleSize);
  167. for (int x = 0; x < matrixWidth; x++) {
  168. if ([image getX:left + (int) (x * moduleSize) y:iOffset]) {
  169. [bits setX:x y:y];
  170. }
  171. }
  172. }
  173. return bits;
  174. }
  175. - (float)moduleSize:(ZXIntArray *)leftTopBlack image:(ZXBitMatrix *)image {
  176. int height = image.height;
  177. int width = image.width;
  178. int x = leftTopBlack.array[0];
  179. int y = leftTopBlack.array[1];
  180. BOOL inBlack = YES;
  181. int transitions = 0;
  182. while (x < width && y < height) {
  183. if (inBlack != [image getX:x y:y]) {
  184. if (++transitions == 5) {
  185. break;
  186. }
  187. inBlack = !inBlack;
  188. }
  189. x++;
  190. y++;
  191. }
  192. if (x == width || y == height) {
  193. return -1;
  194. }
  195. return (x - leftTopBlack.array[0]) / 7.0f;
  196. }
  197. @end