/* * 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 "ZXBitMatrix.h" #import "ZXBoolArray.h" #import "ZXByteArray.h" #import "ZXDecoderResult.h" #import "ZXErrors.h" #import "ZXGenericGF.h" #import "ZXIntArray.h" #import "ZXQRCodeBitMatrixParser.h" #import "ZXQRCodeDataBlock.h" #import "ZXQRCodeDecodedBitStreamParser.h" #import "ZXQRCodeDecoder.h" #import "ZXQRCodeDecoderMetaData.h" #import "ZXQRCodeErrorCorrectionLevel.h" #import "ZXQRCodeFormatInformation.h" #import "ZXQRCodeVersion.h" #import "ZXReedSolomonDecoder.h" @interface ZXQRCodeDecoder () @property (nonatomic, strong, readonly) ZXReedSolomonDecoder *rsDecoder; @end @implementation ZXQRCodeDecoder - (id)init { if (self = [super init]) { _rsDecoder = [[ZXReedSolomonDecoder alloc] initWithField:[ZXGenericGF QrCodeField256]]; } return self; } - (ZXDecoderResult *)decode:(NSArray *)image error:(NSError **)error { return [self decode:image hints:nil error:error]; } - (ZXDecoderResult *)decode:(NSArray *)image hints:(ZXDecodeHints *)hints error:(NSError **)error { int dimension = (int)[image count]; ZXBitMatrix *bits = [[ZXBitMatrix alloc] initWithDimension:dimension]; for (int i = 0; i < dimension; i++) { ZXBoolArray *b = image[i]; for (int j = 0; j < dimension; j++) { if (b.array[j]) { [bits setX:j y:i]; } } } return [self decodeMatrix:bits hints:hints error:error]; } - (ZXDecoderResult *)decodeMatrix:(ZXBitMatrix *)bits error:(NSError **)error { return [self decodeMatrix:bits hints:nil error:error]; } - (ZXDecoderResult *)decodeMatrix:(ZXBitMatrix *)bits hints:(ZXDecodeHints *)hints error:(NSError **)error { ZXQRCodeBitMatrixParser *parser = [[ZXQRCodeBitMatrixParser alloc] initWithBitMatrix:bits error:error]; if (!parser) { return nil; } ZXDecoderResult *result = [self decodeParser:parser hints:hints error:error]; if (result) { return result; } // Revert the bit matrix [parser remask]; // Will be attempting a mirrored reading of the version and format info. [parser setMirror:YES]; // Preemptively read the version. if (![parser readVersionWithError:error]) { return nil; } /* * Since we're here, this means we have successfully detected some kind * of version and format information when mirrored. This is a good sign, * that the QR code may be mirrored, and we should try once more with a * mirrored content. */ // Prepare for a mirrored reading. [parser mirror]; result = [self decodeParser:parser hints:hints error:error]; if (!result) { return nil; } // Success! Notify the caller that the code was mirrored. result.other = [[ZXQRCodeDecoderMetaData alloc] initWithMirrored:YES]; return result; } - (ZXDecoderResult *)decodeParser:(ZXQRCodeBitMatrixParser *)parser hints:(ZXDecodeHints *)hints error:(NSError **)error { ZXQRCodeVersion *version = [parser readVersionWithError:error]; if (!version) { return nil; } ZXQRCodeFormatInformation *formatInfo = [parser readFormatInformationWithError:error]; if (!formatInfo) { return nil; } ZXQRCodeErrorCorrectionLevel *ecLevel = formatInfo.errorCorrectionLevel; ZXByteArray *codewords = [parser readCodewordsWithError:error]; if (!codewords) { return nil; } NSArray *dataBlocks = [ZXQRCodeDataBlock dataBlocks:codewords version:version ecLevel:ecLevel]; int totalBytes = 0; for (ZXQRCodeDataBlock *dataBlock in dataBlocks) { totalBytes += dataBlock.numDataCodewords; } if (totalBytes == 0) { return nil; } ZXByteArray *resultBytes = [[ZXByteArray alloc] initWithLength:totalBytes]; int resultOffset = 0; for (ZXQRCodeDataBlock *dataBlock in dataBlocks) { ZXByteArray *codewordBytes = dataBlock.codewords; int numDataCodewords = [dataBlock numDataCodewords]; if (![self correctErrors:codewordBytes numDataCodewords:numDataCodewords error:error]) { return nil; } for (int i = 0; i < numDataCodewords; i++) { resultBytes.array[resultOffset++] = codewordBytes.array[i]; } } return [ZXQRCodeDecodedBitStreamParser decode:resultBytes version:version ecLevel:ecLevel hints:hints error:error]; } /** * Given data and error-correction codewords received, possibly corrupted by errors, attempts to * correct the errors in-place using Reed-Solomon error correction. * * @param codewordBytes data and error correction codewords * @param numDataCodewords number of codewords that are data bytes * @return NO if error correction fails */ - (BOOL)correctErrors:(ZXByteArray *)codewordBytes numDataCodewords:(int)numDataCodewords error:(NSError **)error { int numCodewords = (int)codewordBytes.length; // First read into an array of ints ZXIntArray *codewordsInts = [[ZXIntArray alloc] initWithLength:numCodewords]; for (int i = 0; i < numCodewords; i++) { codewordsInts.array[i] = codewordBytes.array[i] & 0xFF; } int numECCodewords = (int)codewordBytes.length - numDataCodewords; NSError *decodeError = nil; if (![self.rsDecoder decode:codewordsInts twoS:numECCodewords error:&decodeError]) { if (decodeError.code == ZXReedSolomonError) { if (error) *error = ZXChecksumErrorInstance(); return NO; } else { if (error) *error = decodeError; return NO; } } // Copy back into array of bytes -- only need to worry about the bytes that were data // We don't care about errors in the error-correction codewords for (int i = 0; i < numDataCodewords; i++) { codewordBytes.array[i] = (int8_t) codewordsInts.array[i]; } return YES; } @end