123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- /*
- * Copyright 2013 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 "ZXAztecCode.h"
- #import "ZXAztecEncoder.h"
- #import "ZXAztecHighLevelEncoder.h"
- #import "ZXBitArray.h"
- #import "ZXBitMatrix.h"
- #import "ZXByteArray.h"
- #import "ZXGenericGF.h"
- #import "ZXIntArray.h"
- #import "ZXReedSolomonEncoder.h"
-
- const int ZX_AZTEC_DEFAULT_EC_PERCENT = 33; // default minimal percentage of error check words
- const int ZX_AZTEC_DEFAULT_LAYERS = 0;
- const int ZX_AZTEC_MAX_NB_BITS = 32;
- const int ZX_AZTEC_MAX_NB_BITS_COMPACT = 4;
-
- const int ZX_AZTEC_WORD_SIZE[] = {
- 4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
- 12, 12, 12, 12, 12, 12, 12, 12, 12, 12
- };
-
- @implementation ZXAztecEncoder
-
- + (ZXAztecCode *)encode:(ZXByteArray *)data {
- return [self encode:data minECCPercent:ZX_AZTEC_DEFAULT_EC_PERCENT userSpecifiedLayers:ZX_AZTEC_DEFAULT_LAYERS];
- }
-
- + (ZXAztecCode *)encode:(ZXByteArray *)data minECCPercent:(int)minECCPercent userSpecifiedLayers:(int)userSpecifiedLayers {
- // High-level encode
- ZXBitArray *bits = [[[ZXAztecHighLevelEncoder alloc] initWithText:data] encode];
-
- // stuff bits and choose symbol size
- int eccBits = bits.size * minECCPercent / 100 + 11;
- int totalSizeBits = bits.size + eccBits;
- BOOL compact;
- int layers;
- int totalBitsInLayer;
- int wordSize = ZX_AZTEC_WORD_SIZE[0];
- ZXBitArray *stuffedBits;
- if (userSpecifiedLayers != ZX_AZTEC_DEFAULT_LAYERS) {
- compact = userSpecifiedLayers < 0;
- layers = abs(userSpecifiedLayers);
- if (layers > (compact ? ZX_AZTEC_MAX_NB_BITS_COMPACT : ZX_AZTEC_MAX_NB_BITS)) {
- @throw [NSException exceptionWithName:@"IllegalArgumentException"
- reason:[NSString stringWithFormat:@"Illegal value %d for layers", userSpecifiedLayers]
- userInfo:nil];
- }
- totalBitsInLayer = [self totalBitsInLayer:layers compact:compact];
- wordSize = ZX_AZTEC_WORD_SIZE[layers];
- int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);
- stuffedBits = [self stuffBits:bits wordSize:wordSize];
- if (stuffedBits.size + eccBits > usableBitsInLayers) {
- @throw [NSException exceptionWithName:@"IllegalArgumentException"
- reason:@"Data too large for user specified layer"
- userInfo:nil];
- }
- if (compact && stuffedBits.size > wordSize * 64) {
- // Compact format only allows 64 data words, though C4 can hold more words than that
- @throw [NSException exceptionWithName:@"IllegalArgumentException"
- reason:@"Data too large for user specified layer"
- userInfo:nil];
- }
- } else {
- // We look at the possible table sizes in the order Compact1, Compact2, Compact3,
- // Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1)
- // is the same size, but has more data.
- for (int i = 0; ; i++) {
- if (i > ZX_AZTEC_MAX_NB_BITS) {
- @throw [NSException exceptionWithName:@"IllegalArgumentException"
- reason:@"Data too large for an Aztec code"
- userInfo:nil];
- }
- compact = i <= 3;
- layers = compact ? i + 1 : i;
- totalBitsInLayer = [self totalBitsInLayer:layers compact:compact];
- if (totalSizeBits > totalBitsInLayer) {
- continue;
- }
- // [Re]stuff the bits if this is the first opportunity, or if the
- // wordSize has changed
- if (wordSize != ZX_AZTEC_WORD_SIZE[layers]) {
- wordSize = ZX_AZTEC_WORD_SIZE[layers];
- stuffedBits = [self stuffBits:bits wordSize:wordSize];
- }
- int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);
- if (compact && stuffedBits.size > wordSize * 64) {
- // Compact format only allows 64 data words, though C4 can hold more words than that
- continue;
- }
- if (stuffedBits.size + eccBits <= usableBitsInLayers) {
- break;
- }
- }
- }
- ZXBitArray *messageBits = [self generateCheckWords:stuffedBits totalBits:totalBitsInLayer wordSize:wordSize];
-
- // generate check words
- int messageSizeInWords = stuffedBits.size / wordSize;
- ZXBitArray *modeMessage = [self generateModeMessageCompact:compact layers:layers messageSizeInWords:messageSizeInWords];
-
- // allocate symbol
- int baseMatrixSize = compact ? 11 + layers * 4 : 14 + layers * 4; // not including alignment lines
- int alignmentMap[baseMatrixSize];
- memset(alignmentMap, 0, baseMatrixSize * sizeof(int));
- int matrixSize;
- if (compact) {
- // no alignment marks in compact mode, alignmentMap is a no-op
- matrixSize = baseMatrixSize;
- for (int i = 0; i < baseMatrixSize; i++) {
- alignmentMap[i] = i;
- }
- } else {
- matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);
- int origCenter = baseMatrixSize / 2;
- int center = matrixSize / 2;
- for (int i = 0; i < origCenter; i++) {
- int newOffset = i + i / 15;
- alignmentMap[origCenter - i - 1] = center - newOffset - 1;
- alignmentMap[origCenter + i] = center + newOffset + 1;
- }
- }
- ZXBitMatrix *matrix = [[ZXBitMatrix alloc] initWithDimension:matrixSize];
-
- // draw data bits
- for (int i = 0, rowOffset = 0; i < layers; i++) {
- int rowSize = compact ? (layers - i) * 4 + 9 : (layers - i) * 4 + 12;
- for (int j = 0; j < rowSize; j++) {
- int columnOffset = j * 2;
- for (int k = 0; k < 2; k++) {
- if ([messageBits get:rowOffset + columnOffset + k]) {
- [matrix setX:alignmentMap[i * 2 + k] y:alignmentMap[i * 2 + j]];
- }
- if ([messageBits get:rowOffset + rowSize * 2 + columnOffset + k]) {
- [matrix setX:alignmentMap[i * 2 + j] y:alignmentMap[baseMatrixSize - 1 - i * 2 - k]];
- }
- if ([messageBits get:rowOffset + rowSize * 4 + columnOffset + k]) {
- [matrix setX:alignmentMap[baseMatrixSize - 1 - i * 2 - k] y:alignmentMap[baseMatrixSize - 1 - i * 2 - j]];
- }
- if ([messageBits get:rowOffset + rowSize * 6 + columnOffset + k]) {
- [matrix setX:alignmentMap[baseMatrixSize - 1 - i * 2 - j] y:alignmentMap[i * 2 + k]];
- }
- }
- }
- rowOffset += rowSize * 8;
- }
-
- // draw mode message
- [self drawModeMessage:matrix compact:compact matrixSize:matrixSize modeMessage:modeMessage];
-
- // draw alignment marks
- if (compact) {
- [self drawBullsEye:matrix center:matrixSize / 2 size:5];
- } else {
- [self drawBullsEye:matrix center:matrixSize / 2 size:7];
- for (int i = 0, j = 0; i < baseMatrixSize / 2 - 1; i += 15, j += 16) {
- for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2) {
- [matrix setX:matrixSize / 2 - j y:k];
- [matrix setX:matrixSize / 2 + j y:k];
- [matrix setX:k y:matrixSize / 2 - j];
- [matrix setX:k y:matrixSize / 2 + j];
- }
- }
- }
-
- ZXAztecCode *aztec = [[ZXAztecCode alloc] init];
- aztec.compact = compact;
- aztec.size = matrixSize;
- aztec.layers = layers;
- aztec.codeWords = messageSizeInWords;
- aztec.matrix = matrix;
- return aztec;
- }
-
- + (void)drawBullsEye:(ZXBitMatrix *)matrix center:(int)center size:(int)size {
- for (int i = 0; i < size; i += 2) {
- for (int j = center - i; j <= center + i; j++) {
- [matrix setX:j y:center - i];
- [matrix setX:j y:center + i];
- [matrix setX:center - i y:j];
- [matrix setX:center + i y:j];
- }
- }
- [matrix setX:center - size y:center - size];
- [matrix setX:center - size + 1 y:center - size];
- [matrix setX:center - size y:center - size + 1];
- [matrix setX:center + size y:center - size];
- [matrix setX:center + size y:center - size + 1];
- [matrix setX:center + size y:center + size - 1];
- }
-
- + (ZXBitArray *)generateModeMessageCompact:(BOOL)compact layers:(int)layers messageSizeInWords:(int)messageSizeInWords {
- ZXBitArray *modeMessage = [[ZXBitArray alloc] init];
- if (compact) {
- [modeMessage appendBits:layers - 1 numBits:2];
- [modeMessage appendBits:messageSizeInWords - 1 numBits:6];
- modeMessage = [self generateCheckWords:modeMessage totalBits:28 wordSize:4];
- } else {
- [modeMessage appendBits:layers - 1 numBits:5];
- [modeMessage appendBits:messageSizeInWords - 1 numBits:11];
- modeMessage = [self generateCheckWords:modeMessage totalBits:40 wordSize:4];
- }
- return modeMessage;
- }
-
- + (void)drawModeMessage:(ZXBitMatrix *)matrix compact:(BOOL)compact matrixSize:(int)matrixSize modeMessage:(ZXBitArray *)modeMessage {
- int center = matrixSize / 2;
- if (compact) {
- for (int i = 0; i < 7; i++) {
- int offset = center - 3 + i;
- if ([modeMessage get:i]) {
- [matrix setX:offset y:center - 5];
- }
- if ([modeMessage get:i + 7]) {
- [matrix setX:center + 5 y:offset];
- }
- if ([modeMessage get:20 - i]) {
- [matrix setX:offset y:center + 5];
- }
- if ([modeMessage get:27 - i]) {
- [matrix setX:center - 5 y:offset];
- }
- }
- } else {
- for (int i = 0; i < 10; i++) {
- int offset = center - 5 + i + i / 5;
- if ([modeMessage get:i]) {
- [matrix setX:offset y:center - 7];
- }
- if ([modeMessage get:i + 10]) {
- [matrix setX:center + 7 y:offset];
- }
- if ([modeMessage get:29 - i]) {
- [matrix setX:offset y:center + 7];
- }
- if ([modeMessage get:39 - i]) {
- [matrix setX:center - 7 y:offset];
- }
- }
- }
- }
-
- + (ZXBitArray *)generateCheckWords:(ZXBitArray *)bitArray totalBits:(int)totalBits wordSize:(int)wordSize {
- // bitArray is guaranteed to be a multiple of the wordSize, so no padding needed
- int messageSizeInWords = bitArray.size / wordSize;
- ZXReedSolomonEncoder *rs = [[ZXReedSolomonEncoder alloc] initWithField:[self getGF:wordSize]];
- int totalWords = totalBits / wordSize;
-
- ZXIntArray *messageWords = [self bitsToWords:bitArray wordSize:wordSize totalWords:totalWords];
- [rs encode:messageWords ecBytes:totalWords - messageSizeInWords];
- int startPad = totalBits % wordSize;
- ZXBitArray *messageBits = [[ZXBitArray alloc] init];
- [messageBits appendBits:0 numBits:startPad];
- for (int i = 0; i < totalWords; i++) {
- [messageBits appendBits:messageWords.array[i] numBits:wordSize];
- }
- return messageBits;
- }
-
- + (ZXIntArray *)bitsToWords:(ZXBitArray *)stuffedBits wordSize:(int)wordSize totalWords:(int)totalWords {
- ZXIntArray *message = [[ZXIntArray alloc] initWithLength:totalWords];
- int i;
- int n;
- for (i = 0, n = stuffedBits.size / wordSize; i < n; i++) {
- int32_t value = 0;
- for (int j = 0; j < wordSize; j++) {
- value |= [stuffedBits get:i * wordSize + j] ? (1 << (wordSize - j - 1)) : 0;
- }
- message.array[i] = value;
- }
- return message;
- }
-
- + (ZXGenericGF *)getGF:(int)wordSize {
- switch (wordSize) {
- case 4:
- return [ZXGenericGF AztecParam];
- case 6:
- return [ZXGenericGF AztecData6];
- case 8:
- return [ZXGenericGF AztecData8];
- case 10:
- return [ZXGenericGF AztecData10];
- case 12:
- return [ZXGenericGF AztecData12];
- default:
- return nil;
- }
- }
-
- + (ZXBitArray *)stuffBits:(ZXBitArray *)bits wordSize:(int)wordSize {
- ZXBitArray *arrayOut = [[ZXBitArray alloc] init];
-
- int n = bits.size;
- int mask = (1 << wordSize) - 2;
- for (int i = 0; i < n; i += wordSize) {
- int word = 0;
- for (int j = 0; j < wordSize; j++) {
- if (i + j >= n || [bits get:i + j]) {
- word |= 1 << (wordSize - 1 - j);
- }
- }
- if ((word & mask) == mask) {
- [arrayOut appendBits:word & mask numBits:wordSize];
- i--;
- } else if ((word & mask) == 0) {
- [arrayOut appendBits:word | 1 numBits:wordSize];
- i--;
- } else {
- [arrayOut appendBits:word numBits:wordSize];
- }
- }
-
- return arrayOut;
- }
-
- + (int)totalBitsInLayer:(int)layers compact:(BOOL)compact {
- return ((compact ? 88 : 112) + 16 * layers) * layers;
- }
-
- @end
|