#include "MatrixUtil.h"
#include "MaskUtil.h"
#include <zxing/WriterException.h>
#include "QRCode.h"
namespace zxing {
namespace qrcode {
const int MatrixUtil::POSITION_DETECTION_PATTERN[7][7] = {
{1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 1},
{1, 0, 1, 1, 1, 0, 1},
{1, 0, 1, 1, 1, 0, 1},
{1, 0, 1, 1, 1, 0, 1},
{1, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1},
};
const int MatrixUtil::POSITION_ADJUSTMENT_PATTERN[5][5] = {
{1, 1, 1, 1, 1},
{1, 0, 0, 0, 1},
{1, 0, 1, 0, 1},
{1, 0, 0, 0, 1},
{1, 1, 1, 1, 1},
};
const int MatrixUtil::POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[40][7] = {
{-1, -1, -1, -1, -1, -1, -1},
{ 6, 18, -1, -1, -1, -1, -1},
{ 6, 22, -1, -1, -1, -1, -1},
{ 6, 26, -1, -1, -1, -1, -1},
{ 6, 30, -1, -1, -1, -1, -1},
{ 6, 34, -1, -1, -1, -1, -1},
{ 6, 22, 38, -1, -1, -1, -1},
{ 6, 24, 42, -1, -1, -1, -1},
{ 6, 26, 46, -1, -1, -1, -1},
{ 6, 28, 50, -1, -1, -1, -1},
{ 6, 30, 54, -1, -1, -1, -1},
{ 6, 32, 58, -1, -1, -1, -1},
{ 6, 34, 62, -1, -1, -1, -1},
{ 6, 26, 46, 66, -1, -1, -1},
{ 6, 26, 48, 70, -1, -1, -1},
{ 6, 26, 50, 74, -1, -1, -1},
{ 6, 30, 54, 78, -1, -1, -1},
{ 6, 30, 56, 82, -1, -1, -1},
{ 6, 30, 58, 86, -1, -1, -1},
{ 6, 34, 62, 90, -1, -1, -1},
{ 6, 28, 50, 72, 94, -1, -1},
{ 6, 26, 50, 74, 98, -1, -1},
{ 6, 30, 54, 78, 102, -1, -1},
{ 6, 28, 54, 80, 106, -1, -1},
{ 6, 32, 58, 84, 110, -1, -1},
{ 6, 30, 58, 86, 114, -1, -1},
{ 6, 34, 62, 90, 118, -1, -1},
{ 6, 26, 50, 74, 98, 122, -1},
{ 6, 30, 54, 78, 102, 126, -1},
{ 6, 26, 52, 78, 104, 130, -1},
{ 6, 30, 56, 82, 108, 134, -1},
{ 6, 34, 60, 86, 112, 138, -1},
{ 6, 30, 58, 86, 114, 142, -1},
{ 6, 34, 62, 90, 118, 146, -1},
{ 6, 30, 54, 78, 102, 126, 150},
{ 6, 24, 50, 76, 102, 128, 154},
{ 6, 28, 54, 80, 106, 132, 158},
{ 6, 32, 58, 84, 110, 136, 162},
{ 6, 26, 54, 82, 110, 138, 166},
{ 6, 30, 58, 86, 114, 142, 170},
};
const int MatrixUtil::TYPE_INFO_COORDINATES[16][2] = {
{8, 0},
{8, 1},
{8, 2},
{8, 3},
{8, 4},
{8, 5},
{8, 7},
{8, 8},
{7, 8},
{5, 8},
{4, 8},
{3, 8},
{2, 8},
{1, 8},
{0, 8},
};
const int MatrixUtil::VERSION_INFO_POLY = 0x1f25;
const int MatrixUtil::TYPE_INFO_POLY = 0x537;
const int MatrixUtil::TYPE_INFO_MASK_PATTERN = 0x5412;
void MatrixUtil::buildMatrix(const BitArray& dataBits,
const ErrorCorrectionLevel& ecLevel,
Version& version,
int maskPattern,
ByteMatrix& matrix)
{
clearMatrix(matrix);
embedBasicPatterns(version, matrix);
embedTypeInfo(ecLevel, maskPattern, matrix);
maybeEmbedVersionInfo(version, matrix);
embedDataBits(dataBits, maskPattern, matrix);
}
void MatrixUtil::embedBasicPatterns(const Version& version, ByteMatrix& matrix)
{
embedPositionDetectionPatternsAndSeparators(matrix);
embedDarkDotAtLeftBottomCorner(matrix);
maybeEmbedPositionAdjustmentPatterns(version, matrix);
embedTimingPatterns(matrix);
}
void MatrixUtil::embedTypeInfo(const ErrorCorrectionLevel& ecLevel, int maskPattern, ByteMatrix& matrix)
{
BitArray typeInfoBits;
makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits);
for (int i = 0; i < typeInfoBits.getSize(); ++i) {
bool bit = typeInfoBits.get(typeInfoBits.getSize() - 1 - i);
int x1 = TYPE_INFO_COORDINATES[i][0];
int y1 = TYPE_INFO_COORDINATES[i][1];
matrix.set(x1, y1, bit);
if (i < 8) {
int x2 = matrix.getWidth() - i - 1;
int y2 = 8;
matrix.set(x2, y2, bit);
} else {
int x2 = 8;
int y2 = matrix.getHeight() - 7 + (i - 8);
matrix.set(x2, y2, bit);
}
}
}
void MatrixUtil::maybeEmbedVersionInfo(const Version& version, ByteMatrix& matrix)
{
if (version.getVersionNumber() < 7) {
return;
}
BitArray versionInfoBits;
makeVersionInfoBits(version, versionInfoBits);
int bitIndex = 6 * 3 - 1;
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 3; ++j) {
boolean bit = versionInfoBits.get(bitIndex);
bitIndex--;
matrix.set(i, matrix.getHeight() - 11 + j, bit);
matrix.set(matrix.getHeight() - 11 + j, i, bit);
}
}
}
void MatrixUtil::embedDataBits(const BitArray& dataBits, int maskPattern, ByteMatrix& matrix)
{
int bitIndex = 0;
int direction = -1;
int x = matrix.getWidth() - 1;
int y = matrix.getHeight() - 1;
while (x > 0) {
if (x == 6) {
x -= 1;
}
while (y >= 0 && y < matrix.getHeight()) {
for (int i = 0; i < 2; ++i) {
int xx = x - i;
if (!isEmpty(matrix.get(xx, y))) {
continue;
}
boolean bit;
if (bitIndex < dataBits.getSize()) {
bit = dataBits.get(bitIndex);
++bitIndex;
} else {
bit = false;
}
if (maskPattern != -1 && MaskUtil::getDataMaskBit(maskPattern, xx, y)) {
bit = !bit;
}
matrix.set(xx, y, bit);
}
y += direction;
}
direction = -direction;
y += direction;
x -= 2;
}
if (bitIndex != dataBits.getSize()) {
throw zxing::WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.getSize());
}
}
int MatrixUtil::findMSBSet(int value)
{
int sizeOfTypeBits = (sizeof(int)*8);
int sample = ( value < 0 ) ? 0 : value;
int leadingZeros = ( value < 0 ) ? 0 : sizeOfTypeBits;
while(sample) {
sample >>= 1;
--leadingZeros;
}
return sizeOfTypeBits - leadingZeros;
}
int MatrixUtil::calculateBCHCode(int value, int poly)
{
int msbSetInPoly = findMSBSet(poly);
value <<= msbSetInPoly - 1;
while (findMSBSet(value) >= msbSetInPoly) {
value ^= poly << (findMSBSet(value) - msbSetInPoly);
}
return value;
}
void MatrixUtil::makeTypeInfoBits(const ErrorCorrectionLevel& ecLevel, int maskPattern, BitArray& bits)
{
if (!QRCode::isValidMaskPattern(maskPattern)) {
throw WriterException("Invalid mask pattern");
}
int typeInfo = (ecLevel.bits() << 3) | maskPattern;
bits.appendBits(typeInfo, 5);
int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY);
bits.appendBits(bchCode, 10);
BitArray maskBits;
maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15);
bits.xor_(maskBits);
if (bits.getSize() != 15) {
throw WriterException("should not happen but we got: " + bits.getSize());
}
}
void MatrixUtil::makeVersionInfoBits(const Version& version, BitArray& bits)
{
bits.appendBits(version.getVersionNumber(), 6);
int bchCode = calculateBCHCode(version.getVersionNumber(), VERSION_INFO_POLY);
bits.appendBits(bchCode, 12);
if (bits.getSize() != 18) {
throw WriterException("should not happen but we got: " + bits.getSize());
}
}
void MatrixUtil::embedTimingPatterns(ByteMatrix& matrix)
{
for (size_t i = 8; i < matrix.getWidth() - 8; ++i) {
int bit = (i + 1) % 2;
if (isEmpty(matrix.get(i, 6))) {
matrix.set(i, 6, (byte)bit);
}
if (isEmpty(matrix.get(6, i))) {
matrix.set(6, i, (byte)bit);
}
}
}
void MatrixUtil::embedDarkDotAtLeftBottomCorner(ByteMatrix& matrix)
{
if (matrix.get(8, matrix.getHeight() - 8) == 0) {
throw WriterException();
}
matrix.set(8, matrix.getHeight() - 8, (byte)1);
}
void MatrixUtil::embedHorizontalSeparationPattern(int xStart,
int yStart,
ByteMatrix& matrix)
{
for (int x = 0; x < 8; ++x) {
if (!isEmpty(matrix.get(xStart + x, yStart))) {
throw WriterException();
}
matrix.set(xStart + x, yStart, (byte)0);
}
}
void MatrixUtil::embedVerticalSeparationPattern(int xStart,
int yStart,
ByteMatrix& matrix)
{
for (int y = 0; y < 7; ++y) {
if (!isEmpty(matrix.get(xStart, yStart + y))) {
throw WriterException();
}
matrix.set(xStart, yStart + y, (byte)0);
}
}
void MatrixUtil::embedPositionAdjustmentPattern(int xStart, int yStart, ByteMatrix& matrix)
{
for (int y = 0; y < 5; ++y) {
for (int x = 0; x < 5; ++x) {
matrix.set(xStart + x, yStart + y, (byte)POSITION_ADJUSTMENT_PATTERN[y][x]);
}
}
}
void MatrixUtil::embedPositionDetectionPattern(int xStart, int yStart, ByteMatrix& matrix)
{
for (int y = 0; y < 7; ++y) {
for (int x = 0; x < 7; ++x) {
matrix.set(xStart + x, yStart + y, (byte)POSITION_DETECTION_PATTERN[y][x]);
}
}
}
void MatrixUtil::embedPositionDetectionPatternsAndSeparators(ByteMatrix& matrix)
{
int pdpWidth = 7;
embedPositionDetectionPattern(0, 0, matrix);
embedPositionDetectionPattern(matrix.getWidth() - pdpWidth, 0, matrix);
embedPositionDetectionPattern(0, matrix.getWidth() - pdpWidth, matrix);
int hspWidth = 8;
embedHorizontalSeparationPattern(0, hspWidth - 1, matrix);
embedHorizontalSeparationPattern(matrix.getWidth() - hspWidth,
hspWidth - 1, matrix);
embedHorizontalSeparationPattern(0, matrix.getWidth() - hspWidth, matrix);
int vspSize = 7;
embedVerticalSeparationPattern(vspSize, 0, matrix);
embedVerticalSeparationPattern(matrix.getHeight() - vspSize - 1, 0, matrix);
embedVerticalSeparationPattern(vspSize, matrix.getHeight() - vspSize,
matrix);
}
void MatrixUtil::maybeEmbedPositionAdjustmentPatterns(const Version& version, ByteMatrix& matrix)
{
if (version.getVersionNumber() < 2) {
return;
}
int index = version.getVersionNumber() - 1;
const int *coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index];
int numCoordinates = 7;
for (int i = 0; i < numCoordinates; i++) {
int y = coordinates[i];
if(y < 0)
continue;
for (int j = 0; j < numCoordinates; j++) {
int x = coordinates[j];
if (x < 0)
continue;
if (isEmpty(matrix.get(x, y))) {
embedPositionAdjustmentPattern(x - 2, y - 2, matrix);
}
}
}
}
}
}