Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

ZXOneDReader.m 9.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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 "ZXBinaryBitmap.h"
  17. #import "ZXBitArray.h"
  18. #import "ZXDecodeHints.h"
  19. #import "ZXErrors.h"
  20. #import "ZXIntArray.h"
  21. #import "ZXOneDReader.h"
  22. #import "ZXResult.h"
  23. #import "ZXResultPoint.h"
  24. @implementation ZXOneDReader
  25. - (ZXResult *)decode:(ZXBinaryBitmap *)image error:(NSError **)error {
  26. return [self decode:image hints:nil error:error];
  27. }
  28. // Note that we don't try rotation without the try harder flag, even if rotation was supported.
  29. - (ZXResult *)decode:(ZXBinaryBitmap *)image hints:(ZXDecodeHints *)hints error:(NSError **)error {
  30. NSError *decodeError = nil;
  31. ZXResult *result = [self doDecode:image hints:hints error:&decodeError];
  32. if (result) {
  33. return result;
  34. } else if (decodeError.code == ZXNotFoundError) {
  35. BOOL tryHarder = hints != nil && hints.tryHarder;
  36. if (tryHarder && [image rotateSupported]) {
  37. ZXBinaryBitmap *rotatedImage = [image rotateCounterClockwise];
  38. ZXResult *result = [self doDecode:rotatedImage hints:hints error:error];
  39. if (!result) {
  40. return nil;
  41. }
  42. // Record that we found it rotated 90 degrees CCW / 270 degrees CW
  43. NSMutableDictionary *metadata = [result resultMetadata];
  44. int orientation = 270;
  45. if (metadata != nil && metadata[@(kResultMetadataTypeOrientation)]) {
  46. // But if we found it reversed in doDecode(), add in that result here:
  47. orientation = (orientation + [((NSNumber *)metadata[@(kResultMetadataTypeOrientation)]) intValue]) % 360;
  48. }
  49. [result putMetadata:kResultMetadataTypeOrientation value:@(orientation)];
  50. // Update result points
  51. NSMutableArray *points = [result resultPoints];
  52. if (points != nil) {
  53. int height = [rotatedImage height];
  54. for (int i = 0; i < [points count]; i++) {
  55. points[i] = [[ZXResultPoint alloc] initWithX:height - [(ZXResultPoint *)points[i] y]
  56. y:[(ZXResultPoint *)points[i] x]];
  57. }
  58. }
  59. return result;
  60. }
  61. }
  62. if (error) *error = decodeError;
  63. return nil;
  64. }
  65. - (void)reset {
  66. // do nothing
  67. }
  68. /**
  69. * We're going to examine rows from the middle outward, searching alternately above and below the
  70. * middle, and farther out each time. rowStep is the number of rows between each successive
  71. * attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then
  72. * middle + rowStep, then middle - (2 * rowStep), etc.
  73. * rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily
  74. * decided that moving up and down by about 1/16 of the image is pretty good; we try more of the
  75. * image if "trying harder".
  76. *
  77. * @param image The image to decode
  78. * @param hints Any hints that were requested
  79. * @return The contents of the decoded barcode or nil if an error occurs
  80. */
  81. - (ZXResult *)doDecode:(ZXBinaryBitmap *)image hints:(ZXDecodeHints *)hints error:(NSError **)error {
  82. int width = image.width;
  83. int height = image.height;
  84. ZXBitArray *row = [[ZXBitArray alloc] initWithSize:width];
  85. int middle = height >> 1;
  86. BOOL tryHarder = hints != nil && hints.tryHarder;
  87. int rowStep = MAX(1, height >> (tryHarder ? 8 : 5));
  88. int maxLines;
  89. if (tryHarder) {
  90. maxLines = height;
  91. } else {
  92. maxLines = 15;
  93. }
  94. for (int x = 0; x < maxLines; x++) {
  95. int rowStepsAboveOrBelow = (x + 1) / 2;
  96. BOOL isAbove = (x & 0x01) == 0;
  97. int rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow);
  98. if (rowNumber < 0 || rowNumber >= height) {
  99. break;
  100. }
  101. NSError *rowError = nil;
  102. row = [image blackRow:rowNumber row:row error:&rowError];
  103. if (!row && rowError.code == ZXNotFoundError) {
  104. continue;
  105. } else if (!row) {
  106. if (error) *error = rowError;
  107. return nil;
  108. }
  109. for (int attempt = 0; attempt < 2; attempt++) {
  110. if (attempt == 1) {
  111. [row reverse];
  112. if (hints != nil && hints.resultPointCallback) {
  113. hints = [hints copy];
  114. hints.resultPointCallback = nil;
  115. }
  116. }
  117. ZXResult *result = [self decodeRow:rowNumber row:row hints:hints error:nil];
  118. if (result) {
  119. if (attempt == 1) {
  120. [result putMetadata:kResultMetadataTypeOrientation value:@180];
  121. NSMutableArray *points = [result resultPoints];
  122. if (points != nil) {
  123. points[0] = [[ZXResultPoint alloc] initWithX:width - [(ZXResultPoint *)points[0] x]
  124. y:[(ZXResultPoint *)points[0] y]];
  125. points[1] = [[ZXResultPoint alloc] initWithX:width - [(ZXResultPoint *)points[1] x]
  126. y:[(ZXResultPoint *)points[1] y]];
  127. }
  128. }
  129. return result;
  130. }
  131. }
  132. }
  133. if (error) *error = ZXNotFoundErrorInstance();
  134. return nil;
  135. }
  136. /**
  137. * Records the size of successive runs of white and black pixels in a row, starting at a given point.
  138. * The values are recorded in the given array, and the number of runs recorded is equal to the size
  139. * of the array. If the row starts on a white pixel at the given start point, then the first count
  140. * recorded is the run of white pixels starting from that point; likewise it is the count of a run
  141. * of black pixels if the row begin on a black pixels at that point.
  142. *
  143. * @param row row to count from
  144. * @param start offset into row to start at
  145. * @param counters array into which to record counts or nil if counters cannot be filled entirely
  146. * from row before running out of pixels
  147. */
  148. + (BOOL)recordPattern:(ZXBitArray *)row start:(int)start counters:(ZXIntArray *)counters {
  149. int numCounters = counters.length;
  150. [counters clear];
  151. int32_t *array = counters.array;
  152. int end = row.size;
  153. if (start >= end) {
  154. return NO;
  155. }
  156. BOOL isWhite = ![row get:start];
  157. int counterPosition = 0;
  158. int i = start;
  159. while (i < end) {
  160. if ([row get:i] ^ isWhite) {
  161. array[counterPosition]++;
  162. } else {
  163. counterPosition++;
  164. if (counterPosition == numCounters) {
  165. break;
  166. } else {
  167. array[counterPosition] = 1;
  168. isWhite = !isWhite;
  169. }
  170. }
  171. i++;
  172. }
  173. if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i == end))) {
  174. return NO;
  175. }
  176. return YES;
  177. }
  178. + (BOOL)recordPatternInReverse:(ZXBitArray *)row start:(int)start counters:(ZXIntArray *)counters {
  179. int numTransitionsLeft = counters.length;
  180. BOOL last = [row get:start];
  181. while (start > 0 && numTransitionsLeft >= 0) {
  182. if ([row get:--start] != last) {
  183. numTransitionsLeft--;
  184. last = !last;
  185. }
  186. }
  187. if (numTransitionsLeft >= 0 || ![self recordPattern:row start:start + 1 counters:counters]) {
  188. return NO;
  189. }
  190. return YES;
  191. }
  192. /**
  193. * Determines how closely a set of observed counts of runs of black/white values matches a given
  194. * target pattern. This is reported as the ratio of the total variance from the expected pattern
  195. * proportions across all pattern elements, to the length of the pattern.
  196. *
  197. * @param counters observed counters
  198. * @param pattern expected pattern
  199. * @param maxIndividualVariance The most any counter can differ before we give up
  200. * @return ratio of total variance between counters and pattern compared to total pattern size
  201. */
  202. + (float)patternMatchVariance:(ZXIntArray *)counters pattern:(const int[])pattern maxIndividualVariance:(float)maxIndividualVariance {
  203. int numCounters = counters.length;
  204. int total = 0;
  205. int patternLength = 0;
  206. int32_t *array = counters.array;
  207. for (int i = 0; i < numCounters; i++) {
  208. total += array[i];
  209. patternLength += pattern[i];
  210. }
  211. if (total < patternLength || patternLength == 0) {
  212. return FLT_MAX;
  213. }
  214. float unitBarWidth = (float) total / patternLength;
  215. maxIndividualVariance *= unitBarWidth;
  216. float totalVariance = 0.0f;
  217. for (int x = 0; x < numCounters; x++) {
  218. int counter = array[x];
  219. float scaledPattern = pattern[x] * unitBarWidth;
  220. float variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;
  221. if (variance > maxIndividualVariance) {
  222. return FLT_MAX;
  223. }
  224. totalVariance += variance;
  225. }
  226. return totalVariance / total;
  227. }
  228. /**
  229. * Attempts to decode a one-dimensional barcode format given a single row of
  230. * an image.
  231. *
  232. * @param rowNumber row number from top of the row
  233. * @param row the black/white pixel data of the row
  234. * @param hints decode hints
  235. * @return ZXResult containing encoded string and start/end of barcode or nil
  236. * if an error occurs or barcode cannot be found
  237. */
  238. - (ZXResult *)decodeRow:(int)rowNumber row:(ZXBitArray *)row hints:(ZXDecodeHints *)hints error:(NSError **)error {
  239. @throw [NSException exceptionWithName:NSInternalInconsistencyException
  240. reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
  241. userInfo:nil];
  242. }
  243. @end