123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- /*
- * Copyright 2012 ZXing authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- #import "ZXBitArray.h"
- #import "ZXBarcodeFormat.h"
- #import "ZXDecodeHints.h"
- #import "ZXErrors.h"
- #import "ZXIntArray.h"
- #import "ZXResult.h"
- #import "ZXResultPointCallback.h"
- #import "ZXRSS14Reader.h"
- #import "ZXRSSFinderPattern.h"
- #import "ZXRSSPair.h"
- #import "ZXRSSUtils.h"
-
- const int ZX_RSS14_OUTSIDE_EVEN_TOTAL_SUBSET[5] = {1,10,34,70,126};
- const int ZX_RSS14_INSIDE_ODD_TOTAL_SUBSET[4] = {4,20,48,81};
- const int ZX_RSS14_OUTSIDE_GSUM[5] = {0,161,961,2015,2715};
- const int ZX_RSS14_INSIDE_GSUM[4] = {0,336,1036,1516};
- const int ZX_RSS14_OUTSIDE_ODD_WIDEST[5] = {8,6,4,3,1};
- const int ZX_RSS14_INSIDE_ODD_WIDEST[4] = {2,4,6,8};
-
- @interface ZXRSS14Reader ()
-
- @property (nonatomic, strong, readonly) NSMutableArray *possibleLeftPairs;
- @property (nonatomic, strong, readonly) NSMutableArray *possibleRightPairs;
-
- @end
-
- @implementation ZXRSS14Reader
-
- - (id)init {
- if (self = [super init]) {
- _possibleLeftPairs = [NSMutableArray array];
- _possibleRightPairs = [NSMutableArray array];
- }
-
- return self;
- }
-
- - (ZXResult *)decodeRow:(int)rowNumber row:(ZXBitArray *)row hints:(ZXDecodeHints *)hints error:(NSError **)error {
- ZXRSSPair *leftPair = [self decodePair:row right:NO rowNumber:rowNumber hints:hints];
- [self addOrTally:self.possibleLeftPairs pair:leftPair];
- [row reverse];
- ZXRSSPair *rightPair = [self decodePair:row right:YES rowNumber:rowNumber hints:hints];
- [self addOrTally:self.possibleRightPairs pair:rightPair];
- [row reverse];
-
- for (ZXRSSPair *left in self.possibleLeftPairs) {
- if ([left count] > 1) {
- for (ZXRSSPair *right in self.possibleRightPairs) {
- if ([right count] > 1) {
- if ([self checkChecksum:left rightPair:right]) {
- return [self constructResult:left rightPair:right];
- }
- }
- }
- }
- }
-
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- - (void)addOrTally:(NSMutableArray *)possiblePairs pair:(ZXRSSPair *)pair {
- if (pair == nil) {
- return;
- }
- BOOL found = NO;
- for (ZXRSSPair *other in possiblePairs) {
- if (other.value == pair.value) {
- [other incrementCount];
- found = YES;
- break;
- }
- }
-
- if (!found) {
- [possiblePairs addObject:pair];
- }
- }
-
- - (void)reset {
- [self.possibleLeftPairs removeAllObjects];
- [self.possibleRightPairs removeAllObjects];
- }
-
- - (ZXResult *)constructResult:(ZXRSSPair *)leftPair rightPair:(ZXRSSPair *)rightPair {
- long long symbolValue = 4537077LL * leftPair.value + rightPair.value;
- NSString *text = [@(symbolValue) stringValue];
- NSMutableString *buffer = [NSMutableString stringWithCapacity:14];
-
- for (int i = 13 - (int)[text length]; i > 0; i--) {
- [buffer appendString:@"0"];
- }
-
- [buffer appendString:text];
- int checkDigit = 0;
-
- for (int i = 0; i < 13; i++) {
- int digit = [buffer characterAtIndex:i] - '0';
- checkDigit += (i & 0x01) == 0 ? 3 * digit : digit;
- }
-
- checkDigit = 10 - (checkDigit % 10);
- if (checkDigit == 10) {
- checkDigit = 0;
- }
- [buffer appendFormat:@"%d", checkDigit];
- NSArray *leftPoints = [[leftPair finderPattern] resultPoints];
- NSArray *rightPoints = [[rightPair finderPattern] resultPoints];
- return [ZXResult resultWithText:buffer
- rawBytes:nil
- resultPoints:@[leftPoints[0], leftPoints[1], rightPoints[0], rightPoints[1]]
- format:kBarcodeFormatRSS14];
- }
-
- - (BOOL)checkChecksum:(ZXRSSPair *)leftPair rightPair:(ZXRSSPair *)rightPair {
- // int leftFPValue = leftPair.finderPattern.value;
- // int rightFPValue = rightPair.finderPattern.value;
- // if ((leftFPValue == 0 && rightFPValue == 8) || (leftFPValue == 8 && rightFPValue == 0)) {
- // }
- int checkValue = (leftPair.checksumPortion + 16 * rightPair.checksumPortion) % 79;
- int targetCheckValue = 9 * leftPair.finderPattern.value + rightPair.finderPattern.value;
- if (targetCheckValue > 72) {
- targetCheckValue--;
- }
- if (targetCheckValue > 8) {
- targetCheckValue--;
- }
- return checkValue == targetCheckValue;
- }
-
- - (ZXRSSPair *)decodePair:(ZXBitArray *)row right:(BOOL)right rowNumber:(int)rowNumber hints:(ZXDecodeHints *)hints {
- ZXIntArray *startEnd = [self findFinderPattern:row rowOffset:0 rightFinderPattern:right];
- if (!startEnd) {
- return nil;
- }
- ZXRSSFinderPattern *pattern = [self parseFoundFinderPattern:row rowNumber:rowNumber right:right startEnd:startEnd];
- if (!pattern) {
- return nil;
- }
- id<ZXResultPointCallback> resultPointCallback = hints == nil ? nil : hints.resultPointCallback;
- if (resultPointCallback != nil) {
- float center = (startEnd.array[0] + startEnd.array[1]) / 2.0f;
- if (right) {
- center = [row size] - 1 - center;
- }
- [resultPointCallback foundPossibleResultPoint:[[ZXResultPoint alloc] initWithX:center y:rowNumber]];
- }
- ZXRSSDataCharacter *outside = [self decodeDataCharacter:row pattern:pattern outsideChar:YES];
- ZXRSSDataCharacter *inside = [self decodeDataCharacter:row pattern:pattern outsideChar:NO];
- if (!outside || !inside) {
- return nil;
- }
- return [[ZXRSSPair alloc] initWithValue:1597 * outside.value + inside.value
- checksumPortion:outside.checksumPortion + 4 * inside.checksumPortion
- finderPattern:pattern];
- }
-
- - (ZXRSSDataCharacter *)decodeDataCharacter:(ZXBitArray *)row pattern:(ZXRSSFinderPattern *)pattern outsideChar:(BOOL)outsideChar {
- ZXIntArray *counters = self.dataCharacterCounters;
- [counters clear];
- int32_t *array = counters.array;
-
- if (outsideChar) {
- if (![ZXOneDReader recordPatternInReverse:row start:[pattern startEnd].array[0] counters:counters]) {
- return nil;
- }
- } else {
- if (![ZXOneDReader recordPattern:row start:[pattern startEnd].array[1] counters:counters]) {
- return nil;
- }
-
- for (int i = 0, j = counters.length - 1; i < j; i++, j--) {
- int temp = array[i];
- array[i] = array[j];
- array[j] = temp;
- }
- }
-
- int numModules = outsideChar ? 16 : 15;
- float elementWidth = (float)[ZXAbstractRSSReader count:counters] / (float)numModules;
-
- for (int i = 0; i < counters.length; i++) {
- float value = (float) array[i] / elementWidth;
- int count = (int)(value + 0.5f);
- if (count < 1) {
- count = 1;
- } else if (count > 8) {
- count = 8;
- }
- int offset = i / 2;
- if ((i & 0x01) == 0) {
- self.oddCounts.array[offset] = count;
- self.oddRoundingErrors[offset] = value - count;
- } else {
- self.evenCounts.array[offset] = count;
- self.evenRoundingErrors[offset] = value - count;
- }
- }
-
- if (![self adjustOddEvenCounts:outsideChar numModules:numModules]) {
- return nil;
- }
-
- int oddSum = 0;
- int oddChecksumPortion = 0;
- for (int i = self.oddCounts.length - 1; i >= 0; i--) {
- oddChecksumPortion *= 9;
- oddChecksumPortion += self.oddCounts.array[i];
- oddSum += self.oddCounts.array[i];
- }
- int evenChecksumPortion = 0;
- int evenSum = 0;
- for (int i = self.evenCounts.length - 1; i >= 0; i--) {
- evenChecksumPortion *= 9;
- evenChecksumPortion += self.evenCounts.array[i];
- evenSum += self.evenCounts.array[i];
- }
- int checksumPortion = oddChecksumPortion + 3 * evenChecksumPortion;
-
- if (outsideChar) {
- if ((oddSum & 0x01) != 0 || oddSum > 12 || oddSum < 4) {
- return nil;
- }
- int group = (12 - oddSum) / 2;
- int oddWidest = ZX_RSS14_OUTSIDE_ODD_WIDEST[group];
- int evenWidest = 9 - oddWidest;
- int vOdd = [ZXRSSUtils rssValue:self.oddCounts maxWidth:oddWidest noNarrow:NO];
- int vEven = [ZXRSSUtils rssValue:self.evenCounts maxWidth:evenWidest noNarrow:YES];
- int tEven = ZX_RSS14_OUTSIDE_EVEN_TOTAL_SUBSET[group];
- int gSum = ZX_RSS14_OUTSIDE_GSUM[group];
- return [[ZXRSSDataCharacter alloc] initWithValue:vOdd * tEven + vEven + gSum checksumPortion:checksumPortion];
- } else {
- if ((evenSum & 0x01) != 0 || evenSum > 10 || evenSum < 4) {
- return nil;
- }
- int group = (10 - evenSum) / 2;
- int oddWidest = ZX_RSS14_INSIDE_ODD_WIDEST[group];
- int evenWidest = 9 - oddWidest;
- int vOdd = [ZXRSSUtils rssValue:self.oddCounts maxWidth:oddWidest noNarrow:YES];
- int vEven = [ZXRSSUtils rssValue:self.evenCounts maxWidth:evenWidest noNarrow:NO];
- int tOdd = ZX_RSS14_INSIDE_ODD_TOTAL_SUBSET[group];
- int gSum = ZX_RSS14_INSIDE_GSUM[group];
- return [[ZXRSSDataCharacter alloc] initWithValue:vEven * tOdd + vOdd + gSum checksumPortion:checksumPortion];
- }
- }
-
- - (ZXIntArray *)findFinderPattern:(ZXBitArray *)row rowOffset:(int)rowOffset rightFinderPattern:(BOOL)rightFinderPattern {
- ZXIntArray *counters = self.decodeFinderCounters;
- [counters clear];
- int32_t *array = counters.array;
-
- int width = row.size;
- BOOL isWhite = NO;
- while (rowOffset < width) {
- isWhite = ![row get:rowOffset];
- if (rightFinderPattern == isWhite) {
- break;
- }
- rowOffset++;
- }
-
- int counterPosition = 0;
- int patternStart = rowOffset;
- for (int x = rowOffset; x < width; x++) {
- if ([row get:x] ^ isWhite) {
- array[counterPosition]++;
- } else {
- if (counterPosition == 3) {
- if ([ZXAbstractRSSReader isFinderPattern:counters]) {
- return [[ZXIntArray alloc] initWithInts:patternStart, x, -1];
- }
- patternStart += array[0] + array[1];
- array[0] = array[2];
- array[1] = array[3];
- array[2] = 0;
- array[3] = 0;
- counterPosition--;
- } else {
- counterPosition++;
- }
- array[counterPosition] = 1;
- isWhite = !isWhite;
- }
- }
-
- return nil;
- }
-
- - (ZXRSSFinderPattern *)parseFoundFinderPattern:(ZXBitArray *)row rowNumber:(int)rowNumber right:(BOOL)right startEnd:(ZXIntArray *)startEnd {
- BOOL firstIsBlack = [row get:startEnd.array[0]];
- int firstElementStart = startEnd.array[0] - 1;
-
- while (firstElementStart >= 0 && firstIsBlack ^ [row get:firstElementStart]) {
- firstElementStart--;
- }
-
- firstElementStart++;
- int firstCounter = startEnd.array[0] - firstElementStart;
-
- ZXIntArray *counters = self.decodeFinderCounters;
- int32_t *array = counters.array;
- for (int i = counters.length - 1; i > 0; i--) {
- array[i] = array[i-1];
- }
- array[0] = firstCounter;
- int value = [ZXAbstractRSSReader parseFinderValue:counters finderPatternType:ZX_RSS_PATTERNS_RSS14_PATTERNS];
- if (value == -1) {
- return nil;
- }
- int start = firstElementStart;
- int end = startEnd.array[1];
- if (right) {
- start = [row size] - 1 - start;
- end = [row size] - 1 - end;
- }
- return [[ZXRSSFinderPattern alloc] initWithValue:value
- startEnd:[[ZXIntArray alloc] initWithInts:firstElementStart, startEnd.array[1], -1]
- start:start
- end:end
- rowNumber:rowNumber];
- }
-
- - (BOOL)adjustOddEvenCounts:(BOOL)outsideChar numModules:(int)numModules {
- int oddSum = [ZXAbstractRSSReader count:self.oddCounts];
- int evenSum = [ZXAbstractRSSReader count:self.evenCounts];
- int mismatch = oddSum + evenSum - numModules;
- BOOL oddParityBad = (oddSum & 0x01) == (outsideChar ? 1 : 0);
- BOOL evenParityBad = (evenSum & 0x01) == 1;
-
- BOOL incrementOdd = NO;
- BOOL decrementOdd = NO;
- BOOL incrementEven = NO;
- BOOL decrementEven = NO;
-
- if (outsideChar) {
- if (oddSum > 12) {
- decrementOdd = YES;
- } else if (oddSum < 4) {
- incrementOdd = YES;
- }
- if (evenSum > 12) {
- decrementEven = YES;
- } else if (evenSum < 4) {
- incrementEven = YES;
- }
- } else {
- if (oddSum > 11) {
- decrementOdd = YES;
- } else if (oddSum < 5) {
- incrementOdd = YES;
- }
- if (evenSum > 10) {
- decrementEven = YES;
- } else if (evenSum < 4) {
- incrementEven = YES;
- }
- }
-
- if (mismatch == 1) {
- if (oddParityBad) {
- if (evenParityBad) {
- return NO;
- }
- decrementOdd = YES;
- } else {
- if (!evenParityBad) {
- return NO;
- }
- decrementEven = YES;
- }
- } else if (mismatch == -1) {
- if (oddParityBad) {
- if (evenParityBad) {
- return NO;
- }
- incrementOdd = YES;
- } else {
- if (!evenParityBad) {
- return NO;
- }
- incrementEven = YES;
- }
- } else if (mismatch == 0) {
- if (oddParityBad) {
- if (!evenParityBad) {
- return NO;
- }
- if (oddSum < evenSum) {
- incrementOdd = YES;
- decrementEven = YES;
- } else {
- decrementOdd = YES;
- incrementEven = YES;
- }
- } else {
- if (evenParityBad) {
- return NO;
- }
- }
- } else {
- return NO;
- }
- if (incrementOdd) {
- if (decrementOdd) {
- return NO;
- }
- [ZXAbstractRSSReader increment:self.oddCounts errors:self.oddRoundingErrors];
- }
- if (decrementOdd) {
- [ZXAbstractRSSReader decrement:self.oddCounts errors:self.oddRoundingErrors];
- }
- if (incrementEven) {
- if (decrementEven) {
- return NO;
- }
- [ZXAbstractRSSReader increment:self.evenCounts errors:self.oddRoundingErrors];
- }
- if (decrementEven) {
- [ZXAbstractRSSReader decrement:self.evenCounts errors:self.evenRoundingErrors];
- }
- return YES;
- }
-
- @end
|