/*
|
* 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 "ZXByteArray.h"
|
#import "ZXErrors.h"
|
#import "ZXQRCodeBitMatrixParser.h"
|
#import "ZXQRCodeDataMask.h"
|
#import "ZXQRCodeFormatInformation.h"
|
#import "ZXQRCodeVersion.h"
|
|
@interface ZXQRCodeBitMatrixParser ()
|
|
@property (nonatomic, strong, readonly) ZXBitMatrix *bitMatrix;
|
@property (nonatomic, assign) BOOL shouldMirror;
|
@property (nonatomic, strong) ZXQRCodeFormatInformation *parsedFormatInfo;
|
@property (nonatomic, strong) ZXQRCodeVersion *parsedVersion;
|
|
@end
|
|
@implementation ZXQRCodeBitMatrixParser
|
|
- (id)initWithBitMatrix:(ZXBitMatrix *)bitMatrix error:(NSError **)error {
|
int dimension = bitMatrix.height;
|
if (dimension < 21 || (dimension & 0x03) != 1) {
|
if (error) *error = ZXFormatErrorInstance();
|
return nil;
|
}
|
|
if (self = [super init]) {
|
_bitMatrix = bitMatrix;
|
_parsedFormatInfo = nil;
|
_parsedVersion = nil;
|
}
|
return self;
|
}
|
|
- (ZXQRCodeFormatInformation *)readFormatInformationWithError:(NSError **)error {
|
if (self.parsedFormatInfo != nil) {
|
return self.parsedFormatInfo;
|
}
|
int formatInfoBits1 = 0;
|
|
for (int i = 0; i < 6; i++) {
|
formatInfoBits1 = [self copyBit:i j:8 versionBits:formatInfoBits1];
|
}
|
|
formatInfoBits1 = [self copyBit:7 j:8 versionBits:formatInfoBits1];
|
formatInfoBits1 = [self copyBit:8 j:8 versionBits:formatInfoBits1];
|
formatInfoBits1 = [self copyBit:8 j:7 versionBits:formatInfoBits1];
|
|
for (int j = 5; j >= 0; j--) {
|
formatInfoBits1 = [self copyBit:8 j:j versionBits:formatInfoBits1];
|
}
|
|
int dimension = self.bitMatrix.height;
|
int formatInfoBits2 = 0;
|
int jMin = dimension - 7;
|
|
for (int j = dimension - 1; j >= jMin; j--) {
|
formatInfoBits2 = [self copyBit:8 j:j versionBits:formatInfoBits2];
|
}
|
|
for (int i = dimension - 8; i < dimension; i++) {
|
formatInfoBits2 = [self copyBit:i j:8 versionBits:formatInfoBits2];
|
}
|
|
self.parsedFormatInfo = [ZXQRCodeFormatInformation decodeFormatInformation:formatInfoBits1 maskedFormatInfo2:formatInfoBits2];
|
if (self.parsedFormatInfo != nil) {
|
return self.parsedFormatInfo;
|
}
|
if (error) *error = ZXFormatErrorInstance();
|
return nil;
|
}
|
|
- (ZXQRCodeVersion *)readVersionWithError:(NSError **)error {
|
if (self.parsedVersion != nil) {
|
return self.parsedVersion;
|
}
|
int dimension = self.bitMatrix.height;
|
int provisionalVersion = (dimension - 17) / 4;
|
if (provisionalVersion <= 6) {
|
return [ZXQRCodeVersion versionForNumber:provisionalVersion];
|
}
|
int versionBits = 0;
|
int ijMin = dimension - 11;
|
|
for (int j = 5; j >= 0; j--) {
|
|
for (int i = dimension - 9; i >= ijMin; i--) {
|
versionBits = [self copyBit:i j:j versionBits:versionBits];
|
}
|
|
}
|
|
ZXQRCodeVersion *theParsedVersion = [ZXQRCodeVersion decodeVersionInformation:versionBits];
|
if (theParsedVersion != nil && theParsedVersion.dimensionForVersion == dimension) {
|
self.parsedVersion = theParsedVersion;
|
return self.parsedVersion;
|
}
|
versionBits = 0;
|
|
for (int i = 5; i >= 0; i--) {
|
for (int j = dimension - 9; j >= ijMin; j--) {
|
versionBits = [self copyBit:i j:j versionBits:versionBits];
|
}
|
}
|
|
theParsedVersion = [ZXQRCodeVersion decodeVersionInformation:versionBits];
|
if (theParsedVersion != nil && theParsedVersion.dimensionForVersion == dimension) {
|
self.parsedVersion = theParsedVersion;
|
return self.parsedVersion;
|
}
|
if (error) *error = ZXFormatErrorInstance();
|
return nil;
|
}
|
|
- (int)copyBit:(int)i j:(int)j versionBits:(int)versionBits {
|
BOOL bit = self.shouldMirror ? [self.bitMatrix getX:j y:i] : [self.bitMatrix getX:i y:j];
|
return bit ? (versionBits << 1) | 0x1 : versionBits << 1;
|
}
|
|
- (ZXByteArray *)readCodewordsWithError:(NSError **)error {
|
ZXQRCodeFormatInformation *formatInfo = [self readFormatInformationWithError:error];
|
if (!formatInfo) {
|
return nil;
|
}
|
|
ZXQRCodeVersion *version = [self readVersionWithError:error];
|
if (!version) {
|
return nil;
|
}
|
|
// Get the data mask for the format used in this QR Code. This will exclude
|
// some bits from reading as we wind through the bit matrix.
|
ZXQRCodeDataMask *dataMask = [ZXQRCodeDataMask forReference:[formatInfo dataMask]];
|
int dimension = self.bitMatrix.height;
|
[dataMask unmaskBitMatrix:self.bitMatrix dimension:dimension];
|
|
ZXBitMatrix *functionPattern = [version buildFunctionPattern];
|
|
BOOL readingUp = YES;
|
ZXByteArray *result = [[ZXByteArray alloc] initWithLength:version.totalCodewords];
|
int resultOffset = 0;
|
int currentByte = 0;
|
int bitsRead = 0;
|
// Read columns in pairs, from right to left
|
for (int j = dimension - 1; j > 0; j -= 2) {
|
if (j == 6) {
|
// Skip whole column with vertical alignment pattern;
|
// saves time and makes the other code proceed more cleanly
|
j--;
|
}
|
// Read alternatingly from bottom to top then top to bottom
|
for (int count = 0; count < dimension; count++) {
|
int i = readingUp ? dimension - 1 - count : count;
|
for (int col = 0; col < 2; col++) {
|
// Ignore bits covered by the function pattern
|
if (![functionPattern getX:j - col y:i]) {
|
// Read a bit
|
bitsRead++;
|
currentByte <<= 1;
|
if ([self.bitMatrix getX:j - col y:i]) {
|
currentByte |= 1;
|
}
|
// If we've made a whole byte, save it off
|
if (bitsRead == 8) {
|
result.array[resultOffset++] = (int8_t) currentByte;
|
bitsRead = 0;
|
currentByte = 0;
|
}
|
}
|
}
|
}
|
readingUp ^= YES; // readingUp = !readingUp; // switch directions
|
}
|
if (resultOffset != [version totalCodewords]) {
|
if (error) *error = ZXFormatErrorInstance();
|
return nil;
|
}
|
return result;
|
}
|
|
- (void)remask {
|
if (!self.parsedFormatInfo) {
|
return; // We have no format information, and have no data mask
|
}
|
ZXQRCodeDataMask *dataMask = [ZXQRCodeDataMask forReference:self.parsedFormatInfo.dataMask];
|
int dimension = self.bitMatrix.height;
|
[dataMask unmaskBitMatrix:self.bitMatrix dimension:dimension];
|
}
|
|
- (void)setMirror:(BOOL)mirror {
|
self.parsedVersion = nil;
|
self.parsedFormatInfo = nil;
|
self.shouldMirror = mirror;
|
}
|
|
- (void)mirror {
|
for (int x = 0; x < self.bitMatrix.width; x++) {
|
for (int y = x + 1; y < self.bitMatrix.height; y++) {
|
if ([self.bitMatrix getX:x y:y] != [self.bitMatrix getX:y y:x]) {
|
[self.bitMatrix flipX:y y:x];
|
[self.bitMatrix flipX:x y:y];
|
}
|
}
|
}
|
}
|
|
@end
|