initial implementation of Qr Encoder. Not tested

This commit is contained in:
favoritas37 2015-06-19 01:56:26 +03:00
parent dbc47abff2
commit f6c5917c38
2 changed files with 244 additions and 230 deletions

View File

@ -23,6 +23,9 @@
#include "BlockPair.h"
#include <QList>
#include <math.h>
#include <limits>
#include "MatrixUtil.h"
#include <string>
namespace zxing {
namespace qrcode {
@ -38,7 +41,7 @@ const int Encoder::ALPHANUMERIC_TABLE[Encoder::ALPHANUMERIC_TABLE_SIZE] = {
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f
};
const QString DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1";
const QString Encoder::DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1";
int Encoder::calculateMaskPenalty(const ByteMatrix& matrix)
{
@ -48,97 +51,98 @@ int Encoder::calculateMaskPenalty(const ByteMatrix& matrix)
+ MaskUtil::applyMaskPenaltyRule4(matrix);
}
QRCode* Encoder::encode(const QString& content, const ErrorCorrectionLevel *ecLevel)
Ref<QRCode> Encoder::encode(const QString& content, Ref<ErrorCorrectionLevel> ecLevel)
{
return encode(content, ecLevel, NULL);
}
QRCode* Encoder::encode(const QString& content, const ErrorCorrectionLevel* ecLevel, const EncodeHint* hints)
Ref<QRCode> Encoder::encode(const QString& content, Ref<ErrorCorrectionLevel> ecLevel, const EncodeHint* hints)
{
// // Determine what character encoding has been specified by the caller, if any
// QString encoding = hints == NULL ? "" : hints->get_character_set();
// if (encoding == "")
// encoding = DEFAULT_BYTE_MODE_ENCODING;
// Determine what character encoding has been specified by the caller, if any
QString encoding = hints == NULL ? "" : QString(hints->getCharacterSet().c_str());
if (encoding == "")
encoding = DEFAULT_BYTE_MODE_ENCODING;
// // Pick an encoding mode appropriate for the content. Note that this will not attempt to use
// // multiple modes / segments even if that were more efficient. Twould be nice.
// Mode* mode = chooseMode(content, encoding);
// Pick an encoding mode appropriate for the content. Note that this will not attempt to use
// multiple modes / segments even if that were more efficient. Twould be nice.
Mode* mode = chooseMode(content, encoding);
// // This will store the header information, like mode and
// // length, as well as "header" segments like an ECI segment.
// BitArray headerBits;
// This will store the header information, like mode and
// length, as well as "header" segments like an ECI segment.
BitArray headerBits;
// // Append ECI segment if applicable
// if (mode == &Mode::BYTE && DEFAULT_BYTE_MODE_ENCODING != encoding) {
// zxing::common::CharacterSetECI* eci = zxing::common::CharacterSetECI::getCharacterSetECIByName(encoding);
// if (eci != NULL) {
// appendECI(*eci, headerBits);
// }
// }
// Append ECI segment if applicable
if (mode == &Mode::BYTE && DEFAULT_BYTE_MODE_ENCODING != encoding) {
zxing::common::CharacterSetECI* eci =
zxing::common::CharacterSetECI::getCharacterSetECIByName(encoding.toStdString().c_str());
if (eci != NULL) {
appendECI(*eci, headerBits);
}
}
// // (With ECI in place,) Write the mode marker
// appendModeInfo(*mode, headerBits);
// (With ECI in place,) Write the mode marker
appendModeInfo(*mode, headerBits);
// // Collect data within the main segment, separately, to count its size if needed. Don't add it to
// // main payload yet.
// BitArray dataBits;
// appendBytes(content, *mode, dataBits, encoding);
// Collect data within the main segment, separately, to count its size if needed. Don't add it to
// main payload yet.
BitArray dataBits;
appendBytes(content, *mode, dataBits, encoding);
// // Hard part: need to know version to know how many bits length takes. But need to know how many
// // bits it takes to know version. First we take a guess at version by assuming version will be
// // the minimum, 1:
// Hard part: need to know version to know how many bits length takes. But need to know how many
// bits it takes to know version. First we take a guess at version by assuming version will be
// the minimum, 1:
// int provisionalBitsNeeded = headerBits.getSize()
// + mode.getCharacterCountBits(Version.getVersionForNumber(1))
// + dataBits.getSize();
// Version provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel);
int provisionalBitsNeeded = headerBits.getSize()
+ mode->getCharacterCountBits(Version::getVersionForNumber(1))
+ dataBits.getSize();
Ref<Version> provisionalVersion = chooseVersion(provisionalBitsNeeded, *ecLevel);
// // Use that guess to calculate the right version. I am still not sure this works in 100% of cases.
// Use that guess to calculate the right version. I am still not sure this works in 100% of cases.
// int bitsNeeded = headerBits.getSize()
// + mode.getCharacterCountBits(provisionalVersion)
// + dataBits.getSize();
// Version version = chooseVersion(bitsNeeded, ecLevel);
int bitsNeeded = headerBits.getSize()
+ mode->getCharacterCountBits(provisionalVersion)
+ dataBits.getSize();
Ref<Version> version = chooseVersion(bitsNeeded, *ecLevel);
// BitArray headerAndDataBits = new BitArray();
// headerAndDataBits.appendBitArray(headerBits);
// // Find "length" of main segment and write it
// int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length();
// appendLengthInfo(numLetters, version, mode, headerAndDataBits);
// // Put data together into the overall payload
// headerAndDataBits.appendBitArray(dataBits);
BitArray headerAndDataBits;
headerAndDataBits.appendBitArray(headerBits);
// Find "length" of main segment and write it
int numLetters = (*mode == Mode::BYTE) ? dataBits.getSize() : content.length();
appendLengthInfo(numLetters, *version, *mode, headerAndDataBits);
// Put data together into the overall payload
headerAndDataBits.appendBitArray(dataBits);
// Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
// int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords();
zxing::qrcode::ECBlocks ecBlocks = version->getECBlocksForLevel(*ecLevel);
int numDataBytes = version->getTotalCodewords() - ecBlocks.getTotalECCodewords();
// // Terminate the bits properly.
// terminateBits(numDataBytes, headerAndDataBits);
// Terminate the bits properly.
terminateBits(numDataBytes, headerAndDataBits);
// // Interleave data bits with error correction code.
// BitArray finalBits = interleaveWithECBytes(headerAndDataBits,
// version.getTotalCodewords(),
// numDataBytes,
// ecBlocks.getNumBlocks());
// Interleave data bits with error correction code.
Ref<BitArray> finalBits(interleaveWithECBytes(headerAndDataBits,
version->getTotalCodewords(),
numDataBytes,
1));//ecBlocks->getNumBlocks());
// QRCode qrCode = new QRCode();
Ref<QRCode> qrCode(new QRCode);
// qrCode.setECLevel(ecLevel);
// qrCode.setMode(mode);
// qrCode.setVersion(version);
qrCode->setECLevel(ecLevel);
qrCode->setMode(Ref<Mode>(mode));
qrCode->setVersion(version);
// // Choose the mask pattern and set to "qrCode".
// int dimension = version.getDimensionForVersion();
// ByteMatrix matrix = new ByteMatrix(dimension, dimension);
// int maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix);
// qrCode.setMaskPattern(maskPattern);
// Choose the mask pattern and set to "qrCode".
int dimension = version->getDimensionForVersion();
Ref<ByteMatrix> matrix(new ByteMatrix(dimension, dimension));
int maskPattern = chooseMaskPattern(finalBits, *ecLevel, version, matrix);
qrCode->setMaskPattern(maskPattern);
// // Build the matrix and set it to "qrCode".
// MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix);
// qrCode.setMatrix(matrix);
// Build the matrix and set it to "qrCode".
MatrixUtil::buildMatrix(*finalBits, *ecLevel, *version, maskPattern, *matrix);
qrCode->setMatrix(matrix);
// return qrCode;
return qrCode;
return NULL;
//return NULL;
}
/**
@ -209,35 +213,36 @@ Mode* Encoder::chooseMode(const QString& content, const QString& encoding)
// return true;
//}
// private static int chooseMaskPattern(BitArray bits,
// ErrorCorrectionLevel ecLevel,
// Version version,
// ByteMatrix matrix) throws WriterException {
int Encoder::chooseMaskPattern(Ref<BitArray> bits,
ErrorCorrectionLevel ecLevel,
Ref<Version> version,
Ref<ByteMatrix> matrix)
{
// int minPenalty = Integer.MAX_VALUE; // Lower penalty is better.
// int bestMaskPattern = -1;
// // We try all mask patterns to choose the best one.
// for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) {
// MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix);
// int penalty = calculateMaskPenalty(matrix);
// if (penalty < minPenalty) {
// minPenalty = penalty;
// bestMaskPattern = maskPattern;
// }
// }
// return bestMaskPattern;
// }
int minPenalty = std::numeric_limits<int>::max(); // Lower penalty is better.
int bestMaskPattern = -1;
// We try all mask patterns to choose the best one.
for (int maskPattern = 0; maskPattern < QRCode::NUM_MASK_PATTERNS; maskPattern++) {
MatrixUtil::buildMatrix(*bits, ecLevel, *version, maskPattern, *matrix);
int penalty = calculateMaskPenalty(*matrix);
if (penalty < minPenalty) {
minPenalty = penalty;
bestMaskPattern = maskPattern;
}
}
return bestMaskPattern;
}
Version Encoder::chooseVersion(int numInputBits, ErrorCorrectionLevel &ecLevel)
Ref<Version> Encoder::chooseVersion(int numInputBits, const ErrorCorrectionLevel &ecLevel)
{
// In the following comments, we use numbers of Version 7-H.
for (int versionNum = 1; versionNum <= 40; versionNum++) {
Version& version = *Version::getVersionForNumber(versionNum);
Ref<Version> version = Version::getVersionForNumber(versionNum);
// numBytes = 196
int numBytes = version.getTotalCodewords();
int numBytes = version->getTotalCodewords();
// getNumECBytes = 130
ECBlocks& ecBlocks = version.getECBlocksForLevel(ecLevel);
int numEcBytes = ecBlocks.getECCodewords();
ECBlocks& ecBlocks = version->getECBlocksForLevel(ecLevel);
int numEcBytes = ecBlocks.getTotalECCodewords();
// getNumDataBytes = 196 - 130 = 66
int numDataBytes = numBytes - numEcBytes;
int totalInputBytes = (numInputBits + 7) / 8;
@ -357,9 +362,8 @@ BitArray* Encoder::interleaveWithECBytes(const BitArray& bits,
{
// "bits" must have "getNumDataBytes" bytes of data.
if (bits.getSize() != numDataBytes) {
if (bits.getSize() != numDataBytes)
throw new WriterException("Number of bits and data bytes does not match");
}
// Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll
// store the divided data bytes blocks and error correction bytes blocks into "blocks".
@ -441,142 +445,152 @@ ArrayRef<char> Encoder::generateECBytes(const std::vector<char>& dataBytes, int
return ecBytes;
}
// /**
// * Append mode info. On success, store the result in "bits".
// */
// static void appendModeInfo(Mode mode, BitArray bits) {
// bits.appendBits(mode.getBits(), 4);
// }
/**
* Append mode info. On success, store the result in "bits".
*/
void Encoder::appendModeInfo(const Mode& mode, BitArray& bits)
{
bits.appendBits(mode.getBits(), 4);
}
// /**
// * Append length info. On success, store the result in "bits".
// */
// static void appendLengthInfo(int numLetters, Version version, Mode mode, BitArray bits) throws WriterException {
// int numBits = mode.getCharacterCountBits(version);
// if (numLetters >= (1 << numBits)) {
// throw new WriterException(numLetters + " is bigger than " + ((1 << numBits) - 1));
// }
// bits.appendBits(numLetters, numBits);
// }
// /**
// * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
// */
// static void appendBytes(String content,
// Mode mode,
// BitArray bits,
// String encoding) throws WriterException {
// switch (mode) {
// case NUMERIC:
// appendNumericBytes(content, bits);
// break;
// case ALPHANUMERIC:
// appendAlphanumericBytes(content, bits);
// break;
// case BYTE:
// append8BitBytes(content, bits, encoding);
// break;
// case KANJI:
// appendKanjiBytes(content, bits);
// break;
// default:
// throw new WriterException("Invalid mode: " + mode);
// }
// }
/**
* Append length info. On success, store the result in "bits".
*/
void Encoder::appendLengthInfo(int numLetters, const Version& version, const Mode& mode, BitArray& bits)
{
int numBits = mode.getCharacterCountBits(&version);
if (numLetters >= (1 << numBits)) {
QString message = QString::number(numLetters);
message += " is bigger than ";
message += QString::number((1 << numBits) - 1);
// static void appendNumericBytes(CharSequence content, BitArray bits) {
// int length = content.length();
// int i = 0;
// while (i < length) {
// int num1 = content.charAt(i) - '0';
// if (i + 2 < length) {
// // Encode three numeric letters in ten bits.
// int num2 = content.charAt(i + 1) - '0';
// int num3 = content.charAt(i + 2) - '0';
// bits.appendBits(num1 * 100 + num2 * 10 + num3, 10);
// i += 3;
// } else if (i + 1 < length) {
// // Encode two numeric letters in seven bits.
// int num2 = content.charAt(i + 1) - '0';
// bits.appendBits(num1 * 10 + num2, 7);
// i += 2;
// } else {
// // Encode one numeric letter in four bits.
// bits.appendBits(num1, 4);
// i++;
// }
// }
// }
throw new WriterException(message.toStdString().c_str());
}
bits.appendBits(numLetters, numBits);
}
// static void appendAlphanumericBytes(CharSequence content, BitArray bits) throws WriterException {
// int length = content.length();
// int i = 0;
// while (i < length) {
// int code1 = getAlphanumericCode(content.charAt(i));
// if (code1 == -1) {
// throw new WriterException();
// }
// if (i + 1 < length) {
// int code2 = getAlphanumericCode(content.charAt(i + 1));
// if (code2 == -1) {
// throw new WriterException();
// }
// // Encode two alphanumeric letters in 11 bits.
// bits.appendBits(code1 * 45 + code2, 11);
// i += 2;
// } else {
// // Encode one alphanumeric letter in six bits.
// bits.appendBits(code1, 6);
// i++;
// }
// }
// }
/**
* Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
*/
void Encoder::appendBytes(const QString& content,
Mode& mode,
BitArray& bits,
const QString& encoding)
{
if (mode == Mode::NUMERIC)
appendNumericBytes(content, bits);
else if (mode == Mode::ALPHANUMERIC)
appendAlphanumericBytes(content, bits);
else if (mode == Mode::BYTE)
append8BitBytes(content, bits, encoding);
else if (mode == Mode::KANJI)
appendKanjiBytes(content, bits);
else {
QString message("Invalid mode: ");
message += QString::fromStdString(mode.getName());
throw new WriterException(message.toStdString().c_str());
}
}
// static void append8BitBytes(String content, BitArray bits, String encoding)
// throws WriterException {
// byte[] bytes;
// try {
// bytes = content.getBytes(encoding);
// } catch (UnsupportedEncodingException uee) {
// throw new WriterException(uee);
// }
// for (byte b : bytes) {
// bits.appendBits(b, 8);
// }
// }
void Encoder::appendNumericBytes( const QString& content, BitArray& bits)
{
int length = content.size();
int i = 0;
while (i < length) {
int num1 = content.at(i).toLatin1() - '0';
if (i + 2 < length) {
// Encode three numeric letters in ten bits.
int num2 = content.at(i + 1).toLatin1() - '0';
int num3 = content.at(i + 2).toLatin1() - '0';
bits.appendBits(num1 * 100 + num2 * 10 + num3, 10);
i += 3;
} else if (i + 1 < length) {
// Encode two numeric letters in seven bits.
int num2 = content.at(i + 1).toLatin1() - '0';
bits.appendBits(num1 * 10 + num2, 7);
i += 2;
} else {
// Encode one numeric letter in four bits.
bits.appendBits(num1, 4);
i++;
}
}
}
// static void appendKanjiBytes(String content, BitArray bits) throws WriterException {
// byte[] bytes;
// try {
// bytes = content.getBytes("Shift_JIS");
// } catch (UnsupportedEncodingException uee) {
// throw new WriterException(uee);
// }
// int length = bytes.length;
// for (int i = 0; i < length; i += 2) {
// int byte1 = bytes[i] & 0xFF;
// int byte2 = bytes[i + 1] & 0xFF;
// int code = (byte1 << 8) | byte2;
// int subtracted = -1;
// if (code >= 0x8140 && code <= 0x9ffc) {
// subtracted = code - 0x8140;
// } else if (code >= 0xe040 && code <= 0xebbf) {
// subtracted = code - 0xc140;
// }
// if (subtracted == -1) {
// throw new WriterException("Invalid byte sequence");
// }
// int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
// bits.appendBits(encoded, 13);
// }
// }
void Encoder::appendAlphanumericBytes(const QString& content, BitArray& bits)
{
int length = content.length();
int i = 0;
while (i < length) {
int code1 = getAlphanumericCode(content.at(i).toLatin1());
if (code1 == -1) {
throw new WriterException();
}
if (i + 1 < length) {
int code2 = getAlphanumericCode(content.at(i + 1).toLatin1());
if (code2 == -1) {
throw new WriterException();
}
// Encode two alphanumeric letters in 11 bits.
bits.appendBits(code1 * 45 + code2, 11);
i += 2;
} else {
// Encode one alphanumeric letter in six bits.
bits.appendBits(code1, 6);
i++;
}
}
}
// private static void appendECI(CharacterSetECI eci, BitArray bits) {
// bits.appendBits(Mode.ECI.getBits(), 4);
// // This is correct for values up to 127, which is all we need now.
// bits.appendBits(eci.getValue(), 8);
// }
void Encoder::append8BitBytes(const QString& content, BitArray& bits, const QString& /*encoding*/)
{
// For now we will suppose that all the encoding has been handled by QString class.
// byte[] bytes;
// try {
// bytes = content.getBytes(encoding);
// } catch (UnsupportedEncodingException uee) {
// throw new WriterException(uee);
// }
for (int i=0; i<content.size(); ++i) {
bits.appendBits(content.at(i).toLatin1(), 8);
}
}
void Encoder::appendKanjiBytes(const QString& content, BitArray& bits)
{
// For now we will suppose that all the encoding has been handled by QString class.
// try {
// bytes = content.getBytes("Shift_JIS");
// } catch (UnsupportedEncodingException uee) {
// throw new WriterException(uee);
// }
int length = content.size();
for (int i = 0; i < length; i += 2) {
int byte1 = content.at(i).toLatin1() & 0xFF;
int byte2 = content.at(i + 1).toLatin1() & 0xFF;
int code = (byte1 << 8) | byte2;
int subtracted = -1;
if (code >= 0x8140 && code <= 0x9ffc) {
subtracted = code - 0x8140;
} else if (code >= 0xe040 && code <= 0xebbf) {
subtracted = code - 0xc140;
}
if (subtracted == -1) {
throw new WriterException("Invalid byte sequence");
}
int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
bits.appendBits(encoded, 13);
}
}
void Encoder::appendECI(const zxing::common::CharacterSetECI& eci, BitArray& bits) {
bits.appendBits(Mode::ECI.getBits(), 4);
// This is correct for values up to 127, which is all we need now.
bits.appendBits(eci.getValue(), 8);
}
}
}

View File

@ -38,9 +38,9 @@ private:
* with which clients can specify the encoding mode. For now, we don't need the functionality.
*/
public:
static QRCode* encode(const QString& content, const ErrorCorrectionLevel* ecLevel);
static Ref<QRCode> encode(const QString& content, Ref<ErrorCorrectionLevel> ecLevel);
static QRCode* encode(const QString& content, const ErrorCorrectionLevel* ecLevel, const EncodeHint* hints);
static Ref<QRCode> encode(const QString& content, Ref<ErrorCorrectionLevel> ecLevel, const EncodeHint* hints);
/**
* @return the code point of the table used in alphanumeric mode or
@ -59,12 +59,12 @@ private:
//static bool isOnlyDoubleByteKanji(const QString& content);
static int chooseMaskPattern(BitArray bits,
static int chooseMaskPattern(Ref<BitArray> bits,
ErrorCorrectionLevel ecLevel,
Version version,
ByteMatrix matrix);
Ref<Version> version,
Ref<ByteMatrix> matrix);
static Version chooseVersion(int numInputBits, ErrorCorrectionLevel &ecLevel) ;
static Ref<Version> chooseVersion(int numInputBits, const ErrorCorrectionLevel &ecLevel) ;
/**
* Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
@ -98,7 +98,7 @@ protected:
/**
* Append mode info. On success, store the result in "bits".
*/
static void appendModeInfo(const Mode& mode, BitArray& bits);
static void appendModeInfo(const Mode& mode, BitArray &bits);
/**
@ -110,13 +110,13 @@ protected:
* Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
*/
static void appendBytes(const QString& content,
const Mode& mode,
Mode& mode,
BitArray& bits,
const QString& encoding);
static void appendNumericBytes( const std::vector<char>& content, BitArray& bits);
static void appendNumericBytes(const QString& content, BitArray& bits);
static void appendAlphanumericBytes(const std::vector<char>& content, BitArray& bits);
static void appendAlphanumericBytes(const QString& content, BitArray& bits);
static void append8BitBytes(const QString& content, BitArray& bits, const QString& encoding);