/*
|
* 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 "ZXBitMatrix.h"
|
#import "ZXBoolArray.h"
|
#import "ZXIntArray.h"
|
|
@interface ZXBitMatrix ()
|
|
@property (nonatomic, assign, readonly) int bitsSize;
|
|
@end
|
|
@implementation ZXBitMatrix
|
|
- (id)initWithDimension:(int)dimension {
|
return [self initWithWidth:dimension height:dimension];
|
}
|
|
- (id)initWithWidth:(int)width height:(int)height {
|
if (self = [super init]) {
|
if (width < 1 || height < 1) {
|
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
reason:@"Both dimensions must be greater than 0"
|
userInfo:nil];
|
}
|
_width = width;
|
_height = height;
|
_rowSize = (_width + 31) / 32;
|
_bitsSize = _rowSize * _height;
|
_bits = (int32_t *)malloc(_bitsSize * sizeof(int32_t));
|
[self clear];
|
}
|
|
return self;
|
}
|
|
- (id)initWithWidth:(int)width height:(int)height rowSize:(int)rowSize bits:(int32_t *)bits {
|
if (self = [super init]) {
|
_width = width;
|
_height = height;
|
_rowSize = rowSize;
|
_bitsSize = _rowSize * _height;
|
_bits = (int32_t *)malloc(_bitsSize * sizeof(int32_t));
|
memcpy(_bits, bits, _bitsSize * sizeof(int32_t));
|
}
|
|
return self;
|
}
|
|
- (void)dealloc {
|
if (_bits != NULL) {
|
free(_bits);
|
_bits = NULL;
|
}
|
}
|
|
+ (ZXBitMatrix *)parse:(NSString *)stringRepresentation
|
setString:(NSString *)setString
|
unsetString:(NSString *)unsetString {
|
if (!stringRepresentation) {
|
@throw [NSException exceptionWithName:@"IllegalArgumentException"
|
reason:@"stringRepresentation is required"
|
userInfo:nil];
|
}
|
|
ZXBoolArray *bits = [[ZXBoolArray alloc] initWithLength:(unsigned int)stringRepresentation.length];
|
int bitsPos = 0;
|
int rowStartPos = 0;
|
int rowLength = -1;
|
int nRows = 0;
|
int pos = 0;
|
while (pos < stringRepresentation.length) {
|
if ([stringRepresentation characterAtIndex:pos] == '\n' ||
|
[stringRepresentation characterAtIndex:pos] == '\r') {
|
if (bitsPos > rowStartPos) {
|
if(rowLength == -1) {
|
rowLength = bitsPos - rowStartPos;
|
} else if (bitsPos - rowStartPos != rowLength) {
|
@throw [NSException exceptionWithName:@"IllegalArgumentException"
|
reason:@"row lengths do not match"
|
userInfo:nil];
|
}
|
rowStartPos = bitsPos;
|
nRows++;
|
}
|
pos++;
|
} else if ([[stringRepresentation substringWithRange:NSMakeRange(pos, setString.length)] isEqualToString:setString]) {
|
pos += setString.length;
|
bits.array[bitsPos] = YES;
|
bitsPos++;
|
} else if ([[stringRepresentation substringWithRange:NSMakeRange(pos, unsetString.length)] isEqualToString:unsetString]) {
|
pos += unsetString.length;
|
bits.array[bitsPos] = NO;
|
bitsPos++;
|
} else {
|
@throw [NSException exceptionWithName:@"IllegalArgumentException"
|
reason:[NSString stringWithFormat:@"illegal character encountered: %@", [stringRepresentation substringFromIndex:pos]]
|
userInfo:nil];
|
}
|
}
|
|
// no EOL at end?
|
if (bitsPos > rowStartPos) {
|
if (rowLength == -1) {
|
rowLength = bitsPos - rowStartPos;
|
} else if (bitsPos - rowStartPos != rowLength) {
|
@throw [NSException exceptionWithName:@"IllegalArgumentException"
|
reason:@"row lengths do not match"
|
userInfo:nil];
|
}
|
nRows++;
|
}
|
|
ZXBitMatrix *matrix = [[ZXBitMatrix alloc] initWithWidth:rowLength height:nRows];
|
for (int i = 0; i < bitsPos; i++) {
|
if (bits.array[i]) {
|
[matrix setX:i % rowLength y:i / rowLength];
|
}
|
}
|
return matrix;
|
}
|
|
- (BOOL)getX:(int)x y:(int)y {
|
NSInteger offset = y * self.rowSize + (x / 32);
|
return ((_bits[offset] >> (x & 0x1f)) & 1) != 0;
|
}
|
|
- (void)setX:(int)x y:(int)y {
|
NSInteger offset = y * self.rowSize + (x / 32);
|
_bits[offset] |= 1 << (x & 0x1f);
|
}
|
|
- (void)unsetX:(int)x y:(int)y {
|
int offset = y * self.rowSize + (x / 32);
|
_bits[offset] &= ~(1 << (x & 0x1f));
|
}
|
|
- (void)flipX:(int)x y:(int)y {
|
NSUInteger offset = y * self.rowSize + (x / 32);
|
_bits[offset] ^= 1 << (x & 0x1f);
|
}
|
|
- (void)xor:(ZXBitMatrix *)mask {
|
if (self.width != mask.width || self.height != mask.height
|
|| self.rowSize != mask.rowSize) {
|
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
reason:@"input matrix dimensions do not match"
|
userInfo:nil];
|
}
|
ZXBitArray *rowArray = [[ZXBitArray alloc] initWithSize:self.width];
|
for (int y = 0; y < self.height; y++) {
|
int offset = y * self.rowSize;
|
int32_t *row = [mask rowAtY:y row:rowArray].bits;
|
for (int x = 0; x < self.rowSize; x++) {
|
self.bits[offset + x] ^= row[x];
|
}
|
}
|
}
|
|
- (void)clear {
|
NSInteger max = self.bitsSize;
|
memset(_bits, 0, max * sizeof(int32_t));
|
}
|
|
- (void)setRegionAtLeft:(int)left top:(int)top width:(int)aWidth height:(int)aHeight {
|
if (aHeight < 1 || aWidth < 1) {
|
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
reason:@"Height and width must be at least 1"
|
userInfo:nil];
|
}
|
NSUInteger right = left + aWidth;
|
NSUInteger bottom = top + aHeight;
|
if (bottom > self.height || right > self.width) {
|
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
reason:@"The region must fit inside the matrix"
|
userInfo:nil];
|
}
|
for (NSUInteger y = top; y < bottom; y++) {
|
NSUInteger offset = y * self.rowSize;
|
for (NSInteger x = left; x < right; x++) {
|
_bits[offset + (x / 32)] |= 1 << (x & 0x1f);
|
}
|
}
|
}
|
|
- (ZXBitArray *)rowAtY:(int)y row:(ZXBitArray *)row {
|
if (row == nil || [row size] < self.width) {
|
row = [[ZXBitArray alloc] initWithSize:self.width];
|
} else {
|
[row clear];
|
}
|
int offset = y * self.rowSize;
|
for (int x = 0; x < self.rowSize; x++) {
|
[row setBulk:x * 32 newBits:_bits[offset + x]];
|
}
|
|
return row;
|
}
|
|
- (void)setRowAtY:(int)y row:(ZXBitArray *)row {
|
for (NSUInteger i = 0; i < self.rowSize; i++) {
|
_bits[(y * self.rowSize) + i] = row.bits[i];
|
}
|
}
|
|
- (void)rotate180 {
|
int width = self.width;
|
int height = self.height;
|
ZXBitArray *topRow = [[ZXBitArray alloc] initWithSize:width];
|
ZXBitArray *bottomRow = [[ZXBitArray alloc] initWithSize:width];
|
for (int i = 0; i < (height+1) / 2; i++) {
|
topRow = [self rowAtY:i row:topRow];
|
bottomRow = [self rowAtY:height - 1 - i row:bottomRow];
|
[topRow reverse];
|
[bottomRow reverse];
|
[self setRowAtY:i row:bottomRow];
|
[self setRowAtY:height - 1 - i row:topRow];
|
}
|
}
|
|
- (ZXIntArray *)enclosingRectangle {
|
int left = self.width;
|
int top = self.height;
|
int right = -1;
|
int bottom = -1;
|
|
for (int y = 0; y < self.height; y++) {
|
for (int x32 = 0; x32 < self.rowSize; x32++) {
|
int32_t theBits = _bits[y * self.rowSize + x32];
|
if (theBits != 0) {
|
if (y < top) {
|
top = y;
|
}
|
if (y > bottom) {
|
bottom = y;
|
}
|
if (x32 * 32 < left) {
|
int32_t bit = 0;
|
while ((theBits << (31 - bit)) == 0) {
|
bit++;
|
}
|
if ((x32 * 32 + bit) < left) {
|
left = x32 * 32 + bit;
|
}
|
}
|
if (x32 * 32 + 31 > right) {
|
int bit = 31;
|
while ((theBits >> bit) == 0) {
|
bit--;
|
}
|
if ((x32 * 32 + bit) > right) {
|
right = x32 * 32 + bit;
|
}
|
}
|
}
|
}
|
}
|
|
NSInteger width = right - left;
|
NSInteger height = bottom - top;
|
|
if (width < 0 || height < 0) {
|
return nil;
|
}
|
|
return [[ZXIntArray alloc] initWithInts:left, top, width, height, -1];
|
}
|
|
- (ZXIntArray *)topLeftOnBit {
|
int bitsOffset = 0;
|
while (bitsOffset < self.bitsSize && _bits[bitsOffset] == 0) {
|
bitsOffset++;
|
}
|
if (bitsOffset == self.bitsSize) {
|
return nil;
|
}
|
int y = bitsOffset / self.rowSize;
|
int x = (bitsOffset % self.rowSize) * 32;
|
|
int32_t theBits = _bits[bitsOffset];
|
int32_t bit = 0;
|
while ((theBits << (31 - bit)) == 0) {
|
bit++;
|
}
|
x += bit;
|
return [[ZXIntArray alloc] initWithInts:x, y, -1];
|
}
|
|
- (ZXIntArray *)bottomRightOnBit {
|
int bitsOffset = self.bitsSize - 1;
|
while (bitsOffset >= 0 && _bits[bitsOffset] == 0) {
|
bitsOffset--;
|
}
|
if (bitsOffset < 0) {
|
return nil;
|
}
|
|
int y = bitsOffset / self.rowSize;
|
int x = (bitsOffset % self.rowSize) * 32;
|
|
int32_t theBits = _bits[bitsOffset];
|
int32_t bit = 31;
|
while ((theBits >> bit) == 0) {
|
bit--;
|
}
|
x += bit;
|
|
return [[ZXIntArray alloc] initWithInts:x, y, -1];
|
}
|
|
- (BOOL)isEqual:(NSObject *)o {
|
if (!([o isKindOfClass:[ZXBitMatrix class]])) {
|
return NO;
|
}
|
ZXBitMatrix *other = (ZXBitMatrix *)o;
|
for (int i = 0; i < self.bitsSize; i++) {
|
if (_bits[i] != other.bits[i]) {
|
return NO;
|
}
|
}
|
return self.width == other.width && self.height == other.height && self.rowSize == other.rowSize && self.bitsSize == other.bitsSize;
|
}
|
|
- (NSUInteger)hash {
|
NSInteger hash = self.width;
|
hash = 31 * hash + self.width;
|
hash = 31 * hash + self.height;
|
hash = 31 * hash + self.rowSize;
|
for (NSUInteger i = 0; i < self.bitsSize; i++) {
|
hash = 31 * hash + _bits[i];
|
}
|
return hash;
|
}
|
|
- (NSString *)description {
|
return [self descriptionWithSetString:@"X " unsetString:@" "];
|
}
|
|
- (NSString *)descriptionWithSetString:(NSString *)setString unsetString:(NSString *)unsetString {
|
#pragma GCC diagnostic push
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
return [self descriptionWithSetString:setString unsetString:unsetString lineSeparator:@"\n"];
|
#pragma GCC diagnostic pop
|
}
|
|
- (NSString *)descriptionWithSetString:(NSString *)setString unsetString:(NSString *)unsetString
|
lineSeparator:(NSString *)lineSeparator {
|
NSMutableString *result = [NSMutableString stringWithCapacity:self.height * (self.width + 1)];
|
for (int y = 0; y < self.height; y++) {
|
for (int x = 0; x < self.width; x++) {
|
[result appendString:[self getX:x y:y] ? setString : unsetString];
|
}
|
[result appendString:lineSeparator];
|
}
|
return result;
|
}
|
|
- (id)copyWithZone:(NSZone *)zone {
|
return [[ZXBitMatrix allocWithZone:zone] initWithWidth:self.width height:self.height rowSize:self.rowSize bits:self.bits];
|
}
|
|
@end
|