blob: 3e3a5ffabbe722cb290651425ffe122b5ec9f96f [file] [log] [blame]
/*
* BitMatrixParser.cpp
* zxing
*
* Created by Christian Brunschen on 20/05/2008.
* Copyright 2008 ZXing authors All rights reserved.
*
* 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.
*/
#include <zxing/qrcode/decoder/BitMatrixParser.h>
#include <zxing/qrcode/decoder/DataMask.h>
namespace zxing {
namespace qrcode {
int BitMatrixParser::copyBit(size_t x, size_t y, int versionBits) {
return bitMatrix_->get(x, y) ? (versionBits << 1) | 0x1 : versionBits << 1;
}
BitMatrixParser::BitMatrixParser(Ref<BitMatrix> bitMatrix) :
bitMatrix_(bitMatrix), parsedVersion_(0), parsedFormatInfo_() {
size_t dimension = bitMatrix->getDimension();
if ((dimension < 21) || (dimension & 0x03) != 1) {
throw ReaderException("Dimension must be 1 mod 4 and >= 21");
}
}
Ref<FormatInformation> BitMatrixParser::readFormatInformation() {
if (parsedFormatInfo_ != 0) {
return parsedFormatInfo_;
}
// Read top-left format info bits
int formatInfoBits = 0;
for (int x = 0; x < 6; x++) {
formatInfoBits = copyBit(x, 8, formatInfoBits);
}
// .. and skip a bit in the timing pattern ...
formatInfoBits = copyBit(7, 8, formatInfoBits);
formatInfoBits = copyBit(8, 8, formatInfoBits);
formatInfoBits = copyBit(8, 7, formatInfoBits);
// .. and skip a bit in the timing pattern ...
for (int y = 5; y >= 0; y--) {
formatInfoBits = copyBit(8, y, formatInfoBits);
}
parsedFormatInfo_ = FormatInformation::decodeFormatInformation(formatInfoBits);
if (parsedFormatInfo_ != 0) {
return parsedFormatInfo_;
}
// Hmm, failed. Try the top-right/bottom-left pattern
int dimension = bitMatrix_->getDimension();
formatInfoBits = 0;
int yMin = dimension - 8;
for (int y = dimension - 1; y >= yMin; y--) {
formatInfoBits = copyBit(8, y, formatInfoBits);
}
for (int x = dimension - 7; x < dimension; x++) {
formatInfoBits = copyBit(x, 8, formatInfoBits);
}
parsedFormatInfo_ = FormatInformation::decodeFormatInformation(formatInfoBits);
if (parsedFormatInfo_ != 0) {
return parsedFormatInfo_;
}
throw ReaderException("Could not decode format information");
}
Version *BitMatrixParser::readVersion() {
if (parsedVersion_ != 0) {
return parsedVersion_;
}
int dimension = bitMatrix_->getDimension();
int provisionalVersion = (dimension - 17) >> 2;
if (provisionalVersion <= 6) {
return Version::getVersionForNumber(provisionalVersion);
}
// Read top-right version info: 3 wide by 6 tall
int versionBits = 0;
for (int y = 5; y >= 0; y--) {
int xMin = dimension - 11;
for (int x = dimension - 9; x >= xMin; x--) {
versionBits = copyBit(x, y, versionBits);
}
}
parsedVersion_ = Version::decodeVersionInformation(versionBits);
if (parsedVersion_ != 0 && parsedVersion_->getDimensionForVersion() == dimension) {
return parsedVersion_;
}
// Hmm, failed. Try bottom left: 6 wide by 3 tall
versionBits = 0;
for (int x = 5; x >= 0; x--) {
int yMin = dimension - 11;
for (int y = dimension - 9; y >= yMin; y--) {
versionBits = copyBit(x, y, versionBits);
}
}
parsedVersion_ = Version::decodeVersionInformation(versionBits);
if (parsedVersion_ != 0 && parsedVersion_->getDimensionForVersion() == dimension) {
return parsedVersion_;
}
throw ReaderException("Could not decode version");
}
ArrayRef<unsigned char> BitMatrixParser::readCodewords() {
Ref<FormatInformation> formatInfo = readFormatInformation();
Version *version = readVersion();
// cerr << *bitMatrix_ << endl;
// cerr << bitMatrix_->getDimension() << endl;
// 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.
DataMask &dataMask = DataMask::forReference((int)formatInfo->getDataMask());
// cout << (int)formatInfo->getDataMask() << endl;
int dimension = bitMatrix_->getDimension();
dataMask.unmaskBitMatrix(*bitMatrix_, dimension);
// cerr << *bitMatrix_ << endl;
// cerr << version->getTotalCodewords() << endl;
Ref<BitMatrix> functionPattern = version->buildFunctionPattern();
// cout << *functionPattern << endl;
bool readingUp = true;
ArrayRef<unsigned char> result(version->getTotalCodewords());
int resultOffset = 0;
int currentByte = 0;
int bitsRead = 0;
// Read columns in pairs, from right to left
for (int x = dimension - 1; x > 0; x -= 2) {
if (x == 6) {
// Skip whole column with vertical alignment pattern;
// saves time and makes the other code proceed more cleanly
x--;
}
// Read alternatingly from bottom to top then top to bottom
for (int counter = 0; counter < dimension; counter++) {
int y = readingUp ? dimension - 1 - counter : counter;
for (int col = 0; col < 2; col++) {
// Ignore bits covered by the function pattern
if (!functionPattern->get(x - col, y)) {
// Read a bit
bitsRead++;
currentByte <<= 1;
if (bitMatrix_->get(x - col, y)) {
currentByte |= 1;
}
// If we've made a whole byte, save it off
if (bitsRead == 8) {
result[resultOffset++] = (unsigned char)currentByte;
bitsRead = 0;
currentByte = 0;
}
}
}
}
readingUp = !readingUp; // switch directions
}
if (resultOffset != version->getTotalCodewords()) {
throw ReaderException("Did not read all codewords");
}
return result;
}
}
}